@agentprojectcontext/apx 1.81.0 → 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 +21 -11
  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
@@ -33,6 +33,10 @@ export const DEFAULT_LOCAL = {
33
33
  language: "auto",
34
34
  beam_size: 5,
35
35
  idle_minutes: 10,
36
+ // The daemon re-warms the model inside this window so the first utterance of
37
+ // the day isn't the one that pays the ~10s load. Set false to let the server
38
+ // idle out and hand the RAM back instead.
39
+ keep_warm: true,
36
40
  // Long audio (Telegram voice notes > 10 min) can take several minutes on
37
41
  // CPU. 20 minutes covers ~60-minute notes on a small int8 model.
38
42
  timeout_ms: 20 * 60_000,
@@ -13,12 +13,13 @@ import { TTS_TMP_DIR } from "#core/config/paths.js";
13
13
  export { TTS_TMP_DIR };
14
14
  import { readConfig } from "../config/index.js";
15
15
  import {
16
- selectTtsEngine,
16
+ resolveTtsCandidates,
17
17
  listAvailableTtsEngines,
18
18
  resolveMode,
19
19
  resolveChainOrder,
20
20
  } from "./engines/index.js";
21
21
  import { emotionConfigFor, stripEmotionTags } from "./emotions.js";
22
+ import { logWarn } from "#core/logging.js";
22
23
 
23
24
 
24
25
 
@@ -28,8 +29,11 @@ export function ensureTtsTmpDir() {
28
29
  }
29
30
 
30
31
  /**
31
- * Generate speech audio for `text`. Throws on real errors. Falls back to mock
32
- * silently only when provider="auto" AND nothing real is configured.
32
+ * Generate speech audio for `text`. In chain mode every configured engine is
33
+ * tried in order until one answers, so a dead local server costs a retry and
34
+ * not the voice. The returned `provider` says who actually spoke: "mock" means
35
+ * nothing did and the audio is silence — check it before presenting the result
36
+ * as speech.
33
37
  *
34
38
  * @param {object} opts
35
39
  * @param {string} opts.text Text to speak. Required.
@@ -44,6 +48,47 @@ export function ensureTtsTmpDir() {
44
48
  * @param {object} [opts.globalConfig] Pass-in for tests; falls back to readConfig().
45
49
  * @returns {Promise<{audio_path, duration_s, mime, provider}>}
46
50
  */
51
+ /**
52
+ * Duration of a PCM WAV, read from its header. Most engines don't report one —
53
+ * mock and piper were the only two that did, which is how the desktop ended up
54
+ * able to play its silent placeholder and nothing else — and a player that
55
+ * doesn't know how long the clip is can't draw a progress bar.
56
+ *
57
+ * The chunks are walked rather than read at fixed offsets. Only the canonical
58
+ * 44-byte layout puts `fmt ` at 12 and `data` at 36; a float32 wav carries a
59
+ * `fact` chunk between them, and reading offset 40 there lands mid-chunk and
60
+ * yields a confident wrong answer — a 12.4s clip reported as 3.0s.
61
+ *
62
+ * Returns null for anything that isn't a readable RIFF/WAVE (mp3, ogg).
63
+ */
64
+ function wavDurationSeconds(filePath) {
65
+ try {
66
+ const head = Buffer.alloc(4096);
67
+ const fd = fs.openSync(filePath, "r");
68
+ const read = fs.readSync(fd, head, 0, head.length, 0);
69
+ fs.closeSync(fd);
70
+ if (read < 12) return null;
71
+ if (head.toString("ascii", 0, 4) !== "RIFF" || head.toString("ascii", 8, 12) !== "WAVE") return null;
72
+
73
+ let byteRate = 0;
74
+ let pos = 12;
75
+ while (pos + 8 <= read) {
76
+ const id = head.toString("ascii", pos, pos + 4);
77
+ const size = head.readUInt32LE(pos + 4);
78
+ const body = pos + 8;
79
+ if (id === "fmt " && body + 16 <= read) byteRate = head.readUInt32LE(body + 8);
80
+ // `data` is the last chunk read: its size is the sample bytes, and by
81
+ // now `fmt ` has been seen, since a wav that ordered them the other way
82
+ // would be unplayable everywhere else too.
83
+ if (id === "data") return byteRate ? size / byteRate : null;
84
+ pos = body + size + (size % 2); // chunks are word-aligned
85
+ }
86
+ return null;
87
+ } catch {
88
+ return null;
89
+ }
90
+ }
91
+
47
92
  export async function synthesize({
48
93
  text,
49
94
  voice,
@@ -58,29 +103,74 @@ export async function synthesize({
58
103
  throw new Error("synthesize: text required");
59
104
  }
60
105
  const cfg = globalConfig || readConfig() || {};
61
- const { provider: selectedProvider, adapter, engineConfig } = await selectTtsEngine({
62
- globalConfig: cfg,
63
- provider,
64
- });
106
+ const candidates = await resolveTtsCandidates({ globalConfig: cfg, provider });
107
+ const outDir = ensureTtsTmpDir();
65
108
 
66
- // Safety net: if the engine that will speak does NOT support inline emotion
67
- // tags, scrub any stray [tag] markers so they're never read aloud literally.
68
- const speakText = emotionConfigFor(cfg, selectedProvider).enabled
69
- ? text
70
- : stripEmotionTags(text);
109
+ // Walk the chain. An engine that throws (local server down, key revoked,
110
+ // quota) hands the text to the next one being available and answering are
111
+ // different questions, and only the request settles the second.
112
+ let lastError = null;
113
+ for (const { provider: selectedProvider, adapter, engineConfig } of candidates) {
114
+ // Safety net: if the engine that will speak does NOT support inline emotion
115
+ // tags, scrub any stray [tag] markers so they're never read aloud literally.
116
+ const speakText = emotionConfigFor(cfg, selectedProvider).enabled
117
+ ? text
118
+ : stripEmotionTags(text);
119
+ try {
120
+ const r = await adapter.synthesize({
121
+ text: speakText,
122
+ voice,
123
+ language,
124
+ format,
125
+ style,
126
+ outDir,
127
+ config: engineConfig,
128
+ parentEnginesCfg: cfg.engines,
129
+ signal,
130
+ });
131
+ return {
132
+ ...r,
133
+ // Fill in what the engine didn't measure, so every caller sees the same
134
+ // shape whoever spoke.
135
+ duration_s: r.duration_s ?? (r.audio_path ? wavDurationSeconds(r.audio_path) : null),
136
+ provider: selectedProvider || r.provider,
137
+ };
138
+ } catch (e) {
139
+ lastError = e;
140
+ // An aborted turn is the caller changing its mind, not an engine fault —
141
+ // trying the next voice would speak a reply nobody is waiting for.
142
+ if (e?.name === "AbortError" || signal?.aborted) throw e;
143
+ logWarn("tts", `${selectedProvider} failed — ${e?.message || e}`);
144
+ }
145
+ }
146
+ throw lastError || new Error("synthesize: no TTS engine available");
147
+ }
71
148
 
72
- const outDir = ensureTtsTmpDir();
73
- return adapter.synthesize({
74
- text: speakText,
75
- voice,
76
- language,
77
- format,
78
- style,
79
- outDir,
80
- config: engineConfig,
81
- parentEnginesCfg: cfg.engines,
82
- signal,
83
- }).then((r) => ({ ...r, provider: selectedProvider || r.provider }));
149
+ /**
150
+ * Ask whoever is about to speak to get ready, if they can.
151
+ *
152
+ * Only the engine that would actually handle the next synthesize() is warmed —
153
+ * warming the whole chain would spin up fallbacks that are not going to be
154
+ * used. Engines with nothing to warm (every cloud API) simply have no warmup()
155
+ * and are skipped.
156
+ *
157
+ * Fire-and-forget by contract: a warmup that fails costs a slower first reply,
158
+ * never a failed one, so this resolves rather than throws.
159
+ */
160
+ export async function warmupTts({ globalConfig } = {}) {
161
+ const cfg = globalConfig || readConfig() || {};
162
+ const [first] = await resolveTtsCandidates({ globalConfig: cfg });
163
+ if (!first) return { ok: false, error: "no TTS engine" };
164
+ if (typeof first.adapter.warmup !== "function") {
165
+ return { ok: true, provider: first.provider, skipped: "engine has no warmup" };
166
+ }
167
+ try {
168
+ const r = await first.adapter.warmup(first.engineConfig, cfg.engines);
169
+ return { ok: true, provider: first.provider, ...r };
170
+ } catch (e) {
171
+ logWarn("tts", `warmup of ${first.provider} failed — ${e?.message || e}`);
172
+ return { ok: false, provider: first.provider, error: e?.message || String(e) };
173
+ }
84
174
  }
85
175
 
86
176
  /** List engines and whether they look usable right now. */
@@ -0,0 +1,188 @@
1
+ // In-progress chat turns, so streaming survives the tab that started it.
2
+ //
3
+ // The per-request NDJSON stream belongs to the sender: refresh the page or walk
4
+ // to another chat and it is gone, even though the turn keeps running on the
5
+ // daemon. This registry is the daemon's own record of what is being written
6
+ // right now — the accumulated text plus who/where — so ANY surface can be caught
7
+ // up (via the conversation GET) and then followed live (via the events-ws
8
+ // "turn" frames the chat handlers broadcast). It is pure in-memory runtime
9
+ // state: if the daemon restarts mid-turn the turn is gone anyway.
10
+ //
11
+ // Key is the conversation's identity as the client already addresses it:
12
+ // project agent → `${projectId}:conv:${conversationId}`
13
+ // super-agent → `${projectId}:thread:${channel}:${threadId}`
14
+ // so a client on that conversation can match a frame without extra plumbing.
15
+
16
+ let seq = 0;
17
+ const byId = new Map(); // turnId -> record
18
+ const byKey = new Map(); // key -> turnId (the latest turn on that conversation)
19
+
20
+ /** Public client shape. Abort hooks and internal keys never leave this module. */
21
+ function publicTurn(rec) {
22
+ if (!rec) return null;
23
+ return {
24
+ turn_id: rec.id,
25
+ text: rec.text,
26
+ project_id: rec.project_id ?? null,
27
+ agent_slug: rec.agent_slug,
28
+ conversation_id: rec.conversation_id,
29
+ channel: rec.channel,
30
+ thread_id: rec.thread_id,
31
+ model: rec.model,
32
+ started_at: rec.started_at,
33
+ // The text alone is not enough to re-open a multi-step turn: it loses the
34
+ // actual tools and makes a still-working turn look idle. Keep the compact,
35
+ // render-ready timeline alongside it; abort hooks and private keys remain
36
+ // inside this module.
37
+ ...(rec.parts?.length
38
+ ? {
39
+ parts: rec.parts.map((part) => {
40
+ const copy = { ...part };
41
+ if (copy.streaming === false) delete copy.streaming;
42
+ return copy;
43
+ }),
44
+ }
45
+ : {}),
46
+ };
47
+ }
48
+
49
+ /** Key for a project-agent conversation turn. */
50
+ export function convTurnKey(projectId, conversationId) {
51
+ return `${projectId}:conv:${conversationId}`;
52
+ }
53
+ /** Key for a super-agent channel-thread turn. */
54
+ export function threadTurnKey(projectId, channel, threadId) {
55
+ return `${projectId}:thread:${channel}:${threadId}`;
56
+ }
57
+ /** Key for a super-agent chat turn. Roby's web chat has no conversation id —
58
+ * its thread IS the channel (the ledger is written per channel+day), so there
59
+ * is one live turn per project+channel, exactly as Telegram keys one live turn
60
+ * per chat_id. */
61
+ export function superAgentTurnKey(projectId, channel) {
62
+ return `${projectId}:sa:${channel}`;
63
+ }
64
+
65
+ /** Begin tracking a turn. `meta` is echoed to clients (agent_slug, model, …),
66
+ * except `abort`: a function that stops the run, kept private to this module
67
+ * and to abortActiveTurn. */
68
+ export function startActiveTurn(key, meta = {}) {
69
+ const id = `turn_${Date.now().toString(36)}_${++seq}`;
70
+ const rec = { id, key, text: "", started_at: new Date().toISOString(), ...meta, parts: [] };
71
+ byId.set(id, rec);
72
+ byKey.set(key, id);
73
+ return rec;
74
+ }
75
+
76
+ function syncText(rec) {
77
+ rec.text = rec.parts
78
+ .filter((part) => part.kind === "text")
79
+ .map((part) => part.text || "")
80
+ .join("");
81
+ }
82
+
83
+ /** Grow the accumulated text as tokens arrive. */
84
+ export function appendActiveTurn(id, delta) {
85
+ const rec = byId.get(id);
86
+ if (!rec || !delta) return;
87
+ const last = rec.parts.at(-1);
88
+ if (last?.kind === "text" && last.streaming) last.text += delta;
89
+ else rec.parts.push({ kind: "text", text: delta, streaming: true });
90
+ syncText(rec);
91
+ }
92
+
93
+ /** Record the visible work timeline of a live turn. This is deliberately a
94
+ * small transport shape: text segments plus tool starts/results, never hidden
95
+ * reasoning or the abort hook. A refresh can therefore show the same tools the
96
+ * original pane was watching. */
97
+ export function recordActiveTurnEvent(id, event) {
98
+ const rec = byId.get(id);
99
+ if (!rec || !event) return;
100
+ if (event.type === "assistant_text" && event.text) {
101
+ const last = rec.parts.at(-1);
102
+ if (last?.kind === "text" && last.streaming) {
103
+ last.text = event.text;
104
+ last.streaming = false;
105
+ } else {
106
+ rec.parts.push({ kind: "text", text: event.text });
107
+ }
108
+ syncText(rec);
109
+ return;
110
+ }
111
+ if (event.type === "tool_start" && event.trace?.id) {
112
+ rec.parts.push({
113
+ kind: "tool",
114
+ id: event.trace.id,
115
+ tool: event.trace.tool || "tool",
116
+ args: event.trace.args,
117
+ status: "running",
118
+ });
119
+ return;
120
+ }
121
+ if (event.type === "tool_result" && event.trace?.id) {
122
+ const failed = !!event.trace.result && typeof event.trace.result === "object" && !!event.trace.result.error;
123
+ const part = rec.parts.findLast((item) => item.kind === "tool" && item.id === event.trace.id);
124
+ if (part) {
125
+ part.result = event.trace.result;
126
+ part.status = failed ? "error" : "done";
127
+ } else {
128
+ rec.parts.push({
129
+ kind: "tool",
130
+ id: event.trace.id,
131
+ tool: event.trace.tool || "tool",
132
+ args: event.trace.args,
133
+ result: event.trace.result,
134
+ status: failed ? "error" : "done",
135
+ });
136
+ }
137
+ }
138
+ }
139
+
140
+ /** Stop tracking. Idempotent — the finally block and an error path both call it. */
141
+ export function endActiveTurn(id) {
142
+ const rec = byId.get(id);
143
+ if (!rec) return;
144
+ byId.delete(id);
145
+ if (byKey.get(rec.key) === id) byKey.delete(rec.key);
146
+ }
147
+
148
+ /**
149
+ * Stop the turn running on that conversation. Returns false when there is
150
+ * nothing to stop — no live turn, or one registered without an abort hook.
151
+ *
152
+ * This is what makes "stop" and "interrupt" real. A client closing its NDJSON
153
+ * socket deliberately does NOT end the run (that is what lets a refresh catch
154
+ * up on it), so cancelling has to be said out loud: the surface asks for it,
155
+ * the run's AbortController is signalled here, and the loop notices at its next
156
+ * iteration boundary. Same shape Telegram has had all along — one controller
157
+ * per live conversation, aborted when a newer message says "no, do this
158
+ * instead" (see core/channels/telegram/dispatch.js).
159
+ */
160
+ export function abortActiveTurn(key) {
161
+ const id = byKey.get(key);
162
+ const rec = id ? byId.get(id) : null;
163
+ if (!rec || typeof rec.abort !== "function") return false;
164
+ rec.aborted = true;
165
+ try {
166
+ rec.abort();
167
+ } catch {
168
+ /* the run is already gone; the caller only needs to know we tried */
169
+ }
170
+ return true;
171
+ }
172
+
173
+ /** The turn currently being written on that conversation, if any — the partial
174
+ * a just-arrived client renders before it starts following the live frames. */
175
+ export function getActiveTurnByKey(key) {
176
+ const id = byKey.get(key);
177
+ const rec = id ? byId.get(id) : null;
178
+ return publicTurn(rec);
179
+ }
180
+
181
+ /** Every live turn, optionally scoped to one project. Used by aggregate list
182
+ * surfaces (Inbox / Chats) so they all render the same daemon-owned status. */
183
+ export function listActiveTurns({ projectId } = {}) {
184
+ const want = projectId === undefined || projectId === null ? null : String(projectId);
185
+ return [...byId.values()]
186
+ .filter((rec) => want === null || String(rec.project_id) === want)
187
+ .map(publicTurn);
188
+ }
@@ -15,6 +15,8 @@ import {
15
15
  mergeRedactedChannels,
16
16
  } from "#core/config/redact.js";
17
17
  import { collectSecretValues, registerSecretValues } from "#core/config/secret-values.js";
18
+ import { isBlobKey, resolveSuperAgentBlob } from "#core/apc/agent-identity.js";
19
+ import { broadcastSuperAgentAvatar } from "../events-ws.js";
18
20
 
19
21
  export function register(api, { config, scheduler, plugins }) {
20
22
  api.get("/admin/config", (_req, res) => {
@@ -28,6 +30,11 @@ export function register(api, { config, scheduler, plugins }) {
28
30
 
29
31
  api.patch("/admin/config", (req, res) => {
30
32
  const { set, unset } = req.body || {};
33
+ const avatarTouched = (set && Object.hasOwn(set, "super_agent.icon"))
34
+ || (Array.isArray(unset) && unset.includes("super_agent.icon"));
35
+ if (set && Object.hasOwn(set, "super_agent.icon") && !isBlobKey(set["super_agent.icon"])) {
36
+ return res.status(400).json({ error: "Invalid super-agent avatar" });
37
+ }
31
38
  let cfg;
32
39
  try {
33
40
  cfg = readConfig();
@@ -64,6 +71,7 @@ export function register(api, { config, scheduler, plugins }) {
64
71
  registerSecretValues(collectSecretValues(fresh));
65
72
  if (scheduler) scheduler.globalConfig = config;
66
73
  if (plugins) plugins.config = config;
74
+ if (avatarTouched) broadcastSuperAgentAvatar(fresh);
67
75
  res.json({ ok: true, config: redact(fresh) });
68
76
  });
69
77
 
@@ -75,6 +83,7 @@ export function register(api, { config, scheduler, plugins }) {
75
83
  res.json({
76
84
  enabled: !!sa.enabled,
77
85
  name: resolveAgentName(fresh),
86
+ icon: resolveSuperAgentBlob(fresh),
78
87
  model: sa.model || "",
79
88
  system: sa.system || "",
80
89
  permission_mode: sa.permission_mode || PERMISSION_MODES.PERMISO,
@@ -4,10 +4,17 @@
4
4
  // POST /projects/:pid/agents — create from slug
5
5
  // GET /projects/:pid/agents/:slug/memory
6
6
  // PUT /projects/:pid/agents/:slug/memory
7
- import fs from "node:fs";
8
- import path from "node:path";
9
7
  import { readAgents, readVaultAgents, readVaultAgent } from "#core/apc/parser.js";
10
- import { apcAgentFile, apcDir, apcMemoryFile } from "#core/apc/paths.js";
8
+ import {
9
+ readProjectMemory,
10
+ writeProjectMemory,
11
+ projectMemoryPath,
12
+ } from "#core/apc/project-memory.js";
13
+ import {
14
+ readProjectLocalMemory,
15
+ writeProjectLocalMemory,
16
+ projectLocalMemoryPath,
17
+ } from "#core/stores/project-memory.js";
11
18
  import {
12
19
  writeAgentFile,
13
20
  writeVaultAgentFile,
@@ -19,15 +26,14 @@ import {
19
26
  ensureAgentRuntimeDir,
20
27
  readAgentMemory,
21
28
  writeAgentMemory,
22
- agentMemoryPath,
23
- legacyAgentMemoryPath,
24
29
  } from "#core/agent/memory.js";
25
- import { agentToResponse } from "./shared.js";
30
+ import { createAgent, cloneAgent, setAgentConfig, removeAgent, renameAgent } from "#core/apc/agent-write.js";
31
+ import { agentToResponse, asyncRoute } from "./shared.js";
26
32
  import { normalizeVaultPatch } from "#core/apc/agents-vault.js";
27
- import { PERMISSION_MODES } from "#core/constants/permissions.js";
28
33
  import { listConversations } from "#core/stores/conversations.js";
29
34
  import { listTasks } from "#core/stores/tasks.js";
30
35
  import { listRoutines } from "#core/stores/routines.js";
36
+ import { slugifyName } from "#core/stores/organization.js";
31
37
  import { readProjectMessages } from "#core/stores/messages.js";
32
38
 
33
39
  // Attach a per-agent activity summary ({ threads, records, tasks, heartbeats })
@@ -60,16 +66,6 @@ function attachAgentStats(p, agents) {
60
66
  }
61
67
  }
62
68
 
63
- // Autonomy mirrors the super-agent permission modes (total/automatico/permiso).
64
- // An invalid value is dropped rather than persisted so a typo can't silently
65
- // widen an agent's autonomy.
66
- const AUTONOMY_VALUES = new Set(Object.values(PERMISSION_MODES));
67
- function normalizeAutonomy(v) {
68
- if (v === undefined) return undefined;
69
- if (v === null || v === "") return null;
70
- return AUTONOMY_VALUES.has(v) ? v : undefined;
71
- }
72
-
73
69
  export function register(api, { projects, project }) {
74
70
  // Vault = global agent templates. Two-layer: bundled defaults shipped with
75
71
  // APX (assets/agent-vault-defaults/) + user overrides/new ones in
@@ -175,34 +171,12 @@ export function register(api, { projects, project }) {
175
171
  api.post("/projects/:pid/agents", (req, res) => {
176
172
  const p = project(req, res);
177
173
  if (!p) return;
178
- const {
179
- slug, role, model, skills, language, description, tools, is_master,
180
- parent, type, area, emoji, autonomy,
181
- } = req.body || {};
182
- if (!slug) return res.status(400).json({ error: "slug required" });
183
- if (!/^[a-z][a-z0-9_-]*$/.test(slug))
184
- return res.status(400).json({ error: "invalid slug" });
185
- const existing = readAgents(p.path).find((a) => a.slug === slug);
186
- if (existing)
187
- return res.status(400).json({ error: `agent ${slug} already exists` });
188
- const autonomyVal = normalizeAutonomy(autonomy);
174
+ // Field normalization, avatar pick, slug/type validation and the file write
175
+ // all live in core (agent-write.js), shared with the super-agent's
176
+ // create_agent tool so the two surfaces can never drift. The route only
177
+ // resolves the project, rebuilds the registry, and shapes the response.
189
178
  try {
190
- writeAgentFile(p.path, slug, {
191
- Role: role || null,
192
- Model: model || null,
193
- Language: language || null,
194
- Description: description || null,
195
- Skills: skills || [],
196
- Tools: tools || [],
197
- Master: is_master ? true : null,
198
- Parent: parent || null,
199
- Type: type || null,
200
- Area: area || null,
201
- Emoji: emoji || null,
202
- Autonomy: autonomyVal || null,
203
- });
204
- ensureAgentDir(p.path, slug);
205
- ensureAgentRuntimeDir(p, slug);
179
+ const slug = createAgent(p, req.body || {});
206
180
  projects.rebuild(p.id);
207
181
  const created = readAgents(p.path).find((a) => a.slug === slug);
208
182
  res.status(201).json(agentToResponse(created));
@@ -211,41 +185,36 @@ export function register(api, { projects, project }) {
211
185
  }
212
186
  });
213
187
 
188
+ // Duplicate an agent (frontmatter + prompt + memory) into a fresh "<slug>-n"
189
+ // whose display Name gains a " (n)" suffix. Clone/naming live in core
190
+ // (agent-write.js); the route resolves the project, rebuilds, and returns the
191
+ // new agent so the UI can jump straight to it.
192
+ api.post("/projects/:pid/agents/:slug/clone", (req, res) => {
193
+ const p = project(req, res);
194
+ if (!p) return;
195
+ try {
196
+ const newSlug = cloneAgent(p, req.params.slug);
197
+ projects.rebuild(p.id);
198
+ const created = readAgents(p.path).find((a) => a.slug === newSlug);
199
+ res.status(201).json(agentToResponse(created));
200
+ } catch (e) {
201
+ const status = /not found/.test(e.message) ? 404 : 400;
202
+ res.status(status).json({ error: e.message });
203
+ }
204
+ });
205
+
214
206
  // Edit an existing agent. Merges provided fields into the AGENT.md
215
207
  // frontmatter; `system` rewrites the body (the agent's system prompt).
216
208
  api.patch("/projects/:pid/agents/:slug", (req, res) => {
217
209
  const p = project(req, res);
218
210
  if (!p) return;
219
211
  const slug = req.params.slug;
220
- const existing = readAgents(p.path).find((a) => a.slug === slug);
221
- if (!existing) return res.status(404).json({ error: "agent not found" });
222
- const b = req.body || {};
223
- const fields = { ...(existing.fields || {}) };
224
- const setStr = (key, val) => {
225
- if (val === undefined) return;
226
- if (val === null || val === "") delete fields[key];
227
- else fields[key] = val;
228
- };
229
- setStr("Role", b.role);
230
- setStr("Model", b.model);
231
- setStr("Language", b.language);
232
- setStr("Description", b.description);
233
- setStr("Parent", b.parent);
234
- setStr("Type", b.type);
235
- setStr("Area", b.area);
236
- setStr("Emoji", b.emoji);
237
- setStr("Autonomy", normalizeAutonomy(b.autonomy));
238
- if (b.skills !== undefined) fields.Skills = Array.isArray(b.skills) ? b.skills : [];
239
- if (b.tools !== undefined) fields.Tools = Array.isArray(b.tools) ? b.tools : [];
240
- if (b.is_master !== undefined) {
241
- if (b.is_master) fields.Master = true;
242
- else { delete fields.Master; delete fields.Primary; }
243
- }
244
- const body = b.system !== undefined ? b.system : (existing.body || "");
212
+ if (!readAgents(p.path).some((a) => a.slug === slug))
213
+ return res.status(404).json({ error: "agent not found" });
214
+ // Field normalization + write live in core (agent-write.js), shared with the
215
+ // super-agent's configure_agent / set_agent_prompt tools.
245
216
  try {
246
- writeAgentFile(p.path, slug, fields, body);
247
- ensureAgentDir(p.path, slug);
248
- ensureAgentRuntimeDir(p, slug);
217
+ setAgentConfig(p, slug, req.body || {});
249
218
  projects.rebuild(p.id);
250
219
  const updated = readAgents(p.path).find((a) => a.slug === slug);
251
220
  res.json(agentToResponse(updated));
@@ -254,34 +223,59 @@ export function register(api, { projects, project }) {
254
223
  }
255
224
  });
256
225
 
226
+ // Rename an agent's slug — moves its .apc/agents/<slug>.md and runtime dir and
227
+ // repoints every live reference to it (child `Parent`, routines, group
228
+ // rosters, tasks, deliveries, code sessions, telegram routes, the RAG scope).
229
+ // Distinct from PATCH on purpose: it's an irreversible file move and it
230
+ // changes the resource URL, so the UI must navigate to the returned slug
231
+ // rather than revalidate in place. The new slug can be sent raw (`{ slug }`)
232
+ // or derived from a name (`{ name }`) — the caller-side slugify is mirrored
233
+ // here so either surface yields the same key. The whole registry goes in so
234
+ // the sweep can reach rooms hosted by ANOTHER project that include this agent.
235
+ api.post("/projects/:pid/agents/:slug/rename", asyncRoute(async (req, res) => {
236
+ const p = project(req, res);
237
+ if (!p) return;
238
+ const body = req.body || {};
239
+ const target = body.slug ? String(body.slug) : slugifyName(body.name || "");
240
+ if (!target || !/^[a-z][a-z0-9_-]*$/.test(target)) {
241
+ return res.status(400).json({ error: "valid target slug required" });
242
+ }
243
+ try {
244
+ const finalSlug = await renameAgent(p, req.params.slug, target, { projects: projects.list() });
245
+ projects.rebuild(p.id);
246
+ const updated = readAgents(p.path).find((a) => a.slug === finalSlug);
247
+ res.json(agentToResponse(updated));
248
+ } catch (e) {
249
+ const status = /not found/.test(e.message) ? 404 : 400;
250
+ res.status(status).json({ error: e.message });
251
+ }
252
+ }));
253
+
257
254
  // Delete an agent: removes .apc/agents/<slug>.md and runtime data dir.
258
255
  api.delete("/projects/:pid/agents/:slug", (req, res) => {
259
256
  const p = project(req, res);
260
257
  if (!p) return;
261
- const slug = req.params.slug;
262
- const file = apcAgentFile(p.path, slug);
263
- const runtimeDir = path.dirname(agentMemoryPath(p, slug));
264
- const legacyDir = path.dirname(legacyAgentMemoryPath(p.path, slug));
265
- if (!fs.existsSync(file) && !fs.existsSync(runtimeDir) && !fs.existsSync(legacyDir))
266
- return res.status(404).json({ error: "agent not found" });
267
258
  try {
268
- if (fs.existsSync(file)) fs.rmSync(file);
269
- if (fs.existsSync(runtimeDir)) fs.rmSync(runtimeDir, { recursive: true, force: true });
270
- if (fs.existsSync(legacyDir)) fs.rmSync(legacyDir, { recursive: true, force: true });
259
+ removeAgent(p, req.params.slug);
271
260
  projects.rebuild(p.id);
272
261
  res.json({ ok: true });
273
262
  } catch (e) {
274
- res.status(400).json({ error: e.message });
263
+ const status = /not found/.test(e.message) ? 404 : 400;
264
+ res.status(status).json({ error: e.message });
275
265
  }
276
266
  });
277
267
 
278
- // ---- Project-level memory (.apc/memory.md) ----
268
+ // ---- Project-level memory ----
269
+ // Two files, one boundary: `.apc/memory.md` is committed and only a person
270
+ // writes it (core/apc/project-memory.js); `~/.apx/projects/<id>/memory.md` is
271
+ // local, never committed, and is what the `remember` tool appends to
272
+ // (core/stores/project-memory.js). Both are served here so the Memories screen
273
+ // can show them side by side — a memory nothing displays is the bug this pair
274
+ // of routes exists to prevent.
279
275
  api.get("/projects/:pid/memory", (req, res) => {
280
276
  const p = project(req, res);
281
277
  if (!p) return;
282
- const memPath = apcMemoryFile(p.path);
283
- const body = fs.existsSync(memPath) ? fs.readFileSync(memPath, "utf8") : "";
284
- res.json({ body, path: memPath });
278
+ res.json({ body: readProjectMemory(p.path), path: projectMemoryPath(p.path) });
285
279
  });
286
280
 
287
281
  api.put("/projects/:pid/memory", (req, res) => {
@@ -290,11 +284,26 @@ export function register(api, { projects, project }) {
290
284
  const { body } = req.body || {};
291
285
  if (typeof body !== "string")
292
286
  return res.status(400).json({ error: "body must be string" });
293
- fs.mkdirSync(apcDir(p.path), { recursive: true });
294
- const memPath = apcMemoryFile(p.path);
295
- fs.writeFileSync(memPath, body);
287
+ const { bytes } = writeProjectMemory(p.path, body);
296
288
  try { projects.rebuild(p.id); } catch {}
297
- res.json({ ok: true, bytes: Buffer.byteLength(body, "utf8") });
289
+ res.json({ ok: true, bytes });
290
+ });
291
+
292
+ // The local half — agent-written, never committed.
293
+ api.get("/projects/:pid/memory/local", (req, res) => {
294
+ const p = project(req, res);
295
+ if (!p) return;
296
+ res.json({ body: readProjectLocalMemory(p), path: projectLocalMemoryPath(p) });
297
+ });
298
+
299
+ api.put("/projects/:pid/memory/local", (req, res) => {
300
+ const p = project(req, res);
301
+ if (!p) return;
302
+ const { body } = req.body || {};
303
+ if (typeof body !== "string")
304
+ return res.status(400).json({ error: "body must be string" });
305
+ const { bytes } = writeProjectLocalMemory(p, body);
306
+ res.json({ ok: true, bytes });
298
307
  });
299
308
 
300
309
  // ---- Per-agent memory ----