@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
@@ -89,14 +89,14 @@ function runArtifact({ absPath, cwd, args, timeoutMs = RUN_TIMEOUT_MS }) {
89
89
  });
90
90
  }
91
91
 
92
- export function register(app, { project }) {
93
- app.get("/projects/:pid/artifacts", (req, res) => {
92
+ export function register(api, { project }) {
93
+ api.get("/projects/:pid/artifacts", (req, res) => {
94
94
  const p = project(req, res);
95
95
  if (!p) return;
96
96
  res.json(listArtifacts(p.storagePath));
97
97
  });
98
98
 
99
- app.post("/projects/:pid/artifacts", (req, res) => {
99
+ api.post("/projects/:pid/artifacts", (req, res) => {
100
100
  const p = project(req, res);
101
101
  if (!p) return;
102
102
  const { name, content = "" } = req.body || {};
@@ -109,7 +109,7 @@ export function register(app, { project }) {
109
109
  }
110
110
  });
111
111
 
112
- app.get("/projects/:pid/artifacts/:name", (req, res) => {
112
+ api.get("/projects/:pid/artifacts/:name", (req, res) => {
113
113
  const p = project(req, res);
114
114
  if (!p) return;
115
115
  try {
@@ -119,7 +119,7 @@ export function register(app, { project }) {
119
119
  }
120
120
  });
121
121
 
122
- app.patch("/projects/:pid/artifacts/:name", (req, res) => {
122
+ api.patch("/projects/:pid/artifacts/:name", (req, res) => {
123
123
  const p = project(req, res);
124
124
  if (!p) return;
125
125
  const name = decodeURIComponent(req.params.name);
@@ -144,7 +144,7 @@ export function register(app, { project }) {
144
144
  }
145
145
  });
146
146
 
147
- app.delete("/projects/:pid/artifacts/:name", (req, res) => {
147
+ api.delete("/projects/:pid/artifacts/:name", (req, res) => {
148
148
  const p = project(req, res);
149
149
  if (!p) return;
150
150
  const ok = removeArtifact(
@@ -158,7 +158,7 @@ export function register(app, { project }) {
158
158
  // its own local spawn (stdio inherited) so it can run interactively. Output
159
159
  // is captured up to MAX_CAPTURE_BYTES and the call is bounded by
160
160
  // RUN_TIMEOUT_MS — anything longer should go through the terminal.
161
- app.post("/projects/:pid/artifacts/:name/run", async (req, res) => {
161
+ api.post("/projects/:pid/artifacts/:name/run", async (req, res) => {
162
162
  const p = project(req, res);
163
163
  if (!p) return;
164
164
  const name = decodeURIComponent(req.params.name);
@@ -47,18 +47,18 @@ function modeGuidanceFor(mode) {
47
47
  // codeSessionHistory() (transcript → engine messages) and makeTurnAccumulator()
48
48
  // (stream events → persistable ChatParts) imported above.
49
49
 
50
- export function register(app, { projects, project, config, registries, plugins }) {
50
+ export function register(api, { projects, project, config, registries, plugins }) {
51
51
  const findProject = (req, res) => project(req, res);
52
52
 
53
53
  // ---- List ----------------------------------------------------------------
54
- app.get("/projects/:pid/code/sessions", (req, res) => {
54
+ api.get("/projects/:pid/code/sessions", (req, res) => {
55
55
  const p = findProject(req, res);
56
56
  if (!p) return;
57
57
  res.json({ sessions: listCodeSessions(p.storagePath) });
58
58
  });
59
59
 
60
60
  // ---- Create (captures git baseline) --------------------------------------
61
- app.post("/projects/:pid/code/sessions", (req, res) => {
61
+ api.post("/projects/:pid/code/sessions", (req, res) => {
62
62
  const p = findProject(req, res);
63
63
  if (!p) return;
64
64
  const { title, model, mode, agentSlug } = req.body || {};
@@ -86,7 +86,7 @@ export function register(app, { projects, project, config, registries, plugins }
86
86
  });
87
87
 
88
88
  // ---- Get full transcript -------------------------------------------------
89
- app.get("/projects/:pid/code/sessions/:sid", (req, res) => {
89
+ api.get("/projects/:pid/code/sessions/:sid", (req, res) => {
90
90
  const p = findProject(req, res);
91
91
  if (!p) return;
92
92
  const session = getCodeSession(p.storagePath, req.params.sid);
@@ -95,7 +95,7 @@ export function register(app, { projects, project, config, registries, plugins }
95
95
  });
96
96
 
97
97
  // ---- Patch (rename / model / mode) ---------------------------------------
98
- app.patch("/projects/:pid/code/sessions/:sid", (req, res) => {
98
+ api.patch("/projects/:pid/code/sessions/:sid", (req, res) => {
99
99
  const p = findProject(req, res);
100
100
  if (!p) return;
101
101
  const session = updateCodeSession(p.storagePath, req.params.sid, req.body || {});
@@ -104,7 +104,7 @@ export function register(app, { projects, project, config, registries, plugins }
104
104
  });
105
105
 
106
106
  // ---- Delete --------------------------------------------------------------
107
- app.delete("/projects/:pid/code/sessions/:sid", (req, res) => {
107
+ api.delete("/projects/:pid/code/sessions/:sid", (req, res) => {
108
108
  const p = findProject(req, res);
109
109
  if (!p) return;
110
110
  const ok = removeCodeSession(p.storagePath, req.params.sid);
@@ -113,7 +113,7 @@ export function register(app, { projects, project, config, registries, plugins }
113
113
  });
114
114
 
115
115
  // ---- Changes (diff vs baseline) ------------------------------------------
116
- app.get("/projects/:pid/code/sessions/:sid/changes", (req, res) => {
116
+ api.get("/projects/:pid/code/sessions/:sid/changes", (req, res) => {
117
117
  const p = findProject(req, res);
118
118
  if (!p) return;
119
119
  const session = getCodeSession(p.storagePath, req.params.sid);
@@ -130,7 +130,7 @@ export function register(app, { projects, project, config, registries, plugins }
130
130
  });
131
131
 
132
132
  // ---- Streaming turn ------------------------------------------------------
133
- app.post("/projects/:pid/code/sessions/:sid/chat/stream", async (req, res) => {
133
+ api.post("/projects/:pid/code/sessions/:sid/chat/stream", async (req, res) => {
134
134
  const p = findProject(req, res);
135
135
  if (!p) return;
136
136
  const session = getCodeSession(p.storagePath, req.params.sid);
@@ -27,8 +27,8 @@ function writeProjectJson(root, body) {
27
27
  fs.writeFileSync(p, JSON.stringify(body, null, 2) + "\n");
28
28
  }
29
29
 
30
- export function register(app, { projects, project }) {
31
- app.get("/projects/:pid/config", (req, res) => {
30
+ export function register(api, { projects, project }) {
31
+ api.get("/projects/:pid/config", (req, res) => {
32
32
  const p = project(req, res);
33
33
  if (!p) return;
34
34
  // Redact secrets (engine api_keys, telegram bot tokens) the same way the
@@ -43,7 +43,7 @@ export function register(app, { projects, project }) {
43
43
  });
44
44
  });
45
45
 
46
- app.put("/projects/:pid/config", (req, res) => {
46
+ api.put("/projects/:pid/config", (req, res) => {
47
47
  const p = project(req, res);
48
48
  if (!p) return;
49
49
  const body = req.body || {};
@@ -57,7 +57,7 @@ export function register(app, { projects, project }) {
57
57
  res.json({ ok: true });
58
58
  });
59
59
 
60
- app.patch("/projects/:pid/config", (req, res) => {
60
+ api.patch("/projects/:pid/config", (req, res) => {
61
61
  const p = project(req, res);
62
62
  if (!p) return;
63
63
  const { set, unset } = req.body || {};
@@ -78,7 +78,7 @@ export function register(app, { projects, project }) {
78
78
  res.json({ ok: true, project_only: redactConfig(cfg) });
79
79
  });
80
80
 
81
- app.put("/projects/:pid/apc-project", (req, res) => {
81
+ api.put("/projects/:pid/apc-project", (req, res) => {
82
82
  const p = project(req, res);
83
83
  if (!p) return;
84
84
  const body = req.body || {};
@@ -93,7 +93,7 @@ export function register(app, { projects, project }) {
93
93
  }
94
94
  });
95
95
 
96
- app.patch("/projects/:pid/apc-project", (req, res) => {
96
+ api.patch("/projects/:pid/apc-project", (req, res) => {
97
97
  const p = project(req, res);
98
98
  if (!p) return;
99
99
  try {
@@ -10,8 +10,8 @@
10
10
 
11
11
  import { getConfirmationStore } from "#core/confirmation/pending-store.js";
12
12
 
13
- export function register(app) {
14
- app.post("/super-agent/confirm/:correlationId", async (req, res) => {
13
+ export function register(api) {
14
+ api.post("/super-agent/confirm/:correlationId", async (req, res) => {
15
15
  const { correlationId } = req.params;
16
16
  const { confirmed } = req.body;
17
17
 
@@ -3,8 +3,8 @@
3
3
  import { readAgents } from "#core/apc/parser.js";
4
4
  import { readProjectMessages } from "#core/stores/messages.js";
5
5
 
6
- export function register(app, { project }) {
7
- app.get("/projects/:pid/agents/:slug/connections", (req, res) => {
6
+ export function register(api, { project }) {
7
+ api.get("/projects/:pid/agents/:slug/connections", (req, res) => {
8
8
  const p = project(req, res);
9
9
  if (!p) return;
10
10
  const agents = readAgents(p.path);
@@ -11,9 +11,9 @@ import { listConversations, readConversation, deleteConversation } from "#core/s
11
11
  import { listGlobalThreads, readGlobalThread, deleteGlobalThread } from "#core/stores/messages.js";
12
12
  import { compactConversation } from "#core/stores/conversations-compactor.js";
13
13
  import { replyAsAgent } from "#core/agent/a2a/reply.js";
14
- import { nowIso } from "./shared.js";
14
+ import { nowIso, asyncRoute } from "./shared.js";
15
15
 
16
- export function register(app, { project, config }) {
16
+ export function register(api, { project, config }) {
17
17
  // The super-agent (default name "apx") is a pseudo-agent: it owns
18
18
  // conversations per project but is NOT listed in AGENTS.md. Resolve its slug
19
19
  // so `apx conversations list` (which defaults to the super-agent) works
@@ -22,7 +22,7 @@ export function register(app, { project, config }) {
22
22
  const agentResolvable = (p, slug) =>
23
23
  slug === superAgentSlug() || readAgents(p.path).some((a) => a.slug === slug);
24
24
 
25
- app.get("/projects/:pid/agents/:slug/conversations", (req, res) => {
25
+ api.get("/projects/:pid/agents/:slug/conversations", (req, res) => {
26
26
  const p = project(req, res);
27
27
  if (!p) return;
28
28
  if (!agentResolvable(p, req.params.slug))
@@ -30,7 +30,7 @@ export function register(app, { project, config }) {
30
30
  res.json(listConversations(p.storagePath, req.params.slug));
31
31
  });
32
32
 
33
- app.get("/projects/:pid/agents/:slug/conversations/:id", (req, res) => {
33
+ api.get("/projects/:pid/agents/:slug/conversations/:id", (req, res) => {
34
34
  const p = project(req, res);
35
35
  if (!p) return;
36
36
  const conv = readConversation(p.storagePath, req.params.slug, req.params.id);
@@ -51,7 +51,7 @@ export function register(app, { project, config }) {
51
51
  });
52
52
  });
53
53
 
54
- app.delete("/projects/:pid/agents/:slug/conversations/:id", (req, res) => {
54
+ api.delete("/projects/:pid/agents/:slug/conversations/:id", (req, res) => {
55
55
  const p = project(req, res);
56
56
  if (!p) return;
57
57
  if (!agentResolvable(p, req.params.slug))
@@ -66,13 +66,13 @@ export function register(app, { project, config }) {
66
66
  // in the global per-channel ledger, not in per-agent conversation files.
67
67
  // These endpoints surface that ledger as day-threads so the web Chats
68
68
  // sidebar can list and reopen them.
69
- app.get("/projects/:pid/super-agent/threads", (req, res) => {
69
+ api.get("/projects/:pid/super-agent/threads", (req, res) => {
70
70
  const p = project(req, res);
71
71
  if (!p) return;
72
72
  res.json(listGlobalThreads());
73
73
  });
74
74
 
75
- app.get("/projects/:pid/super-agent/threads/:channel/:id", (req, res) => {
75
+ api.get("/projects/:pid/super-agent/threads/:channel/:id", (req, res) => {
76
76
  const p = project(req, res);
77
77
  if (!p) return;
78
78
  const thread = readGlobalThread({
@@ -83,7 +83,7 @@ export function register(app, { project, config }) {
83
83
  res.json(thread);
84
84
  });
85
85
 
86
- app.delete("/projects/:pid/super-agent/threads/:channel/:id", (req, res) => {
86
+ api.delete("/projects/:pid/super-agent/threads/:channel/:id", (req, res) => {
87
87
  const p = project(req, res);
88
88
  if (!p) return;
89
89
  const ok = deleteGlobalThread({ channel: req.params.channel, date: req.params.id });
@@ -113,17 +113,17 @@ export function register(app, { project, config }) {
113
113
  }
114
114
  }
115
115
 
116
- app.post("/projects/:pid/agents/:slug/compact", (req, res) =>
116
+ api.post("/projects/:pid/agents/:slug/compact", (req, res) =>
117
117
  handleCompact(req, res, null)
118
118
  );
119
119
 
120
- app.post(
120
+ api.post(
121
121
  "/projects/:pid/agents/:slug/conversations/:id/compact",
122
122
  (req, res) => handleCompact(req, res, req.params.id)
123
123
  );
124
124
 
125
125
  // ---- Agent-to-agent routing ----
126
- app.post("/projects/:pid/send", async (req, res) => {
126
+ api.post("/projects/:pid/send", asyncRoute(async (req, res) => {
127
127
  const p = project(req, res);
128
128
  if (!p) return;
129
129
  const { from, to, body, deliver = false, _depth = 0 } = req.body || {};
@@ -199,5 +199,5 @@ export function register(app, { project, config }) {
199
199
  }
200
200
 
201
201
  res.json({ from, to, body, ts, reply });
202
- });
202
+ }));
203
203
  }
@@ -30,8 +30,8 @@ function safeProjects(projects) {
30
30
  }
31
31
  }
32
32
 
33
- export function register(app, ctx) {
34
- app.get("/deck/manifest", (_req, res) => {
33
+ export function register(api, ctx) {
34
+ api.get("/deck/manifest", (_req, res) => {
35
35
  const overrides =
36
36
  (ctx.config?.deck && typeof ctx.config.deck === "object" && ctx.config.deck.widget_overrides) || {};
37
37
  res.json(
@@ -51,7 +51,7 @@ export function register(app, ctx) {
51
51
  // Persists the user's enable/disable choice for an external widget into the
52
52
  // global config under `deck.widget_overrides[id]`. No-op for unknown widget
53
53
  // ids so the deck UI can stay forward-compatible.
54
- app.patch("/deck/widgets/:id", async (req, res) => {
54
+ api.patch("/deck/widgets/:id", async (req, res) => {
55
55
  const id = req.params.id;
56
56
  if (!TOGGLEABLE_WIDGETS.has(id)) {
57
57
  return res.status(404).json({ error: `unknown widget: ${id}` });
@@ -92,7 +92,7 @@ export function register(app, ctx) {
92
92
  //
93
93
  // Reads the project's AGENTS.md + .apc/memory.md, concatenates them with a
94
94
  // small header per file, and ships the result to the daemon-host clipboard.
95
- app.post("/projects/:pid/context/copy", async (req, res) => {
95
+ api.post("/projects/:pid/context/copy", async (req, res) => {
96
96
  const project = ctx.project ? ctx.project(req, res) : null;
97
97
  if (!project) return; // project() already 404'd
98
98
  try {
@@ -106,7 +106,7 @@ export function register(app, ctx) {
106
106
  });
107
107
 
108
108
  // POST /projects/:pid/notes body: { body: "...", title?: "..." }
109
- app.post("/projects/:pid/notes", async (req, res) => {
109
+ api.post("/projects/:pid/notes", async (req, res) => {
110
110
  const project = ctx.project ? ctx.project(req, res) : null;
111
111
  if (!project) return;
112
112
  const { body, title } = req.body || {};
@@ -129,7 +129,7 @@ export function register(app, ctx) {
129
129
  // Light-touch action runner for the deck buttons. Companion clients can't
130
130
  // shell out arbitrary commands — body picks from a whitelist of "intent"
131
131
  // verbs and the server picks the OS command. See deck-exec.js for the kinds.
132
- app.post("/deck/exec", async (req, res) => {
132
+ api.post("/deck/exec", async (req, res) => {
133
133
  const { kind, target, app: appHint, text } = req.body || {};
134
134
  if (!kind || typeof kind !== "string") {
135
135
  return res.status(400).json({ error: "kind required" });
@@ -25,8 +25,8 @@ import {
25
25
  stopDesktop,
26
26
  } from "#core/desktop/process.js";
27
27
 
28
- export function register(app, { plugins, config }) {
29
- app.get("/desktop/status", (_req, res) => {
28
+ export function register(api, { plugins, config }) {
29
+ api.get("/desktop/status", (_req, res) => {
30
30
  // `running` is the live Electron process (pid file) — the source of truth
31
31
  // for the Start/Stop/Restart controls. `connected_clients` is how many of
32
32
  // those windows have an open WS to the daemon (a window can be running but
@@ -41,7 +41,7 @@ export function register(app, { plugins, config }) {
41
41
 
42
42
  // POST /desktop/start — launch the floating window (detached Electron). Same
43
43
  // helper the CLI's `apx desktop start` uses. No-op-safe if already running.
44
- app.post("/desktop/start", async (_req, res) => {
44
+ api.post("/desktop/start", async (_req, res) => {
45
45
  try {
46
46
  const r = await startDesktopDetached({ port: config?.port });
47
47
  if (!r.ok) return res.status(500).json({ ok: false, error: r.error });
@@ -53,7 +53,7 @@ export function register(app, { plugins, config }) {
53
53
 
54
54
  // POST /desktop/stop — terminate the running window (SIGTERM). `stopped` is
55
55
  // false when nothing was running.
56
- app.post("/desktop/stop", (_req, res) => {
56
+ api.post("/desktop/stop", (_req, res) => {
57
57
  const r = stopDesktop();
58
58
  if (!r.ok) return res.status(500).json({ ok: false, error: r.error });
59
59
  res.json({ ok: true, stopped: r.stopped });
@@ -66,7 +66,7 @@ export function register(app, { plugins, config }) {
66
66
  // the renderer (main.js repositions + reloads webContents), NOT a process
67
67
  // kill — the Electron app keeps its tray/shortcut. Returns how many windows
68
68
  // were signalled so the UI can tell "reloaded" from "nothing connected".
69
- app.post("/desktop/restart", (_req, res) => {
69
+ api.post("/desktop/restart", (_req, res) => {
70
70
  import("../desktop-ws.js")
71
71
  .then(({ desktopClients, broadcastDesktop }) => {
72
72
  const reloaded = desktopClients.size;
@@ -76,7 +76,7 @@ export function register(app, { plugins, config }) {
76
76
  .catch((e) => res.status(500).json({ ok: false, error: e.message }));
77
77
  });
78
78
 
79
- app.post("/desktop/message", async (req, res) => {
79
+ api.post("/desktop/message", async (req, res) => {
80
80
  const { text, previousMessages = [] } = req.body || {};
81
81
  if (!text) return res.status(400).json({ error: "text required" });
82
82
  // Respond immediately — the real reply goes over WebSocket.
@@ -101,11 +101,11 @@ export function register(app, { plugins, config }) {
101
101
  // `apx desktop install/uninstall` CLI commands manage. Web admin uses
102
102
  // these so the user can flip the setting without dropping to a terminal.
103
103
 
104
- app.get("/desktop/autostart", (_req, res) => {
104
+ api.get("/desktop/autostart", (_req, res) => {
105
105
  res.json({ ok: true, enabled: autostartIsOn(), platform: process.platform });
106
106
  });
107
107
 
108
- app.post("/desktop/autostart", (req, res) => {
108
+ api.post("/desktop/autostart", (req, res) => {
109
109
  const { enable } = req.body || {};
110
110
  if (typeof enable !== "boolean") {
111
111
  return res.status(400).json({ ok: false, error: "body.enable must be a boolean" });
@@ -18,8 +18,8 @@ import {
18
18
  import { embedOne } from "#core/memory/embeddings.js";
19
19
  import { reindexMemory } from "#core/memory/index.js";
20
20
 
21
- export function register(app) {
22
- app.get("/embeddings/providers", async (_req, res) => {
21
+ export function register(api) {
22
+ api.get("/embeddings/providers", async (_req, res) => {
23
23
  try {
24
24
  const cfg = readConfig();
25
25
  const embedCfg = embeddingsConfig(cfg);
@@ -34,7 +34,7 @@ export function register(app) {
34
34
  }
35
35
  });
36
36
 
37
- app.post("/embeddings/test", async (req, res) => {
37
+ api.post("/embeddings/test", async (req, res) => {
38
38
  try {
39
39
  const { text, provider } = req.body || {};
40
40
  const sample = typeof text === "string" && text.trim()
@@ -54,7 +54,7 @@ export function register(app) {
54
54
  }
55
55
  });
56
56
 
57
- app.post("/embeddings/reindex", async (_req, res) => {
57
+ api.post("/embeddings/reindex", async (_req, res) => {
58
58
  try {
59
59
  const result = await reindexMemory({ config: readConfig() });
60
60
  res.json({ ok: true, ...result });
@@ -5,15 +5,16 @@
5
5
  import { ENGINE_IDS } from "#core/engines/index.js";
6
6
  import { listModels } from "#core/engines/catalog.js";
7
7
  import { ENGINE_PRESETS } from "#core/engines/presets.js";
8
+ import { asyncRoute } from "./shared.js";
8
9
 
9
- export function register(app, { config }) {
10
- app.get("/engines", (_req, res) => res.json({ engines: ENGINE_IDS }));
10
+ export function register(api, { config }) {
11
+ api.get("/engines", (_req, res) => res.json({ engines: ENGINE_IDS }));
11
12
 
12
13
  // Curated fallback catalog shared with the CLI wizard. The web hydrates its
13
14
  // provider forms from here so model lists never drift between surfaces.
14
- app.get("/engines/presets", (_req, res) => res.json({ presets: ENGINE_PRESETS }));
15
+ api.get("/engines/presets", (_req, res) => res.json({ presets: ENGINE_PRESETS }));
15
16
 
16
- app.post("/engines/models", async (req, res) => {
17
+ api.post("/engines/models", asyncRoute(async (req, res) => {
17
18
  const b = req.body || {};
18
19
  const engine = String(b.engine || "").toLowerCase();
19
20
  if (!engine) return res.status(400).json({ models: [], error: "engine requerido" });
@@ -25,13 +26,13 @@ export function register(app, { config }) {
25
26
  const out = await listModels(engine, b.base_url, apiKey);
26
27
  if (out.error) return res.status(502).json({ engine, models: [], error: out.error });
27
28
  res.json({ engine, models: out.models.sort((x, y) => x.localeCompare(y)) });
28
- });
29
+ }));
29
30
 
30
31
  // Legacy GET (Ollama, no auth) — kept for back-compat.
31
- app.get("/engines/models", async (req, res) => {
32
+ api.get("/engines/models", asyncRoute(async (req, res) => {
32
33
  const engine = String(req.query.engine || "").toLowerCase();
33
34
  const out = await listModels(engine, String(req.query.base_url || ""), "");
34
35
  if (out.error) return res.status(502).json({ engine, models: [], error: out.error });
35
36
  res.json({ engine, models: out.models });
36
- });
37
+ }));
37
38
  }
@@ -14,6 +14,7 @@ import {
14
14
  readConversation,
15
15
  setStatus,
16
16
  } from "#core/stores/conversations.js";
17
+ import { asyncRoute } from "./shared.js";
17
18
 
18
19
  // Pick a model for a direct agent chat: explicit override → agent's own model →
19
20
  // super-agent default (resolved via the same router the super-agent uses, so
@@ -29,8 +30,8 @@ async function pickAgentModel({ modelOverride, agent, config }) {
29
30
  }
30
31
  }
31
32
 
32
- export function register(app, { projects, project, config }) {
33
- app.post("/projects/:pid/agents/:slug/exec", async (req, res) => {
33
+ export function register(api, { projects, project, config }) {
34
+ api.post("/projects/:pid/agents/:slug/exec", async (req, res) => {
34
35
  const p = project(req, res);
35
36
  if (!p) return;
36
37
  const {
@@ -107,7 +108,7 @@ export function register(app, { projects, project, config }) {
107
108
  }
108
109
  });
109
110
 
110
- app.post("/projects/:pid/agents/:slug/chat", async (req, res) => {
111
+ api.post("/projects/:pid/agents/:slug/chat", asyncRoute(async (req, res) => {
111
112
  const p = project(req, res);
112
113
  if (!p) return;
113
114
  const {
@@ -206,5 +207,5 @@ export function register(app, { projects, project, config }) {
206
207
  } catch (e) {
207
208
  res.status(500).json({ error: e.message });
208
209
  }
209
- });
210
+ }));
210
211
  }
@@ -27,8 +27,8 @@ function scopeOpts(p, scope) {
27
27
  return { subdir: "", scope: "project" };
28
28
  }
29
29
 
30
- export function register(app, { project }) {
31
- app.get("/projects/:pid/fs/tree", (req, res) => {
30
+ export function register(api, { project }) {
31
+ api.get("/projects/:pid/fs/tree", (req, res) => {
32
32
  const p = project(req, res);
33
33
  if (!p) return;
34
34
  const { subdir, scope } = scopeOpts(p, req.query?.scope);
@@ -39,7 +39,7 @@ export function register(app, { project }) {
39
39
  }
40
40
  });
41
41
 
42
- app.get("/projects/:pid/fs/file", (req, res) => {
42
+ api.get("/projects/:pid/fs/file", (req, res) => {
43
43
  const p = project(req, res);
44
44
  if (!p) return;
45
45
  const rel = req.query?.path;
@@ -54,7 +54,7 @@ export function register(app, { project }) {
54
54
  }
55
55
  });
56
56
 
57
- app.put("/projects/:pid/fs/file", (req, res) => {
57
+ api.put("/projects/:pid/fs/file", (req, res) => {
58
58
  const p = project(req, res);
59
59
  if (!p) return;
60
60
  const { path: rel, content, scope } = req.body || {};
@@ -69,7 +69,7 @@ export function register(app, { project }) {
69
69
  }
70
70
  });
71
71
 
72
- app.post("/projects/:pid/fs/dir", (req, res) => {
72
+ api.post("/projects/:pid/fs/dir", (req, res) => {
73
73
  const p = project(req, res);
74
74
  if (!p) return;
75
75
  const { path: rel, scope } = req.body || {};
@@ -82,7 +82,7 @@ export function register(app, { project }) {
82
82
  }
83
83
  });
84
84
 
85
- app.delete("/projects/:pid/fs/entry", (req, res) => {
85
+ api.delete("/projects/:pid/fs/entry", (req, res) => {
86
86
  const p = project(req, res);
87
87
  if (!p) return;
88
88
  const rel = req.query?.path;
@@ -1,6 +1,6 @@
1
1
  // GET /health — unauthenticated; everything else requires the bearer token.
2
- export function register(app, { version, startedAt }) {
3
- app.get("/health", (_req, res) => {
2
+ export function register(api, { version, startedAt }) {
3
+ api.get("/health", (_req, res) => {
4
4
  res.json({
5
5
  status: "ok",
6
6
  version,
@@ -11,12 +11,12 @@ const ALLOWED_KEYS = new Set([
11
11
  "timezone",
12
12
  ]);
13
13
 
14
- export function register(app) {
15
- app.get("/identity", (_req, res) => {
14
+ export function register(api) {
15
+ api.get("/identity", (_req, res) => {
16
16
  res.json(readIdentity() || {});
17
17
  });
18
18
 
19
- app.patch("/identity", (req, res) => {
19
+ api.patch("/identity", (req, res) => {
20
20
  const body = req.body || {};
21
21
  const patch = {};
22
22
  for (const [k, v] of Object.entries(body)) {
@@ -8,8 +8,8 @@ import { readConfig } from "#core/config/index.js";
8
8
  import { resolveAgentName } from "#core/identity/index.js";
9
9
  import { pageEnvelope } from "./shared.js";
10
10
 
11
- export function register(app, { projects }) {
12
- app.get("/inbox", (req, res) => {
11
+ export function register(api, { projects }) {
12
+ api.get("/inbox", (req, res) => {
13
13
  try {
14
14
  const entries = [];
15
15
  for (const entry of projects.list()) {