@agentprojectcontext/apx 1.81.0 → 1.89.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (838) hide show
  1. package/package.json +9 -8
  2. package/skills/apc-context/SKILL.md +14 -7
  3. package/skills/apx/SKILL.md +62 -2
  4. package/skills/apx-mcp/SKILL.md +42 -15
  5. package/src/core/agent/a2a/peers.js +161 -0
  6. package/src/core/agent/a2a/reply.js +379 -27
  7. package/src/core/agent/abort.js +25 -0
  8. package/src/core/agent/agent-model.js +58 -0
  9. package/src/core/agent/agent-tools.js +101 -0
  10. package/src/core/agent/author-line.js +110 -0
  11. package/src/core/agent/build-agent-system.js +52 -18
  12. package/src/core/agent/channels/voice-context.js +17 -2
  13. package/src/core/agent/closing-floor.js +117 -0
  14. package/src/core/agent/constants.js +119 -11
  15. package/src/core/agent/engine-call.js +56 -0
  16. package/src/core/agent/group/run-group-turn.js +218 -0
  17. package/src/core/agent/group/turn-resolver.js +165 -0
  18. package/src/core/agent/judge.js +138 -11
  19. package/src/core/agent/loop/side-effects.js +106 -3
  20. package/src/core/agent/memory.js +31 -18
  21. package/src/core/agent/model-capabilities.js +47 -0
  22. package/src/core/agent/model-router.js +11 -40
  23. package/src/core/agent/prompt-builder.js +47 -0
  24. package/src/core/agent/prompts/channels/a2a.md +12 -0
  25. package/src/core/agent/prompts/channels/code.md +3 -1
  26. package/src/core/agent/prompts/channels/telegram.md +2 -0
  27. package/src/core/agent/prompts/channels/web_code.md +1 -1
  28. package/src/core/agent/prompts/channels/whatsapp.md +37 -0
  29. package/src/core/agent/prompts/core/agent-base.md +2 -0
  30. package/src/core/agent/prompts/discipline/action.md +1 -0
  31. package/src/core/agent/prompts/discipline/two-segment.md +2 -1
  32. package/src/core/agent/prompts/modes/mobility.md +36 -0
  33. package/src/core/agent/retry.js +42 -0
  34. package/src/core/agent/run-agent.js +322 -25
  35. package/src/core/agent/run-turn.js +253 -0
  36. package/src/core/agent/self-memory.js +18 -52
  37. package/src/core/agent/skills/agent-skills.js +118 -0
  38. package/src/core/agent/skills/catalog.js +1 -1
  39. package/src/core/agent/skills/declared.js +13 -0
  40. package/src/core/agent/skills/index-store.js +8 -1
  41. package/src/core/agent/skills/inspector.js +20 -3
  42. package/src/core/agent/skills/loader.js +18 -3
  43. package/src/core/agent/skills/media.js +121 -0
  44. package/src/core/agent/skills/origin.js +61 -0
  45. package/src/core/agent/skills/sections.js +136 -0
  46. package/src/core/agent/super-agent.js +54 -14
  47. package/src/core/agent/tools/handlers/_calendar.js +50 -0
  48. package/src/core/agent/tools/handlers/add-mcp.js +103 -0
  49. package/src/core/agent/tools/handlers/asana-create-task.js +3 -1
  50. package/src/core/agent/tools/handlers/attach-media.js +56 -0
  51. package/src/core/agent/tools/handlers/calendar-create-event.js +38 -0
  52. package/src/core/agent/tools/handlers/calendar-find-slot.js +39 -0
  53. package/src/core/agent/tools/handlers/calendar-list-events.js +39 -0
  54. package/src/core/agent/tools/handlers/calendar-update-event.js +35 -0
  55. package/src/core/agent/tools/handlers/call-agent.js +14 -6
  56. package/src/core/agent/tools/handlers/call-runtime.js +27 -49
  57. package/src/core/agent/tools/handlers/comment-task.js +78 -0
  58. package/src/core/agent/tools/handlers/complete-task.js +60 -0
  59. package/src/core/agent/tools/handlers/configure-agent.js +70 -0
  60. package/src/core/agent/tools/handlers/create-agent.js +68 -0
  61. package/src/core/agent/tools/handlers/create-task.js +41 -3
  62. package/src/core/agent/tools/handlers/import-agent.js +5 -5
  63. package/src/core/agent/tools/handlers/list-mcp-tools.js +83 -0
  64. package/src/core/agent/tools/handlers/list-routines.js +46 -0
  65. package/src/core/agent/tools/handlers/list-tasks.js +8 -1
  66. package/src/core/agent/tools/handlers/mark-commitment.js +57 -0
  67. package/src/core/agent/tools/handlers/read-skill.js +64 -0
  68. package/src/core/agent/tools/handlers/remember-routine.js +2 -8
  69. package/src/core/agent/tools/handlers/remember.js +82 -6
  70. package/src/core/agent/tools/handlers/remove-agent.js +42 -0
  71. package/src/core/agent/tools/handlers/run-routine.js +94 -0
  72. package/src/core/agent/tools/handlers/run-shell.js +44 -2
  73. package/src/core/agent/tools/handlers/send-telegram.js +41 -19
  74. package/src/core/agent/tools/handlers/set-agent-prompt.js +44 -0
  75. package/src/core/agent/tools/handlers/view-media.js +69 -0
  76. package/src/core/agent/tools/handlers/write-agent-memory.js +52 -0
  77. package/src/core/agent/tools/helpers.js +36 -4
  78. package/src/core/agent/tools/names.js +64 -0
  79. package/src/core/agent/tools/registry-bridge.js +24 -1
  80. package/src/core/agent/tools/registry.js +79 -0
  81. package/src/core/agent/tools/tool-call-parser.js +136 -13
  82. package/src/core/agent/vision-bridge.js +69 -0
  83. package/src/core/apc/agent-identity.js +74 -0
  84. package/src/core/apc/agent-rename-refs.js +187 -0
  85. package/src/core/apc/agent-write.js +312 -0
  86. package/src/core/apc/agents-vault.js +1 -1
  87. package/src/core/apc/blob-keys.js +42 -0
  88. package/src/core/apc/frontmatter.js +166 -10
  89. package/src/core/apc/parser.js +33 -83
  90. package/src/core/apc/paths.js +4 -6
  91. package/src/core/apc/project-memory.js +44 -0
  92. package/src/core/apc/projects-helpers.js +33 -0
  93. package/src/core/apc/scaffold.js +36 -16
  94. package/src/core/channels/telegram/ask-callbacks.js +103 -0
  95. package/src/core/channels/telegram/dispatch.js +154 -30
  96. package/src/core/channels/telegram/helpers.js +59 -7
  97. package/src/core/channels/telegram/inbound/audio.js +26 -30
  98. package/src/core/channels/telegram/inbound/file.js +22 -32
  99. package/src/core/channels/telegram/inbound/photo.js +24 -28
  100. package/src/core/channels/telegram/progress-gate.js +76 -0
  101. package/src/core/channels/telegram/reply.js +151 -43
  102. package/src/core/channels/telegram/voice-note.js +150 -0
  103. package/src/core/config/index.js +53 -54
  104. package/src/core/config/paths.js +117 -31
  105. package/src/core/config/redact.js +89 -4
  106. package/src/core/config/secret-values.js +2 -4
  107. package/src/core/constants/actors.js +5 -0
  108. package/src/core/constants/channels.js +3 -1
  109. package/src/core/constants/task-categories.js +73 -0
  110. package/src/core/constants/task-fields.js +28 -0
  111. package/src/core/engines/_globs.js +33 -0
  112. package/src/core/engines/anthropic.js +19 -1
  113. package/src/core/engines/catalog.js +20 -0
  114. package/src/core/engines/gemini.js +84 -16
  115. package/src/core/engines/index.js +19 -3
  116. package/src/core/engines/mock.js +145 -1
  117. package/src/core/engines/openai-compatible.js +150 -15
  118. package/src/core/engines/presets.js +36 -9
  119. package/src/core/engines/zen.js +131 -0
  120. package/src/core/events/bus.js +140 -0
  121. package/src/core/events/mascot-notify.js +156 -0
  122. package/src/core/http-tools/browser.js +366 -36
  123. package/src/core/http-tools/catalog.js +206 -22
  124. package/src/core/http-tools/grep.js +62 -7
  125. package/src/core/http-tools/registry.js +3 -1
  126. package/src/core/i18n/en.js +34 -3
  127. package/src/core/i18n/es.js +36 -4
  128. package/src/core/i18n/index.js +5 -5
  129. package/src/core/i18n/pt.js +32 -3
  130. package/src/core/identity/self.js +14 -1
  131. package/src/core/images/engines/a1111.js +219 -0
  132. package/src/core/images/engines/index.js +187 -0
  133. package/src/core/images/engines/mock.js +74 -0
  134. package/src/core/images/engines/openai.js +130 -0
  135. package/src/core/images/engines/sdcpp.js +204 -0
  136. package/src/core/images/engines/shared.js +218 -0
  137. package/src/core/images/generate.js +260 -0
  138. package/src/core/integrations/catalog.js +8 -3
  139. package/src/core/integrations/plugins/_google-oauth.js +179 -0
  140. package/src/core/integrations/plugins/asana.js +7 -3
  141. package/src/core/integrations/plugins/calendar.js +404 -0
  142. package/src/core/integrations/store.js +2 -0
  143. package/src/core/memory/broker.js +16 -1
  144. package/src/core/memory/compactor.js +19 -3
  145. package/src/core/memory/consolidate.js +2 -2
  146. package/src/core/memory/dated-log.js +66 -0
  147. package/src/core/memory/embed-engines/custom.js +59 -0
  148. package/src/core/memory/embed-engines/gemini.js +9 -3
  149. package/src/core/memory/embed-engines/index.js +87 -15
  150. package/src/core/memory/embeddings.js +52 -24
  151. package/src/core/memory/index.js +13 -3
  152. package/src/core/memory/indexer.js +83 -27
  153. package/src/core/memory/prune.js +182 -0
  154. package/src/core/memory/store.js +22 -0
  155. package/src/core/memory/summarizer.js +72 -8
  156. package/src/core/mobility/answer.js +80 -0
  157. package/src/core/mobility/geofence.js +667 -0
  158. package/src/core/mobility/osm-route.js +282 -0
  159. package/src/core/mobility/preferences.js +91 -0
  160. package/src/core/mobility/state.js +245 -0
  161. package/src/core/mobility/trip-event.js +384 -0
  162. package/src/core/net/tailscale.js +168 -0
  163. package/src/core/profiles/bundled/secretary/PROFILE.md +32 -42
  164. package/src/core/profiles/bundled/secretary/config.schema.es.json +68 -0
  165. package/src/core/profiles/bundled/secretary/config.schema.json +30 -2
  166. package/src/core/profiles/bundled/secretary/profile.json +3 -1
  167. package/src/core/profiles/bundled/secretary/routines/a2a-sweep.json +20 -0
  168. package/src/core/profiles/bundled/secretary/routines/day-close.json +4 -1
  169. package/src/core/profiles/bundled/secretary/routines/day-open.json +7 -1
  170. package/src/core/profiles/bundled/secretary/routines/watch.json +13 -2
  171. package/src/core/profiles/lifecycle.js +243 -19
  172. package/src/core/profiles/paths.js +12 -0
  173. package/src/core/profiles/store.js +47 -0
  174. package/src/core/routines/active-runs.js +213 -0
  175. package/src/core/routines/delivery.js +612 -0
  176. package/src/core/routines/header.js +88 -0
  177. package/src/core/routines/index.js +26 -0
  178. package/src/core/routines/run-log.js +78 -0
  179. package/src/core/routines/runner.js +629 -66
  180. package/src/core/routines/signals.js +75 -0
  181. package/src/core/runtime-skills/apc-context/SKILL.md +14 -7
  182. package/src/core/runtime-skills/apx/SKILL.md +93 -1
  183. package/src/core/runtime-skills/apx-agency-agents/SKILL.md +2 -2
  184. package/src/core/runtime-skills/apx-agent/SKILL.md +134 -19
  185. package/src/core/runtime-skills/apx-commitment/SKILL.md +21 -11
  186. package/src/core/runtime-skills/apx-image/SKILL.md +60 -0
  187. package/src/core/runtime-skills/apx-integrations/SKILL.md +82 -0
  188. package/src/core/runtime-skills/apx-mcp/SKILL.md +45 -14
  189. package/src/core/runtime-skills/apx-mcp-builder/SKILL.md +4 -4
  190. package/src/core/runtime-skills/apx-profile/SKILL.md +27 -2
  191. package/src/core/runtime-skills/apx-project/SKILL.md +23 -2
  192. package/src/core/runtime-skills/apx-routine/SKILL.md +40 -17
  193. package/src/core/runtime-skills/apx-runtime/SKILL.md +124 -14
  194. package/src/core/runtime-skills/apx-sessions/SKILL.md +7 -6
  195. package/src/core/runtime-skills/apx-skill-builder/SKILL.md +15 -13
  196. package/src/core/runtime-skills/apx-task/SKILL.md +79 -26
  197. package/src/core/runtime-skills/apx-telegram/SKILL.md +12 -12
  198. package/src/core/runtime-skills/apx-transcribe/SKILL.md +56 -0
  199. package/src/core/runtime-skills/apx-voice/SKILL.md +27 -8
  200. package/src/core/runtime-skills/claude-code/SKILL.md +8 -0
  201. package/src/core/runtime-skills/openrouter/SKILL.md +8 -2
  202. package/src/core/runtimes/_spawn.js +9 -1
  203. package/src/core/runtimes/claude-code.js +11 -2
  204. package/src/core/runtimes/codex.js +44 -10
  205. package/src/core/runtimes/index.js +17 -2
  206. package/src/core/runtimes/opencode.js +88 -9
  207. package/src/core/runtimes/outcome.js +119 -0
  208. package/src/core/sessions/index.js +193 -1
  209. package/src/core/stores/agent-inbox.js +109 -23
  210. package/src/core/stores/code-sessions.js +21 -0
  211. package/src/core/stores/commitments.js +27 -3
  212. package/src/core/stores/conversations.js +220 -20
  213. package/src/core/stores/deliveries.js +177 -0
  214. package/src/core/stores/media-archive.js +228 -0
  215. package/src/core/stores/messages.js +1096 -112
  216. package/src/core/stores/organization.js +34 -0
  217. package/src/core/stores/project-memory.js +71 -0
  218. package/src/core/stores/routine-memory.js +16 -8
  219. package/src/core/stores/routines.js +27 -49
  220. package/src/core/stores/sessions.js +4 -8
  221. package/src/core/stores/tasks.js +212 -18
  222. package/src/core/stores/turn-record.js +209 -0
  223. package/src/core/tasks/columns.js +136 -0
  224. package/src/core/tasks/comment-turn.js +258 -0
  225. package/src/core/util/time.js +13 -0
  226. package/src/core/voice/engines/index.js +54 -1
  227. package/src/core/voice/engines/openai.js +50 -4
  228. package/src/core/voice/opus.js +67 -0
  229. package/src/core/voice/pronounceable.js +43 -0
  230. package/src/core/voice/transcription.js +4 -0
  231. package/src/core/voice/tts.js +114 -24
  232. package/src/host/daemon/active-turns.js +188 -0
  233. package/src/host/daemon/api/admin-config.js +9 -0
  234. package/src/host/daemon/api/agents.js +99 -90
  235. package/src/host/daemon/api/artifact-preview.js +7 -6
  236. package/src/host/daemon/api/artifacts.js +3 -2
  237. package/src/host/daemon/api/code.js +55 -9
  238. package/src/host/daemon/api/commitments.js +11 -0
  239. package/src/host/daemon/api/confirm.js +3 -2
  240. package/src/host/daemon/api/conversations.js +543 -35
  241. package/src/host/daemon/api/deck.js +9 -8
  242. package/src/host/daemon/api/deliveries.js +33 -0
  243. package/src/host/daemon/api/desktop.js +5 -4
  244. package/src/host/daemon/api/embeddings.js +15 -6
  245. package/src/host/daemon/api/engines.js +59 -8
  246. package/src/host/daemon/api/exec.js +437 -121
  247. package/src/host/daemon/api/groups.js +215 -0
  248. package/src/host/daemon/api/images.js +107 -0
  249. package/src/host/daemon/api/inbox.js +123 -2
  250. package/src/host/daemon/api/integrations.js +64 -2
  251. package/src/host/daemon/api/mcps.js +7 -6
  252. package/src/host/daemon/api/media.js +283 -0
  253. package/src/host/daemon/api/mobility.js +177 -0
  254. package/src/host/daemon/api/net.js +182 -0
  255. package/src/host/daemon/api/pairing.js +38 -5
  256. package/src/host/daemon/api/profiles.js +30 -2
  257. package/src/host/daemon/api/routines.js +36 -4
  258. package/src/host/daemon/api/runtimes.js +74 -39
  259. package/src/host/daemon/api/sessions-search.js +7 -2
  260. package/src/host/daemon/api/sessions.js +44 -2
  261. package/src/host/daemon/api/shared.js +127 -20
  262. package/src/host/daemon/api/skills.js +11 -4
  263. package/src/host/daemon/api/super-agent.js +384 -53
  264. package/src/host/daemon/api/tasks.js +180 -10
  265. package/src/host/daemon/api/telegram.js +11 -10
  266. package/src/host/daemon/api/thread-faces.js +159 -0
  267. package/src/host/daemon/api/top-level.js +3 -3
  268. package/src/host/daemon/api/transcribe.js +11 -10
  269. package/src/host/daemon/api/tts.js +97 -4
  270. package/src/host/daemon/api/turn-abort.js +23 -0
  271. package/src/host/daemon/api/turns.js +49 -0
  272. package/src/host/daemon/api/voice.js +5 -4
  273. package/src/host/daemon/api/web.js +98 -0
  274. package/src/host/daemon/api.js +39 -0
  275. package/src/host/daemon/delivery-sweep.js +58 -0
  276. package/src/host/daemon/desktop-ws.js +5 -23
  277. package/src/host/daemon/events-ws.js +273 -0
  278. package/src/host/daemon/index.js +59 -15
  279. package/src/host/daemon/plugins/desktop/index.js +17 -4
  280. package/src/host/daemon/plugins/telegram/index.js +78 -40
  281. package/src/host/daemon/pty-bridge.py +99 -0
  282. package/src/host/daemon/routines-scheduler.js +1 -0
  283. package/src/host/daemon/smoke.js +1 -2
  284. package/src/host/daemon/terminal-ws.js +132 -0
  285. package/src/host/daemon/tts-keepwarm.js +87 -0
  286. package/src/host/daemon/wakeup.js +11 -6
  287. package/src/host/daemon/whisper-server.js +44 -0
  288. package/src/host/daemon/whisper-server.py +70 -13
  289. package/src/host/daemon/ws-auth.js +28 -0
  290. package/src/interfaces/android/app/build.gradle +42 -0
  291. package/src/interfaces/android/app/proguard-rules.pro +4 -0
  292. package/src/interfaces/android/app/src/main/AndroidManifest.xml +139 -0
  293. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/AlertScreen.java +139 -0
  294. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/AndroidAutoDetector.java +106 -0
  295. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/ApxCarAppService.java +57 -0
  296. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/ApxPreferences.java +165 -0
  297. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/CarAlertStore.java +84 -0
  298. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/CarMessageActionReceiver.java +47 -0
  299. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/CarMessageNotification.java +152 -0
  300. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/DaemonAddress.java +35 -0
  301. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/DaemonClient.java +339 -0
  302. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/DeviceLocation.java +38 -0
  303. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/ErrandScreen.java +127 -0
  304. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MainActivity.java +872 -0
  305. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MapsNavigationDetector.java +63 -0
  306. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MapsNavigationListenerService.java +453 -0
  307. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MapsShareIntentParser.java +93 -0
  308. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MascotBlobCatalog.java +66 -0
  309. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MascotOverlayService.java +448 -0
  310. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MascotView.java +249 -0
  311. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MessageFrameParser.java +240 -0
  312. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/NotifyChannels.java +69 -0
  313. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/ProximityActionReceiver.java +78 -0
  314. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/ProximityNotification.java +161 -0
  315. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/StartupReceiver.java +57 -0
  316. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/TravelEventGate.java +62 -0
  317. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/TravelStatusBanner.java +116 -0
  318. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/TripLocationService.java +272 -0
  319. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/TripPlacesSheet.java +203 -0
  320. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/TripScreen.java +182 -0
  321. package/src/interfaces/android/app/src/main/res/drawable/app_background.xml +12 -0
  322. package/src/interfaces/android/app/src/main/res/drawable/ic_apx_app.xml +10 -0
  323. package/src/interfaces/android/app/src/main/res/drawable/ic_apx_launcher_foreground.xml +12 -0
  324. package/src/interfaces/android/app/src/main/res/drawable/ic_apx_notification.xml +22 -0
  325. package/src/interfaces/android/app/src/main/res/drawable/ic_trip_car.xml +37 -0
  326. package/src/interfaces/android/app/src/main/res/drawable-nodpi/apx_logo.png +0 -0
  327. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_amatista.png +0 -0
  328. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_aqua.png +0 -0
  329. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_brasa.png +0 -0
  330. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_campana.png +0 -0
  331. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_caracol.png +0 -0
  332. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_carbon.png +0 -0
  333. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_cobalto.png +0 -0
  334. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_cometa.png +0 -0
  335. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_coral.png +0 -0
  336. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_cubi.png +0 -0
  337. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_eclipse.png +0 -0
  338. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_faro.png +0 -0
  339. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_gajo.png +0 -0
  340. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_iris.png +0 -0
  341. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_kiwi.png +0 -0
  342. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_laguna.png +0 -0
  343. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_lila.png +0 -0
  344. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_menta.png +0 -0
  345. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_nimbo.png +0 -0
  346. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_noche.png +0 -0
  347. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_onyx.png +0 -0
  348. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_orbita.png +0 -0
  349. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_ovni.png +0 -0
  350. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_papa.png +0 -0
  351. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_parche.png +0 -0
  352. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_peon.png +0 -0
  353. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_perla.png +0 -0
  354. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_pino.png +0 -0
  355. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_quad.png +0 -0
  356. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_rubi.png +0 -0
  357. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_saturno.png +0 -0
  358. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_trebol.png +0 -0
  359. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_trino.png +0 -0
  360. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_volcan.png +0 -0
  361. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_zafiro.png +0 -0
  362. package/src/interfaces/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +5 -0
  363. package/src/interfaces/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +5 -0
  364. package/src/interfaces/android/app/src/main/res/raw/apx_notification.mp3 +0 -0
  365. package/src/interfaces/android/app/src/main/res/values/colors.xml +4 -0
  366. package/src/interfaces/android/app/src/main/res/values/strings.xml +25 -0
  367. package/src/interfaces/android/app/src/main/res/values/styles.xml +10 -0
  368. package/src/interfaces/android/app/src/main/res/values-v31/styles.xml +14 -0
  369. package/src/interfaces/android/app/src/main/res/xml/automotive_app_desc.xml +9 -0
  370. package/src/interfaces/android/app/src/main/res/xml/network_security_config.xml +10 -0
  371. package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/AndroidAutoDetectorTest.java +85 -0
  372. package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/CarAlertStoreTest.java +104 -0
  373. package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/DaemonAddressTest.java +23 -0
  374. package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/MapsNavigationDetectorTest.java +69 -0
  375. package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/MapsShareIntentParserTest.java +31 -0
  376. package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/MessageFrameParserTest.java +180 -0
  377. package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/TravelEventGateTest.java +64 -0
  378. package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/TripPlacesSheetTest.java +66 -0
  379. package/src/interfaces/android/build.gradle +3 -0
  380. package/src/interfaces/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  381. package/src/interfaces/android/gradle/wrapper/gradle-wrapper.properties +9 -0
  382. package/src/interfaces/android/gradle.properties +3 -0
  383. package/src/interfaces/android/gradlew +248 -0
  384. package/src/interfaces/android/gradlew.bat +82 -0
  385. package/src/interfaces/android/settings.gradle +18 -0
  386. package/src/interfaces/cli/commands/a2a.js +145 -4
  387. package/src/interfaces/cli/commands/agent.js +247 -22
  388. package/src/interfaces/cli/commands/code.js +1 -1
  389. package/src/interfaces/cli/commands/commitment.js +5 -0
  390. package/src/interfaces/cli/commands/config.js +85 -6
  391. package/src/interfaces/cli/commands/exec.js +90 -1
  392. package/src/interfaces/cli/commands/image.js +314 -0
  393. package/src/interfaces/cli/commands/mcp.js +87 -23
  394. package/src/interfaces/cli/commands/memory.js +35 -22
  395. package/src/interfaces/cli/commands/panel.js +94 -0
  396. package/src/interfaces/cli/commands/profile.js +14 -0
  397. package/src/interfaces/cli/commands/project.js +29 -0
  398. package/src/interfaces/cli/commands/routine.js +45 -10
  399. package/src/interfaces/cli/commands/runtime.js +30 -7
  400. package/src/interfaces/cli/commands/session.js +90 -38
  401. package/src/interfaces/cli/commands/sessions.js +35 -2
  402. package/src/interfaces/cli/commands/skills.js +15 -9
  403. package/src/interfaces/cli/commands/status.js +2 -3
  404. package/src/interfaces/cli/commands/task.js +54 -5
  405. package/src/interfaces/cli/commands/telegram.js +12 -4
  406. package/src/interfaces/cli/commands/transcribe.js +177 -0
  407. package/src/interfaces/cli/help/index.js +311 -72
  408. package/src/interfaces/cli/http.js +61 -13
  409. package/src/interfaces/cli/index.js +1 -1
  410. package/src/interfaces/cli/routes/agent.js +5 -4
  411. package/src/interfaces/cli/routes/commitment.js +3 -2
  412. package/src/interfaces/cli/routes/config.js +7 -2
  413. package/src/interfaces/cli/routes/image.js +11 -0
  414. package/src/interfaces/cli/routes/index.js +2 -0
  415. package/src/interfaces/cli/routes/log.js +1 -1
  416. package/src/interfaces/cli/routes/memory.js +3 -2
  417. package/src/interfaces/cli/routes/panel.js +15 -2
  418. package/src/interfaces/cli/routes/profile.js +3 -2
  419. package/src/interfaces/cli/routes/transcribe.js +10 -0
  420. package/src/interfaces/cli/stdin.js +29 -0
  421. package/src/interfaces/desktop/assets/blobs/amatista.png +0 -0
  422. package/src/interfaces/desktop/assets/blobs/aqua.png +0 -0
  423. package/src/interfaces/desktop/assets/blobs/brasa.png +0 -0
  424. package/src/interfaces/desktop/assets/blobs/campana.png +0 -0
  425. package/src/interfaces/desktop/assets/blobs/caracol.png +0 -0
  426. package/src/interfaces/desktop/assets/blobs/carbon.png +0 -0
  427. package/src/interfaces/desktop/assets/blobs/cobalto.png +0 -0
  428. package/src/interfaces/desktop/assets/blobs/cometa.png +0 -0
  429. package/src/interfaces/desktop/assets/blobs/coral.png +0 -0
  430. package/src/interfaces/desktop/assets/blobs/cubi.png +0 -0
  431. package/src/interfaces/desktop/assets/blobs/eclipse.png +0 -0
  432. package/src/interfaces/desktop/assets/blobs/faro.png +0 -0
  433. package/src/interfaces/desktop/assets/blobs/gajo.png +0 -0
  434. package/src/interfaces/desktop/assets/blobs/iris.png +0 -0
  435. package/src/interfaces/desktop/assets/blobs/kiwi.png +0 -0
  436. package/src/interfaces/desktop/assets/blobs/laguna.png +0 -0
  437. package/src/interfaces/desktop/assets/blobs/lila.png +0 -0
  438. package/src/interfaces/desktop/assets/blobs/menta.png +0 -0
  439. package/src/interfaces/desktop/assets/blobs/nimbo.png +0 -0
  440. package/src/interfaces/desktop/assets/blobs/noche.png +0 -0
  441. package/src/interfaces/desktop/assets/blobs/onyx.png +0 -0
  442. package/src/interfaces/desktop/assets/blobs/orbita.png +0 -0
  443. package/src/interfaces/desktop/assets/blobs/ovni.png +0 -0
  444. package/src/interfaces/desktop/assets/blobs/papa.png +0 -0
  445. package/src/interfaces/desktop/assets/blobs/parche.png +0 -0
  446. package/src/interfaces/desktop/assets/blobs/peon.png +0 -0
  447. package/src/interfaces/desktop/assets/blobs/perla.png +0 -0
  448. package/src/interfaces/desktop/assets/blobs/pino.png +0 -0
  449. package/src/interfaces/desktop/assets/blobs/quad.png +0 -0
  450. package/src/interfaces/desktop/assets/blobs/rubi.png +0 -0
  451. package/src/interfaces/desktop/assets/blobs/saturno.png +0 -0
  452. package/src/interfaces/desktop/assets/blobs/trebol.png +0 -0
  453. package/src/interfaces/desktop/assets/blobs/trino.png +0 -0
  454. package/src/interfaces/desktop/assets/blobs/volcan.png +0 -0
  455. package/src/interfaces/desktop/assets/blobs/zafiro.png +0 -0
  456. package/src/interfaces/desktop/assets/notification.mp3 +0 -0
  457. package/src/interfaces/desktop/main.js +473 -29
  458. package/src/interfaces/desktop/mascot-blobs.js +41 -0
  459. package/src/interfaces/desktop/mascot-preload.js +32 -0
  460. package/src/interfaces/desktop/mascot.css +186 -0
  461. package/src/interfaces/desktop/mascot.html +40 -0
  462. package/src/interfaces/desktop/mascot.js +163 -0
  463. package/src/interfaces/desktop/preload.js +10 -0
  464. package/src/interfaces/desktop/renderer.js +308 -48
  465. package/src/interfaces/desktop/style.css +34 -0
  466. package/src/interfaces/mcp-server/index.js +87 -0
  467. package/src/interfaces/tui/_shims/util-locale.ts +0 -2
  468. package/src/interfaces/tui/app.tsx +0 -6
  469. package/src/interfaces/tui/attach.ts +0 -1
  470. package/src/interfaces/tui/context/sdk-apx.tsx +0 -4
  471. package/src/interfaces/tui/plugin/api.tsx +0 -3
  472. package/src/interfaces/tui/plugin/runtime.ts +0 -3
  473. package/src/interfaces/tui/thread.ts +0 -16
  474. package/src/interfaces/web/README.md +22 -14
  475. package/src/interfaces/web/dist/assets/claude-XtpLmGtF.webp +0 -0
  476. package/src/interfaces/web/dist/assets/codex-DExPUgvh.webp +0 -0
  477. package/src/interfaces/web/dist/assets/index-BEoRgIqd.css +32 -0
  478. package/src/interfaces/web/dist/assets/index-BEoRgIqd.css.br +0 -0
  479. package/src/interfaces/web/dist/assets/index-BEoRgIqd.css.gz +0 -0
  480. package/src/interfaces/web/dist/assets/index-C6fXHc1p.js +1074 -0
  481. package/src/interfaces/web/dist/assets/index-C6fXHc1p.js.br +0 -0
  482. package/src/interfaces/web/dist/assets/index-C6fXHc1p.js.gz +0 -0
  483. package/src/interfaces/web/dist/assets/index-C6fXHc1p.js.map +1 -0
  484. package/src/interfaces/web/dist/assets/index-C6fXHc1p.js.map.br +0 -0
  485. package/src/interfaces/web/dist/assets/index-C6fXHc1p.js.map.gz +0 -0
  486. package/src/interfaces/web/dist/favicon/dark/site.webmanifest +12 -6
  487. package/src/interfaces/web/dist/favicon/white/site.webmanifest +12 -6
  488. package/src/interfaces/web/dist/index.html +22 -6
  489. package/src/interfaces/web/dist/index.html.br +0 -0
  490. package/src/interfaces/web/dist/index.html.gz +0 -0
  491. package/src/interfaces/web/dist/manifest.webmanifest +25 -0
  492. package/src/interfaces/web/dist/modules/blobs/amatista.png +0 -0
  493. package/src/interfaces/web/dist/modules/blobs/aqua.png +0 -0
  494. package/src/interfaces/web/dist/modules/blobs/brasa.png +0 -0
  495. package/src/interfaces/web/dist/modules/blobs/campana.png +0 -0
  496. package/src/interfaces/web/dist/modules/blobs/caracol.png +0 -0
  497. package/src/interfaces/web/dist/modules/blobs/carbon.png +0 -0
  498. package/src/interfaces/web/dist/modules/blobs/cobalto.png +0 -0
  499. package/src/interfaces/web/dist/modules/blobs/cometa.png +0 -0
  500. package/src/interfaces/web/dist/modules/blobs/coral.png +0 -0
  501. package/src/interfaces/web/dist/modules/blobs/cubi.png +0 -0
  502. package/src/interfaces/web/dist/modules/blobs/eclipse.png +0 -0
  503. package/src/interfaces/web/dist/modules/blobs/faro.png +0 -0
  504. package/src/interfaces/web/dist/modules/blobs/gajo.png +0 -0
  505. package/src/interfaces/web/dist/modules/blobs/iris.png +0 -0
  506. package/src/interfaces/web/dist/modules/blobs/kiwi.png +0 -0
  507. package/src/interfaces/web/dist/modules/blobs/laguna.png +0 -0
  508. package/src/interfaces/web/dist/modules/blobs/lila.png +0 -0
  509. package/src/interfaces/web/dist/modules/blobs/menta.png +0 -0
  510. package/src/interfaces/web/dist/modules/blobs/nimbo.png +0 -0
  511. package/src/interfaces/web/dist/modules/blobs/noche.png +0 -0
  512. package/src/interfaces/web/dist/modules/blobs/onyx.png +0 -0
  513. package/src/interfaces/web/dist/modules/blobs/orbita.png +0 -0
  514. package/src/interfaces/web/dist/modules/blobs/ovni.png +0 -0
  515. package/src/interfaces/web/dist/modules/blobs/papa.png +0 -0
  516. package/src/interfaces/web/dist/modules/blobs/parche.png +0 -0
  517. package/src/interfaces/web/dist/modules/blobs/peon.png +0 -0
  518. package/src/interfaces/web/dist/modules/blobs/perla.png +0 -0
  519. package/src/interfaces/web/dist/modules/blobs/pino.png +0 -0
  520. package/src/interfaces/web/dist/modules/blobs/quad.png +0 -0
  521. package/src/interfaces/web/dist/modules/blobs/rubi.png +0 -0
  522. package/src/interfaces/web/dist/modules/blobs/saturno.png +0 -0
  523. package/src/interfaces/web/dist/modules/blobs/trebol.png +0 -0
  524. package/src/interfaces/web/dist/modules/blobs/trino.png +0 -0
  525. package/src/interfaces/web/dist/modules/blobs/volcan.png +0 -0
  526. package/src/interfaces/web/dist/modules/blobs/zafiro.png +0 -0
  527. package/src/interfaces/web/dist/sw.js +106 -0
  528. package/src/interfaces/web/dist/sw.js.br +0 -0
  529. package/src/interfaces/web/dist/sw.js.gz +0 -0
  530. package/src/interfaces/web/eslint.config.js +83 -0
  531. package/src/interfaces/web/index.html +20 -4
  532. package/src/interfaces/web/package-lock.json +1651 -1280
  533. package/src/interfaces/web/package.json +9 -10
  534. package/src/interfaces/web/pnpm-lock.yaml +867 -869
  535. package/src/interfaces/web/public/favicon/dark/site.webmanifest +12 -6
  536. package/src/interfaces/web/public/favicon/white/site.webmanifest +12 -6
  537. package/src/interfaces/web/public/manifest.webmanifest +25 -0
  538. package/src/interfaces/web/public/modules/blobs/amatista.png +0 -0
  539. package/src/interfaces/web/public/modules/blobs/aqua.png +0 -0
  540. package/src/interfaces/web/public/modules/blobs/brasa.png +0 -0
  541. package/src/interfaces/web/public/modules/blobs/campana.png +0 -0
  542. package/src/interfaces/web/public/modules/blobs/caracol.png +0 -0
  543. package/src/interfaces/web/public/modules/blobs/carbon.png +0 -0
  544. package/src/interfaces/web/public/modules/blobs/cobalto.png +0 -0
  545. package/src/interfaces/web/public/modules/blobs/cometa.png +0 -0
  546. package/src/interfaces/web/public/modules/blobs/coral.png +0 -0
  547. package/src/interfaces/web/public/modules/blobs/cubi.png +0 -0
  548. package/src/interfaces/web/public/modules/blobs/eclipse.png +0 -0
  549. package/src/interfaces/web/public/modules/blobs/faro.png +0 -0
  550. package/src/interfaces/web/public/modules/blobs/gajo.png +0 -0
  551. package/src/interfaces/web/public/modules/blobs/iris.png +0 -0
  552. package/src/interfaces/web/public/modules/blobs/kiwi.png +0 -0
  553. package/src/interfaces/web/public/modules/blobs/laguna.png +0 -0
  554. package/src/interfaces/web/public/modules/blobs/lila.png +0 -0
  555. package/src/interfaces/web/public/modules/blobs/menta.png +0 -0
  556. package/src/interfaces/web/public/modules/blobs/nimbo.png +0 -0
  557. package/src/interfaces/web/public/modules/blobs/noche.png +0 -0
  558. package/src/interfaces/web/public/modules/blobs/onyx.png +0 -0
  559. package/src/interfaces/web/public/modules/blobs/orbita.png +0 -0
  560. package/src/interfaces/web/public/modules/blobs/ovni.png +0 -0
  561. package/src/interfaces/web/public/modules/blobs/papa.png +0 -0
  562. package/src/interfaces/web/public/modules/blobs/parche.png +0 -0
  563. package/src/interfaces/web/public/modules/blobs/peon.png +0 -0
  564. package/src/interfaces/web/public/modules/blobs/perla.png +0 -0
  565. package/src/interfaces/web/public/modules/blobs/pino.png +0 -0
  566. package/src/interfaces/web/public/modules/blobs/quad.png +0 -0
  567. package/src/interfaces/web/public/modules/blobs/rubi.png +0 -0
  568. package/src/interfaces/web/public/modules/blobs/saturno.png +0 -0
  569. package/src/interfaces/web/public/modules/blobs/trebol.png +0 -0
  570. package/src/interfaces/web/public/modules/blobs/trino.png +0 -0
  571. package/src/interfaces/web/public/modules/blobs/volcan.png +0 -0
  572. package/src/interfaces/web/public/modules/blobs/zafiro.png +0 -0
  573. package/src/interfaces/web/public/sw.js +106 -0
  574. package/src/interfaces/web/src/App.tsx +106 -10
  575. package/src/interfaces/web/src/assets/cli/claude.webp +0 -0
  576. package/src/interfaces/web/src/assets/cli/codex.webp +0 -0
  577. package/src/interfaces/web/src/assets/cli/cursor.svg +1 -0
  578. package/src/interfaces/web/src/assets/cli/opencode.png +0 -0
  579. package/src/interfaces/web/src/components/Combobox.tsx +201 -0
  580. package/src/interfaces/web/src/components/MobileHint.tsx +95 -0
  581. package/src/interfaces/web/src/components/MobileLinkDialog.tsx +156 -0
  582. package/src/interfaces/web/src/components/ModelCombobox.tsx +21 -113
  583. package/src/interfaces/web/src/components/{routines/ReadOnlyBlock.tsx → ReadOnlyBlock.tsx} +13 -4
  584. package/src/interfaces/web/src/components/Roby.tsx +5 -3
  585. package/src/interfaces/web/src/components/RobyBubble.tsx +23 -5
  586. package/src/interfaces/web/src/components/RowMenu.tsx +46 -0
  587. package/src/interfaces/web/src/components/TimezoneSelect.tsx +4 -1
  588. package/src/interfaces/web/src/components/Toast.tsx +50 -14
  589. package/src/interfaces/web/src/components/UiSelect.tsx +12 -5
  590. package/src/interfaces/web/src/components/agents/AgentAvatar.tsx +171 -0
  591. package/src/interfaces/web/src/components/agents/AgentFormFields.tsx +42 -11
  592. package/src/interfaces/web/src/components/agents/AgentModelBadge.tsx +157 -0
  593. package/src/interfaces/web/src/components/agents/AgentModelSelect.tsx +57 -0
  594. package/src/interfaces/web/src/components/agents/AgentSkillsPicker.tsx +115 -0
  595. package/src/interfaces/web/src/components/agents/BlobAvatar.tsx +50 -0
  596. package/src/interfaces/web/src/components/agents/blobPresets.ts +50 -0
  597. package/src/interfaces/web/src/components/agents/modelCatalog.ts +66 -0
  598. package/src/interfaces/web/src/components/chat/ActionGroup.tsx +162 -0
  599. package/src/interfaces/web/src/components/chat/AskAnswersCard.tsx +4 -3
  600. package/src/interfaces/web/src/components/chat/AskQuestionsCard.tsx +1 -1
  601. package/src/interfaces/web/src/components/chat/Attachment.tsx +220 -0
  602. package/src/interfaces/web/src/components/chat/ChatList.tsx +226 -51
  603. package/src/interfaces/web/src/components/chat/ChatRowActivity.tsx +45 -0
  604. package/src/interfaces/web/src/components/chat/Composer.tsx +672 -23
  605. package/src/interfaces/web/src/components/chat/ComposerCommands.tsx +175 -0
  606. package/src/interfaces/web/src/components/chat/ComposerSuggest.tsx +215 -0
  607. package/src/interfaces/web/src/components/chat/ContextBar.tsx +133 -65
  608. package/src/interfaces/web/src/components/chat/InlineAskPanel.tsx +107 -47
  609. package/src/interfaces/web/src/components/chat/MessageBubble.tsx +460 -119
  610. package/src/interfaces/web/src/components/chat/MessageList.tsx +165 -18
  611. package/src/interfaces/web/src/components/chat/ReasoningBlock.tsx +58 -0
  612. package/src/interfaces/web/src/components/chat/SessionPicker.tsx +182 -0
  613. package/src/interfaces/web/src/components/chat/SkillTrace.tsx +183 -0
  614. package/src/interfaces/web/src/components/chat/ToolCall.tsx +11 -11
  615. package/src/interfaces/web/src/components/code/CodeContextTab.tsx +87 -53
  616. package/src/interfaces/web/src/components/code/CodeFileTree.tsx +11 -52
  617. package/src/interfaces/web/src/components/code/CodeFileViewer.tsx +5 -4
  618. package/src/interfaces/web/src/components/code/CodeProjectPicker.tsx +31 -7
  619. package/src/interfaces/web/src/components/code/CodeSessionList.tsx +32 -11
  620. package/src/interfaces/web/src/components/code/CodeSidePanel.tsx +33 -3
  621. package/src/interfaces/web/src/components/code/CodeTerminal.tsx +5 -4
  622. package/src/interfaces/web/src/components/code/NewCodeSessionDialog.tsx +164 -0
  623. package/src/interfaces/web/src/components/commitments/CommitmentDetail.tsx +162 -0
  624. package/src/interfaces/web/src/components/commitments/CommitmentFormDialog.tsx +158 -0
  625. package/src/interfaces/web/src/components/commitments/CommitmentList.tsx +194 -0
  626. package/src/interfaces/web/src/components/commitments/commitmentState.tsx +46 -0
  627. package/src/interfaces/web/src/components/common/BulkActionBar.tsx +60 -0
  628. package/src/interfaces/web/src/components/common/ConfirmDialog.tsx +4 -1
  629. package/src/interfaces/web/src/components/common/SelectCheckbox.tsx +40 -0
  630. package/src/interfaces/web/src/components/common/TabNav.tsx +1 -1
  631. package/src/interfaces/web/src/components/cron/CronPicker.tsx +1 -1
  632. package/src/interfaces/web/src/components/files/FileBrowser.tsx +1 -1
  633. package/src/interfaces/web/src/components/files/FileTree.tsx +6 -4
  634. package/src/interfaces/web/src/components/files/FileViewer.tsx +9 -5
  635. package/src/interfaces/web/src/components/files/MarkdownPreview.tsx +45 -9
  636. package/src/interfaces/web/src/components/images/ImageDefaultsCard.tsx +93 -0
  637. package/src/interfaces/web/src/components/images/ImageProviderList.tsx +142 -0
  638. package/src/interfaces/web/src/components/images/ImageProviderModal.tsx +263 -0
  639. package/src/interfaces/web/src/components/images/ImageTestCard.tsx +142 -0
  640. package/src/interfaces/web/src/components/inbox/ChannelFilter.tsx +178 -0
  641. package/src/interfaces/web/src/components/inbox/InboxList.tsx +77 -81
  642. package/src/interfaces/web/src/components/inbox/InboxRowItem.tsx +140 -0
  643. package/src/interfaces/web/src/components/integrations/ComingSoonPlugin.tsx +2 -2
  644. package/src/interfaces/web/src/components/integrations/PluginConnect.tsx +208 -30
  645. package/src/interfaces/web/src/components/integrations/PluginToolsSection.tsx +1 -1
  646. package/src/interfaces/web/src/components/layout/ProjectAvatar.tsx +39 -24
  647. package/src/interfaces/web/src/components/layout/ProjectSidebar.tsx +253 -58
  648. package/src/interfaces/web/src/components/memory/MemoryBrowser.tsx +94 -47
  649. package/src/interfaces/web/src/components/routines/ExecutionsList.tsx +202 -90
  650. package/src/interfaces/web/src/components/routines/RoutineDetail.tsx +28 -6
  651. package/src/interfaces/web/src/components/routines/RoutineEditor.tsx +16 -5
  652. package/src/interfaces/web/src/components/routines/RoutineList.tsx +49 -9
  653. package/src/interfaces/web/src/components/routines/VarTextarea.tsx +1 -1
  654. package/src/interfaces/web/src/components/routines/runChat.ts +121 -0
  655. package/src/interfaces/web/src/components/sessions/SessionDetail.tsx +148 -0
  656. package/src/interfaces/web/src/components/sessions/SessionTerminal.tsx +98 -0
  657. package/src/interfaces/web/src/components/settings/AccessPanel.tsx +205 -0
  658. package/src/interfaces/web/src/components/settings/DefaultRouterCard.tsx +246 -101
  659. package/src/interfaces/web/src/components/settings/DesktopSettingsPanel.tsx +36 -2
  660. package/src/interfaces/web/src/components/settings/DevicesPanel.tsx +19 -6
  661. package/src/interfaces/web/src/components/settings/EmbedProviderList.tsx +274 -0
  662. package/src/interfaces/web/src/components/settings/EnginesPanel.tsx +26 -6
  663. package/src/interfaces/web/src/components/settings/MemoryPanel.tsx +118 -119
  664. package/src/interfaces/web/src/components/settings/NudgePanel.tsx +4 -3
  665. package/src/interfaces/web/src/components/settings/PairDeviceDialog.tsx +15 -4
  666. package/src/interfaces/web/src/components/settings/PanelPrefs.tsx +374 -0
  667. package/src/interfaces/web/src/components/settings/ProfilePanel.tsx +70 -2
  668. package/src/interfaces/web/src/components/settings/RoutingPanel.tsx +2 -1
  669. package/src/interfaces/web/src/components/settings/SkillsInspectorPanel.tsx +102 -43
  670. package/src/interfaces/web/src/components/settings/SkillsManager.tsx +76 -21
  671. package/src/interfaces/web/src/components/settings/SkillsSettings.tsx +10 -3
  672. package/src/interfaces/web/src/components/settings/SuperAgentPanel.tsx +8 -0
  673. package/src/interfaces/web/src/components/settings/TelegramChannelsPanel.tsx +11 -2
  674. package/src/interfaces/web/src/components/settings/TelegramContactsPanel.tsx +19 -8
  675. package/src/interfaces/web/src/components/settings/TelegramRolesPanel.tsx +12 -2
  676. package/src/interfaces/web/src/components/settings/WebPanel.tsx +22 -31
  677. package/src/interfaces/web/src/components/settings/providers/ProviderCard.tsx +15 -3
  678. package/src/interfaces/web/src/components/settings/providers/ProviderModal.tsx +97 -66
  679. package/src/interfaces/web/src/components/settings/providers/ProviderTestDialog.tsx +150 -0
  680. package/src/interfaces/web/src/components/settings/providers/typeStyles.ts +67 -25
  681. package/src/interfaces/web/src/components/settings/providers/types.ts +3 -0
  682. package/src/interfaces/web/src/components/tasks/TaskBoard.tsx +201 -0
  683. package/src/interfaces/web/src/components/tasks/TaskColumnsDialog.tsx +243 -0
  684. package/src/interfaces/web/src/components/tasks/TaskComments.tsx +167 -0
  685. package/src/interfaces/web/src/components/tasks/TaskDetail.tsx +335 -0
  686. package/src/interfaces/web/src/components/tasks/TaskFormDialog.tsx +406 -0
  687. package/src/interfaces/web/src/components/tasks/TaskList.tsx +258 -0
  688. package/src/interfaces/web/src/components/tasks/TaskSubtasks.tsx +126 -0
  689. package/src/interfaces/web/src/components/tasks/columns.ts +51 -0
  690. package/src/interfaces/web/src/components/tasks/taskStatus.tsx +159 -34
  691. package/src/interfaces/web/src/components/tasks/taskViewPrefs.ts +73 -0
  692. package/src/interfaces/web/src/components/tasks/useTaskColumns.ts +31 -0
  693. package/src/interfaces/web/src/components/ui/chat-input.tsx +136 -12
  694. package/src/interfaces/web/src/components/ui/context-menu.tsx +15 -198
  695. package/src/interfaces/web/src/components/ui/dialog.tsx +3 -2
  696. package/src/interfaces/web/src/components/ui/filter-chips.tsx +4 -0
  697. package/src/interfaces/web/src/components/ui/popover.tsx +14 -49
  698. package/src/interfaces/web/src/components/ui/sheet.tsx +2 -1
  699. package/src/interfaces/web/src/components/ui/spinner.tsx +2 -1
  700. package/src/interfaces/web/src/components/ui.tsx +62 -23
  701. package/src/interfaces/web/src/components/voice/VoiceProviderList.tsx +2 -1
  702. package/src/interfaces/web/src/components/voice/VoiceProviderModal.tsx +66 -8
  703. package/src/interfaces/web/src/components/voice/VoiceQvoxInstallCard.tsx +63 -0
  704. package/src/interfaces/web/src/components/voice/VoiceQvoxStatusCard.tsx +51 -0
  705. package/src/interfaces/web/src/components/voice/VoiceSttCard.tsx +4 -3
  706. package/src/interfaces/web/src/components/voice/VoiceTestCard.tsx +38 -4
  707. package/src/interfaces/web/src/constants/index.ts +1 -0
  708. package/src/interfaces/web/src/hooks/useChannelPrefs.ts +39 -0
  709. package/src/interfaces/web/src/hooks/useChat.ts +1092 -85
  710. package/src/interfaces/web/src/hooks/useChatActivity.ts +24 -0
  711. package/src/interfaces/web/src/hooks/useInbox.ts +26 -4
  712. package/src/interfaces/web/src/hooks/useLiveMessages.ts +16 -0
  713. package/src/interfaces/web/src/hooks/useOllamaModels.ts +79 -0
  714. package/src/interfaces/web/src/hooks/useRecorder.ts +198 -0
  715. package/src/interfaces/web/src/hooks/useTokenBootstrap.ts +23 -6
  716. package/src/interfaces/web/src/i18n/en.ts +832 -175
  717. package/src/interfaces/web/src/i18n/es.ts +841 -188
  718. package/src/interfaces/web/src/lib/api/agents.ts +23 -4
  719. package/src/interfaces/web/src/lib/api/code.ts +17 -0
  720. package/src/interfaces/web/src/lib/api/commitments.ts +7 -1
  721. package/src/interfaces/web/src/lib/api/conversations.ts +38 -11
  722. package/src/interfaces/web/src/lib/api/embeddings.ts +3 -0
  723. package/src/interfaces/web/src/lib/api/engines.ts +14 -0
  724. package/src/interfaces/web/src/lib/api/groups.ts +93 -0
  725. package/src/interfaces/web/src/lib/api/images.ts +193 -0
  726. package/src/interfaces/web/src/lib/api/inbox.ts +29 -5
  727. package/src/interfaces/web/src/lib/api/integrations.ts +9 -0
  728. package/src/interfaces/web/src/lib/api/media.ts +126 -0
  729. package/src/interfaces/web/src/lib/api/net.ts +44 -0
  730. package/src/interfaces/web/src/lib/api/profiles.ts +12 -0
  731. package/src/interfaces/web/src/lib/api/projects.ts +9 -1
  732. package/src/interfaces/web/src/lib/api/routines.ts +9 -1
  733. package/src/interfaces/web/src/lib/api/sessions.ts +18 -0
  734. package/src/interfaces/web/src/lib/api/skills.ts +8 -0
  735. package/src/interfaces/web/src/lib/api/super_agent.ts +3 -0
  736. package/src/interfaces/web/src/lib/api/tasks.ts +46 -6
  737. package/src/interfaces/web/src/lib/api/tools.ts +2 -0
  738. package/src/interfaces/web/src/lib/api/transcribe.ts +31 -0
  739. package/src/interfaces/web/src/lib/api/turns.ts +20 -0
  740. package/src/interfaces/web/src/lib/api/voice.ts +72 -2
  741. package/src/interfaces/web/src/lib/api.ts +3 -0
  742. package/src/interfaces/web/src/lib/channels.ts +214 -0
  743. package/src/interfaces/web/src/lib/chat-activity.ts +175 -0
  744. package/src/interfaces/web/src/lib/chat-prefs.ts +48 -0
  745. package/src/interfaces/web/src/lib/code-context.ts +0 -2
  746. package/src/interfaces/web/src/lib/http.ts +51 -17
  747. package/src/interfaces/web/src/lib/inbox-selection.ts +29 -0
  748. package/src/interfaces/web/src/lib/live.ts +252 -0
  749. package/src/interfaces/web/src/lib/net.ts +211 -0
  750. package/src/interfaces/web/src/lib/notify.ts +331 -0
  751. package/src/interfaces/web/src/lib/projectNav.ts +40 -0
  752. package/src/interfaces/web/src/lib/pwa.ts +115 -0
  753. package/src/interfaces/web/src/lib/qvox.ts +24 -0
  754. package/src/interfaces/web/src/lib/slug.ts +3 -0
  755. package/src/interfaces/web/src/lib/tone.ts +150 -0
  756. package/src/interfaces/web/src/main.tsx +15 -4
  757. package/src/interfaces/web/src/screens/ApxAdminScreen.tsx +33 -11
  758. package/src/interfaces/web/src/screens/InboxScreen.tsx +126 -71
  759. package/src/interfaces/web/src/screens/ProjectScreen.tsx +8 -4
  760. package/src/interfaces/web/src/screens/SettingsScreen.tsx +7 -4
  761. package/src/interfaces/web/src/screens/base/AgentDefaultsTab.tsx +30 -9
  762. package/src/interfaces/web/src/screens/base/CommitmentsTab.tsx +150 -168
  763. package/src/interfaces/web/src/screens/base/LogsTab.tsx +8 -7
  764. package/src/interfaces/web/src/screens/base/SessionsTab.tsx +99 -37
  765. package/src/interfaces/web/src/screens/base/WorkspacesTab.tsx +1 -1
  766. package/src/interfaces/web/src/screens/mobile/MobileChat.tsx +85 -0
  767. package/src/interfaces/web/src/screens/mobile/MobileChatList.tsx +244 -0
  768. package/src/interfaces/web/src/screens/mobile/MobileCommitments.tsx +350 -0
  769. package/src/interfaces/web/src/screens/mobile/MobileScreen.tsx +138 -0
  770. package/src/interfaces/web/src/screens/mobile/MobileTabBar.tsx +105 -0
  771. package/src/interfaces/web/src/screens/mobile/MobileTasks.tsx +407 -0
  772. package/src/interfaces/web/src/screens/mobile/NewChatSheet.tsx +313 -0
  773. package/src/interfaces/web/src/screens/mobile/mobileList.tsx +194 -0
  774. package/src/interfaces/web/src/screens/mobile/routes.ts +239 -0
  775. package/src/interfaces/web/src/screens/modules/CodeScreen.tsx +135 -64
  776. package/src/interfaces/web/src/screens/modules/DeckScreen.tsx +0 -1
  777. package/src/interfaces/web/src/screens/modules/ImagesScreen.tsx +172 -0
  778. package/src/interfaces/web/src/screens/modules/VoiceScreen.tsx +36 -4
  779. package/src/interfaces/web/src/screens/project/AgentBrainGraph.tsx +117 -30
  780. package/src/interfaces/web/src/screens/project/AgentDetailScreen.tsx +537 -92
  781. package/src/interfaces/web/src/screens/project/AgentsTab.tsx +344 -93
  782. package/src/interfaces/web/src/screens/project/ArtifactsTab.tsx +1 -1
  783. package/src/interfaces/web/src/screens/project/ChatTab.tsx +1160 -94
  784. package/src/interfaces/web/src/screens/project/IntegrationsTab.tsx +3 -2
  785. package/src/interfaces/web/src/screens/project/McpsTab.tsx +62 -14
  786. package/src/interfaces/web/src/screens/project/Overview.tsx +36 -15
  787. package/src/interfaces/web/src/screens/project/RoutinesTab.tsx +93 -8
  788. package/src/interfaces/web/src/screens/project/SkillsTab.tsx +4 -1
  789. package/src/interfaces/web/src/screens/project/StructureTab.tsx +5 -4
  790. package/src/interfaces/web/src/screens/project/TasksTab.tsx +351 -129
  791. package/src/interfaces/web/src/screens/project/VarsTab.tsx +17 -5
  792. package/src/interfaces/web/src/styles.css +106 -31
  793. package/src/interfaces/web/src/types/daemon.ts +246 -2
  794. package/src/interfaces/web/src/vite-env.d.ts +20 -0
  795. package/src/interfaces/tui/plugin/command-shim.ts +0 -109
  796. package/src/interfaces/web/components.json +0 -25
  797. package/src/interfaces/web/dist/assets/index-CvEoGtTf.js +0 -849
  798. package/src/interfaces/web/dist/assets/index-CvEoGtTf.js.map +0 -1
  799. package/src/interfaces/web/dist/assets/index-DzBBXFaO.css +0 -1
  800. package/src/interfaces/web/src/components/tasks/TaskDetailPanel.tsx +0 -142
  801. package/src/interfaces/web/src/components/ui/accordion.tsx +0 -72
  802. package/src/interfaces/web/src/components/ui/alert-dialog.tsx +0 -187
  803. package/src/interfaces/web/src/components/ui/alert.tsx +0 -76
  804. package/src/interfaces/web/src/components/ui/aspect-ratio.tsx +0 -22
  805. package/src/interfaces/web/src/components/ui/avatar.tsx +0 -107
  806. package/src/interfaces/web/src/components/ui/breadcrumb.tsx +0 -125
  807. package/src/interfaces/web/src/components/ui/button-group.tsx +0 -87
  808. package/src/interfaces/web/src/components/ui/calendar.tsx +0 -221
  809. package/src/interfaces/web/src/components/ui/card.tsx +0 -103
  810. package/src/interfaces/web/src/components/ui/carousel.tsx +0 -242
  811. package/src/interfaces/web/src/components/ui/chart.tsx +0 -371
  812. package/src/interfaces/web/src/components/ui/checkbox.tsx +0 -29
  813. package/src/interfaces/web/src/components/ui/collapsible.tsx +0 -19
  814. package/src/interfaces/web/src/components/ui/combobox.tsx +0 -295
  815. package/src/interfaces/web/src/components/ui/command.tsx +0 -196
  816. package/src/interfaces/web/src/components/ui/direction.tsx +0 -4
  817. package/src/interfaces/web/src/components/ui/drawer.tsx +0 -134
  818. package/src/interfaces/web/src/components/ui/empty.tsx +0 -104
  819. package/src/interfaces/web/src/components/ui/field.tsx +0 -236
  820. package/src/interfaces/web/src/components/ui/hover-card.tsx +0 -51
  821. package/src/interfaces/web/src/components/ui/input-otp.tsx +0 -85
  822. package/src/interfaces/web/src/components/ui/item.tsx +0 -201
  823. package/src/interfaces/web/src/components/ui/menubar.tsx +0 -280
  824. package/src/interfaces/web/src/components/ui/native-select.tsx +0 -61
  825. package/src/interfaces/web/src/components/ui/navigation-menu.tsx +0 -168
  826. package/src/interfaces/web/src/components/ui/pagination.tsx +0 -130
  827. package/src/interfaces/web/src/components/ui/progress.tsx +0 -83
  828. package/src/interfaces/web/src/components/ui/radio-group.tsx +0 -36
  829. package/src/interfaces/web/src/components/ui/resizable.tsx +0 -50
  830. package/src/interfaces/web/src/components/ui/scroll-area.tsx +0 -53
  831. package/src/interfaces/web/src/components/ui/sidebar.tsx +0 -726
  832. package/src/interfaces/web/src/components/ui/slider.tsx +0 -52
  833. package/src/interfaces/web/src/components/ui/sonner.tsx +0 -49
  834. package/src/interfaces/web/src/components/ui/table.tsx +0 -116
  835. package/src/interfaces/web/src/components/ui/toggle-group.tsx +0 -87
  836. package/src/interfaces/web/src/components/ui/toggle.tsx +0 -45
  837. package/src/interfaces/web/src/hooks/use-mobile.ts +0 -19
  838. package/src/interfaces/web/src/screens/base/GlobalTasksTab.tsx +0 -161
@@ -33,6 +33,19 @@ const RETRYABLE_PHRASES = [
33
33
  /timeout/i,
34
34
  ];
35
35
 
36
+ // A 404 that means "this model is retired / unknown", as opposed to a 404 that
37
+ // means "wrong base_url". Kept narrow on purpose: a typo'd model id also lands
38
+ // here, and rotating past it is still the better outcome than a dead turn.
39
+ const MODEL_RETIRED_PHRASES = [
40
+ /no longer available/i,
41
+ /not found for API version/i,
42
+ /does not exist/i,
43
+ /unknown model/i,
44
+ /model.{0,20}(not found|is not supported)/i,
45
+ /has been (deprecated|retired|removed)/i,
46
+ /model is unavailable/i,
47
+ ];
48
+
36
49
  const FATAL_STATUS = new Set([400, 401, 403, 404, 422]);
37
50
 
38
51
  const FATAL_PHRASES = [
@@ -45,6 +58,8 @@ const FATAL_PHRASES = [
45
58
  ];
46
59
 
47
60
  export function isRetryableEngineError(err) {
61
+ if (err?.name === "AbortError" || err?.code === "ABORT_ERR") return false;
62
+
48
63
  const msg = String(err?.message || err || "");
49
64
  if (!msg) return false;
50
65
 
@@ -69,9 +84,36 @@ export function isRetryableEngineError(err) {
69
84
  /looks like (object|array)|can'?t find closing|unexpected end of json|invalid (json|grammar)/i.test(msg)) {
70
85
  return true;
71
86
  }
87
+ // A gateway 400 with no message at all — Zen answers upstream failures
88
+ // with a bare `{"object":"error","model":"..."}` envelope. There is
89
+ // nothing in it to fix, and it is as likely to be the vendor behind the
90
+ // gateway as it is to be our payload, so the chain gets to try the next
91
+ // model rather than ending the run on an error the user cannot act on.
92
+ if (/^[a-z0-9_-]+ 400:\s*\{"object":\s*"error"/i.test(msg)) return true;
93
+ // A gateway that answers 400 (not 404) for a model it no longer serves:
94
+ // zen 400: Error from provider (Console): Upstream request failed:
95
+ // Model is unavailable
96
+ // Same situation as a retired model, different status code — the model
97
+ // is not coming back this turn, so walk the chain.
98
+ if (MODEL_RETIRED_PHRASES.some((re) => re.test(msg))) return true;
99
+ // A history-fidelity requirement we may not be able to satisfy: the turn
100
+ // being replayed came from a different engine (post-rotation) and never
101
+ // carried the field. Serialising it correctly is zen.js's job; when the
102
+ // data isn't there, a model that doesn't demand it is the only way out.
103
+ if (/reasoning_content|thought.?signature/i.test(msg)) return true;
72
104
  // explicit schema / param errors are our bug, not transient
73
105
  return false;
74
106
  }
107
+ // 404 usually means "you pointed at the wrong endpoint" — fatal, fix it.
108
+ // But it is ALSO how a provider retires a model:
109
+ // gemini 404: This model models/gemini-2.5-flash-lite is no longer
110
+ // available. Please update your code to use models/gemini-3.5-flash-lite
111
+ // openai 404: The model `x` does not exist or you do not have access
112
+ // That is exactly what the fallback chain is for. The model is gone for
113
+ // good, the user cannot act on it mid-turn, and the next model in the
114
+ // chain will answer — so rotate instead of ending the run on a wall of
115
+ // provider text. A 404 that is NOT about the model stays fatal.
116
+ if (status === 404 && MODEL_RETIRED_PHRASES.some((re) => re.test(msg))) return true;
75
117
  return false;
76
118
  }
77
119
  if (status && RETRYABLE_STATUS.has(status)) return true;
@@ -3,12 +3,14 @@ import {
3
3
  extractPseudoToolCalls,
4
4
  extractBareFunctionCalls,
5
5
  cleanTextOfPseudoToolCalls,
6
+ looksLikeFabricatedToolLog,
6
7
  } from "./tools/tool-call-parser.js";
7
- import { resolveActiveModel, fallbackModels } from "./model-router.js";
8
+ import { resolveActiveModel, fallbackModels, isFallbackEnabled } from "./model-router.js";
8
9
  import { MAX_TOOL_ITERS, ACK_ONLY_TOOLS, MAX_CONSECUTIVE_ACKS, TURN_ENDING_TOOLS } from "./constants.js";
9
10
  import { TOOLS } from "./tools/names.js";
10
11
  import { pseudoToolSystem, shouldRetryWithPseudoTools } from "./tools/pseudo-tools.js";
11
12
  import { filterToolSchemas } from "./tools-overlap.js";
13
+ import { buildRuntimeBlock } from "./prompt-builder.js";
12
14
  import { isRetryableEngineError, shortRetryReason } from "./retry.js";
13
15
  import {
14
16
  securityRiskConfig,
@@ -25,17 +27,57 @@ import {
25
27
  } from "./stuck-detector.js";
26
28
  import { createGreetingGuard } from "./loop/greeting-guard.js";
27
29
  import { createSideEffectLedger } from "./loop/side-effects.js";
30
+ import {
31
+ describeTurnImages,
32
+ providerWiresVision,
33
+ withImageDescription,
34
+ } from "./vision-bridge.js";
35
+ import { messagesForModel } from "./model-capabilities.js";
28
36
 
29
37
  async function emitProgress(onEvent, event) {
30
38
  if (typeof onEvent !== "function") return;
31
39
  await onEvent(event);
32
40
  }
33
41
 
42
+ // How much of one tool result the trace carries. The trace is what gets
43
+ // PERSISTED (message store, web viewer) and what the next turns replay as
44
+ // history, so this cap is the memory a past tool call leaves behind.
45
+ const TRACE_RESULT_CAP = 1200;
46
+ // Longest single string field kept inside a structured result.
47
+ const TRACE_FIELD_CAP = 900;
48
+
49
+ // Shrink a tool result for the trace WITHOUT destroying its shape. The old
50
+ // version stringified anything over 400 chars and sliced the JSON text, which
51
+ // left a half-open brace: unparseable, so every downstream reader (history
52
+ // replay, viewer) had to show escaped JSON instead of the actual output. Now
53
+ // the envelope survives and only the long string fields inside are clipped —
54
+ // `{ exit_code, stdout }` stays an object whose stdout is trimmed.
34
55
  function summarizeForTrace(r) {
35
56
  if (r === null || r === undefined) return r;
36
- const s = JSON.stringify(r);
37
- if (s.length <= 400) return r;
38
- return s.slice(0, 380) + "…(truncated)";
57
+ if (typeof r === "string") {
58
+ return r.length <= TRACE_RESULT_CAP ? r : r.slice(0, TRACE_RESULT_CAP) + "…(truncated)";
59
+ }
60
+ if (typeof r !== "object") return r;
61
+ try {
62
+ if (JSON.stringify(r).length <= TRACE_RESULT_CAP) return r;
63
+ } catch {
64
+ return String(r).slice(0, TRACE_RESULT_CAP);
65
+ }
66
+ const clip = (v, depth) => {
67
+ if (typeof v === "string") {
68
+ return v.length <= TRACE_FIELD_CAP ? v : v.slice(0, TRACE_FIELD_CAP) + "…(truncated)";
69
+ }
70
+ if (Array.isArray(v)) {
71
+ const head = v.slice(0, 20).map((x) => clip(x, depth + 1));
72
+ return v.length > 20 ? [...head, `…(${v.length - 20} more)`] : head;
73
+ }
74
+ if (v && typeof v === "object") {
75
+ if (depth >= 3) return "…(nested)";
76
+ return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, clip(x, depth + 1)]));
77
+ }
78
+ return v;
79
+ };
80
+ return clip(r, 0);
39
81
  }
40
82
 
41
83
  function fallbackFinalText(trace, error) {
@@ -50,6 +92,15 @@ function fallbackFinalText(trace, error) {
50
92
  return lines.join("\n");
51
93
  }
52
94
 
95
+ /**
96
+ * Did the engine stop because it ran out of output budget? Every adapter passes
97
+ * the provider's own word through: OpenAI-shaped gateways say "length", Gemini
98
+ * says "MAX_TOKENS".
99
+ */
100
+ function wasTruncated(result) {
101
+ return /^(length|max_tokens)$/i.test(String(result?.finish_reason || ""));
102
+ }
103
+
53
104
  function previewTraceResult(result) {
54
105
  if (result === null || result === undefined) return "ok";
55
106
  if (typeof result === "string") return result.slice(0, 180);
@@ -115,6 +166,59 @@ const WRAPUP_SIGNAL =
115
166
  "Talk like a person giving a quick status update. Do NOT emit a tool call, " +
116
167
  "JSON, or system jargon like \"iteration\" or \"limit\".]";
117
168
 
169
+ // How many times one turn may be nudged past an output-limit truncation before
170
+ // we let it end. A model that answers with a wall of prose every time is not
171
+ // going to start acting on the third try, and the wrap-up still fires.
172
+ const MAX_LENGTH_CONTINUES = 2;
173
+
174
+ // A model that hits its output cap mid-sentence has not finished; it has been
175
+ // cut off. Every adapter reports that as a finish reason, and the loop used to
176
+ // ignore it: no tool calls in a truncated message means "the model stopped
177
+ // calling tools", which reads as a completed turn. So a routine that wrote six
178
+ // ideas out as YAML instead of filing them, ran out of budget halfway through
179
+ // the sixth, and never called the tool, reported `status: ok` with a reply that
180
+ // began mid-word.
181
+ //
182
+ // This note goes in as a conversation turn so weak models actually answer it,
183
+ // and it asks for the ACTION rather than a shorter essay — writing the work out
184
+ // is what filled the budget in the first place.
185
+ const TRUNCATED_SIGNAL =
186
+ "[Internal turn note — this is NOT from the user. Your last message hit the " +
187
+ "output limit and was cut off mid-way, so nothing in it counted as work.\n" +
188
+ "Do NOT rewrite or continue that text. If the task needs tools — creating a " +
189
+ "task, writing a file, sending a message — CALL THEM NOW, one step at a time, " +
190
+ "and keep any prose to a single short line. Composing the result as text is " +
191
+ "not the same as doing it.]";
192
+
193
+ // A turn that WROTE tool results instead of producing them.
194
+ //
195
+ // The shipped example (2026-08-29, Telegram): a fallback model answered with
196
+ // five `[result: shell]` lines — invented commands, invented output — and then
197
+ // "Listo, te mandé el WhatsApp". No tool ran. The user was told a message had
198
+ // been sent to a real phone number that was never sent.
199
+ //
200
+ // Nothing in those lines can be recovered: there is no structured call in them,
201
+ // and reconstructing a shell command from hallucinated prose to run it for real
202
+ // would be a far worse failure than the one it fixes. So the model is told the
203
+ // results are not real and asked to do the work — the same in-band correction
204
+ // the truncation case uses, and for the same reason (weak models answer a
205
+ // conversation turn far more reliably than a system suffix).
206
+ const FABRICATED_RESULTS_SIGNAL =
207
+ "[Internal turn note — this is NOT from the user. Your last message was " +
208
+ "written as if tools had run, but you called none, so none of those results " +
209
+ "exist and nothing you described actually happened.\n" +
210
+ "Lines like `[tool result: …]` in this conversation are a LOG of past calls. " +
211
+ "They are not a format for you to write in — writing one does not run " +
212
+ "anything.\n" +
213
+ "Do the work now: emit real tool calls, one step at a time. If you cannot " +
214
+ "(no such tool, missing permission, unclear arguments), say that plainly to " +
215
+ "the user instead — but never report an action you did not take.]";
216
+
217
+ // How many times one turn may be corrected for this before we let it end. A
218
+ // model that fabricates twice in a row is not going to start acting on the
219
+ // third try, and the wrap-up still fires.
220
+ const MAX_FABRICATION_RETRIES = 2;
221
+
118
222
  /**
119
223
  * Shared tool-calling agent loop used by super-agent and future surfaces.
120
224
  */
@@ -136,6 +240,7 @@ export async function runAgent({
136
240
  onEvent = null,
137
241
  signal,
138
242
  onToken = null,
243
+ onReasoningToken = null,
139
244
  agentName = "apx",
140
245
  suppressTools = null, // optional list of tool names to remove from the registry
141
246
  // Per-reply output cap. Defaults to 512 (tuned for chit-chat + small tool
@@ -165,7 +270,7 @@ export async function runAgent({
165
270
  const triedHealth = new Map(
166
271
  (routing.tried || []).map((t) => [t.modelId, t.healthy !== false])
167
272
  );
168
- const retryChain = fallbackModels(globalConfig).filter((m) => {
273
+ const retryChain = (isFallbackEnabled(globalConfig) ? fallbackModels(globalConfig) : []).filter((m) => {
169
274
  if (m === activeModel) return false;
170
275
  if (triedHealth.get(m) === false) return false;
171
276
  return true;
@@ -183,7 +288,8 @@ export async function runAgent({
183
288
  }
184
289
 
185
290
  // Suppression: callers (notably the routine runner) can disable tools whose
186
- // output would duplicate post_commands. We filter the schemas the engine
291
+ // output would duplicate a sink the run already has — a post_command, or the
292
+ // routine's own `deliver_to`. We filter the schemas the engine
187
293
  // sees AND keep a deny-set so a model that hallucinates a suppressed tool
188
294
  // call gets a clear error rather than firing.
189
295
  let effectiveSchemas = Array.isArray(suppressTools) && suppressTools.length > 0
@@ -194,7 +300,10 @@ export async function runAgent({
194
300
  await emitProgress(onEvent, {
195
301
  type: "tools_suppressed",
196
302
  tools: [...suppressed],
197
- reason: "post_commands_overlap",
303
+ // Why, generically: a post_command pipes this output somewhere, or the
304
+ // routine's `deliver_to` does. Either way the sink already owns the
305
+ // message and the loop must not send a second one.
306
+ reason: "output_sink_overlap",
198
307
  });
199
308
  }
200
309
  // Completion contract: only meaningful when there are real tools to choose
@@ -228,6 +337,9 @@ export async function runAgent({
228
337
  // (In `total` the guard returns early anyway, so this is a no-op there.)
229
338
  if (toolHandlerCtx) toolHandlerCtx.securityRiskActive = true;
230
339
  }
340
+ // Telegram (and any other abortable surface) kills in-flight shell tools
341
+ // when a newer turn supersedes this one. Handlers read it at call time.
342
+ if (toolHandlerCtx && signal) toolHandlerCtx.abortSignal = signal;
231
343
 
232
344
  const rawHandlers = makeToolHandlers(toolHandlerCtx);
233
345
  const handlers = suppressed.size > 0
@@ -235,7 +347,7 @@ export async function runAgent({
235
347
  get(target, name) {
236
348
  if (typeof name === "string" && suppressed.has(name)) {
237
349
  return async () => ({
238
- error: `tool "${name}" is suppressed for this invocation (post_commands already cover this output channel)`,
350
+ error: `tool "${name}" is suppressed for this invocation (this run already delivers to that channel)`,
239
351
  });
240
352
  }
241
353
  return target[name];
@@ -248,6 +360,40 @@ export async function runAgent({
248
360
  // that queue into effectiveSchemas at the top of each iteration, so tools
249
361
  // activated on step N are callable from step N+1. No session → no-op.
250
362
  const toolSession = toolHandlerCtx?.toolSession || null;
363
+
364
+ const schemaOnTheWire = (n) =>
365
+ effectiveSchemas.some((sc) => (sc?.function?.name || sc?.name) === n);
366
+
367
+ /**
368
+ * A tool the model called WITHOUT ever receiving its schema.
369
+ *
370
+ * buildLazyToolsBlock puts the NAMES of not-loaded tools in the system prompt
371
+ * (that is what makes them discoverable), and not every provider refuses a
372
+ * call to a tool that was never in the request. So the model can name a real
373
+ * tool and invent its arguments, and the handler will happily run them:
374
+ * `complete_task` was called as `{project, id}` — `id` is what list_tasks
375
+ * hands back, the schema says `task`, and `action` was missing entirely — and
376
+ * "task required" told the model nothing it could act on. It burned three
377
+ * iterations and left the task open.
378
+ *
379
+ * Activating and returning the schema costs one step and makes the retry
380
+ * informed. It also closes a gap: the role gate (`allowedTools`) only ever
381
+ * filtered the schemas the model was SENT, so a guessed name walked straight
382
+ * past it into the handler map, which holds every tool regardless.
383
+ */
384
+ const revealUnloadedTool = (session, name) => {
385
+ const r = session.activate({ names: [name] });
386
+ if (r.denied?.length) return { error: `tool "${name}" is not available to you` };
387
+ if (r.unknown?.length) return { error: `unknown tool: ${name}` };
388
+ const schema = session.pending.find((sc) => (sc?.function?.name || sc?.name) === name);
389
+ return {
390
+ error: `tool "${name}" was not loaded, so you called it without its schema — nothing ran.`,
391
+ activated: name,
392
+ schema: schema?.function || schema || null,
393
+ note: "Ya está cargada. Llamala de nuevo usando exactamente los parámetros de arriba.",
394
+ };
395
+ };
396
+
251
397
  const drainPendingTools = () => {
252
398
  if (!toolSession || toolSession.pending.length === 0) return;
253
399
  const seen = new Set(
@@ -266,22 +412,49 @@ export async function runAgent({
266
412
  }
267
413
  };
268
414
 
269
- // Attachments ride on THIS turn's user message. Only images are forwarded to
270
- // the model (a multimodal engine renders them; the rest ignore the field);
271
- // every other kind is already described in the prompt text by the channel
272
- // handler, with its local path, so the agent can reach it with file tools.
415
+ // Attachments ride on THIS turn's user message. Multimodal engines (Gemini,
416
+ // Claude, GPT via OpenAI-shaped wire) get the pixels. Text-only engines
417
+ // (zen:big-pickle and most free Zen models) get a short description from the
418
+ // vision bridge instead otherwise the agent only sees "[image attached
419
+ // saved to …]" and roleplays that the photo never arrived.
273
420
  const turnImages = attachments.filter((a) => a?.data && /^image\//.test(a.mime || ""));
421
+ let turnPrompt = prompt;
422
+ let imagesForModel = turnImages;
423
+ if (turnImages.length && !providerWiresVision(activeModel, globalConfig)) {
424
+ const description = await describeTurnImages(turnImages, globalConfig, { signal });
425
+ if (description) {
426
+ turnPrompt = withImageDescription(prompt, description);
427
+ await emitProgress(onEvent, {
428
+ type: "vision_bridged",
429
+ model: activeModel,
430
+ images: turnImages.length,
431
+ });
432
+ // The description replaces the bytes for this text-only model.
433
+ imagesForModel = [];
434
+ }
435
+ // If the bridge is unavailable, keep the internal image field until the
436
+ // per-model call adapter replaces it with a safe attachment marker. Raw
437
+ // bytes still never reach a text-only provider.
438
+ }
274
439
  const conversation = [
275
440
  ...previousMessages,
276
441
  {
277
442
  role: "user",
278
- content: prompt,
279
- ...(turnImages.length > 0 ? { images: turnImages } : {}),
443
+ content: turnPrompt,
444
+ ...(imagesForModel.length > 0 ? { images: imagesForModel } : {}),
280
445
  },
281
446
  ];
282
447
  const trace = [];
283
448
  const totalUsage = { input_tokens: 0, output_tokens: 0 };
284
449
  let lastText = "";
450
+ // How much of `maxIters` this run actually spent. Returned so a CALLER that
451
+ // runs the loop more than once for one turn (the judge loop) can subtract it
452
+ // from a shared budget instead of handing every round the full number again.
453
+ // Counted in iterations, not tool calls: one iteration can carry several
454
+ // parallel calls or none, and iterations are what maxIters is denominated in.
455
+ // The empty-retry below deliberately rewinds `iter`, so it doesn't count here
456
+ // either — same rule in both places.
457
+ let itersUsed = 0;
285
458
 
286
459
  // Collapse repeated greetings within a single turn. A turn can produce several
287
460
  // text segments (pre-tool narration + final answer) and weaker models greet in
@@ -297,6 +470,8 @@ export async function runAgent({
297
470
  // the turn empty, and the retry does NOT consume an iteration of the tool
298
471
  // budget. Bounded so a model that only ever returns empty can't spin forever.
299
472
  let emptyRetries = 0;
473
+ let lengthContinues = 0;
474
+ let fabricationRetries = 0;
300
475
  const MAX_EMPTY_RETRIES = 2;
301
476
  // Side-effect dedupe. Weaker models (Gemini especially) sometimes
302
477
  // re-emit the SAME tool call across iterations — e.g. send_telegram
@@ -316,6 +491,13 @@ export async function runAgent({
316
491
  const stuckDetector = createStuckDetector(stuckCfg);
317
492
  let stuckNudged = false;
318
493
  let forceWrapUp = false;
494
+ // Set when the turn closed on the reserved wrap-up step (budget exhausted, or
495
+ // stuck-aborted). The wrap-up ASKS the user whether to keep going, so such a
496
+ // turn is waiting on a person — exactly like ask_questions — not unfinished.
497
+ // Callers that would otherwise "continue" it (the judge loop) must not, or the
498
+ // user gets the same recap and the same question again every round without
499
+ // ever being given the chance to answer it.
500
+ let endedAwaitingUser = false;
319
501
  const safeSig = (v) => {
320
502
  try {
321
503
  return JSON.stringify(v) ?? "";
@@ -331,8 +513,13 @@ export async function runAgent({
331
513
  const tryCallEngine = async (params, { allowRetry = true } = {}) => {
332
514
  while (true) {
333
515
  try {
334
- return await callEngine({ ...params, modelId: activeModel });
516
+ return await callEngine({
517
+ ...params,
518
+ messages: messagesForModel(params.messages, activeModel, globalConfig),
519
+ modelId: activeModel,
520
+ });
335
521
  } catch (e) {
522
+ if (signal?.aborted || e?.name === "AbortError") throw e;
336
523
  if (!allowRetry || retryChain.length === 0 || !isRetryableEngineError(e)) throw e;
337
524
  const nextModel = retryChain.shift();
338
525
  await emitProgress(onEvent, {
@@ -350,6 +537,12 @@ export async function runAgent({
350
537
  };
351
538
 
352
539
  for (let iter = 0; iter < maxIters; iter++) {
540
+ if (signal?.aborted) {
541
+ const err = new Error("aborted");
542
+ err.name = "AbortError";
543
+ throw err;
544
+ }
545
+ itersUsed = iter + 1;
353
546
  // Merge any tools activated via discover_tools on the previous iteration.
354
547
  drainPendingTools();
355
548
  // Final iteration of a non-contract turn: the model is out of action steps.
@@ -375,9 +568,14 @@ export async function runAgent({
375
568
  effectiveSchemas.length > 0 &&
376
569
  (useContract ||
377
570
  (ackOnlyStreak > 0 && ackOnlyStreak <= MAX_CONSECUTIVE_ACKS));
571
+ // Built per iteration, not per turn: the fallback router can rotate
572
+ // providers mid-turn, and a line that named the model we STARTED on would
573
+ // be exactly the kind of stale fact this block exists to kill.
574
+ const runtime = buildRuntimeBlock(activeModel);
575
+ const systemForCall = runtime ? `${system}\n\n${runtime}` : system;
378
576
  const baseSystem = usePseudoTools
379
- ? pseudoToolSystem(system, effectiveSchemas)
380
- : system;
577
+ ? pseudoToolSystem(systemForCall, effectiveSchemas)
578
+ : systemForCall;
381
579
  let result;
382
580
  try {
383
581
  result = await tryCallEngine({
@@ -400,6 +598,7 @@ export async function runAgent({
400
598
  maxTokens,
401
599
  signal,
402
600
  onToken: ((!forceTool || isFinalWrapUp) && onToken) ? onToken : null,
601
+ onReasoningToken: ((!forceTool || isFinalWrapUp) && onReasoningToken) ? onReasoningToken : null,
403
602
  });
404
603
  } catch (e) {
405
604
  if (usePseudoTools && /^ollama:/i.test(String(activeModel || "")) && /ollama\s+500/i.test(String(e?.message || "")) && trace.length > 0) {
@@ -419,11 +618,24 @@ export async function runAgent({
419
618
  maxTokens,
420
619
  signal,
421
620
  onToken: (iter > 0 && onToken) ? onToken : null,
621
+ onReasoningToken: (iter > 0 && onReasoningToken) ? onReasoningToken : null,
422
622
  });
423
623
  }
424
624
 
425
625
  totalUsage.input_tokens += result.usage?.input_tokens || 0;
426
626
  totalUsage.output_tokens += result.usage?.output_tokens || 0;
627
+
628
+ // The model thinking out loud. Adapters keep it out of `text` so no
629
+ // surface can leak it by forgetting to strip; it rides its own event for
630
+ // the ones that want to show it on purpose, and is ignored by the rest.
631
+ if (result.reasoning) {
632
+ await emitProgress(onEvent, {
633
+ type: "assistant_reasoning",
634
+ reasoning: result.reasoning,
635
+ iteration: iter + 1,
636
+ });
637
+ }
638
+
427
639
  lastText = result.text || "";
428
640
 
429
641
  let toolCalls = result.tool_calls || (result.message && result.message.tool_calls) || null;
@@ -455,18 +667,62 @@ export async function runAgent({
455
667
  }
456
668
 
457
669
  if (!toolCalls || toolCalls.length === 0) {
458
- lastText = cleanTextOfPseudoToolCalls(lastText, callableNames) || lastText;
670
+ // Checked BEFORE the cleaner, which is what strips the evidence.
671
+ const rawText = lastText;
672
+ const fabricated =
673
+ effectiveSchemas.length > 0 && looksLikeFabricatedToolLog(rawText);
674
+ // No `|| rawText` fallback. When the cleaner empties a turn, the turn was
675
+ // markup end to end — a fabricated tool log, or the `[omitted: …]`
676
+ // annotation the model copied out of its own history — and putting the
677
+ // markup back is how a reply that was ONLY that annotation went out to
678
+ // Telegram verbatim on 2026-09-02, twice. There is no answer in there to
679
+ // rescue: an empty turn is retried below and then falls to the surface's
680
+ // never-silent floor, which is a wrap-up written for the user.
681
+ lastText = cleanTextOfPseudoToolCalls(rawText, callableNames);
459
682
  // Dud turn (no tools, no text): re-prompt instead of ending empty, and
460
683
  // don't let it cost an iteration of the tool budget. `iter -= 1` cancels
461
684
  // the loop's `iter++`; the emptyRetries cap stops an all-empty model from
462
685
  // looping forever (after which we break and the surface's last-resort
463
686
  // floor sends a non-silent reply).
464
- if (!String(lastText).trim() && emptyRetries < MAX_EMPTY_RETRIES) {
687
+ if (!String(lastText).trim() && !fabricated && emptyRetries < MAX_EMPTY_RETRIES) {
465
688
  emptyRetries += 1;
466
689
  await emitProgress(onEvent, { type: "empty_retry", iteration: iter + 1, attempt: emptyRetries });
467
690
  iter -= 1;
468
691
  continue;
469
692
  }
693
+ // It narrated the tools instead of calling them. Do NOT let this text
694
+ // stand as the answer — it reports work that never happened. The
695
+ // fabricated turn goes into the history so the model can see what it is
696
+ // being corrected about, followed by the correction.
697
+ if (fabricated && fabricationRetries < MAX_FABRICATION_RETRIES) {
698
+ fabricationRetries += 1;
699
+ await emitProgress(onEvent, {
700
+ type: "fabricated_results",
701
+ model: activeModel,
702
+ iteration: iter + 1,
703
+ attempt: fabricationRetries,
704
+ });
705
+ conversation.push({ role: "assistant", content: rawText });
706
+ conversation.push({ role: "user", content: FABRICATED_RESULTS_SIGNAL });
707
+ continue;
708
+ }
709
+ // Cut off at the output cap, not finished. Keep the truncated text in the
710
+ // history (so the model can see what it was doing) and ask for the action.
711
+ if (wasTruncated(result) && lengthContinues < MAX_LENGTH_CONTINUES) {
712
+ lengthContinues += 1;
713
+ await emitProgress(onEvent, {
714
+ type: "truncated_continue",
715
+ iteration: iter + 1,
716
+ attempt: lengthContinues,
717
+ });
718
+ conversation.push({ role: "assistant", content: lastText });
719
+ conversation.push({ role: "user", content: TRUNCATED_SIGNAL });
720
+ continue;
721
+ }
722
+ // The text we just took as the answer IS the wrap-up: it ends by asking
723
+ // the user whether to continue. Tell the caller so nobody answers on
724
+ // their behalf.
725
+ if (isFinalWrapUp) endedAwaitingUser = true;
470
726
  break;
471
727
  }
472
728
 
@@ -485,6 +741,11 @@ export async function runAgent({
485
741
  // next request, avoiding the 400 "missing thought_signature" error.
486
742
  // Other engines return undefined here, so this field is a no-op for them.
487
743
  ...(result._geminiRawParts ? { _geminiRawParts: result._geminiRawParts } : {}),
744
+ // Same idea for the OpenAI-shaped reasoners: DeepSeek's thinking mode
745
+ // rejects a replayed assistant turn that lost its `reasoning_content`.
746
+ // Stored under an underscore so only the adapters that ask for it put it
747
+ // back on the wire (see zen.js modelReplaysReasoning).
748
+ ...(result.reasoning ? { _reasoning: result.reasoning } : {}),
488
749
  });
489
750
 
490
751
  let finishSummary = null;
@@ -521,14 +782,21 @@ export async function runAgent({
521
782
  },
522
783
  iteration: iter + 1,
523
784
  });
524
- // Dedupe identical side-effecting calls within this turn.
785
+ // Dedupe identical side-effecting calls within this turn — and, for the
786
+ // ones a PERSON reads, calls that only differ in how they were worded.
787
+ // A model relaying something re-words it every time it repeats itself,
788
+ // so exact args alone let three "same" messages through (see
789
+ // loop/side-effects.js).
525
790
  const sig = sideEffects.signature(name, args);
526
- if (sideEffects.seen(sig)) {
791
+ const restated = sideEffects.seen(sig) ? null : sideEffects.nearDuplicate(name, args);
792
+ if (sideEffects.seen(sig) || restated) {
527
793
  toolResult = {
528
794
  ok: true,
529
795
  deduped: true,
530
- note: `Ya ejecuté "${name}" con estos mismos argumentos en este turno; no lo repito.`,
531
- previous: sideEffects.previous(sig),
796
+ note: restated
797
+ ? `Ya dije esto mismo con "${name}" en este turno; no lo repito.`
798
+ : `Ya ejecuté "${name}" con estos mismos argumentos en este turno; no lo repito.`,
799
+ previous: restated || sideEffects.previous(sig),
532
800
  };
533
801
  await emitProgress(onEvent, {
534
802
  type: "tool_deduped",
@@ -568,14 +836,35 @@ export async function runAgent({
568
836
  } else {
569
837
  try {
570
838
  const handler = handlers[name];
571
- toolResult = handler ? await handler(args) : { error: `unknown tool: ${name}` };
839
+ if (!handler) toolResult = { error: `unknown tool: ${name}` };
840
+ // Suppression comes first: a suppressed tool was REMOVED from the
841
+ // schema set on purpose, and telling the model it merely wasn't
842
+ // loaded would invite it to activate the tool and send twice.
843
+ else if (toolSession && !suppressed.has(name) && !schemaOnTheWire(name)) {
844
+ toolResult = revealUnloadedTool(toolSession, name);
845
+ }
846
+ else toolResult = await handler(args);
572
847
  } catch (e) {
573
848
  toolResult = { error: e.message };
574
849
  } finally {
575
850
  if (toolHandlerCtx) toolHandlerCtx.securityGateCleared = false;
576
851
  }
577
852
  }
578
- sideEffects.record(sig, summarizeForTrace(toolResult));
853
+ sideEffects.record(sig, summarizeForTrace(toolResult), { name, args });
854
+ }
855
+
856
+ // A tool may return images for the MODEL to see (view_media loads a skill
857
+ // photo so the model can reason about a position). Lift them off the
858
+ // result and onto the tool message as `images`, so the engine sends them
859
+ // as multimodal parts — and keep the base64 OUT of the trace and the JSON
860
+ // content the model re-reads as text (a dumped data URI there is both a
861
+ // token bomb and a worked example of pasting base64 into prose).
862
+ let toolImages = null;
863
+ if (toolResult && typeof toolResult === "object" && Array.isArray(toolResult.images)) {
864
+ const usable = toolResult.images.filter((im) => im && im.data && im.mime);
865
+ if (usable.length) toolImages = usable;
866
+ const { images, ...rest } = toolResult;
867
+ toolResult = { ...rest, ...(toolImages ? { images_attached: toolImages.length } : {}) };
579
868
  }
580
869
 
581
870
  const traceItem = {
@@ -605,6 +894,9 @@ export async function runAgent({
605
894
  tool_call_id: tc.id || `synth_${iter}_${trace.length}`,
606
895
  tool_name: name,
607
896
  content: JSON.stringify(toolResult),
897
+ // Multimodal tool results (view_media): carried beside the JSON so a
898
+ // vision engine renders them as inlineData parts. Text engines drop it.
899
+ ...(toolImages ? { images: toolImages } : {}),
608
900
  });
609
901
 
610
902
  // Capture turn-ending intents (e.g. ask_questions). The loop cannot
@@ -681,5 +973,10 @@ export async function runAgent({
681
973
  trace,
682
974
  model: activeModel,
683
975
  routing,
976
+ // Iterations of `maxIters` consumed. See itersUsed above.
977
+ iterations: itersUsed,
978
+ // True when the turn closed on the reserved wrap-up — it asked the user a
979
+ // question and the next move is theirs. See endedAwaitingUser above.
980
+ endedAwaitingUser,
684
981
  };
685
982
  }