@agentprojectcontext/apx 1.80.2 → 1.89.1

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 (838) hide show
  1. package/package.json +9 -8
  2. package/skills/apc-context/SKILL.md +14 -7
  3. package/skills/apx/SKILL.md +62 -2
  4. package/skills/apx-mcp/SKILL.md +42 -15
  5. package/src/core/agent/a2a/peers.js +161 -0
  6. package/src/core/agent/a2a/reply.js +379 -27
  7. package/src/core/agent/abort.js +25 -0
  8. package/src/core/agent/agent-model.js +58 -0
  9. package/src/core/agent/agent-tools.js +101 -0
  10. package/src/core/agent/author-line.js +110 -0
  11. package/src/core/agent/build-agent-system.js +52 -18
  12. package/src/core/agent/channels/voice-context.js +17 -2
  13. package/src/core/agent/closing-floor.js +117 -0
  14. package/src/core/agent/constants.js +119 -11
  15. package/src/core/agent/engine-call.js +56 -0
  16. package/src/core/agent/group/run-group-turn.js +218 -0
  17. package/src/core/agent/group/turn-resolver.js +165 -0
  18. package/src/core/agent/judge.js +138 -11
  19. package/src/core/agent/loop/side-effects.js +106 -3
  20. package/src/core/agent/memory.js +31 -18
  21. package/src/core/agent/model-capabilities.js +47 -0
  22. package/src/core/agent/model-router.js +11 -40
  23. package/src/core/agent/prompt-builder.js +47 -0
  24. package/src/core/agent/prompts/channels/a2a.md +12 -0
  25. package/src/core/agent/prompts/channels/code.md +3 -1
  26. package/src/core/agent/prompts/channels/telegram.md +2 -0
  27. package/src/core/agent/prompts/channels/web_code.md +1 -1
  28. package/src/core/agent/prompts/channels/whatsapp.md +37 -0
  29. package/src/core/agent/prompts/core/agent-base.md +2 -0
  30. package/src/core/agent/prompts/discipline/action.md +1 -0
  31. package/src/core/agent/prompts/discipline/two-segment.md +2 -1
  32. package/src/core/agent/prompts/modes/mobility.md +36 -0
  33. package/src/core/agent/retry.js +42 -0
  34. package/src/core/agent/run-agent.js +322 -25
  35. package/src/core/agent/run-turn.js +253 -0
  36. package/src/core/agent/self-memory.js +18 -52
  37. package/src/core/agent/skills/agent-skills.js +118 -0
  38. package/src/core/agent/skills/catalog.js +1 -1
  39. package/src/core/agent/skills/declared.js +13 -0
  40. package/src/core/agent/skills/index-store.js +8 -1
  41. package/src/core/agent/skills/inspector.js +20 -3
  42. package/src/core/agent/skills/loader.js +18 -3
  43. package/src/core/agent/skills/media.js +121 -0
  44. package/src/core/agent/skills/origin.js +61 -0
  45. package/src/core/agent/skills/sections.js +136 -0
  46. package/src/core/agent/super-agent.js +54 -14
  47. package/src/core/agent/tools/handlers/_calendar.js +50 -0
  48. package/src/core/agent/tools/handlers/add-mcp.js +103 -0
  49. package/src/core/agent/tools/handlers/asana-create-task.js +3 -1
  50. package/src/core/agent/tools/handlers/attach-media.js +56 -0
  51. package/src/core/agent/tools/handlers/calendar-create-event.js +38 -0
  52. package/src/core/agent/tools/handlers/calendar-find-slot.js +39 -0
  53. package/src/core/agent/tools/handlers/calendar-list-events.js +39 -0
  54. package/src/core/agent/tools/handlers/calendar-update-event.js +35 -0
  55. package/src/core/agent/tools/handlers/call-agent.js +14 -6
  56. package/src/core/agent/tools/handlers/call-runtime.js +27 -49
  57. package/src/core/agent/tools/handlers/comment-task.js +78 -0
  58. package/src/core/agent/tools/handlers/complete-task.js +60 -0
  59. package/src/core/agent/tools/handlers/configure-agent.js +70 -0
  60. package/src/core/agent/tools/handlers/create-agent.js +68 -0
  61. package/src/core/agent/tools/handlers/create-task.js +41 -3
  62. package/src/core/agent/tools/handlers/import-agent.js +5 -5
  63. package/src/core/agent/tools/handlers/list-mcp-tools.js +83 -0
  64. package/src/core/agent/tools/handlers/list-routines.js +46 -0
  65. package/src/core/agent/tools/handlers/list-tasks.js +8 -1
  66. package/src/core/agent/tools/handlers/mark-commitment.js +57 -0
  67. package/src/core/agent/tools/handlers/read-skill.js +64 -0
  68. package/src/core/agent/tools/handlers/remember-routine.js +2 -8
  69. package/src/core/agent/tools/handlers/remember.js +82 -6
  70. package/src/core/agent/tools/handlers/remove-agent.js +42 -0
  71. package/src/core/agent/tools/handlers/run-routine.js +94 -0
  72. package/src/core/agent/tools/handlers/run-shell.js +44 -2
  73. package/src/core/agent/tools/handlers/send-telegram.js +41 -19
  74. package/src/core/agent/tools/handlers/set-agent-prompt.js +44 -0
  75. package/src/core/agent/tools/handlers/view-media.js +69 -0
  76. package/src/core/agent/tools/handlers/write-agent-memory.js +52 -0
  77. package/src/core/agent/tools/helpers.js +36 -4
  78. package/src/core/agent/tools/names.js +64 -0
  79. package/src/core/agent/tools/registry-bridge.js +24 -1
  80. package/src/core/agent/tools/registry.js +79 -0
  81. package/src/core/agent/tools/tool-call-parser.js +136 -13
  82. package/src/core/agent/vision-bridge.js +69 -0
  83. package/src/core/apc/agent-identity.js +74 -0
  84. package/src/core/apc/agent-rename-refs.js +187 -0
  85. package/src/core/apc/agent-write.js +312 -0
  86. package/src/core/apc/agents-vault.js +1 -1
  87. package/src/core/apc/blob-keys.js +42 -0
  88. package/src/core/apc/frontmatter.js +166 -10
  89. package/src/core/apc/parser.js +33 -83
  90. package/src/core/apc/paths.js +4 -6
  91. package/src/core/apc/project-memory.js +44 -0
  92. package/src/core/apc/projects-helpers.js +33 -0
  93. package/src/core/apc/scaffold.js +36 -16
  94. package/src/core/channels/telegram/ask-callbacks.js +103 -0
  95. package/src/core/channels/telegram/dispatch.js +154 -30
  96. package/src/core/channels/telegram/helpers.js +59 -7
  97. package/src/core/channels/telegram/inbound/audio.js +26 -30
  98. package/src/core/channels/telegram/inbound/file.js +22 -32
  99. package/src/core/channels/telegram/inbound/photo.js +24 -28
  100. package/src/core/channels/telegram/progress-gate.js +76 -0
  101. package/src/core/channels/telegram/reply.js +151 -43
  102. package/src/core/channels/telegram/voice-note.js +150 -0
  103. package/src/core/config/index.js +53 -54
  104. package/src/core/config/paths.js +117 -31
  105. package/src/core/config/redact.js +89 -4
  106. package/src/core/config/secret-values.js +2 -4
  107. package/src/core/constants/actors.js +5 -0
  108. package/src/core/constants/channels.js +3 -1
  109. package/src/core/constants/task-categories.js +73 -0
  110. package/src/core/constants/task-fields.js +28 -0
  111. package/src/core/engines/_globs.js +33 -0
  112. package/src/core/engines/anthropic.js +19 -1
  113. package/src/core/engines/catalog.js +20 -0
  114. package/src/core/engines/gemini.js +84 -16
  115. package/src/core/engines/index.js +19 -3
  116. package/src/core/engines/mock.js +145 -1
  117. package/src/core/engines/openai-compatible.js +150 -15
  118. package/src/core/engines/presets.js +36 -9
  119. package/src/core/engines/zen.js +131 -0
  120. package/src/core/events/bus.js +140 -0
  121. package/src/core/events/mascot-notify.js +156 -0
  122. package/src/core/http-tools/browser.js +366 -36
  123. package/src/core/http-tools/catalog.js +206 -22
  124. package/src/core/http-tools/grep.js +62 -7
  125. package/src/core/http-tools/registry.js +3 -1
  126. package/src/core/i18n/en.js +34 -3
  127. package/src/core/i18n/es.js +36 -4
  128. package/src/core/i18n/index.js +5 -5
  129. package/src/core/i18n/pt.js +32 -3
  130. package/src/core/identity/self.js +14 -1
  131. package/src/core/images/engines/a1111.js +219 -0
  132. package/src/core/images/engines/index.js +187 -0
  133. package/src/core/images/engines/mock.js +74 -0
  134. package/src/core/images/engines/openai.js +130 -0
  135. package/src/core/images/engines/sdcpp.js +204 -0
  136. package/src/core/images/engines/shared.js +218 -0
  137. package/src/core/images/generate.js +260 -0
  138. package/src/core/integrations/catalog.js +8 -3
  139. package/src/core/integrations/plugins/_google-oauth.js +179 -0
  140. package/src/core/integrations/plugins/asana.js +7 -3
  141. package/src/core/integrations/plugins/calendar.js +404 -0
  142. package/src/core/integrations/store.js +2 -0
  143. package/src/core/memory/broker.js +16 -1
  144. package/src/core/memory/compactor.js +19 -3
  145. package/src/core/memory/consolidate.js +2 -2
  146. package/src/core/memory/dated-log.js +66 -0
  147. package/src/core/memory/embed-engines/custom.js +59 -0
  148. package/src/core/memory/embed-engines/gemini.js +9 -3
  149. package/src/core/memory/embed-engines/index.js +87 -15
  150. package/src/core/memory/embeddings.js +52 -24
  151. package/src/core/memory/index.js +13 -3
  152. package/src/core/memory/indexer.js +83 -27
  153. package/src/core/memory/prune.js +182 -0
  154. package/src/core/memory/store.js +22 -0
  155. package/src/core/memory/summarizer.js +72 -8
  156. package/src/core/mobility/answer.js +80 -0
  157. package/src/core/mobility/geofence.js +667 -0
  158. package/src/core/mobility/osm-route.js +282 -0
  159. package/src/core/mobility/preferences.js +91 -0
  160. package/src/core/mobility/state.js +245 -0
  161. package/src/core/mobility/trip-event.js +384 -0
  162. package/src/core/net/tailscale.js +168 -0
  163. package/src/core/profiles/bundled/secretary/PROFILE.md +32 -42
  164. package/src/core/profiles/bundled/secretary/config.schema.es.json +68 -0
  165. package/src/core/profiles/bundled/secretary/config.schema.json +30 -2
  166. package/src/core/profiles/bundled/secretary/profile.json +3 -1
  167. package/src/core/profiles/bundled/secretary/routines/a2a-sweep.json +20 -0
  168. package/src/core/profiles/bundled/secretary/routines/day-close.json +4 -1
  169. package/src/core/profiles/bundled/secretary/routines/day-open.json +7 -1
  170. package/src/core/profiles/bundled/secretary/routines/watch.json +13 -2
  171. package/src/core/profiles/lifecycle.js +243 -19
  172. package/src/core/profiles/paths.js +12 -0
  173. package/src/core/profiles/store.js +47 -0
  174. package/src/core/routines/active-runs.js +213 -0
  175. package/src/core/routines/delivery.js +612 -0
  176. package/src/core/routines/header.js +88 -0
  177. package/src/core/routines/index.js +26 -0
  178. package/src/core/routines/run-log.js +78 -0
  179. package/src/core/routines/runner.js +629 -66
  180. package/src/core/routines/signals.js +75 -0
  181. package/src/core/runtime-skills/apc-context/SKILL.md +14 -7
  182. package/src/core/runtime-skills/apx/SKILL.md +93 -1
  183. package/src/core/runtime-skills/apx-agency-agents/SKILL.md +2 -2
  184. package/src/core/runtime-skills/apx-agent/SKILL.md +134 -19
  185. package/src/core/runtime-skills/apx-commitment/SKILL.md +134 -0
  186. package/src/core/runtime-skills/apx-image/SKILL.md +60 -0
  187. package/src/core/runtime-skills/apx-integrations/SKILL.md +82 -0
  188. package/src/core/runtime-skills/apx-mcp/SKILL.md +45 -14
  189. package/src/core/runtime-skills/apx-mcp-builder/SKILL.md +4 -4
  190. package/src/core/runtime-skills/apx-profile/SKILL.md +27 -2
  191. package/src/core/runtime-skills/apx-project/SKILL.md +23 -2
  192. package/src/core/runtime-skills/apx-routine/SKILL.md +40 -17
  193. package/src/core/runtime-skills/apx-runtime/SKILL.md +124 -14
  194. package/src/core/runtime-skills/apx-sessions/SKILL.md +7 -6
  195. package/src/core/runtime-skills/apx-skill-builder/SKILL.md +15 -13
  196. package/src/core/runtime-skills/apx-task/SKILL.md +79 -26
  197. package/src/core/runtime-skills/apx-telegram/SKILL.md +12 -12
  198. package/src/core/runtime-skills/apx-transcribe/SKILL.md +56 -0
  199. package/src/core/runtime-skills/apx-voice/SKILL.md +27 -8
  200. package/src/core/runtime-skills/claude-code/SKILL.md +8 -0
  201. package/src/core/runtime-skills/openrouter/SKILL.md +8 -2
  202. package/src/core/runtimes/_spawn.js +9 -1
  203. package/src/core/runtimes/claude-code.js +11 -2
  204. package/src/core/runtimes/codex.js +44 -10
  205. package/src/core/runtimes/index.js +17 -2
  206. package/src/core/runtimes/opencode.js +88 -9
  207. package/src/core/runtimes/outcome.js +119 -0
  208. package/src/core/sessions/index.js +193 -1
  209. package/src/core/stores/agent-inbox.js +109 -23
  210. package/src/core/stores/code-sessions.js +21 -0
  211. package/src/core/stores/commitments.js +27 -3
  212. package/src/core/stores/conversations.js +220 -20
  213. package/src/core/stores/deliveries.js +177 -0
  214. package/src/core/stores/media-archive.js +228 -0
  215. package/src/core/stores/messages.js +1096 -112
  216. package/src/core/stores/organization.js +34 -0
  217. package/src/core/stores/project-memory.js +71 -0
  218. package/src/core/stores/routine-memory.js +16 -8
  219. package/src/core/stores/routines.js +27 -49
  220. package/src/core/stores/sessions.js +4 -8
  221. package/src/core/stores/tasks.js +212 -18
  222. package/src/core/stores/turn-record.js +209 -0
  223. package/src/core/tasks/columns.js +136 -0
  224. package/src/core/tasks/comment-turn.js +258 -0
  225. package/src/core/util/time.js +13 -0
  226. package/src/core/voice/engines/index.js +54 -1
  227. package/src/core/voice/engines/openai.js +50 -4
  228. package/src/core/voice/opus.js +67 -0
  229. package/src/core/voice/pronounceable.js +43 -0
  230. package/src/core/voice/transcription.js +4 -0
  231. package/src/core/voice/tts.js +114 -24
  232. package/src/host/daemon/active-turns.js +188 -0
  233. package/src/host/daemon/api/admin-config.js +9 -0
  234. package/src/host/daemon/api/agents.js +99 -90
  235. package/src/host/daemon/api/artifact-preview.js +7 -6
  236. package/src/host/daemon/api/artifacts.js +3 -2
  237. package/src/host/daemon/api/code.js +55 -9
  238. package/src/host/daemon/api/commitments.js +11 -0
  239. package/src/host/daemon/api/confirm.js +3 -2
  240. package/src/host/daemon/api/conversations.js +543 -35
  241. package/src/host/daemon/api/deck.js +9 -8
  242. package/src/host/daemon/api/deliveries.js +33 -0
  243. package/src/host/daemon/api/desktop.js +5 -4
  244. package/src/host/daemon/api/embeddings.js +15 -6
  245. package/src/host/daemon/api/engines.js +59 -8
  246. package/src/host/daemon/api/exec.js +437 -121
  247. package/src/host/daemon/api/groups.js +215 -0
  248. package/src/host/daemon/api/images.js +107 -0
  249. package/src/host/daemon/api/inbox.js +123 -2
  250. package/src/host/daemon/api/integrations.js +64 -2
  251. package/src/host/daemon/api/mcps.js +7 -6
  252. package/src/host/daemon/api/media.js +283 -0
  253. package/src/host/daemon/api/mobility.js +177 -0
  254. package/src/host/daemon/api/net.js +182 -0
  255. package/src/host/daemon/api/pairing.js +38 -5
  256. package/src/host/daemon/api/profiles.js +30 -2
  257. package/src/host/daemon/api/routines.js +36 -4
  258. package/src/host/daemon/api/runtimes.js +74 -39
  259. package/src/host/daemon/api/sessions-search.js +7 -2
  260. package/src/host/daemon/api/sessions.js +44 -2
  261. package/src/host/daemon/api/shared.js +127 -20
  262. package/src/host/daemon/api/skills.js +11 -4
  263. package/src/host/daemon/api/super-agent.js +384 -53
  264. package/src/host/daemon/api/tasks.js +180 -10
  265. package/src/host/daemon/api/telegram.js +11 -10
  266. package/src/host/daemon/api/thread-faces.js +159 -0
  267. package/src/host/daemon/api/top-level.js +3 -3
  268. package/src/host/daemon/api/transcribe.js +11 -10
  269. package/src/host/daemon/api/tts.js +97 -4
  270. package/src/host/daemon/api/turn-abort.js +23 -0
  271. package/src/host/daemon/api/turns.js +49 -0
  272. package/src/host/daemon/api/voice.js +5 -4
  273. package/src/host/daemon/api/web.js +98 -0
  274. package/src/host/daemon/api.js +39 -0
  275. package/src/host/daemon/delivery-sweep.js +58 -0
  276. package/src/host/daemon/desktop-ws.js +5 -23
  277. package/src/host/daemon/events-ws.js +273 -0
  278. package/src/host/daemon/index.js +59 -15
  279. package/src/host/daemon/plugins/desktop/index.js +17 -4
  280. package/src/host/daemon/plugins/telegram/index.js +78 -40
  281. package/src/host/daemon/pty-bridge.py +99 -0
  282. package/src/host/daemon/routines-scheduler.js +1 -0
  283. package/src/host/daemon/smoke.js +1 -2
  284. package/src/host/daemon/terminal-ws.js +132 -0
  285. package/src/host/daemon/tts-keepwarm.js +87 -0
  286. package/src/host/daemon/wakeup.js +11 -6
  287. package/src/host/daemon/whisper-server.js +44 -0
  288. package/src/host/daemon/whisper-server.py +70 -13
  289. package/src/host/daemon/ws-auth.js +28 -0
  290. package/src/interfaces/android/app/build.gradle +42 -0
  291. package/src/interfaces/android/app/proguard-rules.pro +4 -0
  292. package/src/interfaces/android/app/src/main/AndroidManifest.xml +139 -0
  293. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/AlertScreen.java +139 -0
  294. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/AndroidAutoDetector.java +106 -0
  295. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/ApxCarAppService.java +57 -0
  296. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/ApxPreferences.java +165 -0
  297. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/CarAlertStore.java +84 -0
  298. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/CarMessageActionReceiver.java +47 -0
  299. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/CarMessageNotification.java +152 -0
  300. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/DaemonAddress.java +35 -0
  301. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/DaemonClient.java +339 -0
  302. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/DeviceLocation.java +38 -0
  303. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/ErrandScreen.java +127 -0
  304. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MainActivity.java +872 -0
  305. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MapsNavigationDetector.java +63 -0
  306. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MapsNavigationListenerService.java +453 -0
  307. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MapsShareIntentParser.java +93 -0
  308. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MascotBlobCatalog.java +66 -0
  309. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MascotOverlayService.java +448 -0
  310. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MascotView.java +249 -0
  311. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MessageFrameParser.java +240 -0
  312. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/NotifyChannels.java +69 -0
  313. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/ProximityActionReceiver.java +78 -0
  314. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/ProximityNotification.java +161 -0
  315. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/StartupReceiver.java +57 -0
  316. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/TravelEventGate.java +62 -0
  317. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/TravelStatusBanner.java +116 -0
  318. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/TripLocationService.java +272 -0
  319. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/TripPlacesSheet.java +203 -0
  320. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/TripScreen.java +182 -0
  321. package/src/interfaces/android/app/src/main/res/drawable/app_background.xml +12 -0
  322. package/src/interfaces/android/app/src/main/res/drawable/ic_apx_app.xml +10 -0
  323. package/src/interfaces/android/app/src/main/res/drawable/ic_apx_launcher_foreground.xml +12 -0
  324. package/src/interfaces/android/app/src/main/res/drawable/ic_apx_notification.xml +22 -0
  325. package/src/interfaces/android/app/src/main/res/drawable/ic_trip_car.xml +37 -0
  326. package/src/interfaces/android/app/src/main/res/drawable-nodpi/apx_logo.png +0 -0
  327. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_amatista.png +0 -0
  328. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_aqua.png +0 -0
  329. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_brasa.png +0 -0
  330. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_campana.png +0 -0
  331. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_caracol.png +0 -0
  332. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_carbon.png +0 -0
  333. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_cobalto.png +0 -0
  334. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_cometa.png +0 -0
  335. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_coral.png +0 -0
  336. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_cubi.png +0 -0
  337. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_eclipse.png +0 -0
  338. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_faro.png +0 -0
  339. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_gajo.png +0 -0
  340. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_iris.png +0 -0
  341. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_kiwi.png +0 -0
  342. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_laguna.png +0 -0
  343. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_lila.png +0 -0
  344. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_menta.png +0 -0
  345. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_nimbo.png +0 -0
  346. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_noche.png +0 -0
  347. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_onyx.png +0 -0
  348. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_orbita.png +0 -0
  349. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_ovni.png +0 -0
  350. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_papa.png +0 -0
  351. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_parche.png +0 -0
  352. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_peon.png +0 -0
  353. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_perla.png +0 -0
  354. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_pino.png +0 -0
  355. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_quad.png +0 -0
  356. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_rubi.png +0 -0
  357. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_saturno.png +0 -0
  358. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_trebol.png +0 -0
  359. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_trino.png +0 -0
  360. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_volcan.png +0 -0
  361. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_zafiro.png +0 -0
  362. package/src/interfaces/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +5 -0
  363. package/src/interfaces/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +5 -0
  364. package/src/interfaces/android/app/src/main/res/raw/apx_notification.mp3 +0 -0
  365. package/src/interfaces/android/app/src/main/res/values/colors.xml +4 -0
  366. package/src/interfaces/android/app/src/main/res/values/strings.xml +25 -0
  367. package/src/interfaces/android/app/src/main/res/values/styles.xml +10 -0
  368. package/src/interfaces/android/app/src/main/res/values-v31/styles.xml +14 -0
  369. package/src/interfaces/android/app/src/main/res/xml/automotive_app_desc.xml +9 -0
  370. package/src/interfaces/android/app/src/main/res/xml/network_security_config.xml +10 -0
  371. package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/AndroidAutoDetectorTest.java +85 -0
  372. package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/CarAlertStoreTest.java +104 -0
  373. package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/DaemonAddressTest.java +23 -0
  374. package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/MapsNavigationDetectorTest.java +69 -0
  375. package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/MapsShareIntentParserTest.java +31 -0
  376. package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/MessageFrameParserTest.java +180 -0
  377. package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/TravelEventGateTest.java +64 -0
  378. package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/TripPlacesSheetTest.java +66 -0
  379. package/src/interfaces/android/build.gradle +3 -0
  380. package/src/interfaces/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  381. package/src/interfaces/android/gradle/wrapper/gradle-wrapper.properties +9 -0
  382. package/src/interfaces/android/gradle.properties +3 -0
  383. package/src/interfaces/android/gradlew +248 -0
  384. package/src/interfaces/android/gradlew.bat +82 -0
  385. package/src/interfaces/android/settings.gradle +18 -0
  386. package/src/interfaces/cli/commands/a2a.js +145 -4
  387. package/src/interfaces/cli/commands/agent.js +247 -22
  388. package/src/interfaces/cli/commands/code.js +1 -1
  389. package/src/interfaces/cli/commands/commitment.js +5 -0
  390. package/src/interfaces/cli/commands/config.js +85 -6
  391. package/src/interfaces/cli/commands/exec.js +90 -1
  392. package/src/interfaces/cli/commands/image.js +314 -0
  393. package/src/interfaces/cli/commands/mcp.js +87 -23
  394. package/src/interfaces/cli/commands/memory.js +35 -22
  395. package/src/interfaces/cli/commands/panel.js +94 -0
  396. package/src/interfaces/cli/commands/profile.js +14 -0
  397. package/src/interfaces/cli/commands/project.js +29 -0
  398. package/src/interfaces/cli/commands/routine.js +45 -10
  399. package/src/interfaces/cli/commands/runtime.js +30 -7
  400. package/src/interfaces/cli/commands/session.js +90 -38
  401. package/src/interfaces/cli/commands/sessions.js +35 -2
  402. package/src/interfaces/cli/commands/skills.js +15 -9
  403. package/src/interfaces/cli/commands/status.js +2 -3
  404. package/src/interfaces/cli/commands/task.js +54 -5
  405. package/src/interfaces/cli/commands/telegram.js +12 -4
  406. package/src/interfaces/cli/commands/transcribe.js +177 -0
  407. package/src/interfaces/cli/help/index.js +311 -72
  408. package/src/interfaces/cli/http.js +61 -13
  409. package/src/interfaces/cli/index.js +1 -1
  410. package/src/interfaces/cli/routes/agent.js +5 -4
  411. package/src/interfaces/cli/routes/commitment.js +3 -2
  412. package/src/interfaces/cli/routes/config.js +7 -2
  413. package/src/interfaces/cli/routes/image.js +11 -0
  414. package/src/interfaces/cli/routes/index.js +2 -0
  415. package/src/interfaces/cli/routes/log.js +1 -1
  416. package/src/interfaces/cli/routes/memory.js +3 -2
  417. package/src/interfaces/cli/routes/panel.js +15 -2
  418. package/src/interfaces/cli/routes/profile.js +3 -2
  419. package/src/interfaces/cli/routes/transcribe.js +10 -0
  420. package/src/interfaces/cli/stdin.js +29 -0
  421. package/src/interfaces/desktop/assets/blobs/amatista.png +0 -0
  422. package/src/interfaces/desktop/assets/blobs/aqua.png +0 -0
  423. package/src/interfaces/desktop/assets/blobs/brasa.png +0 -0
  424. package/src/interfaces/desktop/assets/blobs/campana.png +0 -0
  425. package/src/interfaces/desktop/assets/blobs/caracol.png +0 -0
  426. package/src/interfaces/desktop/assets/blobs/carbon.png +0 -0
  427. package/src/interfaces/desktop/assets/blobs/cobalto.png +0 -0
  428. package/src/interfaces/desktop/assets/blobs/cometa.png +0 -0
  429. package/src/interfaces/desktop/assets/blobs/coral.png +0 -0
  430. package/src/interfaces/desktop/assets/blobs/cubi.png +0 -0
  431. package/src/interfaces/desktop/assets/blobs/eclipse.png +0 -0
  432. package/src/interfaces/desktop/assets/blobs/faro.png +0 -0
  433. package/src/interfaces/desktop/assets/blobs/gajo.png +0 -0
  434. package/src/interfaces/desktop/assets/blobs/iris.png +0 -0
  435. package/src/interfaces/desktop/assets/blobs/kiwi.png +0 -0
  436. package/src/interfaces/desktop/assets/blobs/laguna.png +0 -0
  437. package/src/interfaces/desktop/assets/blobs/lila.png +0 -0
  438. package/src/interfaces/desktop/assets/blobs/menta.png +0 -0
  439. package/src/interfaces/desktop/assets/blobs/nimbo.png +0 -0
  440. package/src/interfaces/desktop/assets/blobs/noche.png +0 -0
  441. package/src/interfaces/desktop/assets/blobs/onyx.png +0 -0
  442. package/src/interfaces/desktop/assets/blobs/orbita.png +0 -0
  443. package/src/interfaces/desktop/assets/blobs/ovni.png +0 -0
  444. package/src/interfaces/desktop/assets/blobs/papa.png +0 -0
  445. package/src/interfaces/desktop/assets/blobs/parche.png +0 -0
  446. package/src/interfaces/desktop/assets/blobs/peon.png +0 -0
  447. package/src/interfaces/desktop/assets/blobs/perla.png +0 -0
  448. package/src/interfaces/desktop/assets/blobs/pino.png +0 -0
  449. package/src/interfaces/desktop/assets/blobs/quad.png +0 -0
  450. package/src/interfaces/desktop/assets/blobs/rubi.png +0 -0
  451. package/src/interfaces/desktop/assets/blobs/saturno.png +0 -0
  452. package/src/interfaces/desktop/assets/blobs/trebol.png +0 -0
  453. package/src/interfaces/desktop/assets/blobs/trino.png +0 -0
  454. package/src/interfaces/desktop/assets/blobs/volcan.png +0 -0
  455. package/src/interfaces/desktop/assets/blobs/zafiro.png +0 -0
  456. package/src/interfaces/desktop/assets/notification.mp3 +0 -0
  457. package/src/interfaces/desktop/main.js +473 -29
  458. package/src/interfaces/desktop/mascot-blobs.js +41 -0
  459. package/src/interfaces/desktop/mascot-preload.js +32 -0
  460. package/src/interfaces/desktop/mascot.css +186 -0
  461. package/src/interfaces/desktop/mascot.html +40 -0
  462. package/src/interfaces/desktop/mascot.js +163 -0
  463. package/src/interfaces/desktop/preload.js +10 -0
  464. package/src/interfaces/desktop/renderer.js +308 -48
  465. package/src/interfaces/desktop/style.css +34 -0
  466. package/src/interfaces/mcp-server/index.js +87 -0
  467. package/src/interfaces/tui/_shims/util-locale.ts +0 -2
  468. package/src/interfaces/tui/app.tsx +0 -6
  469. package/src/interfaces/tui/attach.ts +0 -1
  470. package/src/interfaces/tui/context/sdk-apx.tsx +0 -4
  471. package/src/interfaces/tui/plugin/api.tsx +0 -3
  472. package/src/interfaces/tui/plugin/runtime.ts +0 -3
  473. package/src/interfaces/tui/thread.ts +0 -16
  474. package/src/interfaces/web/README.md +22 -14
  475. package/src/interfaces/web/dist/assets/claude-XtpLmGtF.webp +0 -0
  476. package/src/interfaces/web/dist/assets/codex-DExPUgvh.webp +0 -0
  477. package/src/interfaces/web/dist/assets/index-BEoRgIqd.css +32 -0
  478. package/src/interfaces/web/dist/assets/index-BEoRgIqd.css.br +0 -0
  479. package/src/interfaces/web/dist/assets/index-BEoRgIqd.css.gz +0 -0
  480. package/src/interfaces/web/dist/assets/index-C6fXHc1p.js +1074 -0
  481. package/src/interfaces/web/dist/assets/index-C6fXHc1p.js.br +0 -0
  482. package/src/interfaces/web/dist/assets/index-C6fXHc1p.js.gz +0 -0
  483. package/src/interfaces/web/dist/assets/index-C6fXHc1p.js.map +1 -0
  484. package/src/interfaces/web/dist/assets/index-C6fXHc1p.js.map.br +0 -0
  485. package/src/interfaces/web/dist/assets/index-C6fXHc1p.js.map.gz +0 -0
  486. package/src/interfaces/web/dist/favicon/dark/site.webmanifest +12 -6
  487. package/src/interfaces/web/dist/favicon/white/site.webmanifest +12 -6
  488. package/src/interfaces/web/dist/index.html +22 -6
  489. package/src/interfaces/web/dist/index.html.br +0 -0
  490. package/src/interfaces/web/dist/index.html.gz +0 -0
  491. package/src/interfaces/web/dist/manifest.webmanifest +25 -0
  492. package/src/interfaces/web/dist/modules/blobs/amatista.png +0 -0
  493. package/src/interfaces/web/dist/modules/blobs/aqua.png +0 -0
  494. package/src/interfaces/web/dist/modules/blobs/brasa.png +0 -0
  495. package/src/interfaces/web/dist/modules/blobs/campana.png +0 -0
  496. package/src/interfaces/web/dist/modules/blobs/caracol.png +0 -0
  497. package/src/interfaces/web/dist/modules/blobs/carbon.png +0 -0
  498. package/src/interfaces/web/dist/modules/blobs/cobalto.png +0 -0
  499. package/src/interfaces/web/dist/modules/blobs/cometa.png +0 -0
  500. package/src/interfaces/web/dist/modules/blobs/coral.png +0 -0
  501. package/src/interfaces/web/dist/modules/blobs/cubi.png +0 -0
  502. package/src/interfaces/web/dist/modules/blobs/eclipse.png +0 -0
  503. package/src/interfaces/web/dist/modules/blobs/faro.png +0 -0
  504. package/src/interfaces/web/dist/modules/blobs/gajo.png +0 -0
  505. package/src/interfaces/web/dist/modules/blobs/iris.png +0 -0
  506. package/src/interfaces/web/dist/modules/blobs/kiwi.png +0 -0
  507. package/src/interfaces/web/dist/modules/blobs/laguna.png +0 -0
  508. package/src/interfaces/web/dist/modules/blobs/lila.png +0 -0
  509. package/src/interfaces/web/dist/modules/blobs/menta.png +0 -0
  510. package/src/interfaces/web/dist/modules/blobs/nimbo.png +0 -0
  511. package/src/interfaces/web/dist/modules/blobs/noche.png +0 -0
  512. package/src/interfaces/web/dist/modules/blobs/onyx.png +0 -0
  513. package/src/interfaces/web/dist/modules/blobs/orbita.png +0 -0
  514. package/src/interfaces/web/dist/modules/blobs/ovni.png +0 -0
  515. package/src/interfaces/web/dist/modules/blobs/papa.png +0 -0
  516. package/src/interfaces/web/dist/modules/blobs/parche.png +0 -0
  517. package/src/interfaces/web/dist/modules/blobs/peon.png +0 -0
  518. package/src/interfaces/web/dist/modules/blobs/perla.png +0 -0
  519. package/src/interfaces/web/dist/modules/blobs/pino.png +0 -0
  520. package/src/interfaces/web/dist/modules/blobs/quad.png +0 -0
  521. package/src/interfaces/web/dist/modules/blobs/rubi.png +0 -0
  522. package/src/interfaces/web/dist/modules/blobs/saturno.png +0 -0
  523. package/src/interfaces/web/dist/modules/blobs/trebol.png +0 -0
  524. package/src/interfaces/web/dist/modules/blobs/trino.png +0 -0
  525. package/src/interfaces/web/dist/modules/blobs/volcan.png +0 -0
  526. package/src/interfaces/web/dist/modules/blobs/zafiro.png +0 -0
  527. package/src/interfaces/web/dist/sw.js +106 -0
  528. package/src/interfaces/web/dist/sw.js.br +0 -0
  529. package/src/interfaces/web/dist/sw.js.gz +0 -0
  530. package/src/interfaces/web/eslint.config.js +83 -0
  531. package/src/interfaces/web/index.html +20 -4
  532. package/src/interfaces/web/package-lock.json +1651 -1280
  533. package/src/interfaces/web/package.json +9 -10
  534. package/src/interfaces/web/pnpm-lock.yaml +867 -869
  535. package/src/interfaces/web/public/favicon/dark/site.webmanifest +12 -6
  536. package/src/interfaces/web/public/favicon/white/site.webmanifest +12 -6
  537. package/src/interfaces/web/public/manifest.webmanifest +25 -0
  538. package/src/interfaces/web/public/modules/blobs/amatista.png +0 -0
  539. package/src/interfaces/web/public/modules/blobs/aqua.png +0 -0
  540. package/src/interfaces/web/public/modules/blobs/brasa.png +0 -0
  541. package/src/interfaces/web/public/modules/blobs/campana.png +0 -0
  542. package/src/interfaces/web/public/modules/blobs/caracol.png +0 -0
  543. package/src/interfaces/web/public/modules/blobs/carbon.png +0 -0
  544. package/src/interfaces/web/public/modules/blobs/cobalto.png +0 -0
  545. package/src/interfaces/web/public/modules/blobs/cometa.png +0 -0
  546. package/src/interfaces/web/public/modules/blobs/coral.png +0 -0
  547. package/src/interfaces/web/public/modules/blobs/cubi.png +0 -0
  548. package/src/interfaces/web/public/modules/blobs/eclipse.png +0 -0
  549. package/src/interfaces/web/public/modules/blobs/faro.png +0 -0
  550. package/src/interfaces/web/public/modules/blobs/gajo.png +0 -0
  551. package/src/interfaces/web/public/modules/blobs/iris.png +0 -0
  552. package/src/interfaces/web/public/modules/blobs/kiwi.png +0 -0
  553. package/src/interfaces/web/public/modules/blobs/laguna.png +0 -0
  554. package/src/interfaces/web/public/modules/blobs/lila.png +0 -0
  555. package/src/interfaces/web/public/modules/blobs/menta.png +0 -0
  556. package/src/interfaces/web/public/modules/blobs/nimbo.png +0 -0
  557. package/src/interfaces/web/public/modules/blobs/noche.png +0 -0
  558. package/src/interfaces/web/public/modules/blobs/onyx.png +0 -0
  559. package/src/interfaces/web/public/modules/blobs/orbita.png +0 -0
  560. package/src/interfaces/web/public/modules/blobs/ovni.png +0 -0
  561. package/src/interfaces/web/public/modules/blobs/papa.png +0 -0
  562. package/src/interfaces/web/public/modules/blobs/parche.png +0 -0
  563. package/src/interfaces/web/public/modules/blobs/peon.png +0 -0
  564. package/src/interfaces/web/public/modules/blobs/perla.png +0 -0
  565. package/src/interfaces/web/public/modules/blobs/pino.png +0 -0
  566. package/src/interfaces/web/public/modules/blobs/quad.png +0 -0
  567. package/src/interfaces/web/public/modules/blobs/rubi.png +0 -0
  568. package/src/interfaces/web/public/modules/blobs/saturno.png +0 -0
  569. package/src/interfaces/web/public/modules/blobs/trebol.png +0 -0
  570. package/src/interfaces/web/public/modules/blobs/trino.png +0 -0
  571. package/src/interfaces/web/public/modules/blobs/volcan.png +0 -0
  572. package/src/interfaces/web/public/modules/blobs/zafiro.png +0 -0
  573. package/src/interfaces/web/public/sw.js +106 -0
  574. package/src/interfaces/web/src/App.tsx +106 -10
  575. package/src/interfaces/web/src/assets/cli/claude.webp +0 -0
  576. package/src/interfaces/web/src/assets/cli/codex.webp +0 -0
  577. package/src/interfaces/web/src/assets/cli/cursor.svg +1 -0
  578. package/src/interfaces/web/src/assets/cli/opencode.png +0 -0
  579. package/src/interfaces/web/src/components/Combobox.tsx +201 -0
  580. package/src/interfaces/web/src/components/MobileHint.tsx +95 -0
  581. package/src/interfaces/web/src/components/MobileLinkDialog.tsx +156 -0
  582. package/src/interfaces/web/src/components/ModelCombobox.tsx +21 -113
  583. package/src/interfaces/web/src/components/{routines/ReadOnlyBlock.tsx → ReadOnlyBlock.tsx} +13 -4
  584. package/src/interfaces/web/src/components/Roby.tsx +5 -3
  585. package/src/interfaces/web/src/components/RobyBubble.tsx +23 -5
  586. package/src/interfaces/web/src/components/RowMenu.tsx +46 -0
  587. package/src/interfaces/web/src/components/TimezoneSelect.tsx +4 -1
  588. package/src/interfaces/web/src/components/Toast.tsx +50 -14
  589. package/src/interfaces/web/src/components/UiSelect.tsx +12 -5
  590. package/src/interfaces/web/src/components/agents/AgentAvatar.tsx +171 -0
  591. package/src/interfaces/web/src/components/agents/AgentFormFields.tsx +42 -11
  592. package/src/interfaces/web/src/components/agents/AgentModelBadge.tsx +157 -0
  593. package/src/interfaces/web/src/components/agents/AgentModelSelect.tsx +57 -0
  594. package/src/interfaces/web/src/components/agents/AgentSkillsPicker.tsx +115 -0
  595. package/src/interfaces/web/src/components/agents/BlobAvatar.tsx +50 -0
  596. package/src/interfaces/web/src/components/agents/blobPresets.ts +50 -0
  597. package/src/interfaces/web/src/components/agents/modelCatalog.ts +66 -0
  598. package/src/interfaces/web/src/components/chat/ActionGroup.tsx +162 -0
  599. package/src/interfaces/web/src/components/chat/AskAnswersCard.tsx +4 -3
  600. package/src/interfaces/web/src/components/chat/AskQuestionsCard.tsx +1 -1
  601. package/src/interfaces/web/src/components/chat/Attachment.tsx +220 -0
  602. package/src/interfaces/web/src/components/chat/ChatList.tsx +226 -51
  603. package/src/interfaces/web/src/components/chat/ChatRowActivity.tsx +45 -0
  604. package/src/interfaces/web/src/components/chat/Composer.tsx +672 -23
  605. package/src/interfaces/web/src/components/chat/ComposerCommands.tsx +175 -0
  606. package/src/interfaces/web/src/components/chat/ComposerSuggest.tsx +215 -0
  607. package/src/interfaces/web/src/components/chat/ContextBar.tsx +133 -65
  608. package/src/interfaces/web/src/components/chat/InlineAskPanel.tsx +107 -47
  609. package/src/interfaces/web/src/components/chat/MessageBubble.tsx +460 -119
  610. package/src/interfaces/web/src/components/chat/MessageList.tsx +165 -18
  611. package/src/interfaces/web/src/components/chat/ReasoningBlock.tsx +58 -0
  612. package/src/interfaces/web/src/components/chat/SessionPicker.tsx +182 -0
  613. package/src/interfaces/web/src/components/chat/SkillTrace.tsx +183 -0
  614. package/src/interfaces/web/src/components/chat/ToolCall.tsx +11 -11
  615. package/src/interfaces/web/src/components/code/CodeContextTab.tsx +87 -53
  616. package/src/interfaces/web/src/components/code/CodeFileTree.tsx +11 -52
  617. package/src/interfaces/web/src/components/code/CodeFileViewer.tsx +5 -4
  618. package/src/interfaces/web/src/components/code/CodeProjectPicker.tsx +31 -7
  619. package/src/interfaces/web/src/components/code/CodeSessionList.tsx +32 -11
  620. package/src/interfaces/web/src/components/code/CodeSidePanel.tsx +33 -3
  621. package/src/interfaces/web/src/components/code/CodeTerminal.tsx +5 -4
  622. package/src/interfaces/web/src/components/code/NewCodeSessionDialog.tsx +164 -0
  623. package/src/interfaces/web/src/components/commitments/CommitmentDetail.tsx +162 -0
  624. package/src/interfaces/web/src/components/commitments/CommitmentFormDialog.tsx +158 -0
  625. package/src/interfaces/web/src/components/commitments/CommitmentList.tsx +194 -0
  626. package/src/interfaces/web/src/components/commitments/commitmentState.tsx +46 -0
  627. package/src/interfaces/web/src/components/common/BulkActionBar.tsx +60 -0
  628. package/src/interfaces/web/src/components/common/ConfirmDialog.tsx +4 -1
  629. package/src/interfaces/web/src/components/common/SelectCheckbox.tsx +40 -0
  630. package/src/interfaces/web/src/components/common/TabNav.tsx +1 -1
  631. package/src/interfaces/web/src/components/cron/CronPicker.tsx +1 -1
  632. package/src/interfaces/web/src/components/files/FileBrowser.tsx +1 -1
  633. package/src/interfaces/web/src/components/files/FileTree.tsx +6 -4
  634. package/src/interfaces/web/src/components/files/FileViewer.tsx +9 -5
  635. package/src/interfaces/web/src/components/files/MarkdownPreview.tsx +45 -9
  636. package/src/interfaces/web/src/components/images/ImageDefaultsCard.tsx +93 -0
  637. package/src/interfaces/web/src/components/images/ImageProviderList.tsx +142 -0
  638. package/src/interfaces/web/src/components/images/ImageProviderModal.tsx +263 -0
  639. package/src/interfaces/web/src/components/images/ImageTestCard.tsx +142 -0
  640. package/src/interfaces/web/src/components/inbox/ChannelFilter.tsx +178 -0
  641. package/src/interfaces/web/src/components/inbox/InboxList.tsx +77 -81
  642. package/src/interfaces/web/src/components/inbox/InboxRowItem.tsx +140 -0
  643. package/src/interfaces/web/src/components/integrations/ComingSoonPlugin.tsx +2 -2
  644. package/src/interfaces/web/src/components/integrations/PluginConnect.tsx +208 -30
  645. package/src/interfaces/web/src/components/integrations/PluginToolsSection.tsx +1 -1
  646. package/src/interfaces/web/src/components/layout/ProjectAvatar.tsx +39 -24
  647. package/src/interfaces/web/src/components/layout/ProjectSidebar.tsx +253 -58
  648. package/src/interfaces/web/src/components/memory/MemoryBrowser.tsx +94 -47
  649. package/src/interfaces/web/src/components/routines/ExecutionsList.tsx +202 -90
  650. package/src/interfaces/web/src/components/routines/RoutineDetail.tsx +28 -6
  651. package/src/interfaces/web/src/components/routines/RoutineEditor.tsx +16 -5
  652. package/src/interfaces/web/src/components/routines/RoutineList.tsx +49 -9
  653. package/src/interfaces/web/src/components/routines/VarTextarea.tsx +1 -1
  654. package/src/interfaces/web/src/components/routines/runChat.ts +121 -0
  655. package/src/interfaces/web/src/components/sessions/SessionDetail.tsx +148 -0
  656. package/src/interfaces/web/src/components/sessions/SessionTerminal.tsx +98 -0
  657. package/src/interfaces/web/src/components/settings/AccessPanel.tsx +205 -0
  658. package/src/interfaces/web/src/components/settings/DefaultRouterCard.tsx +246 -101
  659. package/src/interfaces/web/src/components/settings/DesktopSettingsPanel.tsx +36 -2
  660. package/src/interfaces/web/src/components/settings/DevicesPanel.tsx +19 -6
  661. package/src/interfaces/web/src/components/settings/EmbedProviderList.tsx +274 -0
  662. package/src/interfaces/web/src/components/settings/EnginesPanel.tsx +26 -6
  663. package/src/interfaces/web/src/components/settings/MemoryPanel.tsx +118 -119
  664. package/src/interfaces/web/src/components/settings/NudgePanel.tsx +4 -3
  665. package/src/interfaces/web/src/components/settings/PairDeviceDialog.tsx +15 -4
  666. package/src/interfaces/web/src/components/settings/PanelPrefs.tsx +374 -0
  667. package/src/interfaces/web/src/components/settings/ProfilePanel.tsx +70 -2
  668. package/src/interfaces/web/src/components/settings/RoutingPanel.tsx +2 -1
  669. package/src/interfaces/web/src/components/settings/SkillsInspectorPanel.tsx +102 -43
  670. package/src/interfaces/web/src/components/settings/SkillsManager.tsx +76 -21
  671. package/src/interfaces/web/src/components/settings/SkillsSettings.tsx +10 -3
  672. package/src/interfaces/web/src/components/settings/SuperAgentPanel.tsx +8 -0
  673. package/src/interfaces/web/src/components/settings/TelegramChannelsPanel.tsx +11 -2
  674. package/src/interfaces/web/src/components/settings/TelegramContactsPanel.tsx +19 -8
  675. package/src/interfaces/web/src/components/settings/TelegramRolesPanel.tsx +12 -2
  676. package/src/interfaces/web/src/components/settings/WebPanel.tsx +22 -31
  677. package/src/interfaces/web/src/components/settings/providers/ProviderCard.tsx +15 -3
  678. package/src/interfaces/web/src/components/settings/providers/ProviderModal.tsx +97 -66
  679. package/src/interfaces/web/src/components/settings/providers/ProviderTestDialog.tsx +150 -0
  680. package/src/interfaces/web/src/components/settings/providers/typeStyles.ts +67 -25
  681. package/src/interfaces/web/src/components/settings/providers/types.ts +3 -0
  682. package/src/interfaces/web/src/components/tasks/TaskBoard.tsx +201 -0
  683. package/src/interfaces/web/src/components/tasks/TaskColumnsDialog.tsx +243 -0
  684. package/src/interfaces/web/src/components/tasks/TaskComments.tsx +167 -0
  685. package/src/interfaces/web/src/components/tasks/TaskDetail.tsx +335 -0
  686. package/src/interfaces/web/src/components/tasks/TaskFormDialog.tsx +406 -0
  687. package/src/interfaces/web/src/components/tasks/TaskList.tsx +258 -0
  688. package/src/interfaces/web/src/components/tasks/TaskSubtasks.tsx +126 -0
  689. package/src/interfaces/web/src/components/tasks/columns.ts +51 -0
  690. package/src/interfaces/web/src/components/tasks/taskStatus.tsx +159 -34
  691. package/src/interfaces/web/src/components/tasks/taskViewPrefs.ts +73 -0
  692. package/src/interfaces/web/src/components/tasks/useTaskColumns.ts +31 -0
  693. package/src/interfaces/web/src/components/ui/chat-input.tsx +136 -12
  694. package/src/interfaces/web/src/components/ui/context-menu.tsx +15 -198
  695. package/src/interfaces/web/src/components/ui/dialog.tsx +3 -2
  696. package/src/interfaces/web/src/components/ui/filter-chips.tsx +4 -0
  697. package/src/interfaces/web/src/components/ui/popover.tsx +14 -49
  698. package/src/interfaces/web/src/components/ui/sheet.tsx +2 -1
  699. package/src/interfaces/web/src/components/ui/spinner.tsx +2 -1
  700. package/src/interfaces/web/src/components/ui.tsx +62 -23
  701. package/src/interfaces/web/src/components/voice/VoiceProviderList.tsx +2 -1
  702. package/src/interfaces/web/src/components/voice/VoiceProviderModal.tsx +66 -8
  703. package/src/interfaces/web/src/components/voice/VoiceQvoxInstallCard.tsx +63 -0
  704. package/src/interfaces/web/src/components/voice/VoiceQvoxStatusCard.tsx +51 -0
  705. package/src/interfaces/web/src/components/voice/VoiceSttCard.tsx +4 -3
  706. package/src/interfaces/web/src/components/voice/VoiceTestCard.tsx +38 -4
  707. package/src/interfaces/web/src/constants/index.ts +1 -0
  708. package/src/interfaces/web/src/hooks/useChannelPrefs.ts +39 -0
  709. package/src/interfaces/web/src/hooks/useChat.ts +1092 -85
  710. package/src/interfaces/web/src/hooks/useChatActivity.ts +24 -0
  711. package/src/interfaces/web/src/hooks/useInbox.ts +26 -4
  712. package/src/interfaces/web/src/hooks/useLiveMessages.ts +16 -0
  713. package/src/interfaces/web/src/hooks/useOllamaModels.ts +79 -0
  714. package/src/interfaces/web/src/hooks/useRecorder.ts +198 -0
  715. package/src/interfaces/web/src/hooks/useTokenBootstrap.ts +23 -6
  716. package/src/interfaces/web/src/i18n/en.ts +832 -175
  717. package/src/interfaces/web/src/i18n/es.ts +841 -188
  718. package/src/interfaces/web/src/lib/api/agents.ts +23 -4
  719. package/src/interfaces/web/src/lib/api/code.ts +17 -0
  720. package/src/interfaces/web/src/lib/api/commitments.ts +7 -1
  721. package/src/interfaces/web/src/lib/api/conversations.ts +38 -11
  722. package/src/interfaces/web/src/lib/api/embeddings.ts +3 -0
  723. package/src/interfaces/web/src/lib/api/engines.ts +14 -0
  724. package/src/interfaces/web/src/lib/api/groups.ts +93 -0
  725. package/src/interfaces/web/src/lib/api/images.ts +193 -0
  726. package/src/interfaces/web/src/lib/api/inbox.ts +29 -5
  727. package/src/interfaces/web/src/lib/api/integrations.ts +9 -0
  728. package/src/interfaces/web/src/lib/api/media.ts +126 -0
  729. package/src/interfaces/web/src/lib/api/net.ts +44 -0
  730. package/src/interfaces/web/src/lib/api/profiles.ts +12 -0
  731. package/src/interfaces/web/src/lib/api/projects.ts +9 -1
  732. package/src/interfaces/web/src/lib/api/routines.ts +9 -1
  733. package/src/interfaces/web/src/lib/api/sessions.ts +18 -0
  734. package/src/interfaces/web/src/lib/api/skills.ts +8 -0
  735. package/src/interfaces/web/src/lib/api/super_agent.ts +3 -0
  736. package/src/interfaces/web/src/lib/api/tasks.ts +46 -6
  737. package/src/interfaces/web/src/lib/api/tools.ts +2 -0
  738. package/src/interfaces/web/src/lib/api/transcribe.ts +31 -0
  739. package/src/interfaces/web/src/lib/api/turns.ts +20 -0
  740. package/src/interfaces/web/src/lib/api/voice.ts +72 -2
  741. package/src/interfaces/web/src/lib/api.ts +3 -0
  742. package/src/interfaces/web/src/lib/channels.ts +214 -0
  743. package/src/interfaces/web/src/lib/chat-activity.ts +175 -0
  744. package/src/interfaces/web/src/lib/chat-prefs.ts +48 -0
  745. package/src/interfaces/web/src/lib/code-context.ts +0 -2
  746. package/src/interfaces/web/src/lib/http.ts +51 -17
  747. package/src/interfaces/web/src/lib/inbox-selection.ts +29 -0
  748. package/src/interfaces/web/src/lib/live.ts +252 -0
  749. package/src/interfaces/web/src/lib/net.ts +211 -0
  750. package/src/interfaces/web/src/lib/notify.ts +331 -0
  751. package/src/interfaces/web/src/lib/projectNav.ts +40 -0
  752. package/src/interfaces/web/src/lib/pwa.ts +115 -0
  753. package/src/interfaces/web/src/lib/qvox.ts +24 -0
  754. package/src/interfaces/web/src/lib/slug.ts +3 -0
  755. package/src/interfaces/web/src/lib/tone.ts +150 -0
  756. package/src/interfaces/web/src/main.tsx +15 -4
  757. package/src/interfaces/web/src/screens/ApxAdminScreen.tsx +33 -11
  758. package/src/interfaces/web/src/screens/InboxScreen.tsx +126 -71
  759. package/src/interfaces/web/src/screens/ProjectScreen.tsx +8 -4
  760. package/src/interfaces/web/src/screens/SettingsScreen.tsx +7 -4
  761. package/src/interfaces/web/src/screens/base/AgentDefaultsTab.tsx +30 -9
  762. package/src/interfaces/web/src/screens/base/CommitmentsTab.tsx +150 -168
  763. package/src/interfaces/web/src/screens/base/LogsTab.tsx +8 -7
  764. package/src/interfaces/web/src/screens/base/SessionsTab.tsx +99 -37
  765. package/src/interfaces/web/src/screens/base/WorkspacesTab.tsx +1 -1
  766. package/src/interfaces/web/src/screens/mobile/MobileChat.tsx +85 -0
  767. package/src/interfaces/web/src/screens/mobile/MobileChatList.tsx +244 -0
  768. package/src/interfaces/web/src/screens/mobile/MobileCommitments.tsx +350 -0
  769. package/src/interfaces/web/src/screens/mobile/MobileScreen.tsx +138 -0
  770. package/src/interfaces/web/src/screens/mobile/MobileTabBar.tsx +105 -0
  771. package/src/interfaces/web/src/screens/mobile/MobileTasks.tsx +407 -0
  772. package/src/interfaces/web/src/screens/mobile/NewChatSheet.tsx +313 -0
  773. package/src/interfaces/web/src/screens/mobile/mobileList.tsx +194 -0
  774. package/src/interfaces/web/src/screens/mobile/routes.ts +239 -0
  775. package/src/interfaces/web/src/screens/modules/CodeScreen.tsx +135 -64
  776. package/src/interfaces/web/src/screens/modules/DeckScreen.tsx +0 -1
  777. package/src/interfaces/web/src/screens/modules/ImagesScreen.tsx +172 -0
  778. package/src/interfaces/web/src/screens/modules/VoiceScreen.tsx +36 -4
  779. package/src/interfaces/web/src/screens/project/AgentBrainGraph.tsx +117 -30
  780. package/src/interfaces/web/src/screens/project/AgentDetailScreen.tsx +537 -92
  781. package/src/interfaces/web/src/screens/project/AgentsTab.tsx +344 -93
  782. package/src/interfaces/web/src/screens/project/ArtifactsTab.tsx +1 -1
  783. package/src/interfaces/web/src/screens/project/ChatTab.tsx +1160 -94
  784. package/src/interfaces/web/src/screens/project/IntegrationsTab.tsx +3 -2
  785. package/src/interfaces/web/src/screens/project/McpsTab.tsx +62 -14
  786. package/src/interfaces/web/src/screens/project/Overview.tsx +36 -15
  787. package/src/interfaces/web/src/screens/project/RoutinesTab.tsx +93 -8
  788. package/src/interfaces/web/src/screens/project/SkillsTab.tsx +4 -1
  789. package/src/interfaces/web/src/screens/project/StructureTab.tsx +5 -4
  790. package/src/interfaces/web/src/screens/project/TasksTab.tsx +351 -129
  791. package/src/interfaces/web/src/screens/project/VarsTab.tsx +17 -5
  792. package/src/interfaces/web/src/styles.css +106 -31
  793. package/src/interfaces/web/src/types/daemon.ts +246 -2
  794. package/src/interfaces/web/src/vite-env.d.ts +20 -0
  795. package/src/interfaces/tui/plugin/command-shim.ts +0 -109
  796. package/src/interfaces/web/components.json +0 -25
  797. package/src/interfaces/web/dist/assets/index-CvEoGtTf.js +0 -849
  798. package/src/interfaces/web/dist/assets/index-CvEoGtTf.js.map +0 -1
  799. package/src/interfaces/web/dist/assets/index-DzBBXFaO.css +0 -1
  800. package/src/interfaces/web/src/components/tasks/TaskDetailPanel.tsx +0 -142
  801. package/src/interfaces/web/src/components/ui/accordion.tsx +0 -72
  802. package/src/interfaces/web/src/components/ui/alert-dialog.tsx +0 -187
  803. package/src/interfaces/web/src/components/ui/alert.tsx +0 -76
  804. package/src/interfaces/web/src/components/ui/aspect-ratio.tsx +0 -22
  805. package/src/interfaces/web/src/components/ui/avatar.tsx +0 -107
  806. package/src/interfaces/web/src/components/ui/breadcrumb.tsx +0 -125
  807. package/src/interfaces/web/src/components/ui/button-group.tsx +0 -87
  808. package/src/interfaces/web/src/components/ui/calendar.tsx +0 -221
  809. package/src/interfaces/web/src/components/ui/card.tsx +0 -103
  810. package/src/interfaces/web/src/components/ui/carousel.tsx +0 -242
  811. package/src/interfaces/web/src/components/ui/chart.tsx +0 -371
  812. package/src/interfaces/web/src/components/ui/checkbox.tsx +0 -29
  813. package/src/interfaces/web/src/components/ui/collapsible.tsx +0 -19
  814. package/src/interfaces/web/src/components/ui/combobox.tsx +0 -295
  815. package/src/interfaces/web/src/components/ui/command.tsx +0 -196
  816. package/src/interfaces/web/src/components/ui/direction.tsx +0 -4
  817. package/src/interfaces/web/src/components/ui/drawer.tsx +0 -134
  818. package/src/interfaces/web/src/components/ui/empty.tsx +0 -104
  819. package/src/interfaces/web/src/components/ui/field.tsx +0 -236
  820. package/src/interfaces/web/src/components/ui/hover-card.tsx +0 -51
  821. package/src/interfaces/web/src/components/ui/input-otp.tsx +0 -85
  822. package/src/interfaces/web/src/components/ui/item.tsx +0 -201
  823. package/src/interfaces/web/src/components/ui/menubar.tsx +0 -280
  824. package/src/interfaces/web/src/components/ui/native-select.tsx +0 -61
  825. package/src/interfaces/web/src/components/ui/navigation-menu.tsx +0 -168
  826. package/src/interfaces/web/src/components/ui/pagination.tsx +0 -130
  827. package/src/interfaces/web/src/components/ui/progress.tsx +0 -83
  828. package/src/interfaces/web/src/components/ui/radio-group.tsx +0 -36
  829. package/src/interfaces/web/src/components/ui/resizable.tsx +0 -50
  830. package/src/interfaces/web/src/components/ui/scroll-area.tsx +0 -53
  831. package/src/interfaces/web/src/components/ui/sidebar.tsx +0 -726
  832. package/src/interfaces/web/src/components/ui/slider.tsx +0 -52
  833. package/src/interfaces/web/src/components/ui/sonner.tsx +0 -49
  834. package/src/interfaces/web/src/components/ui/table.tsx +0 -116
  835. package/src/interfaces/web/src/components/ui/toggle-group.tsx +0 -87
  836. package/src/interfaces/web/src/components/ui/toggle.tsx +0 -45
  837. package/src/interfaces/web/src/hooks/use-mobile.ts +0 -19
  838. package/src/interfaces/web/src/screens/base/GlobalTasksTab.tsx +0 -161
@@ -0,0 +1,215 @@
1
+ // Group chat = owner + N project agents in one room. A group is a THREAD on the
2
+ // message ledger (channel "group"), so it lists and opens through the same
3
+ // endpoints as a2a threads: `GET /super-agent/threads` (list, in conversations.js)
4
+ // and `GET /super-agent/threads/group/:id` (detail). This router owns only what
5
+ // those don't: creating a room, adding a participant, and running the owner's
6
+ // turn as a streamed mention-cascade.
7
+ //
8
+ // POST /projects/:pid/groups create { title, participants } → { id }
9
+ // POST /projects/:pid/groups/:gid/participants add an agent { slug }
10
+ // POST /projects/:pid/groups/:gid/message/stream owner speaks → NDJSON cascade
11
+ //
12
+ // The cascade registers in active-turns like every other turn, keyed as the
13
+ // thread it is (project + channel "group" + room id), so `POST
14
+ // /projects/:pid/turns/abort { channel: "group", thread_id }` can stop it. It
15
+ // had no such record, which meant the one turn shape that can run ten agent
16
+ // replies off a single line was the one nobody could interrupt.
17
+ import { readAgents } from "#core/apc/parser.js";
18
+ import {
19
+ createGroupThread, addGroupParticipant, removeGroupParticipant, readProjectGroupThread, truncateGroupThread,
20
+ lastGroupOwnerMedia, GROUP_CHANNEL,
21
+ } from "#core/stores/messages.js";
22
+ import { runGroupTurn } from "#core/agent/group/run-group-turn.js";
23
+ import { readTurnAttachments } from "./media.js";
24
+ import { startActiveTurn, endActiveTurn, threadTurnKey } from "../active-turns.js";
25
+ import { wasAborted } from "./turn-abort.js";
26
+ import { asyncRoute } from "./shared.js";
27
+
28
+ const KEEPALIVE_MS = 15000;
29
+
30
+ // Best-effort owner display name for the transcript: the Telegram roster entry
31
+ // tagged role "owner", else a neutral fallback.
32
+ function ownerName(config) {
33
+ const owner = (config?.telegram?.contacts || []).find((c) => c.role === "owner");
34
+ return owner?.name || "Owner";
35
+ }
36
+
37
+ export function register(api, { projects, project, config, plugins, registries }) {
38
+ api.post("/projects/:pid/groups", (req, res) => {
39
+ const p = project(req, res);
40
+ if (!p) return;
41
+ const { title, participants, members } = req.body || {};
42
+
43
+ // Cross-project rooms (inbox / mobile): `members: [{ project_id, slug }]`.
44
+ // Same-project rooms keep the old `participants: string[]` shape.
45
+ let slugs = [];
46
+ let homes = null;
47
+ if (Array.isArray(members) && members.length) {
48
+ const seen = new Set();
49
+ homes = {};
50
+ for (const m of members) {
51
+ const slug = typeof m?.slug === "string" ? m.slug.trim() : "";
52
+ if (!slug || seen.has(slug)) continue;
53
+ const homeId = m.project_id ?? p.id;
54
+ let home;
55
+ try { home = projects.get(homeId); } catch { home = null; }
56
+ if (!home?.path) continue;
57
+ if (!readAgents(home.path).some((a) => a.slug === slug)) continue;
58
+ seen.add(slug);
59
+ slugs.push(slug);
60
+ homes[slug] = home.id;
61
+ }
62
+ // All members live on the host project → no homes map needed.
63
+ if (slugs.length && Object.values(homes).every((id) => String(id) === String(p.id))) {
64
+ homes = null;
65
+ }
66
+ } else {
67
+ const known = new Set(readAgents(p.path).map((a) => a.slug));
68
+ slugs = (Array.isArray(participants) ? participants : []).filter((s) => known.has(s));
69
+ }
70
+
71
+ if (slugs.length < 1) return res.status(400).json({ error: "at least one existing agent required" });
72
+ try {
73
+ const id = createGroupThread(p.logMessage, { title: title || null, participants: slugs, homes });
74
+ try { projects.rebuild(p.id); } catch { /* best-effort */ }
75
+ res.status(201).json({
76
+ id, channel: "group", title: title || slugs.join(" · "), participants: slugs,
77
+ ...(homes ? { homes } : {}),
78
+ });
79
+ } catch (e) {
80
+ res.status(400).json({ error: e.message });
81
+ }
82
+ });
83
+
84
+ api.post("/projects/:pid/groups/:gid/participants", (req, res) => {
85
+ const p = project(req, res);
86
+ if (!p) return;
87
+ const { slug } = req.body || {};
88
+ if (!readAgents(p.path).some((a) => a.slug === slug))
89
+ return res.status(404).json({ error: "agent not found" });
90
+ const thread = readProjectGroupThread(p.storagePath, req.params.gid);
91
+ if (!thread) return res.status(404).json({ error: "group not found" });
92
+ if (!thread.participants.includes(slug)) {
93
+ addGroupParticipant(p.logMessage, req.params.gid, [...thread.participants, slug], slug);
94
+ try { projects.rebuild(p.id); } catch { /* best-effort */ }
95
+ }
96
+ res.json({ id: req.params.gid, participants: [...new Set([...thread.participants, slug])] });
97
+ });
98
+
99
+ // Remove an agent from a room. Records a "… salió del chat" notice and drops
100
+ // them from the roster, so the cascade no longer seeds or lists them.
101
+ api.delete("/projects/:pid/groups/:gid/participants/:slug", (req, res) => {
102
+ const p = project(req, res);
103
+ if (!p) return;
104
+ const thread = readProjectGroupThread(p.storagePath, req.params.gid);
105
+ if (!thread) return res.status(404).json({ error: "group not found" });
106
+ const next = thread.participants.filter((s) => s !== req.params.slug);
107
+ if (next.length !== thread.participants.length) {
108
+ removeGroupParticipant(p.logMessage, req.params.gid, req.params.slug, next);
109
+ try { projects.rebuild(p.id); } catch { /* best-effort */ }
110
+ }
111
+ res.json({ id: req.params.gid, participants: next });
112
+ });
113
+
114
+ // Rewind a group so a regenerate/edit can overwrite everything after a point.
115
+ // `keep_visible` = how many pane bubbles to keep (tools + consecutive same
116
+ // speaker collapse like the web transcript).
117
+ api.post("/projects/:pid/groups/:gid/truncate", (req, res) => {
118
+ const p = project(req, res);
119
+ if (!p) return;
120
+ const keepVisible = Number(req.body?.keep_visible);
121
+ if (!Number.isInteger(keepVisible) || keepVisible < 0)
122
+ return res.status(400).json({ error: "keep_visible must be a non-negative integer" });
123
+ if (!readProjectGroupThread(p.storagePath, req.params.gid))
124
+ return res.status(404).json({ error: "group not found" });
125
+ const out = truncateGroupThread(p.storagePath, req.params.gid, keepVisible);
126
+ try { projects.rebuild(p.id); } catch { /* best-effort */ }
127
+ res.json({ ok: true, ...out });
128
+ });
129
+
130
+ // The owner speaks; the room answers as an NDJSON cascade of speaker events.
131
+ // `rerun: true` re-runs against the existing last owner message (regenerate).
132
+ // Optional `from` resumes the cascade at that speaker so earlier replies
133
+ // this turn stay put; without it the whole room-answer is re-run.
134
+ api.post("/projects/:pid/groups/:gid/message/stream", asyncRoute(async (req, res) => {
135
+ const p = project(req, res);
136
+ if (!p) return;
137
+ const { prompt, rerun, attachments, from, reason } = req.body || {};
138
+ if (!readProjectGroupThread(p.storagePath, req.params.gid)) return res.status(404).json({ error: "group not found" });
139
+ // Files the composer uploaded to ~/.apx/media: images ride on each speaker's
140
+ // turn (vision), and a marker naming each file is folded into the prompt so a
141
+ // photo with no caption is still a turn and a non-vision agent is told a file
142
+ // arrived and where it lives. Same handling as the 1:1 chat.
143
+ //
144
+ // On a REGENERATE (rerun), the owner already sent the turn — re-read the
145
+ // photo it carried from the ledger and re-attach it, or the re-run agents
146
+ // lose sight of the image the original turn answered.
147
+ const rerunAttach = rerun
148
+ ? (() => { const md = lastGroupOwnerMedia(p.storagePath, req.params.gid); return md ? [md] : []; })()
149
+ : attachments;
150
+ const turnFiles = readTurnAttachments(rerunAttach);
151
+ const turnPrompt = rerun ? "" : [...turnFiles.markers, prompt].filter(Boolean).join(" ");
152
+ if (!rerun && !turnPrompt.trim()) return res.status(400).json({ error: "prompt required" });
153
+
154
+ res.setHeader("content-type", "application/x-ndjson; charset=utf-8");
155
+ res.setHeader("cache-control", "no-cache, no-transform");
156
+ res.setHeader("x-accel-buffering", "no");
157
+ res.flushHeaders?.();
158
+
159
+ let lastWriteAt = Date.now();
160
+ const send = (event) => { lastWriteAt = Date.now(); res.write(JSON.stringify(event) + "\n"); };
161
+ const keepalive = setInterval(() => {
162
+ if (Date.now() - lastWriteAt < KEEPALIVE_MS) return;
163
+ lastWriteAt = Date.now();
164
+ try { res.write("\n"); } catch { /* socket gone */ }
165
+ }, KEEPALIVE_MS);
166
+ keepalive.unref?.();
167
+ res.on("close", () => clearInterval(keepalive));
168
+
169
+ // The room's kill switch. A cascade is the longest-running turn shape the
170
+ // daemon has — one owner line can fan out into MAX_TURNS_PER_MESSAGE full
171
+ // tool loops — and until this record existed the Stop button could only
172
+ // close the browser's socket while the room kept working and kept writing.
173
+ // Closing the socket deliberately does not stop the run (that is what lets
174
+ // another tab catch up), so stopping is asked for out loud, through
175
+ // POST /projects/:pid/turns/abort, which reaches `abort` here.
176
+ const turnAbort = new AbortController();
177
+ const active = startActiveTurn(threadTurnKey(p.id, GROUP_CHANNEL, req.params.gid), {
178
+ project_id: p.id,
179
+ channel: GROUP_CHANNEL,
180
+ thread_id: req.params.gid,
181
+ abort: () => turnAbort.abort(),
182
+ });
183
+ send({ type: "start", turn_id: active.id, channel: GROUP_CHANNEL, thread_id: req.params.gid });
184
+
185
+ try {
186
+ await runGroupTurn({
187
+ p, gid: req.params.gid, text: turnPrompt, rerun: !!rerun,
188
+ from: typeof from === "string" && from.trim() ? from.trim() : null,
189
+ reason: typeof reason === "string" && reason.trim() ? reason.trim() : null,
190
+ attachments: turnFiles.attachments, media: turnFiles.media,
191
+ ownerName: ownerName(config), config, projects, plugins, registries,
192
+ signal: turnAbort.signal,
193
+ onEvent: send,
194
+ });
195
+ try { projects.rebuild(p.id); } catch { /* best-effort */ }
196
+ // Stopped, not broken. runGroupTurn persists the interrupted speaker's
197
+ // partial and returns normally, so the room is consistent either way; the
198
+ // client only needs to know which of the two happened. `aborted` rather
199
+ // than `error` for the same reason the 1:1 stream draws the line there —
200
+ // a client that paints errors red must not accuse the daemon of breaking
201
+ // every time the owner presses Stop.
202
+ const stopped = turnAbort.signal.aborted;
203
+ send(stopped ? { type: "aborted" } : { type: "final" });
204
+ clearInterval(keepalive);
205
+ res.end();
206
+ } catch (e) {
207
+ try { projects.rebuild(p.id); } catch { /* best-effort */ }
208
+ send(wasAborted(e, turnAbort) ? { type: "aborted" } : { type: "error", error: e.message });
209
+ clearInterval(keepalive);
210
+ res.end();
211
+ } finally {
212
+ endActiveTurn(active.id);
213
+ }
214
+ }));
215
+ }
@@ -0,0 +1,107 @@
1
+ // Daemon HTTP routes for image generation.
2
+ //
3
+ // POST /images/generate { prompt, provider?, width?, height?, size?,
4
+ // steps?, cfg_scale?, seed?, sampler?, scheduler?,
5
+ // count?, format?, negative_prompt?, model?,
6
+ // init_image?, denoising_strength?,
7
+ // control_image?, control_strength? }
8
+ // init_image / control_image are base64 (bare or a
9
+ // data: URL). They are two different operations —
10
+ // repaint this canvas vs. keep this structure — and
11
+ // no engine accepts both. Bodies are capped at 2 MB
12
+ // by the express json parser. The echoed `request`
13
+ // has the payloads replaced by a size marker.
14
+ // → { images: [{path, url, bytes, mime, seed}],
15
+ // provider, model, request, ignored, elapsed_ms }
16
+ //
17
+ // GET /images/providers → { configured_provider, mode, order, defaults,
18
+ // engines: [{id, available, configured, …}] }
19
+ //
20
+ // GET /images/capabilities?provider=<id>
21
+ // → { provider, capabilities: {models, samplers, …} }
22
+ //
23
+ // GET /images/file?path=<abs>
24
+ // → the bytes, sandboxed to ~/.apx/images
25
+ //
26
+ // Glue only: every decision (engine routing, defaults, where files land) lives
27
+ // in core/images/. `url` is added here because it is a transport concern —
28
+ // core has no idea an HTTP client will need a way to fetch the file back.
29
+ import fs from "node:fs/promises";
30
+ import path from "node:path";
31
+ import { IMAGES_DIR } from "#core/config/paths.js";
32
+ import { readConfig } from "#core/config/index.js";
33
+ import { generate, listProviders, capabilities } from "#core/images/generate.js";
34
+ import { apiPath } from "./prefix.js";
35
+ import { asyncRoute } from "./shared.js";
36
+
37
+ /** Where a client can fetch this file from. Absolute paths stay server-side. */
38
+ function fileUrl(absPath) {
39
+ return `${apiPath("/images/file")}?path=${encodeURIComponent(absPath)}`;
40
+ }
41
+
42
+ export function register(api) {
43
+ api.post("/images/generate", asyncRoute(async (req, res) => {
44
+ const body = req.body || {};
45
+ if (typeof body.prompt !== "string" || !body.prompt.trim()) {
46
+ return res.status(400).json({ error: "prompt required" });
47
+ }
48
+ try {
49
+ const result = await generate({ ...body, globalConfig: readConfig() });
50
+ res.json({
51
+ ...result,
52
+ images: result.images.map((img) => ({ ...img, url: fileUrl(img.path) })),
53
+ });
54
+ } catch (e) {
55
+ res.status(502).json({ error: e.message });
56
+ }
57
+ }));
58
+
59
+ api.get("/images/providers", asyncRoute(async (_req, res) => {
60
+ try {
61
+ res.json(await listProviders(readConfig()));
62
+ } catch (e) {
63
+ res.status(500).json({ error: e.message });
64
+ }
65
+ }));
66
+
67
+ api.get("/images/capabilities", asyncRoute(async (req, res) => {
68
+ try {
69
+ res.json(await capabilities({
70
+ provider: req.query.provider ? String(req.query.provider) : undefined,
71
+ globalConfig: readConfig(),
72
+ }));
73
+ } catch (e) {
74
+ res.status(400).json({ error: e.message });
75
+ }
76
+ }));
77
+
78
+ // Sandboxed to the gallery: a path that escapes it is refused, so a leaked
79
+ // absolute path in a reply can never be turned into an arbitrary file read.
80
+ api.get("/images/file", asyncRoute(async (req, res) => {
81
+ const rawPath = String(req.query.path || "");
82
+ if (!rawPath) return res.status(400).json({ error: "path required" });
83
+ const root = path.resolve(IMAGES_DIR);
84
+ const resolved = path.resolve(rawPath);
85
+ if (resolved !== root && !resolved.startsWith(root + path.sep)) {
86
+ return res.status(403).json({ error: "path outside images dir" });
87
+ }
88
+ let bytes;
89
+ try {
90
+ bytes = await fs.readFile(resolved);
91
+ } catch {
92
+ return res.status(404).json({ error: "not found" });
93
+ }
94
+ const ext = path.extname(resolved).toLowerCase();
95
+ const mime =
96
+ ext === ".png" ? "image/png" :
97
+ ext === ".jpg" || ext === ".jpeg" ? "image/jpeg" :
98
+ ext === ".webp" ? "image/webp" :
99
+ ext === ".gif" ? "image/gif" :
100
+ "application/octet-stream";
101
+ res.setHeader("Content-Type", mime);
102
+ // Immutable content at a unique path — cache hard so a gallery of
103
+ // thumbnails doesn't re-read disk on every render.
104
+ res.setHeader("Cache-Control", "private, max-age=86400");
105
+ res.send(bytes);
106
+ }));
107
+ }
@@ -4,9 +4,101 @@
4
4
  // The conversation-first entry point. Project-first navigation is unaffected —
5
5
  // this is a second axis over the same data, not a replacement for it.
6
6
  import { listAgentInbox } from "#core/stores/agent-inbox.js";
7
+ import { listProjectA2AThreads, listProjectGroupThreads } from "#core/stores/messages.js";
7
8
  import { readConfig } from "#core/config/index.js";
8
9
  import { resolveAgentName } from "#core/identity/index.js";
9
- import { pageEnvelope } from "./shared.js";
10
+ import { faceResolverFor, readAgentsSafe } from "./thread-faces.js";
11
+ import { pageEnvelope, A2A_SLUG_PREFIX, GROUP_SLUG_PREFIX } from "./shared.js";
12
+ import { convTurnKey, getActiveTurnByKey, listActiveTurns } from "../active-turns.js";
13
+
14
+ function activeTurnForRow(row, activeTurns) {
15
+ if (row.kind === "agent" && row.project_id != null && row.conversation_id) {
16
+ return getActiveTurnByKey(convTurnKey(row.project_id, row.conversation_id));
17
+ }
18
+ if (row.kind === "super_agent" && row.channel && row.conversation_id) {
19
+ return activeTurns.find((turn) =>
20
+ turn.channel === row.channel && turn.thread_id === row.conversation_id
21
+ ) || null;
22
+ }
23
+ return null;
24
+ }
25
+
26
+ // a2a "group chats" aren't any single agent's conversation, so listAgentInbox
27
+ // (per-agent) doesn't see them. Fold each project's a2a pairs in as their own
28
+ // rows — this is the one place that shows EVERY conversation, so a group chat
29
+ // belongs here next to the individual ones.
30
+ //
31
+ // Faces and the "Andy · Claude" title come from the shared resolver in
32
+ // thread-faces.js, the same one the Chats sidebar and the thread header read
33
+ // through — this row is not where that gets decided.
34
+ function a2aInboxRows(entries, faces) {
35
+ const rows = [];
36
+ for (const e of entries) {
37
+ let threads = [];
38
+ try { threads = listProjectA2AThreads(e.storagePath); } catch { /* skip */ }
39
+ const agents = readAgentsSafe(e.path);
40
+ for (const raw of threads) {
41
+ const th = faces.decorate(raw, agents);
42
+ rows.push({
43
+ project_id: e.id,
44
+ project_name: e.name,
45
+ project_path: e.path,
46
+ agent_slug: `${A2A_SLUG_PREFIX }${th.id}`,
47
+ agent_name: th.title,
48
+ agent_emoji: null,
49
+ agent_icon: null,
50
+ kind: "a2a",
51
+ participants: th.participants,
52
+ participant_faces: th.participant_faces,
53
+ ...(th.requested_by ? { requested_by: th.requested_by } : {}),
54
+ pinned: false,
55
+ conversation_id: th.id,
56
+ channel: "a2a",
57
+ messages: th.messages,
58
+ preview: th.preview || null,
59
+ preview_at: th.preview_at || null,
60
+ last_activity_at: th.last_ts,
61
+ });
62
+ }
63
+ }
64
+ return rows;
65
+ }
66
+
67
+ // Group rooms (owner + N agents) are threads on the ledger, same as a2a — fold
68
+ // them in here too so the one place that shows EVERY conversation shows them.
69
+ // Shaped exactly like an a2a row (kind "group") so the frontend reuses the same
70
+ // multi-face rendering and thread-selection it already has for a2a.
71
+ function groupInboxRows(entries, faces) {
72
+ const rows = [];
73
+ for (const e of entries) {
74
+ let threads = [];
75
+ try { threads = listProjectGroupThreads(e.storagePath); } catch { /* skip */ }
76
+ const agents = readAgentsSafe(e.path);
77
+ for (const raw of threads) {
78
+ const th = faces.decorate(raw, agents);
79
+ rows.push({
80
+ project_id: e.id,
81
+ project_name: e.name,
82
+ project_path: e.path,
83
+ agent_slug: `${GROUP_SLUG_PREFIX}${th.id}`,
84
+ agent_name: th.title,
85
+ agent_emoji: null,
86
+ agent_icon: null,
87
+ kind: "group",
88
+ participants: th.participants,
89
+ participant_faces: th.participant_faces,
90
+ pinned: false,
91
+ conversation_id: th.id,
92
+ channel: "group",
93
+ messages: th.messages,
94
+ preview: th.preview || null,
95
+ preview_at: th.preview_at || null,
96
+ last_activity_at: th.last_ts,
97
+ });
98
+ }
99
+ }
100
+ return rows;
101
+ }
10
102
 
11
103
  export function register(api, { projects }) {
12
104
  api.get("/inbox", (req, res) => {
@@ -23,8 +115,22 @@ export function register(api, { projects }) {
23
115
  });
24
116
  }
25
117
 
118
+ // `?channel=web` scopes the individual-agent rows to one channel — the
119
+ // inbox and the phone are web-only so a Telegram thread never surfaces
120
+ // there. a2a group rows are their own channel and are added below,
121
+ // unaffected by this filter.
122
+ const channel = typeof req.query.channel === "string" && req.query.channel
123
+ ? req.query.channel
124
+ : null;
125
+
26
126
  const { rows, skipped } = listAgentInbox(entries, {
27
127
  includeEmpty: req.query.include_empty === "1" || req.query.include_empty === "true",
128
+ channel,
129
+ // No `?channel=` means "show me everything", and everything now means
130
+ // one row per channel rather than one row per agent wearing whichever
131
+ // channel happened to speak last. Asking for a single channel already
132
+ // answers the question, so it keeps the one-row-per-agent shape.
133
+ perChannel: !channel,
28
134
  });
29
135
 
30
136
  // The super-agent's display name lives in identity.json, and core must not
@@ -35,7 +141,22 @@ export function register(api, { projects }) {
35
141
  r.kind === "super_agent" ? { ...r, agent_name: r.agent_name || superName } : r
36
142
  );
37
143
 
38
- const envelope = pageEnvelope(named, req.query);
144
+ // Faces and titles for the multi-agent rows: one resolver for the request,
145
+ // shared with the Chats sidebar and the thread header (thread-faces.js).
146
+ // It knows the super-agent's own face too, so "golf-coach ↔ Roby" shows
147
+ // Roby with an avatar instead of the bare `super_agent` slug.
148
+ const faces = faceResolverFor(projects);
149
+
150
+ // Merge a2a group chats in and re-sort so the newest conversation wins
151
+ // regardless of whether it was an individual or a group one.
152
+ const activeTurns = listActiveTurns();
153
+ const merged = [...named, ...a2aInboxRows(entries, faces), ...groupInboxRows(entries, faces)]
154
+ .map((row) => ({ ...row, active_turn: activeTurnForRow(row, activeTurns) }))
155
+ .sort(
156
+ (a, b) => new Date(b.last_activity_at || 0).getTime() - new Date(a.last_activity_at || 0).getTime()
157
+ );
158
+
159
+ const envelope = pageEnvelope(merged, req.query);
39
160
  if (skipped.length) envelope.meta = { ...(envelope.meta || {}), skipped };
40
161
  res.json(envelope);
41
162
  } catch (e) {
@@ -25,8 +25,37 @@ import {
25
25
  getPluginService,
26
26
  reconcilePluginMcp,
27
27
  } from "#core/integrations/index.js";
28
+ import { verifyState, callbackUrl } from "#core/integrations/plugins/_google-oauth.js";
28
29
  import { asyncRoute } from "./shared.js";
29
30
 
31
+ // The browser origin behind this request, reconstructed so an OAuth redirect URI
32
+ // built at authorize-time matches the one the callback trades the code with.
33
+ function requestOrigin(req) {
34
+ const proto = String(req.headers["x-forwarded-proto"] || (req.secure ? "https" : "http")).split(",")[0].trim();
35
+ const host = req.headers["x-forwarded-host"] || req.headers.host;
36
+ return `${proto}://${host}`;
37
+ }
38
+
39
+ // The little page the OAuth popup lands on: tells the opener panel it's done and
40
+ // closes itself. Kept self-contained (no bundle) since it renders outside the SPA.
41
+ function oauthResultPage(ok, message = "") {
42
+ const title = ok ? "Conectado ✓" : "No se pudo conectar";
43
+ const detail = ok ? "Ya podés cerrar esta ventana." : String(message || "Reintentá desde el panel.");
44
+ const safe = detail.replace(/[<>&]/g, (c) => ({ "<": "&lt;", ">": "&gt;", "&": "&amp;" })[c]);
45
+ return `<!doctype html><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
46
+ <title>${title}</title>
47
+ <body style="margin:0;font:15px/1.5 system-ui,sans-serif;background:#0b0f14;color:#e6edf3;display:grid;place-items:center;height:100vh">
48
+ <div style="max-width:22rem;text-align:center;padding:1.5rem">
49
+ <div style="font-size:2rem;margin-bottom:.5rem">${ok ? "✅" : "⚠️"}</div>
50
+ <h1 style="font-size:1.05rem;margin:0 0 .35rem">${title}</h1>
51
+ <p style="margin:0;color:#9aa7b4">${safe}</p>
52
+ </div>
53
+ <script>
54
+ try { window.opener && window.opener.postMessage({ source: "apx-oauth", ok: ${ok ? "true" : "false"} }, "*"); } catch (e) {}
55
+ ${ok ? "setTimeout(function(){ try { window.close(); } catch (e) {} }, 1200);" : ""}
56
+ </script>`;
57
+ }
58
+
30
59
  // Integration scope vocabulary: project | global, accepting "default" for
31
60
  // global and both "shared" and "runtime" for project. See the note in
32
61
  // api/mcps.js — do not merge these three helpers.
@@ -65,6 +94,37 @@ export function register(api, { projects, project, registries }) {
65
94
  }
66
95
  }
67
96
 
97
+ // OAuth redirect landing. Google sends the browser here (no bearer token — see
98
+ // the auth allowlist in shared.js), with a signed `state` that says which
99
+ // plugin/project/scope started the flow. We trade the code for a refresh token
100
+ // and persist it, then render a page that tells the panel it's done.
101
+ api.get("/integrations/oauth/callback", asyncRoute(async (req, res) => {
102
+ const fail = (msg) => res.status(400).type("html").send(oauthResultPage(false, msg));
103
+ const { code, state, error } = req.query || {};
104
+ if (error) return fail(`Google devolvió un error: ${error}`);
105
+ const decoded = verifyState(state);
106
+ if (!decoded || !code) return fail("El enlace de autorización venció o es inválido. Reintentá desde el panel.");
107
+ const svc = getPluginService(decoded.slug);
108
+ if (!svc || typeof svc.completeOAuth !== "function") return fail(`El plugin "${decoded.slug}" no soporta OAuth.`);
109
+ const scope = normalizeIntegrationScope(decoded.scope) || "project";
110
+ const p = projects.get(Number(decoded.pid) || 0);
111
+ if (!p) return fail("No encontré el proyecto para guardar la conexión.");
112
+ const storagePath = storagePathForScope(scope, p, projects);
113
+ if (!storagePath) return fail("El proyecto no tiene ruta de storage.");
114
+ const store = new IntegrationStore(storagePath);
115
+ const record = store.get(decoded.slug);
116
+ if (!record) return fail("La integración no está configurada (¿cargaste el client_id?).");
117
+ try {
118
+ const redirectUri = callbackUrl(requestOrigin(req));
119
+ const { patch } = await svc.completeOAuth(record, { code: String(code), redirectUri });
120
+ store.upsert(decoded.slug, patch);
121
+ reconcileMcp(svc, storagePath, scope, p);
122
+ res.type("html").send(oauthResultPage(true));
123
+ } catch (e) {
124
+ fail(e.message || String(e));
125
+ }
126
+ }));
127
+
68
128
  // List stored integrations in the chosen scope (secrets redacted).
69
129
  api.get("/projects/:pid/integrations", (req, res) => {
70
130
  const p = project(req, res);
@@ -90,6 +150,7 @@ export function register(api, { projects, project, registries }) {
90
150
  const resolved = resolveIntegration({
91
151
  projectStorage: p.storagePath,
92
152
  slug: entry.slug,
153
+ defaultStorage: storagePathForScope("global", p, projects),
93
154
  requireEnabled: false,
94
155
  });
95
156
  status = svc.status(resolved?.record || null);
@@ -195,8 +256,9 @@ export function register(api, { projects, project, registries }) {
195
256
  if (!record) return res.status(404).json({ error: "integration not configured" });
196
257
  try {
197
258
  // Actions get a 2nd ctx arg (existing plugins ignore it). Obsidian's
198
- // sync_memory uses it to reach every project's memory.md.
199
- const actionCtx = { storagePath, scope, project: p, projects, registries };
259
+ // sync_memory uses it to reach every project's memory.md; calendar's
260
+ // authorize uses `origin` to build the OAuth redirect URI.
261
+ const actionCtx = { storagePath, scope, project: p, projects, registries, origin: requestOrigin(req) };
200
262
  res.json(await fn.call(svc.actions, record, actionCtx));
201
263
  } catch (e) {
202
264
  res.status(400).json({ error: e.message });
@@ -9,6 +9,7 @@
9
9
  // POST /projects/:pid/mcps/:name/call
10
10
  import fs from "node:fs";
11
11
  import path from "node:path";
12
+ import { asyncRoute } from "./shared.js";
12
13
  import {
13
14
  readApfMcps,
14
15
  writeApfMcps,
@@ -198,7 +199,7 @@ export function register(api, { projects, registries, project }) {
198
199
  // Full tool catalog — tools/list with input schemas, all pages merged.
199
200
  // This is what `apx mcp tools` renders; /test below stays as the
200
201
  // lightweight smoke check for the web UI card.
201
- api.get("/projects/:pid/mcps/:name/tools", async (req, res) => {
202
+ api.get("/projects/:pid/mcps/:name/tools", asyncRoute(async (req, res) => {
202
203
  const p = project(req, res);
203
204
  if (!p) return;
204
205
  try {
@@ -207,9 +208,9 @@ export function register(api, { projects, registries, project }) {
207
208
  } catch (e) {
208
209
  res.status(500).json({ error: e.message });
209
210
  }
210
- });
211
+ }));
211
212
 
212
- api.post("/projects/:pid/mcps/:name/call", async (req, res) => {
213
+ api.post("/projects/:pid/mcps/:name/call", asyncRoute(async (req, res) => {
213
214
  const p = project(req, res);
214
215
  if (!p) return;
215
216
  const { tool, params } = req.body || {};
@@ -220,12 +221,12 @@ export function register(api, { projects, registries, project }) {
220
221
  } catch (e) {
221
222
  res.status(500).json({ error: e.message });
222
223
  }
223
- });
224
+ }));
224
225
 
225
226
  // Smoke test — calls tools/list and reports either the tool catalog or a
226
227
  // clean error message. Used by the "Test" button in the MCP card so the
227
228
  // user can sanity-check a freshly-saved MCP without firing a real tool.
228
- api.post("/projects/:pid/mcps/:name/test", async (req, res) => {
229
+ api.post("/projects/:pid/mcps/:name/test", asyncRoute(async (req, res) => {
229
230
  const p = project(req, res);
230
231
  if (!p) return;
231
232
  try {
@@ -242,7 +243,7 @@ export function register(api, { projects, registries, project }) {
242
243
  } catch (e) {
243
244
  res.status(200).json({ ok: false, error: e.message });
244
245
  }
245
- });
246
+ }));
246
247
 
247
248
  // In-memory log buffer for one MCP — stderr tail (stdio) or fetch summary
248
249
  // (http) plus a ring of recent events.