@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
@@ -0,0 +1,283 @@
1
+ // Attachment bytes, both directions.
2
+ //
3
+ // GET /media?path=<abs> stream a stored attachment
4
+ // POST /media/upload?name=… store one the web composer is about to send
5
+ //
6
+ // A photo, voice note or document that arrives over a channel is downloaded to
7
+ // ~/.apx/media and its path recorded on the stored turn (dispatch folds it into
8
+ // the one inbound record). This streams those bytes back so a viewer can show
9
+ // the actual file instead of the text marker the agent was handed — a thread
10
+ // that read "[document received: … saved to /Users/…]" told the user nothing.
11
+ //
12
+ // Sandboxed to that directory, like /voice/tts: the path arrives from a stored
13
+ // record, so it is treated as untrusted input. The content type is derived from
14
+ // the extension only — never from the request — so a stray file cannot be
15
+ // served back as HTML.
16
+ //
17
+ // The web composer writes into the same directory (subdir `web/`) so a file
18
+ // picked, pasted or dropped in the browser reaches the agent by exactly the
19
+ // route a Telegram photo does: bytes on disk, path on the turn.
20
+ import crypto from "node:crypto";
21
+ import express from "express";
22
+ import fs from "node:fs";
23
+ import path from "node:path";
24
+ import { APX_HOME } from "#core/config/index.js";
25
+
26
+ const MIME = {
27
+ ".oga": "audio/ogg", ".ogg": "audio/ogg", ".opus": "audio/ogg",
28
+ // A voice note recorded in the browser is Opus in a WebM container. Without
29
+ // its own extension it would land as ".webm" → video/webm → a <video> element
30
+ // with no picture. `.weba` is the audio-only spelling of the same container.
31
+ ".weba": "audio/webm",
32
+ ".mp3": "audio/mpeg", ".wav": "audio/wav", ".m4a": "audio/mp4", ".aac": "audio/aac",
33
+ ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".png": "image/png", ".gif": "image/gif",
34
+ ".webp": "image/webp", ".heic": "image/heic",
35
+ ".mp4": "video/mp4", ".mov": "video/quicktime", ".webm": "video/webm",
36
+ ".pdf": "application/pdf",
37
+ ".txt": "text/plain", ".md": "text/markdown", ".csv": "text/csv",
38
+ ".json": "application/json", ".log": "text/plain",
39
+ };
40
+
41
+ // What the composer may hand over. The gate is the extension — the request's
42
+ // own content type is never trusted here either — so the set stays small and
43
+ // purposeful: images a multimodal engine can see, audio it can transcribe,
44
+ // documents its file tools can open. Nothing executable, and nothing a browser
45
+ // would run if it ever escaped the Content-Disposition below.
46
+ const UPLOAD_EXT = new Set([
47
+ ".jpg", ".jpeg", ".png", ".gif", ".webp", ".heic",
48
+ ".oga", ".ogg", ".opus", ".weba", ".mp3", ".wav", ".m4a", ".aac",
49
+ ".mp4", ".mov", ".webm",
50
+ ".pdf", ".txt", ".md", ".csv", ".json", ".log",
51
+ ]);
52
+
53
+ // 25 MB. Above this the base64 an image turn carries starts to dominate the
54
+ // model's context window, and the point of the cap is the turn, not the disk.
55
+ const MAX_UPLOAD = 25 * 1024 * 1024;
56
+
57
+ // An extension the allowlist trusts must be backed by the bytes it claims:
58
+ // otherwise ".png" is just a four-character promise, and the file that lands in
59
+ // the media dir is whatever the caller wanted to put there. Only the formats
60
+ // with a stable signature are checked; the rest pass on the extension alone.
61
+ const MAGIC = [
62
+ { ext: [".jpg", ".jpeg"], at: 0, bytes: [0xff, 0xd8, 0xff] },
63
+ { ext: [".png"], at: 0, bytes: [0x89, 0x50, 0x4e, 0x47] },
64
+ { ext: [".gif"], at: 0, bytes: [0x47, 0x49, 0x46, 0x38] },
65
+ { ext: [".webp"], at: 8, bytes: [0x57, 0x45, 0x42, 0x50] },
66
+ { ext: [".pdf"], at: 0, bytes: [0x25, 0x50, 0x44, 0x46] },
67
+ ];
68
+
69
+ function magicMatches(ext, buf) {
70
+ const rule = MAGIC.find((m) => m.ext.includes(ext));
71
+ if (!rule) return true;
72
+ if (buf.length < rule.at + rule.bytes.length) return false;
73
+ return rule.bytes.every((b, i) => buf[rule.at + i] === b);
74
+ }
75
+
76
+ /** The media dir itself. One root for the read and the write side. */
77
+ function mediaRoot() {
78
+ return path.resolve(APX_HOME, "media");
79
+ }
80
+
81
+ /** `photo` | `audio` | `video` | `document`, from the resolved mime. */
82
+ function kindFromMime(mime) {
83
+ if (mime.startsWith("image/")) return "photo";
84
+ if (mime.startsWith("audio/")) return "audio";
85
+ if (mime.startsWith("video/")) return "video";
86
+ return "document";
87
+ }
88
+
89
+ function mimeFor(file) {
90
+ return MIME[path.extname(file).toLowerCase()] || "application/octet-stream";
91
+ }
92
+
93
+ /**
94
+ * A filename fit to store and to show. The caller's name is a display string
95
+ * from an untrusted client: only its basename survives, separators and control
96
+ * characters included, and the stem is capped so a 300-character name cannot
97
+ * push the stored path past the filesystem's limit.
98
+ */
99
+ function safeName(raw) {
100
+ const base = path.basename(String(raw || "")).replace(/[^\w.\- ]+/g, "_").trim();
101
+ const ext = path.extname(base).toLowerCase();
102
+ const stem = base.slice(0, base.length - ext.length).slice(0, 60).trim() || "file";
103
+ return { name: `${stem}${ext}`, ext, stem };
104
+ }
105
+
106
+ // Shown in the browser rather than pushed to Downloads. Everything else gets
107
+ // `attachment`, so an unknown type can never render in the app's own origin.
108
+ const INLINE = /^(audio|image|video)\//;
109
+
110
+ function tryRealpath(p) {
111
+ try {
112
+ return fs.realpathSync(p);
113
+ } catch {
114
+ return null;
115
+ }
116
+ }
117
+
118
+ /** The real path of a file inside ~/.apx/media, or null if it escapes. */
119
+ function resolveInMediaDir(raw) {
120
+ const root = mediaRoot();
121
+ const asked = path.resolve(raw);
122
+ // Lexical guard first, so a path from outside the media dir never reaches the
123
+ // filesystem at all. Both spellings of the root count: what a stored turn
124
+ // carries is the resolved path, and on a home behind a symlink (a macOS temp
125
+ // dir, a relocated ~) the unresolved root would refuse our own file.
126
+ const roots = [root, tryRealpath(root)].filter(Boolean);
127
+ if (!roots.some((r) => asked === r || asked.startsWith(r + path.sep))) return null;
128
+ if (!fs.existsSync(asked)) return null;
129
+ // Symlinks are resolved on both sides: a link inside the media dir must not
130
+ // become a window onto the rest of the disk.
131
+ const real = fs.realpathSync(asked);
132
+ const realRoot = fs.realpathSync(root);
133
+ if (!real.startsWith(realRoot + path.sep)) return null;
134
+ return real;
135
+ }
136
+
137
+ /**
138
+ * The files a turn is being sent with, resolved from the media dir.
139
+ *
140
+ * Returns what each consumer needs: `attachments` for the model (images only —
141
+ * a multimodal engine renders them, the others ignore the field), `markers` for
142
+ * the prompt (so an engine without vision still knows a file arrived and where
143
+ * it is), and `media` shaped as the ledger meta a stored turn carries, so a
144
+ * reopened thread shows the file instead of the marker. Mirrors what the
145
+ * Telegram inbound handlers hand to dispatch.
146
+ *
147
+ * Anything that does not resolve inside the media dir is dropped in silence:
148
+ * the paths come back from a client, and a turn is not the place to explain a
149
+ * path that a caller had no business sending.
150
+ */
151
+ export function readTurnAttachments(raw) {
152
+ const attachments = [];
153
+ const markers = [];
154
+ let media = null;
155
+ for (const item of Array.isArray(raw) ? raw : []) {
156
+ let resolved = null;
157
+ try {
158
+ resolved = resolveInMediaDir(String(item?.path || ""));
159
+ } catch {
160
+ resolved = null;
161
+ }
162
+ if (!resolved) continue;
163
+ const mime = mimeFor(resolved);
164
+ const name = item?.name ? safeName(item.name).name : path.basename(resolved);
165
+ let size = null;
166
+ try {
167
+ size = fs.statSync(resolved).size;
168
+ } catch {
169
+ /* the stat is metadata; a readable file without it still goes through */
170
+ }
171
+ if (mime.startsWith("image/")) {
172
+ try {
173
+ attachments.push({
174
+ kind: "image",
175
+ mime,
176
+ data: fs.readFileSync(resolved).toString("base64"),
177
+ path: resolved,
178
+ });
179
+ } catch {
180
+ /* unreadable: the marker below still names the path */
181
+ }
182
+ markers.push(`[image attached — saved to ${resolved}]`);
183
+ } else {
184
+ markers.push(
185
+ `[file attached: ${name}, ${mime} — saved to ${resolved}. You can open it with your file tools.]`,
186
+ );
187
+ }
188
+ // One attachment per stored turn, as on every other channel: the ledger row
189
+ // holds a single file, so a turn sent with several records the first.
190
+ media ??= {
191
+ media_kind: kindFromMime(mime),
192
+ local_path: resolved,
193
+ file_name: name,
194
+ mime_type: mime,
195
+ file_size: size,
196
+ };
197
+ }
198
+ return { attachments, markers, media };
199
+ }
200
+
201
+ // Body parser for the upload: raw bytes, not multipart (one file per request,
202
+ // so a boundary parser would earn nothing) and not base64 in JSON (which would
203
+ // inflate every image by a third and fight the 2 MB global JSON limit). Its own
204
+ // errors are answered here as JSON — the default is an HTML error page, which a
205
+ // fetch() caller can only report as gibberish.
206
+ const rawBody = express.raw({ type: "*/*", limit: MAX_UPLOAD });
207
+ function readRawBody(req, res, next) {
208
+ rawBody(req, res, (err) => {
209
+ if (!err) return next();
210
+ if (err.type === "entity.too.large") {
211
+ return res.status(413).json({ error: `file too large (max ${MAX_UPLOAD / (1024 * 1024)} MB)` });
212
+ }
213
+ res.status(400).json({ error: err.message || "upload failed" });
214
+ });
215
+ }
216
+
217
+ export function register(api) {
218
+ // Store one attachment the composer is about to send. The name rides in the
219
+ // query because the body is the file itself; the extension it carries is the
220
+ // only thing that decides what this is willing to keep.
221
+ api.post("/media/upload", readRawBody, (req, res) => {
222
+ const buf = Buffer.isBuffer(req.body) ? req.body : Buffer.alloc(0);
223
+ if (!buf.length) return res.status(400).json({ error: "empty upload" });
224
+
225
+ const { name, ext, stem } = safeName(req.query.name);
226
+ if (!ext || !UPLOAD_EXT.has(ext)) {
227
+ return res.status(415).json({ error: `file type not allowed: ${ext || "no extension"}` });
228
+ }
229
+ if (!magicMatches(ext, buf)) {
230
+ return res.status(415).json({ error: `the file's bytes do not match ${ext}` });
231
+ }
232
+
233
+ try {
234
+ const dir = path.join(mediaRoot(), "web");
235
+ fs.mkdirSync(dir, { recursive: true });
236
+ // The stored name is ours, never the caller's: a random stem keeps two
237
+ // uploads of "image.png" apart and keeps a guessed path from reaching a
238
+ // file that is not the guesser's.
239
+ const stored = path.join(dir, `${crypto.randomUUID().slice(0, 8)}-${stem}${ext}`);
240
+ fs.writeFileSync(stored, buf);
241
+ // The real path is the canonical one: it is what the turn side resolves
242
+ // to (symlinks and all), so client and daemon name the same file the same
243
+ // way — on a macOS temp dir, /var and /private/var are the same file and
244
+ // two different strings.
245
+ const real = fs.realpathSync(stored);
246
+ const mime = mimeFor(real);
247
+ res.status(201).json({
248
+ path: real,
249
+ name,
250
+ mime,
251
+ kind: kindFromMime(mime),
252
+ size: buf.length,
253
+ });
254
+ } catch (e) {
255
+ res.status(500).json({ error: e?.message || "upload failed" });
256
+ }
257
+ });
258
+
259
+ api.get("/media", (req, res) => {
260
+ const raw = String(req.query.path || "");
261
+ if (!raw) return res.status(400).json({ error: "path required" });
262
+ let resolved;
263
+ try {
264
+ resolved = resolveInMediaDir(raw);
265
+ } catch (e) {
266
+ return res.status(500).json({ error: e?.message || "media read failed" });
267
+ }
268
+ if (!resolved) return res.status(404).json({ error: "not found" });
269
+
270
+ const mime = MIME[path.extname(resolved).toLowerCase()] || "application/octet-stream";
271
+ const name = path.basename(resolved).replace(/["\\]/g, "");
272
+ res.setHeader("Content-Type", mime);
273
+ res.setHeader("X-Content-Type-Options", "nosniff");
274
+ res.setHeader(
275
+ "Content-Disposition",
276
+ `${INLINE.test(mime) ? "inline" : "attachment"}; filename="${name}"`,
277
+ );
278
+ res.setHeader("Cache-Control", "private, max-age=300");
279
+ const stream = fs.createReadStream(resolved);
280
+ stream.on("error", () => res.destroy());
281
+ stream.pipe(res);
282
+ });
283
+ }
@@ -0,0 +1,177 @@
1
+ import {
2
+ acceptMobilityEvent,
3
+ dispatchMobilityEvent,
4
+ dispatchMobilityPosition,
5
+ } from "#core/mobility/trip-event.js";
6
+ import { acceptMobilityPosition, lockTripErrand, tripErrand, tripPlaces } from "#core/mobility/geofence.js";
7
+ import { answerMobilityAlert, MOBILITY_ANSWERS } from "#core/mobility/answer.js";
8
+ import { doneTask } from "#core/stores/tasks.js";
9
+ import { resolveLang } from "#core/i18n/index.js";
10
+ import {
11
+ listMobilityAlerts,
12
+ mobilityContext,
13
+ recordMobilityAlert,
14
+ updateMobilityAlert,
15
+ } from "#core/mobility/state.js";
16
+
17
+ /**
18
+ * Close the task a proximity alert was about.
19
+ *
20
+ * The daemon's half of core/mobility/answer.js: the alert records WHICH project
21
+ * the task came from, so this resolves that one project rather than searching
22
+ * every registered one — two projects can hold tasks whose short ids share a
23
+ * prefix. Mirrors closeAlertTask() in the Telegram adapter, which does the same
24
+ * thing with the plugin's own registry handle.
25
+ */
26
+ function closeAlertTaskFor(ctx, alert) {
27
+ if (!alert?.task_id) return false;
28
+ let storagePath = null;
29
+ try {
30
+ storagePath = ctx.projects?.get(alert.project_id)?.storagePath || null;
31
+ } catch {
32
+ storagePath = null;
33
+ }
34
+ if (!storagePath) return false;
35
+ try {
36
+ return Boolean(doneTask(storagePath, alert.task_id, "mobility"));
37
+ } catch (error) {
38
+ console.warn(`[mobility] could not close task ${alert.task_id}: ${error.message}`);
39
+ return false;
40
+ }
41
+ }
42
+
43
+ export function register(api, ctx) {
44
+ // What the phone's trip banner shows when it is tapped: the current trip and
45
+ // the errands APX is watching for on it. Read-only and deliberately separate
46
+ // from the alert path — a driver looking at the list is not being
47
+ // interrupted by it, so nothing here fires a reminder or spends a one-shot.
48
+ api.get("/mobility/trip", (req, res) => {
49
+ const state = mobilityContext();
50
+ const trip = state.trip?.active ? state.trip : null;
51
+ res.json({
52
+ trip,
53
+ places: trip ? tripPlaces(trip.trip_id) : [],
54
+ });
55
+ });
56
+
57
+ // A button on the NATIVE card — the phone's notification, or the head unit.
58
+ //
59
+ // This is the other half of the alert no longer depending on Telegram: the
60
+ // card is pushed over the events socket (core/events/bus.js) and answered
61
+ // here, so a install with no Telegram plugin has a complete round trip.
62
+ // `navigate` and `add_stop` land on the same branch as "voy": the driver did
63
+ // not promise to go, they started going.
64
+ api.post("/mobility/alerts/:id/answer", (req, res) => {
65
+ const action = String(req.body?.action || "").trim();
66
+ if (!MOBILITY_ANSWERS.includes(action)) {
67
+ return res.status(400).json({ error: `action must be one of ${MOBILITY_ANSWERS.join(", ")}` });
68
+ }
69
+ const result = answerMobilityAlert(req.params.id, action, {
70
+ lang: resolveLang(ctx.config),
71
+ // Closing the task needs the project registry, which core does not have.
72
+ closeTask: (alert) => closeAlertTaskFor(ctx, alert),
73
+ });
74
+ if (!result.ok) {
75
+ return res.status(result.reason === "unknown-alert" ? 404 : 400).json({ error: result.reason });
76
+ }
77
+ res.json({ ok: true, ack: result.ack, alert: result.alert });
78
+ });
79
+
80
+ // "Voy" / "Hoy no" pressed on the phone's own list instead of on the
81
+ // Telegram card. Same answer, same record, same follow-up after the trip —
82
+ // the surface is different, the promise is not. Answering here also burns
83
+ // the one-shot, which is the point: the owner has already decided, so the
84
+ // proximity alert for that errand has nothing left to ask.
85
+ api.post("/mobility/errands/answer", (req, res) => {
86
+ const state = mobilityContext();
87
+ const trip = state.trip?.active ? state.trip : null;
88
+ if (!trip) return res.status(409).json({ error: "no trip is running" });
89
+ const taskId = String(req.body?.task_id || "").trim();
90
+ const answer = String(req.body?.answer || "").trim();
91
+ if (!taskId) return res.status(400).json({ error: "task_id required" });
92
+ // "next" is the phone's half of «avisar en la siguiente»: same three
93
+ // answers the Telegram card offers, because the docs promise the two
94
+ // surfaces ask the same question. "skip" stays for the older APK.
95
+ // "done" is the car's "ya la hice": the owner read the list on the head
96
+ // unit and is closing the errand from there, without waiting to be asked.
97
+ // It closes the TASK, which is the one answer here that does.
98
+ if (!["go", "next", "skip", "done"].includes(answer)) {
99
+ return res.status(400).json({ error: "answer must be go, next, skip or done" });
100
+ }
101
+
102
+ const existing = listMobilityAlerts()
103
+ .filter((alert) => alert.trip_id === trip.trip_id && alert.task_id === taskId)
104
+ .pop();
105
+ // No alert yet means the owner is answering BEFORE being asked — reading
106
+ // the list and deciding on their own. That still deserves a record, or the
107
+ // decision would be forgotten the moment the reminder fired anyway.
108
+ const alert = existing || (() => {
109
+ const errand = tripErrand(trip.trip_id, taskId);
110
+ return errand ? recordMobilityAlert(errand) : null;
111
+ })();
112
+ if (!alert) return res.status(404).json({ error: "that errand is not on this trip" });
113
+
114
+ const answered_at = new Date().toISOString();
115
+ // "next" declines the PLACE: the errand stops counting as announced, so a
116
+ // different shop can raise it again later in the drive, and the outcome
117
+ // closes it for the after-trip follow-up — nothing was promised here.
118
+ // "skip" is still the whole errand, for the rest of the trip.
119
+ if (answer === "done") {
120
+ // Through core, like every other answer, so closing from the car records
121
+ // the same outcome as closing from the after-trip follow-up.
122
+ const result = answerMobilityAlert(alert.id, "done", {
123
+ lang: resolveLang(ctx.config),
124
+ closeTask: (target) => closeAlertTaskFor(ctx, target),
125
+ });
126
+ return res.json({ ok: true, alert_id: alert.id, answer, ack: result.ack });
127
+ }
128
+
129
+ const patch = {
130
+ go: { answer: "go", answered_at },
131
+ next: { answer: "next", answered_at, outcome: "skipped_place" },
132
+ skip: { answer: "skip", answered_at, outcome: "skipped" },
133
+ }[answer];
134
+ updateMobilityAlert(alert.id, patch);
135
+ if (answer === "go") lockTripErrand(trip.trip_id, taskId, "accepted");
136
+ res.json({ ok: true, alert_id: alert.id, answer });
137
+ });
138
+
139
+ api.post("/mobility/events", (req, res) => {
140
+ let event;
141
+ try {
142
+ event = acceptMobilityEvent(req.body);
143
+ } catch (error) {
144
+ return res.status(400).json({ error: error.message });
145
+ }
146
+ if (event.duplicate) return res.json({ accepted: true, duplicate: true, event_id: event.event_id });
147
+ res.status(202).json({ accepted: true, event_id: event.event_id });
148
+ const dispatch = ctx.mobilityDispatch || dispatchMobilityEvent;
149
+ setImmediate(() => {
150
+ Promise.resolve(dispatch(event, ctx)).catch((error) => {
151
+ console.error(`[mobility] ${error?.message || error}`);
152
+ });
153
+ });
154
+ });
155
+
156
+ // The live GPS stream while a trip is running. Answered before any work for
157
+ // the same reason the event route is: the phone is on mobile data in a
158
+ // moving car, and a request it has to hold open until a place search and a
159
+ // Telegram send have finished is a request that times out. Proximity is
160
+ // evaluated after the ack, and the one-alert-per-place guarantee lives in
161
+ // core/mobility/geofence.js, not here.
162
+ api.post("/mobility/positions", (req, res) => {
163
+ let position;
164
+ try {
165
+ position = acceptMobilityPosition(req.body);
166
+ } catch (error) {
167
+ return res.status(400).json({ error: error.message });
168
+ }
169
+ res.status(202).json({ accepted: true, trip_id: position.trip_id });
170
+ const dispatch = ctx.mobilityPositionDispatch || dispatchMobilityPosition;
171
+ setImmediate(() => {
172
+ Promise.resolve(dispatch(position, ctx)).catch((error) => {
173
+ console.error(`[mobility] ${error?.message || error}`);
174
+ });
175
+ });
176
+ });
177
+ }
@@ -0,0 +1,182 @@
1
+ // GET /net/endpoints every URL this daemon can be reached at
2
+ // POST /net/tailscale/serve publish it over HTTPS on the tailnet
3
+ // DELETE /net/tailscale/serve stop publishing
4
+ //
5
+ // One daemon, several addresses: loopback for the toolchain, a LAN address for
6
+ // a phone on the same Wi-Fi, and — when Tailscale is up — a name that keeps
7
+ // working from a train. The panel needs the whole list for two reasons: to show
8
+ // you which URL to install the app from, and so a client whose address stopped
9
+ // answering can try the others instead of just failing.
10
+ //
11
+ // The HTTPS one is not interchangeable with the others. Service workers (the
12
+ // installable app), getUserMedia (voice notes) and the clipboard are all gated
13
+ // on a secure context, so over http://192.168.x.x the browser switches them
14
+ // off. `tailscale serve` is the only way to a real certificate here that does
15
+ // not involve buying a domain.
16
+ import os from "node:os";
17
+ import { effectivePort, effectiveHost, readConfig } from "#core/config/index.js";
18
+ import { detectLanAddresses, isLoopback, isWildcard } from "#core/net/lan.js";
19
+ import {
20
+ disableTailscaleServe,
21
+ enableTailscaleServe,
22
+ tailscaleServe,
23
+ tailscaleStatus,
24
+ } from "#core/net/tailscale.js";
25
+ import { asyncRoute } from "./shared.js";
26
+
27
+ /** A Tailscale address, so a LAN scan does not report the tailnet as Wi-Fi. */
28
+ function isTailscaleAddress(address, iface) {
29
+ if (/^utun|^tailscale/i.test(String(iface || ""))) return true;
30
+ const p = String(address || "").split(".").map(Number);
31
+ // 100.64.0.0/10 — the CGNAT range Tailscale hands out.
32
+ return p[0] === 100 && p[1] >= 64 && p[1] <= 127;
33
+ }
34
+
35
+ /**
36
+ * Every base URL that reaches this daemon, best first.
37
+ *
38
+ * "Best" is ordered by how long it keeps working: the HTTPS tailnet name (from
39
+ * anywhere, and a secure context), then the tailnet IP, then the LAN, then
40
+ * loopback, which only ever helps the machine it runs on.
41
+ */
42
+ export async function reachableEndpoints({ port, host }) {
43
+ const out = [];
44
+ const ts = await tailscaleStatus();
45
+ const serve = ts.running ? await tailscaleServe(port) : { serving: false, url: null };
46
+
47
+ if (serve.serving && serve.url) {
48
+ out.push({ url: serve.url, kind: "tailscale-https", label: ts.dnsName || "tailscale", secure: true });
49
+ }
50
+ if (ts.running && ts.ipv4) {
51
+ out.push({ url: `http://${ts.ipv4}:${port}`, kind: "tailscale", label: ts.hostname || "tailscale", secure: false });
52
+ }
53
+
54
+ // A daemon bound to one specific address is only reachable there; a wildcard
55
+ // (or loopback-only, which still runs the LAN listener when shared) is
56
+ // reachable at every interface it holds.
57
+ const bound = host && !isLoopback(host) && !isWildcard(host) ? host : null;
58
+ if (bound && !out.some((e) => e.url.includes(bound))) {
59
+ out.push({ url: `http://${bound}:${port}`, kind: "lan", label: "LAN", secure: false });
60
+ }
61
+ if (!bound) {
62
+ for (const a of detectLanAddresses()) {
63
+ if (isTailscaleAddress(a.address, a.iface)) continue;
64
+ out.push({ url: `http://${a.address}:${port}`, kind: "lan", label: a.iface, secure: false });
65
+ }
66
+ }
67
+
68
+ out.push({ url: `http://127.0.0.1:${port}`, kind: "loopback", label: os.hostname(), secure: true });
69
+ // Deduplicate while keeping the first (best) spelling of each URL.
70
+ const seen = new Set();
71
+ return out.filter((e) => (seen.has(e.url) ? false : seen.add(e.url)));
72
+ }
73
+
74
+ /** Hostnames that are this daemon under another name — the CORS allowlist. */
75
+ export async function selfOrigins({ port, host }) {
76
+ const eps = await reachableEndpoints({ port, host });
77
+ return new Set(eps.map((e) => new URL(e.url).host));
78
+ }
79
+
80
+ // ── CORS, narrowly: this daemon under one of its OWN other names ───────────
81
+ //
82
+ // The panel installed from one address (say the LAN) can end up talking to
83
+ // another (the tailnet) when the first stops answering. That is a cross-origin
84
+ // request to the same process, and it needs a header to be allowed — but only
85
+ // for hosts that ARE us. Anything else, including a page on the same port at
86
+ // some other hostname, gets nothing.
87
+ //
88
+ // The allowlist is cached: building it asks Tailscale, which is far too slow to
89
+ // do per request. Loopback and the LAN are known synchronously and seeded
90
+ // immediately, so the first request is answered correctly even before the
91
+ // asynchronous half lands.
92
+ const CORS_TTL_MS = 30_000;
93
+ let allowCache = { at: 0, hosts: null, refreshing: false };
94
+
95
+ function knownHostsSync({ port, host }) {
96
+ const hosts = new Set([`127.0.0.1:${port}`, `localhost:${port}`]);
97
+ if (host && !isLoopback(host) && !isWildcard(host)) hosts.add(`${host}:${port}`);
98
+ for (const a of detectLanAddresses()) hosts.add(`${a.address}:${port}`);
99
+ return hosts;
100
+ }
101
+
102
+ function refreshAllowed(ctx) {
103
+ if (allowCache.refreshing) return;
104
+ allowCache.refreshing = true;
105
+ selfOrigins(ctx)
106
+ .then((hosts) => {
107
+ allowCache = { at: Date.now(), hosts, refreshing: false };
108
+ })
109
+ .catch(() => {
110
+ allowCache = { ...allowCache, at: Date.now(), refreshing: false };
111
+ });
112
+ }
113
+
114
+ /** Express middleware allowing cross-origin calls between our own addresses. */
115
+ export function corsBetweenOwnAddresses({ port, host }) {
116
+ const ctx = { port, host };
117
+ return function cors(req, res, next) {
118
+ const origin = req.headers.origin;
119
+ if (!origin) return next();
120
+ if (!allowCache.hosts || Date.now() - allowCache.at > CORS_TTL_MS) {
121
+ if (!allowCache.hosts) allowCache = { ...allowCache, hosts: knownHostsSync(ctx) };
122
+ refreshAllowed(ctx);
123
+ }
124
+ let originHost = "";
125
+ try {
126
+ originHost = new URL(origin).host;
127
+ } catch {
128
+ return next();
129
+ }
130
+ if (!allowCache.hosts.has(originHost)) return next();
131
+ res.setHeader("access-control-allow-origin", origin);
132
+ res.setHeader("vary", "Origin");
133
+ res.setHeader("access-control-allow-credentials", "true");
134
+ if (req.method === "OPTIONS") {
135
+ res.setHeader("access-control-allow-methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS");
136
+ res.setHeader(
137
+ "access-control-allow-headers",
138
+ req.headers["access-control-request-headers"] || "authorization,content-type",
139
+ );
140
+ res.setHeader("access-control-max-age", "600");
141
+ return res.status(204).end();
142
+ }
143
+ return next();
144
+ };
145
+ }
146
+
147
+ export function register(api) {
148
+ api.get("/net/endpoints", asyncRoute(async (_req, res) => {
149
+ const cfg = readConfig();
150
+ const port = effectivePort(cfg);
151
+ const host = effectiveHost(cfg);
152
+ const ts = await tailscaleStatus();
153
+ const serve = ts.installed ? await tailscaleServe(port) : { serving: false, url: null, error: null };
154
+ res.json({
155
+ port,
156
+ bind: host,
157
+ shared: !isLoopback(host),
158
+ endpoints: await reachableEndpoints({ port, host }),
159
+ tailscale: { ...ts, serving: serve.serving, serve_url: serve.url, serve_error: serve.error },
160
+ });
161
+ }));
162
+
163
+ api.post("/net/tailscale/serve", asyncRoute(async (_req, res) => {
164
+ const port = effectivePort(readConfig());
165
+ try {
166
+ const out = await enableTailscaleServe(port);
167
+ res.json({ ok: true, ...out });
168
+ } catch (e) {
169
+ res.status(400).json({ ok: false, error: e.message });
170
+ }
171
+ }));
172
+
173
+ api.delete("/net/tailscale/serve", asyncRoute(async (_req, res) => {
174
+ const port = effectivePort(readConfig());
175
+ try {
176
+ const out = await disableTailscaleServe(port);
177
+ res.json({ ok: true, ...out });
178
+ } catch (e) {
179
+ res.status(400).json({ ok: false, error: e.message });
180
+ }
181
+ }));
182
+ }