@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,10 +1,14 @@
1
1
  import { useState } from "react";
2
+ import { Plus } from "lucide-react";
2
3
  import { useNavigate } from "react-router-dom";
3
4
  import { Tasks } from "../../lib/api";
4
5
  import type { TaskStatus } from "../../types/daemon";
5
6
  import { Section } from "../../components/Section";
6
7
  import { PagedList, usePagedQuery } from "../../components/Pager";
7
- import { Badge, Button, Empty, Loading } from "../../components/ui";
8
+ import { Badge, Button, Dialog, Empty, Field, FilterChips, Input, Loading } from "../../components/ui";
9
+ import { UiSelect } from "../../components/UiSelect";
10
+ import { useProjects } from "../../hooks/useProjects";
11
+ import { useToast } from "../../components/Toast";
8
12
  import { t } from "../../i18n";
9
13
 
10
14
  // All projects' tasks, aggregated (GET /tasks), server-paginated.
@@ -15,38 +19,79 @@ export function GlobalTasksTab() {
15
19
  // right now" is not "what is open". Only meaningful for open tasks.
16
20
  const [status, setStatus] = useState<TaskStatus | "">("");
17
21
  const effectiveStatus = state === "open" ? status : "";
22
+ // The per-project Tasks screen has always had an inline add form; this one
23
+ // did not, so on the base workspace the only way to file a task was to ask
24
+ // the agent. A picker is the price of the cross-project view.
25
+ const [adding, setAdding] = useState(false);
26
+ const [title, setTitle] = useState("");
27
+ const [target, setTarget] = useState("");
28
+ const [due, setDue] = useState("");
29
+ const [busy, setBusy] = useState(false);
30
+ const { projects } = useProjects();
31
+ const toast = useToast();
32
+
18
33
  const paged = usePagedQuery({
19
- key: `/tasks?state=${state}&status=${effectiveStatus}`,
34
+ key: `/api/tasks?state=${state}&status=${effectiveStatus}`,
20
35
  fetchPage: (limit, offset) => Tasks.globalPage({ state, limit, offset, status: effectiveStatus }),
21
36
  resetKey: `${state}|${effectiveStatus}`,
22
37
  });
23
38
 
39
+ const create = async () => {
40
+ if (!title.trim() || !target) return;
41
+ setBusy(true);
42
+ try {
43
+ await Tasks.add(String(target), { title: title.trim(), due: due || null });
44
+ await paged.mutate();
45
+ setAdding(false);
46
+ setTitle(""); setDue("");
47
+ } catch (e) {
48
+ toast.error((e as Error).message);
49
+ } finally {
50
+ setBusy(false);
51
+ }
52
+ };
53
+
24
54
  return (
25
55
  <Section
26
56
  fullHeight
27
57
  title={t("project.global_tasks.title")}
28
58
  description={t("project.global_tasks.subtitle")}
29
59
  action={
30
- <div className="flex gap-1">
31
- {(["open", "done", "dropped", "all"] as const).map((s) => (
32
- <Button key={s} size="sm" variant={state === s ? "primary" : "ghost"} onClick={() => setState(s)}>{s}</Button>
33
- ))}
34
- </div>
60
+ <Button
61
+ size="sm"
62
+ variant="primary"
63
+ onClick={() => { setTarget(String(projects[0]?.id ?? "")); setAdding(true); }}
64
+ >
65
+ <Plus size={14} /> {t("project.global_tasks.add")}
66
+ </Button>
67
+ }
68
+ filters={
69
+ <>
70
+ <FilterChips
71
+ value={state}
72
+ onChange={setState}
73
+ label={t("project.global_tasks.title")}
74
+ options={(["open", "done", "dropped", "all"] as const).map((s) => ({ value: s, label: s }))}
75
+ />
76
+ {state === "open" ? (
77
+ <>
78
+ <span className="mx-1 h-4 w-px bg-border" aria-hidden />
79
+ <FilterChips
80
+ value={status}
81
+ onChange={setStatus}
82
+ label={t("project.global_tasks.any_status")}
83
+ options={[
84
+ { value: "" as const, label: t("project.global_tasks.any_status") },
85
+ ...(["pending", "running", "in_review", "blocked"] as const).map((s) => ({
86
+ value: s, label: s.replace("_", " "),
87
+ })),
88
+ ]}
89
+ />
90
+ </>
91
+ ) : null}
92
+ </>
35
93
  }
36
94
  >
37
- {state === "open" ? (
38
- <div className="mb-3 flex flex-wrap gap-1">
39
- <Button size="sm" variant={status === "" ? "primary" : "ghost"} onClick={() => setStatus("")}>
40
- {t("project.global_tasks.any_status")}
41
- </Button>
42
- {(["pending", "running", "in_review", "blocked"] as const).map((s) => (
43
- <Button key={s} size="sm" variant={status === s ? "primary" : "ghost"} onClick={() => setStatus(s)}>
44
- {s.replace("_", " ")}
45
- </Button>
46
- ))}
47
- </div>
48
- ) : null}
49
-
50
95
  {paged.isLoading && <Loading />}
51
96
  {!paged.isLoading && paged.total === 0 && <Empty>{t("project.global_tasks.empty")}</Empty>}
52
97
  <PagedList paged={paged} fullHeight>
@@ -73,6 +118,44 @@ export function GlobalTasksTab() {
73
118
  ))}
74
119
  </ul>
75
120
  </PagedList>
121
+
122
+ <Dialog
123
+ open={adding}
124
+ onClose={() => setAdding(false)}
125
+ title={t("project.global_tasks.add_title")}
126
+ footer={
127
+ <>
128
+ <Button onClick={() => setAdding(false)}>{t("common.cancel")}</Button>
129
+ <Button variant="primary" loading={busy} disabled={!title.trim() || !target} onClick={create}>
130
+ {t("project.global_tasks.add")}
131
+ </Button>
132
+ </>
133
+ }
134
+ >
135
+ <div className="flex flex-col gap-3 p-5">
136
+ <Field label={t("project.global_tasks.field_title")}>
137
+ <Input
138
+ value={title}
139
+ onChange={(e) => setTitle(e.target.value)}
140
+ autoFocus
141
+ onKeyDown={(e) => { if (e.key === "Enter" && title.trim() && target) create(); }}
142
+ />
143
+ </Field>
144
+ {/* Required, unlike the per-project screen: a cross-project view has
145
+ no "here" to default to, and guessing one would file the task
146
+ somewhere the user did not look. */}
147
+ <Field label={t("project.global_tasks.field_project")}>
148
+ <UiSelect
149
+ value={String(target)}
150
+ onChange={setTarget}
151
+ options={projects.map((p) => ({ value: String(p.id), label: p.name || p.path }))}
152
+ />
153
+ </Field>
154
+ <Field label={t("project.global_tasks.field_due")}>
155
+ <Input type="date" value={due} onChange={(e) => setDue(e.target.value)} />
156
+ </Field>
157
+ </div>
158
+ </Dialog>
76
159
  </Section>
77
160
  );
78
161
  }
@@ -23,6 +23,16 @@ function actorLabel(m: MessageEntry): string {
23
23
  return m.agent_slug || m.actor_id || m.author || m.actor_kind || "—";
24
24
  }
25
25
 
26
+ /** Structured lifecycle events APX records as they happen (a routine created,
27
+ * a routine updated). Returns the label to badge, or null for ordinary rows. */
28
+ function lifecycleEvent(m: MessageEntry): string | null {
29
+ const e = m.meta?.event;
30
+ if (typeof e !== "string") return null;
31
+ if (e === "routine_created") return t("logs.event_routine_created");
32
+ if (e === "routine_updated") return t("logs.event_routine_updated");
33
+ return null;
34
+ }
35
+
26
36
  /** Model that produced this record — persisted in meta by every channel that
27
37
  * runs an agent turn. Absent on user/system rows and on pre-1.74 history. */
28
38
  function modelOf(m: MessageEntry): string | null {
@@ -56,6 +66,11 @@ function LogRow({ m }: { m: MessageEntry }) {
56
66
  <span className="font-mono">{fmtTs(m.ts)}</span>
57
67
  <Badge tone="info">{m.channel}</Badge>
58
68
  {m.type && <Badge>{m.type}</Badge>}
69
+ {/* Lifecycle events get their own chip rather than blending into the
70
+ body text. "A routine now exists" is the kind of thing people
71
+ scroll back looking for, and a green badge is findable in a way a
72
+ sentence in a log line is not. */}
73
+ {lifecycleEvent(m) && <Badge tone="success">{lifecycleEvent(m)}</Badge>}
59
74
  <span className="font-medium text-foreground">{actorLabel(m)}</span>
60
75
  {model && <span className="font-mono text-[11px] text-sky-400/90">{model}</span>}
61
76
  {tokens !== null && <span className="font-mono text-[11px]">{tokens} tok</span>}
@@ -79,7 +94,7 @@ function LogRow({ m }: { m: MessageEntry }) {
79
94
 
80
95
  function DaemonErrors() {
81
96
  const [open, setOpen] = useState(false);
82
- const logs = useSWR(open ? "/admin/logs?errors" : null, () => Admin.logs("errors", 200));
97
+ const logs = useSWR(open ? "/api/admin/logs?errors" : null, () => Admin.logs("errors", 200));
83
98
  const entries = logs.data?.entries || [];
84
99
  return (
85
100
  <details className="mb-3 rounded-lg border border-border bg-muted/20" onToggle={(e) => setOpen((e.target as HTMLDetailsElement).open)}>
@@ -116,8 +131,8 @@ export function LogsTab({ pid }: { pid?: string }) {
116
131
  const ch = channel.trim() || undefined;
117
132
 
118
133
  const key = isGlobal
119
- ? `/messages/global?channel=${ch ?? ""}`
120
- : `/projects/${pid}/messages?channel=${ch ?? ""}`;
134
+ ? `/api/messages/global?channel=${ch ?? ""}`
135
+ : `/api/projects/${pid}/messages?channel=${ch ?? ""}`;
121
136
  const list = useSWR<MessageEntry[]>(key, () =>
122
137
  isGlobal
123
138
  ? Messages.global({ channel: ch, limit: 300 })
@@ -34,7 +34,7 @@ export function SessionsTab({ pid }: { pid?: string } = {}) {
34
34
  }, [input]);
35
35
 
36
36
  const paged = usePagedQuery<SessionRow>({
37
- key: `/sessions?engine=${engine}&q=${query}&deep=${deep ? 1 : 0}&cwd=${cwd || ""}`,
37
+ key: `/api/sessions?engine=${engine}&q=${query}&deep=${deep ? 1 : 0}&cwd=${cwd || ""}`,
38
38
  fetchPage: (limit, offset) =>
39
39
  Sessions.page({ engine: engine || undefined, q: query || undefined, deep, cwd, limit, offset }),
40
40
  resetKey: `${engine}|${query}|${deep ? 1 : 0}|${cwd || ""}`,
@@ -50,7 +50,7 @@ function ResizeHandleH() {
50
50
  // what the session changed vs its git baseline.
51
51
  export function CodeScreen() {
52
52
  const toast = useToast();
53
- const projects = useSWR("/projects", () => Projects.list());
53
+ const projects = useSWR("/api/projects", () => Projects.list());
54
54
  const projectList = useMemo(() => projects.data || [], [projects.data]);
55
55
 
56
56
  const [pid, setPid] = useState<string>("");
@@ -16,7 +16,7 @@ import { t } from "../../i18n";
16
16
  export function DeckScreen() {
17
17
  const toast = useToast();
18
18
  const { data, error, isLoading, mutate } = useSWR(
19
- "/deck/manifest",
19
+ "/api/deck/manifest",
20
20
  () => Deck.manifest(),
21
21
  { refreshInterval: 30_000 }
22
22
  );
@@ -12,7 +12,7 @@ import { t } from "../../i18n";
12
12
  // shortcut, appearance, activation) live in Settings → Desktop.
13
13
  export function DesktopScreen() {
14
14
  const { data: msgs, isLoading: msgsLoading, mutate: mutateMsgs } = useSWR(
15
- "/messages/global?channel=desktop",
15
+ "/api/messages/global?channel=desktop",
16
16
  () => fetchDesktopMessages(40),
17
17
  { refreshInterval: 8000 },
18
18
  );
@@ -23,7 +23,7 @@ export function VoiceScreen() {
23
23
  isLoading: provLoading,
24
24
  error: provError,
25
25
  mutate: mutateProviders,
26
- } = useSWR("/tts/providers", () => Voice.providers());
26
+ } = useSWR("/api/tts/providers", () => Voice.providers());
27
27
 
28
28
  // editing holds the engine id being configured. "__new__" opens the modal in
29
29
  // "create custom provider" mode.
@@ -61,12 +61,12 @@ export function AgentDetailScreen({ pid }: { pid: string }) {
61
61
  const [tab, setTab] = useState<TabKey>("overview");
62
62
  const TABS = buildTabs();
63
63
 
64
- const detail = useSWR(`/projects/${pid}/agents/${slug}`, () => Agents.get(pid, slug));
65
- const agents = useSWR(`/projects/${pid}/agents`, () => Agents.list(pid));
66
- const routines = useSWR(`/projects/${pid}/routines`, () => Routines.list(pid));
67
- const records = useSWR(`/projects/${pid}/messages?agent=${slug}`, () => Messages.project(pid, { agent: slug, limit: 200 }));
68
- const threads = useSWR(`/projects/${pid}/agents/${slug}/conversations`, () => Conversations.list(pid, slug));
69
- const tasks = useSWR(`/projects/${pid}/tasks?all`, () => Tasks.list(pid, "all"));
64
+ const detail = useSWR(`/api/projects/${pid}/agents/${slug}`, () => Agents.get(pid, slug));
65
+ const agents = useSWR(`/api/projects/${pid}/agents`, () => Agents.list(pid));
66
+ const routines = useSWR(`/api/projects/${pid}/routines`, () => Routines.list(pid));
67
+ const records = useSWR(`/api/projects/${pid}/messages?agent=${slug}`, () => Messages.project(pid, { agent: slug, limit: 200 }));
68
+ const threads = useSWR(`/api/projects/${pid}/agents/${slug}/conversations`, () => Conversations.list(pid, slug));
69
+ const tasks = useSWR(`/api/projects/${pid}/tasks?all`, () => Tasks.list(pid, "all"));
70
70
 
71
71
  const a = detail.data;
72
72
  const myRoutines = routinesForAgent(routines.data || [], slug);
@@ -318,7 +318,7 @@ function Stat({ label, value, icon: I }: { label: string; value: number; icon: t
318
318
  // bare textarea.
319
319
  function MemoryEditor({ pid, slug, onSaved }: { pid: string; slug: string; onSaved: () => void }) {
320
320
  const toast = useToast();
321
- const body = useSWR(`/memory/${pid}/agent:${slug}`, () => Agents.memory.get(pid, slug).then((r) => r.body));
321
+ const body = useSWR(`/api/memory/${pid}/agent:${slug}`, () => Agents.memory.get(pid, slug).then((r) => r.body));
322
322
 
323
323
  const file = useMemo<FileContent | null>(() => {
324
324
  if (body.data === undefined) return null;
@@ -425,7 +425,7 @@ function Field2({ label, value }: { label: string; value: string }) {
425
425
  }
426
426
 
427
427
  function ToolsPicker({ value, onChange }: { value: string; onChange: (v: string) => void }) {
428
- const cat = useSWR("/tools", () => Tools.list());
428
+ const cat = useSWR("/api/tools", () => Tools.list());
429
429
  const selected = csv(value);
430
430
  const catalog = cat.data || [];
431
431
  const toggle = (name: string) => {
@@ -89,7 +89,7 @@ function buildTree(agents: AgentEntry[]) {
89
89
  export function AgentsTab({ pid }: { pid: string }) {
90
90
  const navigate = useNavigate();
91
91
  const toast = useToast();
92
- const list = useSWR(`/projects/${pid}/agents?stats=1`, () => Agents.list(pid, { stats: true }));
92
+ const list = useSWR(`/api/projects/${pid}/agents?stats=1`, () => Agents.list(pid, { stats: true }));
93
93
  const [view, setView] = useState<"hierarchy" | "list">("hierarchy");
94
94
  const [creating, setCreating] = useState(false);
95
95
 
@@ -154,7 +154,7 @@ function ImportVaultDialog({
154
154
  open, onClose, onImported, pid, existing,
155
155
  }: { open: boolean; onClose: () => void; onImported: () => void; pid: string; existing: string[] }) {
156
156
  const toast = useToast();
157
- const vault = useSWR(open ? "/agents/vault" : null, () => Agents.vault());
157
+ const vault = useSWR(open ? "/api/agents/vault" : null, () => Agents.vault());
158
158
  const [busy, setBusy] = useState("");
159
159
  const items = vault.data || [];
160
160
 
@@ -20,10 +20,24 @@ import type { AgentEntry } from "../../types/daemon";
20
20
  // with a real APC agent slug (which must match /^[a-z][a-z0-9_-]*$/).
21
21
  const ROBY_SLUG = "__super_agent__";
22
22
 
23
- export function ChatTab({ pid }: { pid: string }) {
23
+ /**
24
+ * `hideSidebar` lets another screen (the agent inbox) supply its own
25
+ * conversation list and embed just the thread. The chat surface is not forked
26
+ * — one implementation, two frames around it.
27
+ */
28
+ export function ChatTab({
29
+ pid,
30
+ hideSidebar = false,
31
+ initialSelection,
32
+ }: {
33
+ pid: string;
34
+ hideSidebar?: boolean;
35
+ /** Open a specific conversation on mount instead of a fresh live session. */
36
+ initialSelection?: ChatKey;
37
+ }) {
24
38
  const toast = useToast();
25
39
  const [params, setSearchParams] = useSearchParams();
26
- const agents = useSWR(`/projects/${pid}/agents`, () => Agents.list(pid));
40
+ const agents = useSWR(`/api/projects/${pid}/agents`, () => Agents.list(pid));
27
41
  const [creating, setCreating] = useState(false);
28
42
  const [model, setModel] = useState("");
29
43
  const [dismissedAskKey, setDismissedAskKey] = useState<string | null>(null);
@@ -36,6 +50,10 @@ export function ChatTab({ pid }: { pid: string }) {
36
50
  // defaulting to a live session with the super-agent so the chat works even on
37
51
  // a brand-new project with zero agents and zero conversations.
38
52
  const [selected, setSelected] = useState<ChatKey>(() => {
53
+ // An embedding screen that already knows which conversation the user
54
+ // picked wins over the URL — otherwise clicking a row in the inbox opens
55
+ // an empty new session and the message you clicked on is nowhere.
56
+ if (initialSelection) return initialSelection;
39
57
  const agent = params.get("agent");
40
58
  const conv = params.get("conv");
41
59
  const channel = params.get("channel");
@@ -147,10 +165,10 @@ export function ChatTab({ pid }: { pid: string }) {
147
165
  try {
148
166
  if (selected.kind === "conv") {
149
167
  await Conversations.remove(pid, selected.agentSlug, selected.convId);
150
- void mutate(`/projects/${pid}/agents/${selected.agentSlug}/conversations`);
168
+ void mutate(`/api/projects/${pid}/agents/${selected.agentSlug}/conversations`);
151
169
  } else if (selected.kind === "thread") {
152
170
  await Conversations.removeThread(pid, selected.channel, selected.threadId);
153
- void mutate(`/projects/${pid}/super-agent/threads`);
171
+ void mutate(`/api/projects/${pid}/super-agent/threads`);
154
172
  }
155
173
  toast.success(t("project.chat.deleted"));
156
174
  setConfirmDelete(false);
@@ -183,7 +201,7 @@ export function ChatTab({ pid }: { pid: string }) {
183
201
 
184
202
  return (
185
203
  <div className="flex h-full overflow-hidden rounded-xl border border-border bg-card/40">
186
- <ChatList
204
+ {hideSidebar ? null : <ChatList
187
205
  pid={pid}
188
206
  agents={agentList}
189
207
  superAgentSlug={ROBY_SLUG}
@@ -191,7 +209,7 @@ export function ChatTab({ pid }: { pid: string }) {
191
209
  selected={selected}
192
210
  onSelect={selectChat}
193
211
  onNewChat={onNewChat}
194
- />
212
+ />}
195
213
 
196
214
  <section className="flex min-w-0 flex-1 flex-col">
197
215
  <header className="flex shrink-0 items-center justify-between gap-3 border-b border-border px-4 py-3">
@@ -19,7 +19,7 @@ export function ConfigTab({ pid }: { pid: string }) {
19
19
  const toast = useToast();
20
20
  const navigate = useNavigate();
21
21
  const { project, mutate: mutateProject } = useProject(pid);
22
- const cfg = useSWR(`/projects/${pid}/config`, () => Projects.config.show(pid));
22
+ const cfg = useSWR(`/api/projects/${pid}/config`, () => Projects.config.show(pid));
23
23
  const isBase = String(pid) === "0";
24
24
 
25
25
  if (cfg.isLoading) return <Loading />;
@@ -73,9 +73,9 @@ function sourceLabel(source: string): string {
73
73
 
74
74
  export function McpsTab({ pid }: { pid: string }) {
75
75
  const toast = useToast();
76
- const list = useSWR(`/projects/${pid}/mcps`, () => Mcps.list(pid));
77
- const conflicts = useSWR(`/projects/${pid}/mcps/check`, () => Mcps.check(pid));
78
- const vars = useSWR<VarsList>(`/projects/${pid}/vars`, () => Vars.list(pid));
76
+ const list = useSWR(`/api/projects/${pid}/mcps`, () => Mcps.list(pid));
77
+ const conflicts = useSWR(`/api/projects/${pid}/mcps/check`, () => Mcps.check(pid));
78
+ const vars = useSWR<VarsList>(`/api/projects/${pid}/vars`, () => Vars.list(pid));
79
79
  const [dialog, setDialog] = useState<DialogMode>(null);
80
80
  const [activeMcp, setActiveMcp] = useState<string | null>(null);
81
81
  const [results, setResults] = useState<Record<string, McpResult>>({});
@@ -14,12 +14,12 @@ import type { AgentEntry, RoutineEntry, TaskEntry } from "../../types/daemon";
14
14
  // automation), what's in flight (task workflow), and what just happened.
15
15
  export function Overview({ pid }: { pid: string }) {
16
16
  const navigate = useNavigate();
17
- const tasks = useSWR(`/projects/${pid}/tasks?state=open`, () => Tasks.list(pid), { refreshInterval: 20_000 });
18
- const summary = useSWR(`/projects/${pid}/tasks-summary`, () => Tasks.summary(pid), { refreshInterval: 20_000 });
19
- const routines = useSWR(`/projects/${pid}/routines`, () => Routines.list(pid));
20
- const agents = useSWR(`/projects/${pid}/agents`, () => Agents.list(pid));
21
- const mcps = useSWR(`/projects/${pid}/mcps`, () => Mcps.list(pid));
22
- const artifacts = useSWR(`/projects/${pid}/artifacts`, () => Artifacts.list(pid));
17
+ const tasks = useSWR(`/api/projects/${pid}/tasks?state=open`, () => Tasks.list(pid), { refreshInterval: 20_000 });
18
+ const summary = useSWR(`/api/projects/${pid}/tasks-summary`, () => Tasks.summary(pid), { refreshInterval: 20_000 });
19
+ const routines = useSWR(`/api/projects/${pid}/routines`, () => Routines.list(pid));
20
+ const agents = useSWR(`/api/projects/${pid}/agents`, () => Agents.list(pid));
21
+ const mcps = useSWR(`/api/projects/${pid}/mcps`, () => Mcps.list(pid));
22
+ const artifacts = useSWR(`/api/projects/${pid}/artifacts`, () => Artifacts.list(pid));
23
23
 
24
24
  const agentList = agents.data ?? [];
25
25
  const orchestrators = agentList.filter((a) => a.is_master || a.type === "orchestrator");
@@ -5,6 +5,7 @@ import { Plus } from "lucide-react";
5
5
  import { Routines, type RoutineEntry } from "../../lib/api";
6
6
  import { Button, Dialog, Empty, Loading } from "../../components/ui";
7
7
  import { useToast } from "../../components/Toast";
8
+ import { Section } from "../../components/Section";
8
9
  import { t } from "../../i18n";
9
10
  import { RoutineList } from "../../components/routines/RoutineList";
10
11
  import { RoutineDetail } from "../../components/routines/RoutineDetail";
@@ -15,7 +16,7 @@ import { RoutineEditor } from "../../components/routines/RoutineEditor";
15
16
  // editing is behind a button, delete uses the shared <Dialog>.
16
17
  export function RoutinesTab({ pid }: { pid: string }) {
17
18
  const toast = useToast();
18
- const list = useSWR(`/projects/${pid}/routines`, () => Routines.list(pid));
19
+ const list = useSWR(`/api/projects/${pid}/routines`, () => Routines.list(pid));
19
20
  const [params, setParams] = useSearchParams();
20
21
  const [editing, setEditing] = useState<Partial<RoutineEntry> | null>(null);
21
22
  const [confirmDelete, setConfirmDelete] = useState<RoutineEntry | null>(null);
@@ -56,7 +57,7 @@ export function RoutinesTab({ pid }: { pid: string }) {
56
57
  // Refresh the routine list (last status) and its executions list.
57
58
  await Promise.all([
58
59
  list.mutate(),
59
- globalMutate(`/projects/${pid}/routines/${r.name}/runs`),
60
+ globalMutate(`/api/projects/${pid}/routines/${r.name}/runs`),
60
61
  ]);
61
62
  } catch (e: any) { toast.error(e?.message || t("project.routines.run_error")); }
62
63
  finally { setRunning(null); }
@@ -75,23 +76,24 @@ export function RoutinesTab({ pid }: { pid: string }) {
75
76
  };
76
77
 
77
78
  return (
78
- <div className="flex h-full min-h-0 flex-col gap-3">
79
- {/* header (title + new) */}
80
- <div className="flex shrink-0 items-start justify-between gap-4">
81
- <div>
82
- <h2 className="text-lg font-semibold tracking-tight">{t("project.routines.title")}</h2>
83
- <p className="mt-0.5 text-sm text-muted-fg">{t("project.routines.subtitle")}</p>
84
- </div>
79
+ // Same frame as every other list page. This one used to draw its own
80
+ // header outside a card while Tasks and Commitments sat inside one, so
81
+ // moving between them the whole page shifted.
82
+ <Section
83
+ fullHeight
84
+ title={t("project.routines.title")}
85
+ description={t("project.routines.subtitle")}
86
+ action={
85
87
  <Button size="sm" variant="primary" onClick={() => setEditing({ kind: "super_agent", schedule: "every:10m", enabled: true })}>
86
88
  <Plus size={14} /> {t("project.routines.new_btn")}
87
89
  </Button>
88
- </div>
89
-
90
+ }
91
+ >
90
92
  {list.isLoading && <Loading />}
91
93
  {!list.isLoading && rows.length === 0 && <Empty>{t("project.routines.empty")}</Empty>}
92
94
 
93
95
  {rows.length > 0 && (
94
- <div className="grid min-h-0 flex-1 grid-rows-[minmax(0,1fr)] grid-cols-[minmax(200px,260px)_1fr] overflow-hidden rounded-xl border border-border bg-card/40">
96
+ <div className="grid min-h-0 flex-1 grid-rows-[minmax(0,1fr)] grid-cols-[minmax(200px,260px)_1fr] overflow-hidden rounded-lg border border-border">
95
97
  <RoutineList routines={rows} selectedName={selected?.name ?? null} onSelect={selectRoutine} />
96
98
  <div className="min-h-0 min-w-0 overflow-hidden">
97
99
  {selected
@@ -148,6 +150,6 @@ export function RoutinesTab({ pid }: { pid: string }) {
148
150
  >
149
151
  <p className="text-sm text-muted-fg">{t("project.routines.run_confirm_body")}</p>
150
152
  </Dialog>
151
- </div>
153
+ </Section>
152
154
  );
153
155
  }
@@ -12,7 +12,7 @@ import type { OrgArea, OrgRole } from "../../types/daemon";
12
12
 
13
13
  export function StructureTab({ pid }: { pid: string }) {
14
14
  const toast = useToast();
15
- const org = useSWR(`/projects/${pid}/organization`, () => Org.get(pid));
15
+ const org = useSWR(`/api/projects/${pid}/organization`, () => Org.get(pid));
16
16
 
17
17
  const [areaDialog, setAreaDialog] = useState<{ editing?: OrgArea | null } | null>(null);
18
18
  const [roleDialog, setRoleDialog] = useState<{ editing?: OrgRole | null; presetArea?: string | null } | null>(null);
@@ -18,7 +18,7 @@ export function TasksTab({ pid }: { pid: string }) {
18
18
  // dedupingInterval:0 so switching the state filter always revalidates the
19
19
  // target page instead of showing the stale cached one from a prior switch.
20
20
  const paged = usePagedQuery({
21
- key: `/projects/${pid}/tasks?state=${state}`,
21
+ key: `/api/projects/${pid}/tasks?state=${state}`,
22
22
  fetchPage: (limit, offset) => Tasks.listPage(pid, { state, limit, offset }),
23
23
  resetKey: state,
24
24
  swr: { dedupingInterval: 0, revalidateOnFocus: true },
@@ -23,7 +23,7 @@ export function VarsTab({ pid }: { pid: string }) {
23
23
  );
24
24
  const [revealAll, setRevealAll] = useState(false);
25
25
  const list = useSWR<VarsList>(
26
- `/projects/${pid}/vars?reveal=${revealAll ? 1 : 0}`,
26
+ `/api/projects/${pid}/vars?reveal=${revealAll ? 1 : 0}`,
27
27
  () => Vars.list(pid, { reveal: revealAll }),
28
28
  );
29
29
  const [openCreate, setOpenCreate] = useState<{ name?: string; value?: string; scope?: VarScope } | null>(null);
@@ -55,7 +55,7 @@ export interface AgentDetail extends AgentEntry {
55
55
 
56
56
  export interface RoutineEntry {
57
57
  name: string;
58
- kind: "heartbeat" | "exec_agent" | "super_agent" | "telegram" | "shell";
58
+ kind: "heartbeat" | "exec_agent" | "super_agent" | "telegram" | "shell" | "watch";
59
59
  schedule: string;
60
60
  spec: Record<string, unknown>;
61
61
  enabled: boolean;
@@ -228,6 +228,15 @@ export interface ConversationMessage {
228
228
  actor_kind?: string;
229
229
  model?: string;
230
230
  usage?: ChatUsage;
231
+ /** Compact record of what the turn did, written at the time (the live tool
232
+ * events are long gone by the time a thread is read back). */
233
+ tool_summary?: ToolSummary;
234
+ }
235
+
236
+ export interface ToolSummary {
237
+ total: number;
238
+ failed: number;
239
+ tools: { name: string; count: number; failed: number }[];
231
240
  }
232
241
 
233
242
  export interface ConversationDetail {
@@ -6,28 +6,23 @@ import path from "node:path";
6
6
  // Vite config for the APX admin panel.
7
7
  //
8
8
  // - Single SPA, no SSR (this is a local admin tool).
9
- // - During `vite dev`, every daemon API prefix is proxied to the running
10
- // daemon so we develop the UI with hot reload against REAL data. Frontend
11
- // routes (/, /settings, /p/:id) are NOT listed, so vite serves index.html
12
- // for them (SPA routing).
9
+ // - During `vite dev`, /api is proxied to the running daemon so we develop the
10
+ // UI with hot reload against REAL data. Frontend routes (/, /settings,
11
+ // /p/:id) live outside /api, so vite serves index.html for them (SPA
12
+ // routing). `ws: true` also carries the /api/desktop/ws upgrade through.
13
13
  // - `vite build` emits to ./dist; the daemon serves that folder when present.
14
+ //
15
+ // This used to be a hand-maintained list of every daemon prefix, kept in sync
16
+ // by hand with two other copies (the SPA fallback and the auth allowlist) —
17
+ // and it drifted. Now the daemon mounts everything under /api
18
+ // (src/host/daemon/api/prefix.js), so one entry covers the whole surface and
19
+ // a new route can never be missed here.
14
20
 
15
21
  const DAEMON_TARGET = process.env.APX_DAEMON_URL || "http://127.0.0.1:7430";
16
22
 
17
- // Keep in sync with API_PREFIXES in src/host/daemon/api/shared.js — that's the
18
- // source of truth for what is a daemon route. A missing prefix means `vite dev`
19
- // silently serves the SPA shell for that call instead of the real response
20
- // (e.g. an empty /pair/list).
21
- const API_PREFIXES = [
22
- "/health", "/admin", "/projects", "/telegram", "/engines", "/runtimes",
23
- "/messages", "/sessions", "/tools", "/mcp", "/voice", "/tts", "/desktop", "/overlay",
24
- "/transcribe", "/run", "/files", "/memory", "/env", "/pair", "/deck",
25
- "/super-agent", "/identity", "/agents", "/tasks", "/code",
26
- ];
27
-
28
- const proxy = Object.fromEntries(
29
- API_PREFIXES.map((p) => [p, { target: DAEMON_TARGET, changeOrigin: false, ws: true }])
30
- );
23
+ const proxy = {
24
+ "/api": { target: DAEMON_TARGET, changeOrigin: false, ws: true },
25
+ };
31
26
 
32
27
  export default defineConfig({
33
28
  plugins: [react(), tailwindcss()],