@agentprojectcontext/apx 1.77.2 → 1.78.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 (294) 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 +13 -48
  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/tools/handlers/call-runtime.js +4 -4
  15. package/src/core/agent/tools/handlers/search-sessions.js +1 -1
  16. package/src/core/agent/tools/handlers/set-permission-mode.js +11 -4
  17. package/src/core/agent/tools/names.js +58 -0
  18. package/src/core/apc/context-copy.js +1 -1
  19. package/src/core/apc/frontmatter.js +85 -0
  20. package/src/core/apc/parser.js +9 -21
  21. package/src/core/apc/paths.js +8 -8
  22. package/src/core/apc/scaffold.js +5 -4
  23. package/src/core/channels/telegram/config.js +201 -0
  24. package/src/core/config/index.js +25 -183
  25. package/src/core/config/paths.js +54 -1
  26. package/src/core/confirmation/adapters/web.js +1 -1
  27. package/src/core/deck/manifest.js +12 -12
  28. package/src/core/desktop/autostart.js +3 -2
  29. package/src/core/desktop/process.js +4 -4
  30. package/src/core/engines/presets.js +2 -2
  31. package/src/core/http-tools/browser.js +1 -1
  32. package/src/core/http-tools/catalog.js +551 -0
  33. package/src/core/http-tools/fetch.js +1 -1
  34. package/src/core/http-tools/glob.js +3 -3
  35. package/src/core/http-tools/grep.js +2 -9
  36. package/src/core/http-tools/inline-handlers.js +59 -0
  37. package/src/core/http-tools/registry.js +31 -615
  38. package/src/core/http-tools/search.js +2 -2
  39. package/src/core/identity/self.js +6 -7
  40. package/src/core/identity/telegram.js +3 -3
  41. package/src/core/logging.js +6 -5
  42. package/src/core/mcp/sources.js +12 -16
  43. package/src/core/memory/indexer.js +2 -5
  44. package/src/core/profiles/lifecycle.js +0 -2
  45. package/src/core/profiles/store.js +2 -7
  46. package/src/core/routines/runner.js +1 -1
  47. package/src/core/runtime-skills/apx-agency-agents/SKILL.md +2 -2
  48. package/src/core/runtime-skills/apx-profile/SKILL.md +3 -3
  49. package/src/core/runtime-skills/apx-project/SKILL.md +1 -1
  50. package/src/core/runtime-skills/apx-task/SKILL.md +8 -8
  51. package/src/core/runtime-skills/apx-telegram/SKILL.md +4 -4
  52. package/src/core/runtime-skills/apx-voice/SKILL.md +4 -4
  53. package/src/core/runtimes/detect.js +2 -2
  54. package/src/core/sessions/index.js +800 -0
  55. package/src/core/stores/code-sessions.js +3 -14
  56. package/src/core/stores/engine-sessions.js +0 -4
  57. package/src/core/stores/messages.js +0 -5
  58. package/src/core/stores/organization.js +9 -11
  59. package/src/core/stores/routines.js +6 -11
  60. package/src/core/stores/runtime-callbacks.js +2 -1
  61. package/src/core/util/json-file.js +128 -0
  62. package/src/core/vars/sources.js +14 -21
  63. package/src/core/voice/stt-models.js +1 -1
  64. package/src/core/voice/tts.js +5 -4
  65. package/src/host/daemon/api/admin-config.js +5 -5
  66. package/src/host/daemon/api/admin.js +8 -7
  67. package/src/host/daemon/api/agents.js +16 -16
  68. package/src/host/daemon/api/artifact-preview.js +8 -8
  69. package/src/host/daemon/api/artifacts.js +7 -7
  70. package/src/host/daemon/api/code.js +8 -8
  71. package/src/host/daemon/api/config.js +6 -6
  72. package/src/host/daemon/api/confirm.js +2 -2
  73. package/src/host/daemon/api/connections.js +2 -2
  74. package/src/host/daemon/api/conversations.js +12 -12
  75. package/src/host/daemon/api/deck.js +6 -6
  76. package/src/host/daemon/api/desktop.js +8 -8
  77. package/src/host/daemon/api/embeddings.js +4 -4
  78. package/src/host/daemon/api/engines.js +8 -7
  79. package/src/host/daemon/api/exec.js +5 -4
  80. package/src/host/daemon/api/files-project.js +6 -6
  81. package/src/host/daemon/api/health.js +2 -2
  82. package/src/host/daemon/api/identity.js +3 -3
  83. package/src/host/daemon/api/inbox.js +2 -2
  84. package/src/host/daemon/api/integrations.js +23 -19
  85. package/src/host/daemon/api/mcps.js +16 -12
  86. package/src/host/daemon/api/messages.js +5 -5
  87. package/src/host/daemon/api/organization.js +8 -8
  88. package/src/host/daemon/api/pairing.js +10 -7
  89. package/src/host/daemon/api/plugins.js +3 -3
  90. package/src/host/daemon/api/prefix.js +20 -0
  91. package/src/host/daemon/api/profiles.js +9 -9
  92. package/src/host/daemon/api/projects.js +5 -5
  93. package/src/host/daemon/api/routines.js +8 -8
  94. package/src/host/daemon/api/run.js +2 -2
  95. package/src/host/daemon/api/runtimes.js +6 -6
  96. package/src/host/daemon/api/sessions-search.js +18 -11
  97. package/src/host/daemon/api/sessions.js +6 -6
  98. package/src/host/daemon/api/shared.js +59 -13
  99. package/src/host/daemon/api/skills.js +12 -12
  100. package/src/host/daemon/api/super-agent.js +4 -4
  101. package/src/host/daemon/api/tasks.js +11 -11
  102. package/src/host/daemon/api/telegram.js +19 -20
  103. package/src/host/daemon/api/tools.js +7 -7
  104. package/src/host/daemon/api/top-level.js +7 -7
  105. package/src/host/daemon/api/transcribe.js +6 -6
  106. package/src/host/daemon/api/tts.js +3 -3
  107. package/src/host/daemon/api/vars.js +11 -8
  108. package/src/host/daemon/api/voice.js +6 -6
  109. package/src/host/daemon/api/web.js +28 -32
  110. package/src/host/daemon/api.js +77 -48
  111. package/src/host/daemon/db.js +1 -1
  112. package/src/host/daemon/desktop-ws.js +7 -3
  113. package/src/host/daemon/index.js +11 -5
  114. package/src/host/daemon/plugins/desktop/index.js +1 -1
  115. package/src/host/daemon/plugins/telegram/index.js +1 -1
  116. package/src/host/daemon/stt-venv.js +20 -81
  117. package/src/host/daemon/wakeup.js +1 -1
  118. package/src/interfaces/acp/index.js +3 -3
  119. package/src/interfaces/acp/session.js +2 -2
  120. package/src/interfaces/cli/commands/a2a.js +2 -2
  121. package/src/interfaces/cli/commands/agent.js +3 -3
  122. package/src/interfaces/cli/commands/artifact.js +13 -13
  123. package/src/interfaces/cli/commands/chat.js +3 -3
  124. package/src/interfaces/cli/commands/command.js +2 -2
  125. package/src/interfaces/cli/commands/config.js +4 -4
  126. package/src/interfaces/cli/commands/daemon.js +17 -18
  127. package/src/interfaces/cli/commands/desktop.js +7 -14
  128. package/src/interfaces/cli/commands/exec.js +2 -2
  129. package/src/interfaces/cli/commands/log.js +3 -4
  130. package/src/interfaces/cli/commands/mcp.js +9 -9
  131. package/src/interfaces/cli/commands/memory.js +2 -2
  132. package/src/interfaces/cli/commands/messages.js +5 -5
  133. package/src/interfaces/cli/commands/model.js +0 -18
  134. package/src/interfaces/cli/commands/obsidian.js +5 -5
  135. package/src/interfaces/cli/commands/org.js +5 -5
  136. package/src/interfaces/cli/commands/pair.js +6 -6
  137. package/src/interfaces/cli/commands/plugins.js +2 -2
  138. package/src/interfaces/cli/commands/profile.js +10 -10
  139. package/src/interfaces/cli/commands/project-config.js +6 -6
  140. package/src/interfaces/cli/commands/project.js +10 -10
  141. package/src/interfaces/cli/commands/routine.js +8 -9
  142. package/src/interfaces/cli/commands/runtime.js +2 -2
  143. package/src/interfaces/cli/commands/search.js +0 -1
  144. package/src/interfaces/cli/commands/session.js +12 -40
  145. package/src/interfaces/cli/commands/sessions.js +21 -798
  146. package/src/interfaces/cli/commands/setup.js +16 -6
  147. package/src/interfaces/cli/commands/skills.js +1 -2
  148. package/src/interfaces/cli/commands/status.js +3 -3
  149. package/src/interfaces/cli/commands/task.js +8 -8
  150. package/src/interfaces/cli/commands/telegram.js +21 -21
  151. package/src/interfaces/cli/commands/voice.js +6 -6
  152. package/src/interfaces/cli/help/index.js +2245 -0
  153. package/src/interfaces/cli/http.js +9 -4
  154. package/src/interfaces/cli/index.js +14 -2857
  155. package/src/interfaces/cli/routes/acp.js +13 -0
  156. package/src/interfaces/cli/routes/agent.js +27 -0
  157. package/src/interfaces/cli/routes/artifact.js +28 -0
  158. package/src/interfaces/cli/routes/chat.js +11 -0
  159. package/src/interfaces/cli/routes/code.js +11 -0
  160. package/src/interfaces/cli/routes/command.js +21 -0
  161. package/src/interfaces/cli/routes/config.js +16 -0
  162. package/src/interfaces/cli/routes/connections.js +11 -0
  163. package/src/interfaces/cli/routes/conversations.js +21 -0
  164. package/src/interfaces/cli/routes/daemon.js +19 -0
  165. package/src/interfaces/cli/routes/desktop.js +20 -0
  166. package/src/interfaces/cli/routes/env.js +13 -0
  167. package/src/interfaces/cli/routes/exec.js +11 -0
  168. package/src/interfaces/cli/routes/identity.js +11 -0
  169. package/src/interfaces/cli/routes/index.js +71 -0
  170. package/src/interfaces/cli/routes/init.js +11 -0
  171. package/src/interfaces/cli/routes/log.js +20 -0
  172. package/src/interfaces/cli/routes/mcp.js +22 -0
  173. package/src/interfaces/cli/routes/memory.js +11 -0
  174. package/src/interfaces/cli/routes/messages.js +16 -0
  175. package/src/interfaces/cli/routes/model.js +11 -0
  176. package/src/interfaces/cli/routes/obsidian.js +17 -0
  177. package/src/interfaces/cli/routes/org.js +34 -0
  178. package/src/interfaces/cli/routes/overlay.js +10 -0
  179. package/src/interfaces/cli/routes/pair.js +17 -0
  180. package/src/interfaces/cli/routes/panel.js +16 -0
  181. package/src/interfaces/cli/routes/permission.js +11 -0
  182. package/src/interfaces/cli/routes/plugins.js +21 -0
  183. package/src/interfaces/cli/routes/profile.js +27 -0
  184. package/src/interfaces/cli/routes/project.js +41 -0
  185. package/src/interfaces/cli/routes/restart.js +15 -0
  186. package/src/interfaces/cli/routes/routine.js +28 -0
  187. package/src/interfaces/cli/routes/run.js +11 -0
  188. package/src/interfaces/cli/routes/search.js +11 -0
  189. package/src/interfaces/cli/routes/send.js +11 -0
  190. package/src/interfaces/cli/routes/session.js +26 -0
  191. package/src/interfaces/cli/routes/sessions.js +15 -0
  192. package/src/interfaces/cli/routes/setup.js +18 -0
  193. package/src/interfaces/cli/routes/skills.js +20 -0
  194. package/src/interfaces/cli/routes/status.js +12 -0
  195. package/src/interfaces/cli/routes/task.js +26 -0
  196. package/src/interfaces/cli/routes/telegram.js +39 -0
  197. package/src/interfaces/cli/routes/update.js +24 -0
  198. package/src/interfaces/cli/routes/voice.js +17 -0
  199. package/src/interfaces/desktop/main.js +6 -7
  200. package/src/interfaces/desktop/renderer.js +0 -5
  201. package/src/interfaces/mcp-server/index.js +15 -14
  202. package/src/interfaces/tui/_shims/globals.d.ts +8 -0
  203. package/src/interfaces/tui/tsconfig.json +4 -1
  204. package/src/interfaces/web/README.md +6 -6
  205. package/src/interfaces/web/dist/assets/index-CBR_-QyA.js +824 -0
  206. package/src/interfaces/web/dist/assets/index-CBR_-QyA.js.map +1 -0
  207. package/src/interfaces/web/dist/index.html +1 -1
  208. package/src/interfaces/web/src/components/AddProjectDialog.tsx +1 -1
  209. package/src/interfaces/web/src/components/agents/AgentFormFields.tsx +1 -1
  210. package/src/interfaces/web/src/components/chat/ChatList.tsx +2 -2
  211. package/src/interfaces/web/src/components/chat/SkillPicker.tsx +1 -1
  212. package/src/interfaces/web/src/components/code/CodeFileTree.tsx +1 -1
  213. package/src/interfaces/web/src/components/code/CodeTerminal.tsx +1 -1
  214. package/src/interfaces/web/src/components/desktop/DesktopStatusCard.tsx +1 -1
  215. package/src/interfaces/web/src/components/files/FileBrowser.tsx +2 -2
  216. package/src/interfaces/web/src/components/memory/MemoryBrowser.tsx +2 -2
  217. package/src/interfaces/web/src/components/routines/ExecutionsList.tsx +1 -1
  218. package/src/interfaces/web/src/components/routines/RoutineEditor.tsx +2 -2
  219. package/src/interfaces/web/src/components/settings/DesktopSettingsPanel.tsx +1 -1
  220. package/src/interfaces/web/src/components/settings/MemoryPanel.tsx +1 -1
  221. package/src/interfaces/web/src/components/settings/SkillsInspectorPanel.tsx +1 -1
  222. package/src/interfaces/web/src/components/settings/SkillsManager.tsx +2 -2
  223. package/src/interfaces/web/src/components/tasks/TaskDetailPanel.tsx +1 -1
  224. package/src/interfaces/web/src/hooks/useDaemonStatus.ts +1 -1
  225. package/src/interfaces/web/src/hooks/useDevices.ts +1 -1
  226. package/src/interfaces/web/src/hooks/useEngines.ts +1 -1
  227. package/src/interfaces/web/src/hooks/useGlobalConfig.ts +2 -2
  228. package/src/interfaces/web/src/hooks/useIdentity.ts +1 -1
  229. package/src/interfaces/web/src/hooks/useInbox.ts +1 -1
  230. package/src/interfaces/web/src/hooks/useProfiles.ts +3 -3
  231. package/src/interfaces/web/src/hooks/useProjects.ts +1 -1
  232. package/src/interfaces/web/src/hooks/useTelegram.ts +3 -3
  233. package/src/interfaces/web/src/hooks/useTokenBootstrap.ts +30 -7
  234. package/src/interfaces/web/src/lib/api/admin.ts +11 -11
  235. package/src/interfaces/web/src/lib/api/agents.ts +14 -14
  236. package/src/interfaces/web/src/lib/api/artifacts.ts +11 -11
  237. package/src/interfaces/web/src/lib/api/code.ts +1 -1
  238. package/src/interfaces/web/src/lib/api/conversations.ts +8 -8
  239. package/src/interfaces/web/src/lib/api/deck.ts +3 -3
  240. package/src/interfaces/web/src/lib/api/desktop.ts +8 -8
  241. package/src/interfaces/web/src/lib/api/embeddings.ts +3 -3
  242. package/src/interfaces/web/src/lib/api/engines.ts +3 -3
  243. package/src/interfaces/web/src/lib/api/filesystem.ts +2 -2
  244. package/src/interfaces/web/src/lib/api/health.ts +1 -1
  245. package/src/interfaces/web/src/lib/api/identity.ts +2 -2
  246. package/src/interfaces/web/src/lib/api/inbox.ts +1 -1
  247. package/src/interfaces/web/src/lib/api/integrations.ts +8 -8
  248. package/src/interfaces/web/src/lib/api/mcps.ts +6 -6
  249. package/src/interfaces/web/src/lib/api/messages.ts +3 -3
  250. package/src/interfaces/web/src/lib/api/organization.ts +7 -7
  251. package/src/interfaces/web/src/lib/api/profiles.ts +8 -8
  252. package/src/interfaces/web/src/lib/api/projectFiles.ts +5 -5
  253. package/src/interfaces/web/src/lib/api/projects.ts +12 -12
  254. package/src/interfaces/web/src/lib/api/routines.ts +7 -7
  255. package/src/interfaces/web/src/lib/api/sessions.ts +2 -2
  256. package/src/interfaces/web/src/lib/api/skills.ts +11 -11
  257. package/src/interfaces/web/src/lib/api/super_agent.ts +3 -3
  258. package/src/interfaces/web/src/lib/api/tasks.ts +12 -12
  259. package/src/interfaces/web/src/lib/api/telegram.ts +14 -14
  260. package/src/interfaces/web/src/lib/api/tools.ts +1 -1
  261. package/src/interfaces/web/src/lib/api/vars.ts +4 -4
  262. package/src/interfaces/web/src/lib/api/voice.ts +6 -6
  263. package/src/interfaces/web/src/screens/base/AgentDefaultsTab.tsx +1 -1
  264. package/src/interfaces/web/src/screens/base/GlobalTasksTab.tsx +1 -1
  265. package/src/interfaces/web/src/screens/base/LogsTab.tsx +3 -3
  266. package/src/interfaces/web/src/screens/base/SessionsTab.tsx +1 -1
  267. package/src/interfaces/web/src/screens/modules/CodeScreen.tsx +1 -1
  268. package/src/interfaces/web/src/screens/modules/DeckScreen.tsx +1 -1
  269. package/src/interfaces/web/src/screens/modules/DesktopScreen.tsx +1 -1
  270. package/src/interfaces/web/src/screens/modules/VoiceScreen.tsx +1 -1
  271. package/src/interfaces/web/src/screens/project/AgentDetailScreen.tsx +8 -8
  272. package/src/interfaces/web/src/screens/project/AgentsTab.tsx +2 -2
  273. package/src/interfaces/web/src/screens/project/ChatTab.tsx +3 -3
  274. package/src/interfaces/web/src/screens/project/ConfigTab.tsx +1 -1
  275. package/src/interfaces/web/src/screens/project/McpsTab.tsx +3 -3
  276. package/src/interfaces/web/src/screens/project/Overview.tsx +6 -6
  277. package/src/interfaces/web/src/screens/project/RoutinesTab.tsx +2 -2
  278. package/src/interfaces/web/src/screens/project/StructureTab.tsx +1 -1
  279. package/src/interfaces/web/src/screens/project/TasksTab.tsx +1 -1
  280. package/src/interfaces/web/src/screens/project/VarsTab.tsx +1 -1
  281. package/src/interfaces/web/vite.config.ts +13 -18
  282. package/src/interfaces/web/dist/assets/index-BWWpTTSd.js +0 -824
  283. package/src/interfaces/web/dist/assets/index-BWWpTTSd.js.map +0 -1
  284. package/src/skills/apc-context/SKILL.md +0 -159
  285. /package/src/{host/daemon → core}/runtimes/_spawn.js +0 -0
  286. /package/src/{host/daemon → core}/runtimes/aider.js +0 -0
  287. /package/src/{host/daemon → core}/runtimes/antigravity.js +0 -0
  288. /package/src/{host/daemon → core}/runtimes/claude-code.js +0 -0
  289. /package/src/{host/daemon → core}/runtimes/codex.js +0 -0
  290. /package/src/{host/daemon → core}/runtimes/cursor-agent.js +0 -0
  291. /package/src/{host/daemon → core}/runtimes/gemini-cli.js +0 -0
  292. /package/src/{host/daemon → core}/runtimes/index.js +0 -0
  293. /package/src/{host/daemon → core}/runtimes/opencode.js +0 -0
  294. /package/src/{host/daemon → core}/runtimes/qwen-code.js +0 -0
@@ -18,7 +18,7 @@
18
18
  <link rel="apple-touch-icon" href="/favicon/dark/apple-touch-icon.png" media="(prefers-color-scheme: dark)" />
19
19
  <link rel="manifest" href="/favicon/white/site.webmanifest" media="(prefers-color-scheme: light)" />
20
20
  <link rel="manifest" href="/favicon/dark/site.webmanifest" media="(prefers-color-scheme: dark)" />
21
- <script type="module" crossorigin src="/assets/index-BWWpTTSd.js"></script>
21
+ <script type="module" crossorigin src="/assets/index-CBR_-QyA.js"></script>
22
22
  <link rel="stylesheet" crossorigin href="/assets/index-D_EJEA1n.css">
23
23
  </head>
24
24
  <body class="bg-background text-foreground antialiased">
@@ -73,7 +73,7 @@ export function AddProjectDialog({ open, onClose }: { open: boolean; onClose: ()
73
73
  try {
74
74
  const out = await Projects.register(trimmed);
75
75
  toast.success(t("add_project.registered", { id: out.id }));
76
- await mutate("/projects");
76
+ await mutate("/api/projects");
77
77
  onClose();
78
78
  navigate(`/p/${out.id}`);
79
79
  } catch (e) {
@@ -61,7 +61,7 @@ export function AreaRoleFields({
61
61
  onArea: (v: string) => void;
62
62
  onRole: (v: string) => void;
63
63
  }) {
64
- const org = useSWR(`/projects/${pid}/organization`, () => Org.get(pid));
64
+ const org = useSWR(`/api/projects/${pid}/organization`, () => Org.get(pid));
65
65
  const [areaDialog, setAreaDialog] = useState(false);
66
66
  const [roleDialog, setRoleDialog] = useState(false);
67
67
 
@@ -110,7 +110,7 @@ function AgentConvFetcher({
110
110
  onLoaded: (slug: string, data: ConversationListEntry[] | undefined) => void;
111
111
  }) {
112
112
  const { data } = useSWR(
113
- `/projects/${pid}/agents/${slug}/conversations`,
113
+ `/api/projects/${pid}/agents/${slug}/conversations`,
114
114
  () => Conversations.list(pid, slug),
115
115
  { revalidateOnFocus: false },
116
116
  );
@@ -142,7 +142,7 @@ export function ChatList({
142
142
  // sidebar shows just that project's own agent conversations.
143
143
  const isBase = String(pid) === "0";
144
144
  const threadsQ = useSWR(
145
- isBase ? `/projects/${pid}/super-agent/threads` : null,
145
+ isBase ? `/api/projects/${pid}/super-agent/threads` : null,
146
146
  () => Conversations.threads(pid),
147
147
  { revalidateOnFocus: false },
148
148
  );
@@ -27,7 +27,7 @@ export function SkillPicker({ value, projectPath, onPick }: SkillPickerProps) {
27
27
  const query = open ? extractQuery(value) : "";
28
28
 
29
29
  const { data } = useSWR(
30
- open ? ["/skills", projectPath || ""] : null,
30
+ open ? ["/api/skills", projectPath || ""] : null,
31
31
  () => Skills.list(projectPath),
32
32
  );
33
33
 
@@ -120,7 +120,7 @@ export function CodeFileTree({
120
120
  setLoading(true);
121
121
  try {
122
122
  const r = await http.post<{ ok: boolean; stdout: string; stderr: string }>(
123
- "/run",
123
+ "/api/run",
124
124
  {
125
125
  cmd: "find . -type f -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/dist/*' -not -path '*/.claude/*' | sed 's|^\\./||' | sort | head -500",
126
126
  project: pid,
@@ -50,7 +50,7 @@ export function CodeTerminal({
50
50
  setBusy(true);
51
51
  try {
52
52
  const r = await http.post<{ ok: boolean; stdout: string; stderr: string; exit_code: number; cwd: string }>(
53
- "/run",
53
+ "/api/run",
54
54
  { cmd: trimmed, project: pid },
55
55
  );
56
56
  if (r.stdout) setLines((l) => [...l, { type: "out", text: r.stdout }]);
@@ -17,7 +17,7 @@ export function DesktopStatusCard({ showConfigLink = false }: { showConfigLink?:
17
17
  const toast = useToast();
18
18
 
19
19
  const { data: status, isLoading: stLoading, mutate: mutateStatus } = useSWR(
20
- "/desktop/status",
20
+ "/api/desktop/status",
21
21
  () => Desktop.status(),
22
22
  { refreshInterval: 5000 },
23
23
  );
@@ -29,10 +29,10 @@ export function FileBrowser({
29
29
  const [selected, setSelected] = useState<string | null>(null);
30
30
  const [newOpen, setNewOpen] = useState(false);
31
31
 
32
- const treeKey = `/projects/${pid}/fs/tree?scope=${scope}`;
32
+ const treeKey = `/api/projects/${pid}/fs/tree?scope=${scope}`;
33
33
  const tree = useSWR(treeKey, () => ProjectFiles.tree(pid, scope));
34
34
 
35
- const fileKey = selected ? `/projects/${pid}/fs/file?scope=${scope}&path=${selected}` : null;
35
+ const fileKey = selected ? `/api/projects/${pid}/fs/file?scope=${scope}&path=${selected}` : null;
36
36
  const file = useSWR<FileContent | null>(fileKey, () => (selected ? ProjectFiles.read(pid, selected, scope) : null));
37
37
 
38
38
  const onSelect = (node: FileNode) => setSelected(node.path);
@@ -67,9 +67,9 @@ export function MemoryBrowser({ pid }: { pid: string }) {
67
67
  const toast = useToast();
68
68
  const [sel, setSel] = useState<Sel>({ kind: "project" });
69
69
 
70
- const agents = useSWR(`/projects/${pid}/agents`, () => Agents.list(pid));
70
+ const agents = useSWR(`/api/projects/${pid}/agents`, () => Agents.list(pid));
71
71
 
72
- const bodyKey = `/memory/${pid}/${selId(sel)}`;
72
+ const bodyKey = `/api/memory/${pid}/${selId(sel)}`;
73
73
  const body = useSWR(bodyKey, () => loadBody(pid, sel));
74
74
 
75
75
  // Adapt the raw memory body into the FileContent shape FileViewer expects.
@@ -120,7 +120,7 @@ function RunDetailPanel({ m, onClose }: { m: MessageEntry; onClose: () => void }
120
120
  * opens a side grid column with that run's details. */
121
121
  export function ExecutionsList({ pid, name, running }: { pid: string; name: string; running?: boolean }) {
122
122
  const runs = useSWR(
123
- `/projects/${pid}/routines/${name}/runs`,
123
+ `/api/projects/${pid}/routines/${name}/runs`,
124
124
  async () => {
125
125
  const msgs = await Messages.project(pid, { channel: "routine", limit: 200 });
126
126
  // Keep one row per run: the runner's end-of-run system summary.
@@ -15,7 +15,7 @@ export function RoutineEditor({
15
15
  draft, onClose, onSaved, pid,
16
16
  }: { draft: Partial<RoutineEntry> | null; onClose: () => void; onSaved: () => void; pid: string }) {
17
17
  const toast = useToast();
18
- const agentsList = useSWR(draft ? `/projects/${pid}/agents` : null, () => Agents.list(pid));
18
+ const agentsList = useSWR(draft ? `/api/projects/${pid}/agents` : null, () => Agents.list(pid));
19
19
  const [busy, setBusy] = useState(false);
20
20
 
21
21
  const [name, setName] = useState("");
@@ -35,7 +35,7 @@ export function RoutineEditor({
35
35
  const [post, setPost] = useState("");
36
36
 
37
37
  // Telegram channels for the selector (default + any project/global channels).
38
- const tgChannels = useSWR(draft && kind === "telegram" ? "/telegram/channels" : null, () => Telegram.channels.list());
38
+ const tgChannels = useSWR(draft && kind === "telegram" ? "/api/telegram/channels" : null, () => Telegram.channels.list());
39
39
 
40
40
  // Load draft → fields.
41
41
  useEffect(() => {
@@ -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);
@@ -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
 
@@ -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
  );
@@ -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 {
@@ -30,6 +30,19 @@ export type AuthState = Status & { reload: () => void };
30
30
  * the panel is opened over the LAN or a tunnel there's no auto-token: the
31
31
  * operator must pair the browser (see PairingScreen).
32
32
  */
33
+ // One in-flight confirm per pairing id. The daemon burns the nonce on first
34
+ // use, so a duplicate call would answer 409 and undo an otherwise successful
35
+ // pairing.
36
+ const inFlightConfirm = new Map<string, ReturnType<typeof Pair.confirm>>();
37
+
38
+ function confirmOnce(pairingId: string) {
39
+ const existing = inFlightConfirm.get(pairingId);
40
+ if (existing) return existing;
41
+ const p = Pair.confirm({ pairing_id: pairingId, label: deviceLabel(), kind: "web" });
42
+ inFlightConfirm.set(pairingId, p);
43
+ return p;
44
+ }
45
+
33
46
  export function useTokenBootstrap(): AuthState {
34
47
  const [state, setState] = useState<Status>({ status: "loading" });
35
48
  const [nonce, setNonce] = useState(0);
@@ -44,7 +57,7 @@ export function useTokenBootstrap(): AuthState {
44
57
  (async () => {
45
58
  // 1. Is the daemon even up?
46
59
  try {
47
- const probe = await fetch("/health");
60
+ const probe = await fetch("/api/health");
48
61
  if (!probe.ok) throw new Error(`HTTP ${probe.status}`);
49
62
  } catch (e) {
50
63
  if (!cancelled) setState({ status: "error", reason: String(e) });
@@ -57,15 +70,25 @@ export function useTokenBootstrap(): AuthState {
57
70
  const params = new URLSearchParams(hash);
58
71
  const pairId = params.get("pair");
59
72
  if (pairId) {
60
- history.replaceState(null, "", window.location.pathname + window.location.search);
61
73
  try {
62
- const res = await Pair.confirm({ pairing_id: pairId, label: deviceLabel(), kind: "web" });
74
+ // The nonce is ONE-SHOT on the daemon, and StrictMode runs this
75
+ // effect twice in development — so the second run must reuse the
76
+ // first call's promise instead of confirming again and getting
77
+ // "already confirmed" back. Keyed by id, module-level, because the
78
+ // component remounts between the two runs.
79
+ const res = await confirmOnce(pairId);
63
80
  setToken(res.token);
64
81
  try { localStorage.setItem(STORAGE.token, res.token); } catch { /* quota */ }
65
- if (!cancelled) setState({ status: "ok" });
82
+ // Only NOW drop the fragment. Stripping it first meant a failed
83
+ // confirm left the device with no way to retry: the nonce was spent
84
+ // and the link that carried it was already gone from the URL.
85
+ history.replaceState(null, "", window.location.pathname + window.location.search);
86
+ setState({ status: "ok" });
66
87
  return;
67
88
  } catch {
68
- // Expired/used nonce fall through to the manual pairing screen.
89
+ // Genuinely expired or already spent. Keep the fragment so a reload
90
+ // reports the same thing instead of silently showing a blank
91
+ // pairing screen, and fall through to the manual path.
69
92
  }
70
93
  }
71
94
 
@@ -84,7 +107,7 @@ export function useTokenBootstrap(): AuthState {
84
107
 
85
108
  // 3. Loopback endpoint — only succeeds on local same-origin requests.
86
109
  try {
87
- const t = await fetch("/admin/web-token");
110
+ const t = await fetch("/api/admin/web-token");
88
111
  if (t.ok) {
89
112
  const body = await t.json();
90
113
  if (body?.token) {
@@ -102,7 +125,7 @@ export function useTokenBootstrap(): AuthState {
102
125
 
103
126
  // 5. Validate the token with one cheap authenticated call.
104
127
  try {
105
- await http.get("/projects");
128
+ await http.get("/api/projects");
106
129
  if (!cancelled) setState({ status: "ok" });
107
130
  } catch (e) {
108
131
  if (e instanceof HttpError && (e.status === 401 || e.status === 403)) {
@@ -2,29 +2,29 @@ import { http } from "../http";
2
2
  import type { GlobalConfig, PairedClient, PairInit, PairStatus, SuperAgentConfig } from "../../types/daemon";
3
3
 
4
4
  export const Admin = {
5
- reload: () => http.post<{ ok: true; super_agent_model: string; fallback_order: string[] }>("/admin/reload"),
6
- shutdown: () => http.post<{ ok: true }>("/admin/shutdown"),
5
+ reload: () => http.post<{ ok: true; super_agent_model: string; fallback_order: string[] }>("/api/admin/reload"),
6
+ shutdown: () => http.post<{ ok: true }>("/api/admin/shutdown"),
7
7
  config: {
8
- get: () => http.get<{ config: GlobalConfig }>("/admin/config"),
8
+ get: () => http.get<{ config: GlobalConfig }>("/api/admin/config"),
9
9
  patch: (body: { set?: Record<string, unknown>; unset?: string[] }) =>
10
- http.patch<{ ok: true; config: GlobalConfig }>("/admin/config", body),
10
+ http.patch<{ ok: true; config: GlobalConfig }>("/api/admin/config", body),
11
11
  },
12
- superAgent: () => http.get<SuperAgentConfig>("/admin/super-agent"),
12
+ superAgent: () => http.get<SuperAgentConfig>("/api/admin/super-agent"),
13
13
  logs: (file: "errors" | "apx" = "errors", limit = 200) =>
14
14
  http.get<{ file: string; entries?: Array<Record<string, unknown>>; lines?: string[] }>(
15
- `/admin/logs?file=${file}&limit=${limit}`,
15
+ `/api/admin/logs?file=${file}&limit=${limit}`,
16
16
  ),
17
17
  };
18
18
 
19
19
  export const Pair = {
20
- list: () => http.get<{ clients: PairedClient[] }>("/pair/list"),
21
- revoke: (id: string) => http.del<void>(`/pair/revoke/${encodeURIComponent(id)}`),
20
+ list: () => http.get<{ clients: PairedClient[] }>("/api/pair/list"),
21
+ revoke: (id: string) => http.del<void>(`/api/pair/revoke/${encodeURIComponent(id)}`),
22
22
  // Mint a pairing nonce (localhost-only on the daemon). Used by the web's
23
23
  // "Vincular dispositivo" — same nonce the terminal `apx pair` uses.
24
- init: () => http.post<PairInit>("/pair/init", {}),
25
- status: (pid: string) => http.get<PairStatus>(`/pair/status/${encodeURIComponent(pid)}`),
24
+ init: () => http.post<PairInit>("/api/pair/init", {}),
25
+ status: (pid: string) => http.get<PairStatus>(`/api/pair/status/${encodeURIComponent(pid)}`),
26
26
  // Browser plays the "device" role: confirm a nonce (scanned via QR or pasted)
27
27
  // to mint a per-client token. Unauthenticated by design (nonce-gated).
28
28
  confirm: (body: { pairing_id: string; label?: string; kind?: string }) =>
29
- http.post<{ token: string; client_id: string; label: string; kind: string }>("/pair/confirm", body),
29
+ http.post<{ token: string; client_id: string; label: string; kind: string }>("/api/pair/confirm", body),
30
30
  };
@@ -3,24 +3,24 @@ import type { AgentDetail, AgentEntry, ChatUsage } from "../../types/daemon";
3
3
 
4
4
  export const Agents = {
5
5
  list: (pid: string, opts?: { stats?: boolean }) =>
6
- http.get<AgentEntry[]>(`/projects/${pid}/agents${opts?.stats ? "?stats=1" : ""}`),
7
- get: (pid: string, slug: string) => http.get<AgentDetail>(`/projects/${pid}/agents/${slug}`),
6
+ http.get<AgentEntry[]>(`/api/projects/${pid}/agents${opts?.stats ? "?stats=1" : ""}`),
7
+ get: (pid: string, slug: string) => http.get<AgentDetail>(`/api/projects/${pid}/agents/${slug}`),
8
8
  create: (pid: string, body: Partial<AgentEntry> & { slug: string }) =>
9
- http.post<AgentEntry>(`/projects/${pid}/agents`, body),
9
+ http.post<AgentEntry>(`/api/projects/${pid}/agents`, body),
10
10
  update: (pid: string, slug: string, body: Partial<AgentEntry> & { system?: string }) =>
11
- http.patch<AgentEntry>(`/projects/${pid}/agents/${encodeURIComponent(slug)}`, body),
11
+ http.patch<AgentEntry>(`/api/projects/${pid}/agents/${encodeURIComponent(slug)}`, body),
12
12
  remove: (pid: string, slug: string) =>
13
- http.del<{ ok: boolean }>(`/projects/${pid}/agents/${encodeURIComponent(slug)}`),
13
+ http.del<{ ok: boolean }>(`/api/projects/${pid}/agents/${encodeURIComponent(slug)}`),
14
14
  chat: (pid: string, slug: string, body: { prompt: string; conversation_id?: string; model?: string; channel?: string }) =>
15
15
  http.post<{ conversation_id: string; text: string; usage?: ChatUsage; engine: string }>(
16
- `/projects/${pid}/agents/${encodeURIComponent(slug)}/chat`,
16
+ `/api/projects/${pid}/agents/${encodeURIComponent(slug)}/chat`,
17
17
  body,
18
18
  ),
19
19
  memory: {
20
20
  get: (pid: string, slug: string) =>
21
- http.get<{ body: string }>(`/projects/${pid}/agents/${slug}/memory`),
21
+ http.get<{ body: string }>(`/api/projects/${pid}/agents/${slug}/memory`),
22
22
  put: (pid: string, slug: string, body: string) =>
23
- http.put<{ ok: boolean; bytes: number }>(`/projects/${pid}/agents/${slug}/memory`, { body }),
23
+ http.put<{ ok: boolean; bytes: number }>(`/api/projects/${pid}/agents/${slug}/memory`, { body }),
24
24
  },
25
25
  // Vault = global agent templates. Two-layer: bundled defaults shipped with
26
26
  // APX + user overrides/new ones in ~/.apx/agents. The API merges both and
@@ -28,20 +28,20 @@ export const Agents = {
28
28
  // Tombstones (deleted bundled defaults) are hidden unless includeRemoved=true.
29
29
  vault: (opts?: { includeRemoved?: boolean }) =>
30
30
  http.get<(AgentEntry & { source?: "bundled" | "user" | "user-override" })[]>(
31
- opts?.includeRemoved ? "/agents/vault?include_removed=1" : "/agents/vault",
31
+ opts?.includeRemoved ? "/api/agents/vault?include_removed=1" : "/api/agents/vault",
32
32
  ),
33
33
  vaultCreate: (slug: string, fields: Record<string, unknown> = {}, body = "") =>
34
- http.post<AgentEntry>("/agents/vault", { slug, fields, body }),
34
+ http.post<AgentEntry>("/api/agents/vault", { slug, fields, body }),
35
35
  vaultPatch: (slug: string, patch: { fields?: Record<string, unknown>; body?: string }) =>
36
- http.patch<AgentEntry>(`/agents/vault/${encodeURIComponent(slug)}`, patch),
36
+ http.patch<AgentEntry>(`/api/agents/vault/${encodeURIComponent(slug)}`, patch),
37
37
  vaultRemove: (slug: string) =>
38
38
  http.del<{ ok: boolean; removed: "user" | "tomb" | "user+tomb" }>(
39
- `/agents/vault/${encodeURIComponent(slug)}`,
39
+ `/api/agents/vault/${encodeURIComponent(slug)}`,
40
40
  ),
41
41
  vaultRestore: (slug: string) =>
42
42
  http.post<{ ok: boolean; agent: AgentEntry | null }>(
43
- `/agents/vault/${encodeURIComponent(slug)}/restore`,
43
+ `/api/agents/vault/${encodeURIComponent(slug)}/restore`,
44
44
  ),
45
45
  import: (pid: string, slug: string) =>
46
- http.post<AgentEntry>(`/projects/${pid}/agents/import`, { slug }),
46
+ http.post<AgentEntry>(`/api/projects/${pid}/agents/import`, { slug }),
47
47
  };
@@ -53,43 +53,43 @@ export interface ArtifactTunnel {
53
53
 
54
54
  export const Artifacts = {
55
55
  list: (pid: string) =>
56
- http.get<ArtifactEntry[]>(`/projects/${encodeURIComponent(pid)}/artifacts`),
56
+ http.get<ArtifactEntry[]>(`/api/projects/${encodeURIComponent(pid)}/artifacts`),
57
57
  read: (pid: string, name: string) =>
58
58
  http.get<ArtifactContent>(
59
- `/projects/${encodeURIComponent(pid)}/artifacts/${encodeURIComponent(name)}`,
59
+ `/api/projects/${encodeURIComponent(pid)}/artifacts/${encodeURIComponent(name)}`,
60
60
  ),
61
61
  run: (pid: string, name: string, args: string[] = []) =>
62
62
  http.post<ArtifactRunResult>(
63
- `/projects/${encodeURIComponent(pid)}/artifacts/${encodeURIComponent(name)}/run`,
63
+ `/api/projects/${encodeURIComponent(pid)}/artifacts/${encodeURIComponent(name)}/run`,
64
64
  { args },
65
65
  ),
66
66
  remove: (pid: string, name: string) =>
67
67
  http.del<void>(
68
- `/projects/${encodeURIComponent(pid)}/artifacts/${encodeURIComponent(name)}`,
68
+ `/api/projects/${encodeURIComponent(pid)}/artifacts/${encodeURIComponent(name)}`,
69
69
  ),
70
70
  write: (pid: string, name: string, content: string) =>
71
71
  http.patch<{ ok: boolean; name: string }>(
72
- `/projects/${encodeURIComponent(pid)}/artifacts/${encodeURIComponent(name)}`,
72
+ `/api/projects/${encodeURIComponent(pid)}/artifacts/${encodeURIComponent(name)}`,
73
73
  { content },
74
74
  ),
75
75
  rename: (pid: string, name: string, newName: string) =>
76
76
  http.patch<{ ok: boolean; name: string }>(
77
- `/projects/${encodeURIComponent(pid)}/artifacts/${encodeURIComponent(name)}`,
77
+ `/api/projects/${encodeURIComponent(pid)}/artifacts/${encodeURIComponent(name)}`,
78
78
  { newName },
79
79
  ),
80
80
 
81
81
  // Start (or reuse) an ephemeral local preview server for an artifact.
82
82
  preview: (pid: string, name: string, watch = true) =>
83
83
  http.post<ArtifactPreview>(
84
- `/projects/${encodeURIComponent(pid)}/artifacts/${encodeURIComponent(name)}/preview`,
84
+ `/api/projects/${encodeURIComponent(pid)}/artifacts/${encodeURIComponent(name)}/preview`,
85
85
  { watch },
86
86
  ),
87
87
  // List running preview servers for a project.
88
88
  previews: (pid: string) =>
89
- http.get<ArtifactPreview[]>(`/projects/${encodeURIComponent(pid)}/previews`),
90
- stopPreview: (id: string) => http.del<void>(`/previews/${encodeURIComponent(id)}`),
89
+ http.get<ArtifactPreview[]>(`/api/projects/${encodeURIComponent(pid)}/previews`),
90
+ stopPreview: (id: string) => http.del<void>(`/api/previews/${encodeURIComponent(id)}`),
91
91
  // Open / close a public tunnel to a running preview.
92
92
  openTunnel: (id: string, provider?: string) =>
93
- http.post<ArtifactTunnel>(`/previews/${encodeURIComponent(id)}/tunnel`, { provider }),
94
- closeTunnel: (id: string) => http.del<void>(`/previews/${encodeURIComponent(id)}/tunnel`),
93
+ http.post<ArtifactTunnel>(`/api/previews/${encodeURIComponent(id)}/tunnel`, { provider }),
94
+ closeTunnel: (id: string) => http.del<void>(`/api/previews/${encodeURIComponent(id)}/tunnel`),
95
95
  };
@@ -79,7 +79,7 @@ export type CodeStreamEvent = ChatStreamEvent & {
79
79
  tool?: string;
80
80
  };
81
81
 
82
- const base = (pid: string | number) => `/projects/${pid}/code/sessions`;
82
+ const base = (pid: string | number) => `/api/projects/${pid}/code/sessions`;
83
83
 
84
84
  export const Code = {
85
85
  sessions: {
@@ -3,26 +3,26 @@ import type { ConversationDetail, ConversationListEntry, ThreadListEntry, Thread
3
3
 
4
4
  export const Conversations = {
5
5
  list: (pid: string, slug: string) =>
6
- http.get<ConversationListEntry[]>(`/projects/${pid}/agents/${slug}/conversations`),
6
+ http.get<ConversationListEntry[]>(`/api/projects/${pid}/agents/${slug}/conversations`),
7
7
  get: (pid: string, slug: string, id: string) =>
8
- http.get<ConversationDetail>(`/projects/${pid}/agents/${slug}/conversations/${id}`),
8
+ http.get<ConversationDetail>(`/api/projects/${pid}/agents/${slug}/conversations/${id}`),
9
9
  // Super-agent channel threads (telegram, web quick-chat, desktop …) derived
10
10
  // from the global message ledger — one thread per channel+day.
11
11
  threads: (pid: string) =>
12
- http.get<ThreadListEntry[]>(`/projects/${pid}/super-agent/threads`),
12
+ http.get<ThreadListEntry[]>(`/api/projects/${pid}/super-agent/threads`),
13
13
  thread: (pid: string, channel: string, id: string) =>
14
- http.get<ThreadDetail>(`/projects/${pid}/super-agent/threads/${channel}/${id}`),
14
+ http.get<ThreadDetail>(`/api/projects/${pid}/super-agent/threads/${channel}/${id}`),
15
15
  // Delete a persisted agent conversation (its `.md` file).
16
16
  remove: (pid: string, slug: string, id: string) =>
17
- http.del<{ ok: boolean }>(`/projects/${pid}/agents/${slug}/conversations/${id}`),
17
+ http.del<{ ok: boolean }>(`/api/projects/${pid}/agents/${slug}/conversations/${id}`),
18
18
  // Delete a super-agent channel thread (its channel+day ledger file).
19
19
  removeThread: (pid: string, channel: string, id: string) =>
20
- http.del<{ ok: boolean }>(`/projects/${pid}/super-agent/threads/${channel}/${id}`),
20
+ http.del<{ ok: boolean }>(`/api/projects/${pid}/super-agent/threads/${channel}/${id}`),
21
21
  compact: (pid: string, slug: string, id?: string) =>
22
22
  http.post<{ ok?: boolean }>(
23
23
  id
24
- ? `/projects/${pid}/agents/${slug}/conversations/${id}/compact`
25
- : `/projects/${pid}/agents/${slug}/compact`,
24
+ ? `/api/projects/${pid}/agents/${slug}/conversations/${id}/compact`
25
+ : `/api/projects/${pid}/agents/${slug}/compact`,
26
26
  {}
27
27
  ),
28
28
  };