@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
@@ -15,17 +15,45 @@ import os from "node:os";
15
15
  import path from "node:path";
16
16
  import { callEngine } from "#core/engines/index.js";
17
17
  import { runSuperAgent } from "#core/agent/super-agent.js";
18
- import { computeSuppressedTools } from "#core/agent/index.js";
19
- import { summarizeToolTrace } from "#core/agent/tool-summary.js";
18
+ import { runAgent, computeSuppressedTools } from "#core/agent/index.js";
19
+ import { TELEGRAM_TOOL_ITERS, ROUTINE_UNCAPPED_TOOL_ITERS } from "#core/agent/constants.js";
20
+ import { createToolSession, makeToolHandlers } from "#core/agent/tools/registry.js";
20
21
  import { readAgents } from "#core/apc/parser.js";
22
+ import { scopeProjects } from "#core/apc/projects-helpers.js";
21
23
  import { buildAgentSystem } from "#core/agent/build-agent-system.js";
24
+ import { resolveAgentModel } from "#core/agent/agent-model.js";
25
+ import { resolveAgentAllowedTools } from "#core/agent/agent-tools.js";
22
26
  import { resolveAgentName, SUPERAGENT_ACTOR_ID } from "#core/identity/index.js";
27
+ import { recordAgentTurn } from "#core/stores/turn-record.js";
23
28
  import { resolveArtifactRef, ARTIFACTS_SKIP_SIGNAL } from "#core/stores/artifacts.js";
24
29
  import {
25
30
  ensureRoutineMemory,
26
31
  readRoutineMemoryForPrompt,
27
32
  routineMemoryPath,
28
33
  } from "#core/stores/routine-memory.js";
34
+ import { buildRoutineHeader, prependRoutineHeader } from "#core/routines/header.js";
35
+ import {
36
+ startRoutineRun,
37
+ setRoutineRunPhase,
38
+ startRoutineRunTool,
39
+ finishRoutineRunTool,
40
+ addRoutineRunText,
41
+ setRoutineRunConversation,
42
+ endRoutineRun,
43
+ } from "#core/routines/active-runs.js";
44
+ import {
45
+ resolveDeliveryChannels,
46
+ deliverySuppressedTools,
47
+ deliverRoutineOutput,
48
+ alreadyServedChannels,
49
+ routineOutputText,
50
+ notifyOwnerViaRoby,
51
+ readAbstention,
52
+ abstentionChannels,
53
+ ABSTAIN_MARKER,
54
+ AGENT_WEB_CHAT_ID,
55
+ } from "#core/routines/delivery.js";
56
+ import { recordDelivery, markDelivery, DELIVERY_STATUS } from "#core/stores/deliveries.js";
29
57
  import { CHANNELS } from "#core/constants/channels.js";
30
58
  import { detectSignals, formatSignals, peakSeverity, thresholdsFromConfig } from "#core/routines/signals.js";
31
59
  import { readActiveProfile, effectiveProfileConfig } from "#core/profiles/store.js";
@@ -35,6 +63,7 @@ import {
35
63
  computeNextRun,
36
64
  } from "#core/stores/routines.js";
37
65
  import { nowIso } from "#core/util/time.js";
66
+ import { loadAgentSkills, collectAgentSkillMedia } from "#core/agent/skills/agent-skills.js";
38
67
 
39
68
  // --------------------- handlers ---------------------------------------------
40
69
 
@@ -51,43 +80,282 @@ async function handleHeartbeat(ctx, routine) {
51
80
  body: message,
52
81
  meta: { routine: routine.name },
53
82
  });
54
- return { status: "ok", note: `logged to messages on channel '${channel}'` };
83
+ // `text` is the run's output (routineOutputText): a heartbeat's product IS
84
+ // this line, and without naming it here a heartbeat with a delivery channel
85
+ // had something to say and delivered an empty string.
86
+ return { status: "ok", text: message, note: `logged to messages on channel '${channel}'` };
87
+ }
88
+
89
+ // Is this routine "telegram-bound" — i.e. is its PRODUCT a Telegram message? The
90
+ // reliable signal is a `apx telegram …` post_command, which pipes the model's
91
+ // final text straight into Telegram and shows up here as a suppressed
92
+ // send_telegram (see tools-overlap.js). There the final turn literally becomes
93
+ // the message, so its dedicated Telegram ceiling is the right shape.
94
+ //
95
+ // Merely HAVING the send_telegram tool available does NOT count: it's a
96
+ // near-universal default (an empty allowed_tools falls back to the broad set),
97
+ // so keying on it would mark almost every routine telegram-bound and cap the
98
+ // exact background work this distinction exists to free. Magui filling a backlog
99
+ // might send a summary at the end, but her job is the backlog, not the message —
100
+ // she must finish first. So only the post_command sink marks telegram-bound.
101
+ export function routineReportsToTelegram({ autoSuppress, deliverTo }) {
102
+ // `deliver_to: ["telegram"]` is the same shape of sink, declared on the
103
+ // routine instead of in a shell string: the runner hands the final text to
104
+ // Telegram, so the final turn IS the message here too. No routine that
105
+ // predates deliver_to has one, so this widens the rule without moving any
106
+ // existing routine from one budget to the other.
107
+ if ((deliverTo || []).includes(CHANNELS.TELEGRAM)) return true;
108
+ return (autoSuppress || []).includes("send_telegram");
109
+ }
110
+
111
+ // Tool-loop budget for a routine: the dedicated Telegram or non-Telegram
112
+ // runaway ceiling. Both honor a per-deployment config override.
113
+ export function routineToolIters(config, { telegramBound }) {
114
+ const sa = config?.super_agent || {};
115
+ const pick = (raw, fallback) => {
116
+ const n = Number(raw);
117
+ return Number.isFinite(n) && n > 0 ? n : fallback;
118
+ };
119
+ return telegramBound
120
+ ? pick(sa.telegram_max_iters, TELEGRAM_TOOL_ITERS)
121
+ : pick(sa.routine_max_iters, ROUTINE_UNCAPPED_TOOL_ITERS);
122
+ }
123
+
124
+ /**
125
+ * Bridge the agent loop's progress events onto the open run record, so a panel
126
+ * can watch a run TAKE its steps. Until this existed the only trace of a run
127
+ * was written at the end, which meant a four-minute routine was four minutes of
128
+ * nothing followed by a wall of text.
129
+ *
130
+ * Returns null when no run is open (a direct handler call in a test), and the
131
+ * loop then pays one `if` per event instead of building a record nobody reads.
132
+ */
133
+ function runProgressEvents(runId) {
134
+ if (!runId) return null;
135
+ return (ev) => {
136
+ if (ev?.type === "tool_start") {
137
+ startRoutineRunTool(runId, { traceId: ev.trace?.id, tool: ev.trace?.tool, args: ev.trace?.args });
138
+ } else if (ev?.type === "tool_result") {
139
+ finishRoutineRunTool(runId, { traceId: ev.trace?.id, tool: ev.trace?.tool, result: ev.trace?.result });
140
+ } else if (ev?.type === "assistant_text") {
141
+ addRoutineRunText(runId, ev.text);
142
+ }
143
+ };
55
144
  }
56
145
 
57
146
  async function handleExecAgent(ctx, routine) {
58
- const { project, globalConfig } = ctx;
147
+ const { project, globalConfig, projects, plugins, registries } = ctx;
59
148
  const { agent: slug, prompt } = routine.spec;
60
149
  if (!slug || !prompt) throw new Error("exec_agent: spec needs { agent, prompt }");
61
150
 
62
151
  const agents = readAgents(project.path);
63
152
  const agent = agents.find((a) => a.slug === slug);
64
153
  if (!agent) throw new Error(`agent ${slug} not found`);
65
- const model = agent.fields.Model;
66
- if (!model) throw new Error(`agent ${slug} has no model`);
67
-
68
- const result = await callEngine({
69
- modelId: model,
70
- system: buildAgentSystem(project, agent, {
71
- invocation: "routine",
72
- routine: routine.name,
73
- extraParts: [`Reply briefly, max 4 sentences.`],
74
- }),
75
- messages: [{ role: "user", content: prompt }],
76
- config: project.config || globalConfig,
154
+ const agentName = agent.fields?.Name || agent.name || slug;
155
+ const config = project.config || globalConfig;
156
+ const model = await resolveAgentModel({ agent, config });
157
+ if (!model) throw new Error(`no model for agent ${slug} (no override, no router default)`);
158
+
159
+ // Explicit opt-out ONLY. `spec.no_tools: true` keeps the old one-shot text
160
+ // path (weather-style "write a sentence, post_commands send it").
161
+ //
162
+ // An empty `allowed_tools` used to count as that opt-out, and it is also what
163
+ // the store writes when nobody specified any — `apx routine add` without
164
+ // `--allowed-tools` produced `[]`. So every routine created the obvious way
165
+ // silently ran with NO tools: the agent could not read a file, reach Asana or
166
+ // write its ledger, and the "answer" was a model narrating work it never did.
167
+ // The two states are now distinct: `[]` means "no override, use the agent's
168
+ // declared tools", and refusing tools altogether is something you have to say.
169
+ // A non-Roby agent that delivers to web gets its OWN persistent web chat
170
+ // (web-main), written by the delivery adapter. Recording a fresh per-run
171
+ // routine conversation here too would give it a second, noisier thread — so
172
+ // for that case keep only the ledger audit and let the web chat be the thread.
173
+ const isProjectAgent = slug !== SUPERAGENT_ACTOR_ID;
174
+ const deliversToWeb = (ctx.deliverTo || []).includes(CHANNELS.WEB);
175
+ const ledgerOnly = isProjectAgent && deliversToWeb;
176
+
177
+ const noTools = routine.spec?.no_tools === true;
178
+ // Only a NON-EMPTY list overrides the agent's own declared tools.
179
+ const toolOverride =
180
+ Array.isArray(routine.allowed_tools) && routine.allowed_tools.length > 0
181
+ ? routine.allowed_tools
182
+ : undefined;
183
+
184
+ const system = buildAgentSystem(project, agent, {
185
+ invocation: "routine",
186
+ routine: routine.name,
187
+ channel: CHANNELS.ROUTINE,
188
+ globalConfig: config,
77
189
  });
78
190
 
79
- project.logMessage({
191
+ // Images the agent's skills declare: the pool attach_media / view_media
192
+ // validate against, and the sink a queued attachment lands in for delivery.
193
+ // Same skills the system prompt above rendered, so the manifest the model
194
+ // sees and the ids it can attach never drift.
195
+ const attachableMedia = collectAgentSkillMedia(loadAgentSkills(project, agent));
196
+ const mediaSink = [];
197
+
198
+ let text = "";
199
+ let trace = [];
200
+ let usage = null;
201
+ let allowedTools = [];
202
+ // What ANSWERED, which is not always what was resolved: the router can fall
203
+ // back mid-run, and the record should name the model that produced the reply.
204
+ let answeredOn = model;
205
+
206
+ if (noTools) {
207
+ const result = await callEngine({
208
+ modelId: model,
209
+ system,
210
+ messages: [{ role: "user", content: prompt }],
211
+ config,
212
+ });
213
+ text = result.text || "";
214
+ usage = result.usage;
215
+ answeredOn = result.model || model;
216
+ } else {
217
+ const cfg = structuredClone(config || {});
218
+ cfg.super_agent = {
219
+ ...(config?.super_agent || {}),
220
+ // A scheduled run has no human to click Confirm. Default to total so
221
+ // a Magui-style cron can edit its ledger; pin permission_mode on the
222
+ // routine itself to lock it down.
223
+ permission_mode: routine.permission_mode || "total",
224
+ ...(toolOverride ? { allowed_tools: toolOverride } : {}),
225
+ };
226
+ const autoSuppress = computeSuppressedTools(routine.post_commands);
227
+ const explicitSuppress = Array.isArray(routine.spec?.suppress_tools)
228
+ ? routine.spec.suppress_tools.filter((s) => typeof s === "string")
229
+ : [];
230
+ // A configured delivery owns the message the same way a post_command sink
231
+ // does — leaving send_telegram in the loop next to `deliver_to: telegram`
232
+ // is the two-messages bug of spec/done/01 with a different sink.
233
+ const deliverSuppress = deliverySuppressedTools(ctx.deliverTo);
234
+ const suppressTools = [...new Set([...autoSuppress, ...explicitSuppress, ...deliverSuppress])];
235
+ allowedTools = resolveAgentAllowedTools(agent, { override: toolOverride });
236
+ const toolSession = createToolSession(CHANNELS.ROUTINE, { allowedTools });
237
+ // A routine that reports to Telegram keeps the bounded chat budget; one that
238
+ // does background work nobody watches runs to completion (Magui's backlog
239
+ // refill was being cut off at ~23 steps by the Telegram budget).
240
+ const telegramBound = routineReportsToTelegram({ autoSuppress, deliverTo: ctx.deliverTo });
241
+ const maxIters = routineToolIters(cfg, { telegramBound });
242
+
243
+ const result = await runAgent({
244
+ globalConfig: cfg,
245
+ system,
246
+ prompt,
247
+ overrideModel: model,
248
+ toolSchemas: toolSession.initialSchemas,
249
+ makeToolHandlers,
250
+ toolHandlerCtx: {
251
+ // Scoped to the routine's OWN project, so an unqualified path is read
252
+ // against it instead of against the default project. See scopeProjects.
253
+ projects: scopeProjects(projects, project.id),
254
+ plugins,
255
+ registries,
256
+ globalConfig: cfg,
257
+ channel: CHANNELS.ROUTINE,
258
+ channelMeta: {
259
+ routineName: routine.name,
260
+ routineId: routine.id || "",
261
+ projectPath: project.path,
262
+ // Who is speaking, for any tool that signs what it writes
263
+ // (comment_task). run-turn.js sets the same key.
264
+ agentSlug: slug,
265
+ },
266
+ toolSession,
267
+ // Skill media plumbing for attach_media / view_media (see above).
268
+ attachableMedia,
269
+ mediaSink,
270
+ requestConfirmation: null,
271
+ },
272
+ agentName: slug,
273
+ onEvent: runProgressEvents(ctx.runId),
274
+ suppressTools: suppressTools.length > 0 ? suppressTools : null,
275
+ maxIters,
276
+ // A routine has to both act and report, and 2048 was tight enough that a
277
+ // run which wrote its output before filing it hit the cap mid-item. The
278
+ // loop now recovers from that (see wasTruncated), but the headroom keeps
279
+ // it from being the normal case.
280
+ maxTokens: 4096,
281
+ });
282
+ text = result.text || "";
283
+ trace = Array.isArray(result.trace) ? result.trace : [];
284
+ usage = result.usage;
285
+ answeredOn = result.model || model;
286
+
287
+ const blocked = blockedForPermission(trace);
288
+ if (blocked.length) {
289
+ const { conversationId } = recordRoutineTurn(project, routine, {
290
+ slug, model: answeredOn, prompt, reply: text, trace, usage, conversation: !ledgerOnly,
291
+ });
292
+ return {
293
+ status: "error",
294
+ error:
295
+ `blocked waiting for a confirmation nobody can give: ${blocked.join(", ")}. ` +
296
+ `A scheduled run has no one to approve a dangerous tool — either allow it on ` +
297
+ `this routine (allowed_tools) or use a tool that does not need approval.`,
298
+ blocked_tools: blocked,
299
+ reply: text,
300
+ trace,
301
+ agent_slug: slug,
302
+ agent_name: agentName,
303
+ model: answeredOn,
304
+ conversation_id: conversationId || (ledgerOnly ? AGENT_WEB_CHAT_ID : null),
305
+ allowed_tools: allowedTools,
306
+ };
307
+ }
308
+ }
309
+
310
+ const { conversationId } = recordRoutineTurn(project, routine, {
311
+ slug, model: answeredOn, prompt, reply: text, trace, usage, conversation: !ledgerOnly,
312
+ });
313
+ setRoutineRunConversation(ctx.runId, { conversationId, agentSlug: slug });
314
+ return {
315
+ status: "ok",
316
+ reply: text,
317
+ trace,
80
318
  agent_slug: slug,
319
+ agent_name: agentName,
320
+ model: answeredOn,
321
+ conversation_id: conversationId || (ledgerOnly ? AGENT_WEB_CHAT_ID : null),
322
+ allowed_tools: allowedTools,
323
+ usage,
324
+ // Images the agent queued with attach_media, for the delivery adapters.
325
+ ...(mediaSink.length ? { attachments: mediaSink } : {}),
326
+ };
327
+ }
328
+
329
+ /**
330
+ * File this run the way a message to you is filed: one conversation to reopen,
331
+ * one set of ledger rows to search — written by the SAME recorder every other
332
+ * channel uses, so a run records which agent answered, on which model, and what
333
+ * it cost. It used to have its own pair of writers here, and the file half of
334
+ * that pair wrote text and nothing else: the thread opened as "0 tok", no
335
+ * model, no actor. A scheduled run is the case where the stored record is the
336
+ * only record there will ever be — nobody watched it stream.
337
+ */
338
+ function recordRoutineTurn(project, routine, { slug, model, prompt, reply, trace, usage, conversation = true }) {
339
+ return recordAgentTurn({
340
+ project,
341
+ agentSlug: slug,
81
342
  channel: CHANNELS.ROUTINE,
82
- direction: "out",
83
- type: "agent",
84
- actor_id: slug,
85
- actor_kind: "agent",
86
- author: slug,
87
- body: result.text,
88
- meta: { routine: routine.name, usage: result.usage },
343
+ title: routine.name,
344
+ model,
345
+ prompt,
346
+ // False for a delivering project agent: its user-facing thread is its own
347
+ // persistent web chat, not a per-run routine conversation. The ledger audit
348
+ // (usage/model/trace) is still written either way.
349
+ conversation,
350
+ // The thread says whose clock woke it; the ledger row keeps the bare prompt
351
+ // (it already carries `meta.routine`, and search should match the words the
352
+ // routine actually asked).
353
+ filedPrompt: `[routine: ${routine.name}]\n\n${prompt}`,
354
+ reply,
355
+ trace,
356
+ usage,
357
+ scope: { routine: routine.name },
89
358
  });
90
- return { status: "ok", reply: result.text };
91
359
  }
92
360
 
93
361
  async function handleSuperAgent(ctx, routine, extraChannelMeta = {}) {
@@ -99,7 +367,9 @@ async function handleSuperAgent(ctx, routine, extraChannelMeta = {}) {
99
367
  cfg.super_agent = {
100
368
  ...(globalConfig?.super_agent || {}),
101
369
  ...(routine.permission_mode ? { permission_mode: routine.permission_mode } : {}),
102
- ...(Array.isArray(routine.allowed_tools) ? { allowed_tools: routine.allowed_tools } : {}),
370
+ // Same rule as exec_agent: an empty list is the store's default, not a
371
+ // deliberate "no tools".
372
+ ...(routine.allowed_tools?.length ? { allowed_tools: routine.allowed_tools } : {}),
103
373
  };
104
374
 
105
375
  // Auto-suppress tools whose output would duplicate post_commands.
@@ -110,7 +380,18 @@ async function handleSuperAgent(ctx, routine, extraChannelMeta = {}) {
110
380
  const explicitSuppress = Array.isArray(routine.spec?.suppress_tools)
111
381
  ? routine.spec.suppress_tools.filter((s) => typeof s === "string")
112
382
  : [];
113
- const suppressTools = [...new Set([...autoSuppress, ...explicitSuppress])];
383
+ // Same rule as exec_agent: a channel the runner is going to deliver to loses
384
+ // its in-loop twin, so the run produces one message and not two.
385
+ const deliverSuppress = deliverySuppressedTools(ctx.deliverTo);
386
+ const suppressTools = [...new Set([...autoSuppress, ...explicitSuppress, ...deliverSuppress])];
387
+
388
+ // Same rule as exec_agent: only a `apx telegram …` post_command makes the run
389
+ // telegram-bound (bounded chat budget). A super_agent report that sends via the
390
+ // send_telegram tool — like the secretary open/close — is work that should run
391
+ // to completion, which also lifts it off runSuperAgent's low chit-chat default
392
+ // of MAX_TOOL_ITERS.
393
+ const telegramBound = routineReportsToTelegram({ autoSuppress, deliverTo: ctx.deliverTo });
394
+ const maxIters = routineToolIters(cfg, { telegramBound });
114
395
 
115
396
  const result = await runSuperAgent({
116
397
  globalConfig: cfg,
@@ -143,6 +424,8 @@ async function handleSuperAgent(ctx, routine, extraChannelMeta = {}) {
143
424
  ...extraChannelMeta,
144
425
  },
145
426
  suppressTools: suppressTools.length > 0 ? suppressTools : null,
427
+ maxIters,
428
+ onEvent: runProgressEvents(ctx.runId),
146
429
  });
147
430
 
148
431
  // A tool that needed a human in a run with no human is a DEAD END, not a
@@ -151,21 +434,23 @@ async function handleSuperAgent(ctx, routine, extraChannelMeta = {}) {
151
434
  // and it took twenty-one shell commands to work out why. Name it.
152
435
  const blocked = blockedForPermission(result.trace);
153
436
 
154
- project.logMessage({
437
+ // Same recorder as exec_agent and as a message typed by hand — the prompt, the
438
+ // steps, and a reply stamped with model and usage. The super-agent's chats
439
+ // live in the ledger rather than in per-agent conversation files, so only that
440
+ // half is written; what goes into it is the same shape either way.
441
+ recordAgentTurn({
442
+ project,
443
+ agentSlug: SUPERAGENT_ACTOR_ID,
444
+ agentName: result.name || resolveAgentName(globalConfig),
445
+ actorKind: "superagent",
446
+ conversation: false,
155
447
  channel: CHANNELS.ROUTINE,
156
- direction: "out",
157
- type: "agent",
158
- actor_id: SUPERAGENT_ACTOR_ID,
159
- actor_kind: "superagent",
160
- author: result.name || resolveAgentName(globalConfig),
161
- body: result.text || "",
162
- meta: {
163
- routine: routine.name,
164
- tool_trace: result.trace,
165
- // Compact form too: the viewer renders this without parsing the trace.
166
- ...(summarizeToolTrace(result.trace) ? { tool_summary: summarizeToolTrace(result.trace) } : {}),
167
- usage: result.usage,
168
- },
448
+ model: result.model,
449
+ prompt,
450
+ reply: result.text || "",
451
+ trace: result.trace,
452
+ usage: result.usage,
453
+ scope: { routine: routine.name },
169
454
  });
170
455
  if (blocked.length) {
171
456
  return {
@@ -198,6 +483,21 @@ export function blockedForPermission(trace) {
198
483
  return [...names];
199
484
  }
200
485
 
486
+ /**
487
+ * The active agent profile's settings, or `{}`. Read by the watcher (for its
488
+ * thresholds) and by delivery (for `deliver_to: ["profile"]`), and swallowing
489
+ * the error is the point: a profile that fails to load must not take down a
490
+ * run that has nothing to do with it.
491
+ */
492
+ function activeProfileConfig(globalConfig) {
493
+ try {
494
+ const active = readActiveProfile(globalConfig);
495
+ return active ? (effectiveProfileConfig(active, globalConfig) || {}) : {};
496
+ } catch {
497
+ return {};
498
+ }
499
+ }
500
+
201
501
  async function handleTelegram(ctx, routine) {
202
502
  const { plugins } = ctx;
203
503
  const tg = plugins?.get("telegram");
@@ -211,9 +511,10 @@ async function handleTelegram(ctx, routine) {
211
511
 
212
512
  function handleShell(ctx, routine) {
213
513
  return new Promise((resolve, reject) => {
214
- const { command, timeout_ms = 30_000 } = routine.spec;
215
- if (!command) return reject(new Error("shell routine needs spec.command"));
216
- const child = spawn("sh", ["-c", command], {
514
+ const { command, cmd, timeout_ms = 30_000 } = routine.spec || {};
515
+ const finalCmd = command || cmd;
516
+ if (!finalCmd) return reject(new Error("shell routine needs spec.command (or spec.cmd)"));
517
+ const child = spawn("sh", ["-c", finalCmd], {
217
518
  cwd: ctx.project.path,
218
519
  stdio: ["ignore", "pipe", "pipe"],
219
520
  });
@@ -253,11 +554,7 @@ async function handleWatch(ctx, routine) {
253
554
  // Thresholds come from the active profile — "stale after 7 days" is a
254
555
  // judgement about how someone works, not a constant — with the routine able
255
556
  // to override for its own narrower purpose.
256
- let profileConfig = {};
257
- try {
258
- const active = readActiveProfile(globalConfig);
259
- if (active) profileConfig = effectiveProfileConfig(active, globalConfig) || {};
260
- } catch { /* a broken profile must not take the watcher down */ }
557
+ const profileConfig = activeProfileConfig(globalConfig);
261
558
 
262
559
  const entries = [];
263
560
  for (const entry of projects?.list?.() || []) {
@@ -275,6 +572,9 @@ async function handleWatch(ctx, routine) {
275
572
  ...thresholdsFromConfig(profileConfig),
276
573
  ...(thresholds && typeof thresholds === "object" ? thresholds : {}),
277
574
  types: Array.isArray(types) ? types : [],
575
+ // So the a2a detector surfaces each message once — everything that arrived
576
+ // since this watch last ran, and nothing it already considered.
577
+ a2a_since: routine.last_run_at || routine.last_run || "",
278
578
  });
279
579
 
280
580
  if (!signals.length) {
@@ -297,7 +597,8 @@ async function handleWatch(ctx, routine) {
297
597
  `${formatSignals(signals)}\n\n` +
298
598
  "These were found deterministically — they are facts, not guesses. Your job is " +
299
599
  "judgement: decide whether any of it is worth interrupting for right now, and if " +
300
- "so say the one thing that matters. Staying quiet is a valid outcome.",
600
+ "so say the one thing that matters. Staying quiet is a valid outcome — reply with " +
601
+ `${ABSTAIN_MARKER} on the first line when you choose it, and your reasoning after.`,
301
602
  },
302
603
  };
303
604
 
@@ -314,6 +615,10 @@ async function handleWatch(ctx, routine) {
314
615
  signals: signals.length,
315
616
  peak_severity: peakSeverity(signals),
316
617
  signal_types: [...new Set(signals.map((s) => s.type))],
618
+ // Solicited when the owner explicitly asked to be told about one of these
619
+ // (an a2a message tagged solicited). It crosses the interruption budget the
620
+ // same way a reply does — see the gate in core/routines/delivery.js.
621
+ solicited: signals.some((s) => s.payload?.solicited === true),
317
622
  ...(skipped.length ? { skipped } : {}),
318
623
  };
319
624
  }
@@ -352,6 +657,46 @@ function injectPreOutput(prompt, preOutput) {
352
657
  return prompt.replace(/\{\{pre_output\}\}/g, preOutput || "");
353
658
  }
354
659
 
660
+ // The one-line notification a delivery hands the mascot / push feed: short
661
+ // enough for a bubble, in the message's own language. Two tiers, so tokens are
662
+ // only spent when they buy something:
663
+ // • a short message IS its own headline — take its first meaningful line, free;
664
+ // • a long one gets a real ≤100-char summary from the model ("you have a golf
665
+ // trivia to answer" rather than the first 100 chars of a wall of text).
666
+ const NOTIFY_MAX_CHARS = 100;
667
+ const NOTIFY_SUMMARY_OVER = 280;
668
+
669
+ function firstLineNotify(text) {
670
+ const line = String(text || "")
671
+ .split("\n").map((s) => s.trim()).find(Boolean) || "";
672
+ // Strip the loudest markdown so the bubble reads as prose, keep emoji.
673
+ return line.replace(/[*_`#>]+/g, "").replace(/\s+/g, " ").trim().slice(0, NOTIFY_MAX_CHARS);
674
+ }
675
+
676
+ export async function buildDeliveryNotify({ text, model, config }) {
677
+ const clean = String(text || "").trim();
678
+ if (!clean) return "";
679
+ if (clean.length <= NOTIFY_SUMMARY_OVER) return firstLineNotify(clean);
680
+ // Long → a dedicated tiny summary. Kept model-authored (never a canned string)
681
+ // and in the message's own language; a model that is down falls back to the
682
+ // truncated headline rather than going silent.
683
+ try {
684
+ const r = await callEngine({
685
+ modelId: model,
686
+ system:
687
+ "You write ONE ultra-short notification line (max 100 characters) that tells the user " +
688
+ "what a message is about, so they know whether to open it. Write in the SAME language as " +
689
+ "the message. No quotes, no preamble, no trailing period — just the line.",
690
+ messages: [{ role: "user", content: `Message:\n\n${clean}\n\nNotification line (≤100 chars):` }],
691
+ config,
692
+ });
693
+ const line = String(r.text || "").replace(/\s+/g, " ").trim().slice(0, NOTIFY_MAX_CHARS);
694
+ return line || firstLineNotify(clean);
695
+ } catch {
696
+ return firstLineNotify(clean);
697
+ }
698
+ }
699
+
355
700
  /** Decide whether to skip the LLM call based on skip_prompt_on + pre results. */
356
701
  function shouldSkipPrompt(routine, preExitCode, preStdout) {
357
702
  const mode = routine.skip_prompt_on || "signal";
@@ -379,12 +724,49 @@ function shouldSkipPrompt(routine, preExitCode, preStdout) {
379
724
  * @returns {object} { status, last_run_at, next_run_at, ...handler-result }
380
725
  */
381
726
  export async function runRoutineNow(ctx, routine) {
727
+ // The run record is opened HERE, around everything, so a routine is visible
728
+ // as running from its first millisecond — including while a pre_command is
729
+ // still shelling out, which is where the old panel showed nothing at all.
730
+ const run = startRoutineRun({
731
+ projectRoot: ctx.project?.storagePath || os.homedir(),
732
+ routine,
733
+ trigger: ctx.trigger || "manual",
734
+ });
735
+ let out = null;
736
+ let failure = null;
737
+ try {
738
+ out = await runRoutinePipeline({ ...ctx, runId: run.id }, routine);
739
+ return out;
740
+ } catch (e) {
741
+ failure = e;
742
+ throw e;
743
+ } finally {
744
+ endRoutineRun(run.id, {
745
+ status: failure || out?.status === "error" ? "error" : "ok",
746
+ error: failure?.message || out?.error || null,
747
+ conversationId: out?.conversation_id || null,
748
+ agentSlug: out?.agent_slug || null,
749
+ text: out?.reply || out?.text || null,
750
+ });
751
+ }
752
+ }
753
+
754
+ async function runRoutinePipeline(ctx, routine) {
382
755
  const cwd = ctx.project?.path || os.homedir();
383
756
  const storagePath = ctx.project?.storagePath || os.homedir();
384
757
 
385
758
  const hasPreCmds = Array.isArray(routine.pre_commands) && routine.pre_commands.length > 0;
386
759
  const hasPostCmds = Array.isArray(routine.post_commands) && routine.post_commands.length > 0;
387
760
 
761
+ // Where this run's output goes, resolved ONCE and before the handler runs —
762
+ // the handlers need it too, because a channel the runner is going to deliver
763
+ // to has its in-loop twin (send_telegram) suppressed for the run.
764
+ const delivery = resolveDeliveryChannels(routine, {
765
+ profileConfig: activeProfileConfig(ctx.globalConfig),
766
+ globalConfig: ctx.project?.config || ctx.globalConfig,
767
+ });
768
+ const runCtx = delivery.channels.length ? { ...ctx, deliverTo: delivery.channels } : ctx;
769
+
388
770
  let preStdout = "";
389
771
  let preExitCode = 0;
390
772
  let preOutputFile = null;
@@ -419,23 +801,44 @@ export async function runRoutineNow(ctx, routine) {
419
801
 
420
802
  // ── Phase 2: LLM / handler ────────────────────────────────────────────────
421
803
  const skip = hasPreCmds && shouldSkipPrompt(routine, preExitCode, preStdout);
804
+ setRoutineRunPhase(ctx.runId, "agent");
422
805
 
423
806
  let result = { status: "ok" };
424
807
  let status = "ok";
425
808
  let errMsg = null;
426
809
 
427
810
  if (!skip) {
428
- const enrichedRoutine = (hasPreCmds && preStdout)
429
- ? {
430
- ...routine,
431
- spec: {
432
- ...routine.spec,
433
- // {{pre_output}} works in both the LLM prompt and the telegram text.
434
- prompt: injectPreOutput(routine.spec?.prompt, preStdout),
435
- text: injectPreOutput(routine.spec?.text, preStdout),
436
- },
437
- }
438
- : routine;
811
+ // The automation header: name / id / memory path / last run / this run,
812
+ // built once so a single run stamps one instant. Prepended to the LLM
813
+ // prompt so the model always opens on its identity and the current time
814
+ // natively — the routine no longer needs an `echo …date…` pre_command
815
+ // feeding {{pre_output}} just to know what day it is. It rides on
816
+ // `spec.prompt` ONLY: a telegram routine's `spec.text` is the message body
817
+ // sent verbatim to the chat (handleTelegram), so a header there would leak
818
+ // into the delivered message, not brief a model.
819
+ const header = buildRoutineHeader(routine, {
820
+ storagePath,
821
+ config: ctx.project?.config || ctx.globalConfig,
822
+ nowMs: Date.now(),
823
+ });
824
+
825
+ // Injected unconditionally, including when there is no pre output at all.
826
+ // A routine that asks for {{pre_output}} and gets nothing must see an empty
827
+ // slot, not the literal braces: a placeholder that survives into the prompt
828
+ // reaches the model as text and it will dutifully try to make sense of it.
829
+ // The replace is a no-op for the routines that never mention it.
830
+ const enrichedRoutine = {
831
+ ...routine,
832
+ spec: {
833
+ ...routine.spec,
834
+ // {{pre_output}} works in both the LLM prompt and the telegram text;
835
+ // the header rides on the prompt only (see above). Keys the spec does
836
+ // not have stay absent — a `prompt: undefined` on a shell routine would
837
+ // be a new shape for every reader downstream.
838
+ ...(typeof routine.spec?.prompt === "string" ? { prompt: prependRoutineHeader(injectPreOutput(routine.spec.prompt, preStdout), header) } : {}),
839
+ ...(typeof routine.spec?.text === "string" ? { text: injectPreOutput(routine.spec.text, preStdout) } : {}),
840
+ },
841
+ };
439
842
 
440
843
  const handler = HANDLERS[enrichedRoutine.kind];
441
844
  if (!handler) {
@@ -443,7 +846,7 @@ export async function runRoutineNow(ctx, routine) {
443
846
  errMsg = `unknown routine kind: ${enrichedRoutine.kind}`;
444
847
  } else {
445
848
  try {
446
- result = await handler(ctx, enrichedRoutine);
849
+ result = await handler(runCtx, enrichedRoutine);
447
850
  if (result?.status === "error") {
448
851
  status = "error";
449
852
  errMsg = result.error || result.stderr || `routine ${routine.name} returned error status`;
@@ -458,15 +861,164 @@ export async function runRoutineNow(ctx, routine) {
458
861
  result = { status: "ok", skipped: true, note: "pre_commands signalled skip" };
459
862
  }
460
863
 
864
+ // ── Phase 2.5: delivery ───────────────────────────────────────────────────
865
+ // The run's own answer, handed to every channel the routine is configured
866
+ // for. Before this phase existed the answer went to the ledger and stopped
867
+ // there: reaching a person meant a shell post_command or a paragraph in the
868
+ // prompt asking the model to call send_telegram, and the failure mode of both
869
+ // is silence. See core/routines/delivery.js.
870
+ setRoutineRunPhase(ctx.runId, "delivery");
871
+ const llmOutput = result?.reply || result?.text || "";
872
+ const deliveryText = routineOutputText(result);
873
+ // A run that judged it had nothing worth interrupting for. Not an empty run
874
+ // and not a failed one — a decision, and the watch prompts have always told
875
+ // the model that making it is free. Until this branch existed it was not:
876
+ // "Me mantengo en silencio…" was delivered as the news and charged to the
877
+ // interruption budget like any other push. See readAbstention in delivery.js.
878
+ const abstention = readAbstention(deliveryText);
879
+ const wanted = delivery.channels.length + delivery.unknown.length;
880
+ let deliveries = [];
881
+ let deliverySkipped = [];
882
+ if (wanted > 0 && status === "ok" && abstention) {
883
+ result = { ...result, abstained: true, abstain_reason: abstention.reason || null };
884
+ // No gate, so no nudge is spent and no cooldown consumed; no push channel,
885
+ // so nothing reaches the phone. A bare marker with no reasoning is written
886
+ // nowhere at all — the run log already records that it ran and stayed quiet.
887
+ if (abstention.reason) {
888
+ deliveries = await deliverRoutineOutput(runCtx, {
889
+ routine,
890
+ channels: abstentionChannels(delivery.channels),
891
+ text: abstention.reason,
892
+ gate: null,
893
+ abstained: true,
894
+ agent:
895
+ result?.agent_slug && result.agent_slug !== SUPERAGENT_ACTOR_ID
896
+ ? { slug: result.agent_slug, name: result.agent_name || result.agent_slug, model: result.model, usage: result.usage }
897
+ : { slug: SUPERAGENT_ACTOR_ID, model: result?.model, usage: result?.usage },
898
+ });
899
+ }
900
+ } else if (wanted > 0 && status === "ok" && deliveryText) {
901
+ deliverySkipped = alreadyServedChannels({
902
+ routine,
903
+ channels: delivery.channels,
904
+ postSinks: computeSuppressedTools(routine.post_commands),
905
+ trace: result?.trace,
906
+ });
907
+ const skipIds = new Set(deliverySkipped.map((d) => d.channel));
908
+ // The interruption budget, applied to the push channels delivery owns now
909
+ // that send_telegram is suppressed for a delivering routine. An anchor is
910
+ // `scheduled` and exempt; a watch/a2a run carries its detector's peak
911
+ // severity (a blocker is critical and crosses quiet-hours); a run the owner
912
+ // solicited crosses the budget like a reply. See core/routines/delivery.js.
913
+ const gate = {
914
+ severity: result?.peak_severity || "normal",
915
+ scheduled: routine.spec?.anchor === true,
916
+ unsolicited: !(result?.solicited === true),
917
+ project_id: ctx.project?.id ?? null,
918
+ };
919
+ const attachments = Array.isArray(result?.attachments) ? result.attachments : [];
920
+ const channels = delivery.channels.filter((c) => !skipIds.has(c));
921
+
922
+ // Manu's rule: a routine run by an agent that is NOT Roby must post to its
923
+ // OWN web chat and, instead of pinging the phone itself, leave a delivery
924
+ // for Roby to notify. So for a project agent we split the configured
925
+ // channels — the chat channels go to the agent's own thread, and any push
926
+ // channel (telegram) becomes an a2a emit to Roby, who decides the notify.
927
+ const runByAgent =
928
+ result?.agent_slug && result.agent_slug !== SUPERAGENT_ACTOR_ID
929
+ ? { slug: result.agent_slug, name: result.agent_name || result.agent_slug, model: result.model, usage: result.usage }
930
+ : null;
931
+
932
+ if (runByAgent) {
933
+ // The short notification line the mascot / push feed shows (see
934
+ // buildDeliveryNotify): a headline for a short tip, a model summary for a
935
+ // long one. Rides on the delivery so the web ledger row carries it and the
936
+ // event feed can hand it to the pet.
937
+ runByAgent.notify = await buildDeliveryNotify({
938
+ text: deliveryText,
939
+ model: runByAgent.model,
940
+ config: ctx.project?.config || ctx.globalConfig,
941
+ });
942
+ const pushChannels = channels.filter((c) => c === CHANNELS.TELEGRAM);
943
+ const chatChannels = channels.filter((c) => c !== CHANNELS.TELEGRAM);
944
+ deliveries = await deliverRoutineOutput(runCtx, {
945
+ routine, channels: chatChannels, text: deliveryText, gate, attachments, agent: runByAgent,
946
+ });
947
+
948
+ // The delivery queue — one record per thing left waiting for Manu, visible
949
+ // as it piles up and gets crossed off (see core/stores/deliveries.js).
950
+ const priority = routine.spec?.anchor === true;
951
+ const delId = recordDelivery(storagePath, {
952
+ agent: runByAgent.slug,
953
+ agentName: runByAgent.name,
954
+ routine: routine.name,
955
+ routineId: routine.id || "",
956
+ notify: runByAgent.notify,
957
+ priority,
958
+ project_id: ctx.project?.id ?? null,
959
+ });
960
+
961
+ // How Roby tells Manu, by priority:
962
+ // • an anchor (urgent) delivery notifies NOW, crossing the budget;
963
+ // • an ordinary one is LEFT PENDING — the daemon's grace sweep notifies it
964
+ // ~a minute later, unless Manu opens the chat and replies first, which
965
+ // marks it answered and cancels the notify (answerDeliveries).
966
+ if (pushChannels.length) {
967
+ if (priority) {
968
+ const r = await notifyOwnerViaRoby(runCtx, {
969
+ routine, agent: runByAgent, text: deliveryText, notify: runByAgent.notify, gate,
970
+ });
971
+ if (r.sent) {
972
+ markDelivery(storagePath, delId, DELIVERY_STATUS.NOTIFIED, { channel: "telegram" });
973
+ deliveries.push({ channel: "telegram(roby)", status: "ok", note: r.reason || "roby notified" });
974
+ } else if (r.held) {
975
+ markDelivery(storagePath, delId, DELIVERY_STATUS.HELD, { reason: r.reason });
976
+ deliveries.push({ channel: "telegram(roby)", status: "held", reason: r.reason });
977
+ } else {
978
+ deliveries.push({ channel: "telegram(roby)", status: "error", error: r.reason || "not sent" });
979
+ }
980
+ } else {
981
+ deliveries.push({ channel: "telegram(roby)", status: "deferred", note: "grace window — Roby notifies unless answered first" });
982
+ }
983
+ }
984
+ } else {
985
+ // The super-agent's own routine. Carry its model/usage so the delivered
986
+ // web-thread row is attributed like every other agent turn (the ledger
987
+ // adapter reads agent.model/agent.usage), not filed as "0 tok"/no model.
988
+ deliveries = await deliverRoutineOutput(runCtx, {
989
+ routine, channels, text: deliveryText, gate, attachments,
990
+ agent: { slug: SUPERAGENT_ACTOR_ID, model: result?.model, usage: result?.usage },
991
+ });
992
+ }
993
+ for (const id of delivery.unknown) {
994
+ deliveries.push({ channel: id, status: "error", error: `unknown delivery channel: ${id}` });
995
+ }
996
+ // Asked to deliver, delivered nowhere, and nothing else was going to carry
997
+ // it — that is the exact shape of failure this feature exists to end, so it
998
+ // is reported as an error rather than as a run that "went fine". A `held`
999
+ // delivery is NOT that failure: the budget withheld it on purpose, and the
1000
+ // message survives in the run log to fold into the next brief.
1001
+ const anyOk =
1002
+ deliveries.some((d) => d.status === "ok" || d.status === "held" || d.status === "deferred") ||
1003
+ deliverySkipped.length > 0;
1004
+ if (!anyOk) {
1005
+ status = "error";
1006
+ errMsg =
1007
+ `nothing delivered: ${deliveries.map((d) => `${d.channel} (${d.error})`).join("; ")}. ` +
1008
+ `The run produced an answer and no one received it.`;
1009
+ }
1010
+ }
1011
+
461
1012
  // ── Phase 3: post_commands ────────────────────────────────────────────────
462
1013
  const postRuns = [];
463
1014
  if (hasPostCmds) {
464
- const llmOutput = result?.reply || result?.text || "";
1015
+ setRoutineRunPhase(ctx.runId, "post");
465
1016
  const postEnv = {
466
1017
  ...pipelineEnv,
467
1018
  APX_LLM_OUTPUT: llmOutput.slice(0, 32_000),
468
1019
  APX_STATUS: status,
469
1020
  APX_SKIPPED: skip ? "1" : "0",
1021
+ APX_DELIVERED: deliveries.filter((d) => d.status === "ok").map((d) => d.channel).join(","),
470
1022
  };
471
1023
  for (const rawCmd of routine.post_commands) {
472
1024
  const cmd = resolveArtifactRef(rawCmd, storagePath);
@@ -494,16 +1046,27 @@ export async function runRoutineNow(ctx, routine) {
494
1046
  actor_id: "apx:routine",
495
1047
  author: "apx",
496
1048
  body: status === "ok"
497
- ? `routine ${routine.name} ok${skip ? " (skipped LLM)" : ""}`
1049
+ ? `routine ${routine.name} ok${skip ? " (skipped LLM)" : result?.abstained ? " (stayed quiet)" : ""}`
498
1050
  : `routine ${routine.name} error: ${errMsg}`,
499
1051
  meta: {
500
1052
  routine: routine.name, status, skipped: skip, result,
501
- // Persisted run flow so the UI can replay pre → action → post.
1053
+ // Persisted run flow so the UI can replay pre → action → delivery → post.
502
1054
  flow: {
503
1055
  pre: hasPreCmds ? { output: preStdout.slice(0, 8000), exit: preExitCode } : null,
1056
+ delivery: wanted ? { channels: delivery.channels, source: delivery.source, results: deliveries, skipped: deliverySkipped } : null,
504
1057
  post: postRuns.length ? postRuns : null,
505
1058
  },
506
1059
  },
507
1060
  });
508
- return { ...result, last_run_at: lastRun, next_run_at: next };
1061
+ return {
1062
+ ...result,
1063
+ // A caller that ran the routine by hand (CLI, API, the web panel) should be
1064
+ // able to see where the answer went without going to read the ledger.
1065
+ ...(wanted
1066
+ ? { delivery: { channels: delivery.channels, source: delivery.source, results: deliveries, skipped: deliverySkipped } }
1067
+ : {}),
1068
+ ...(status === "error" && result?.status !== "error" ? { status, error: errMsg } : {}),
1069
+ last_run_at: lastRun,
1070
+ next_run_at: next,
1071
+ };
509
1072
  }