@agentprojectcontext/apx 1.80.2 → 1.89.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (838) hide show
  1. package/package.json +9 -8
  2. package/skills/apc-context/SKILL.md +14 -7
  3. package/skills/apx/SKILL.md +62 -2
  4. package/skills/apx-mcp/SKILL.md +42 -15
  5. package/src/core/agent/a2a/peers.js +161 -0
  6. package/src/core/agent/a2a/reply.js +379 -27
  7. package/src/core/agent/abort.js +25 -0
  8. package/src/core/agent/agent-model.js +58 -0
  9. package/src/core/agent/agent-tools.js +101 -0
  10. package/src/core/agent/author-line.js +110 -0
  11. package/src/core/agent/build-agent-system.js +52 -18
  12. package/src/core/agent/channels/voice-context.js +17 -2
  13. package/src/core/agent/closing-floor.js +117 -0
  14. package/src/core/agent/constants.js +119 -11
  15. package/src/core/agent/engine-call.js +56 -0
  16. package/src/core/agent/group/run-group-turn.js +218 -0
  17. package/src/core/agent/group/turn-resolver.js +165 -0
  18. package/src/core/agent/judge.js +138 -11
  19. package/src/core/agent/loop/side-effects.js +106 -3
  20. package/src/core/agent/memory.js +31 -18
  21. package/src/core/agent/model-capabilities.js +47 -0
  22. package/src/core/agent/model-router.js +11 -40
  23. package/src/core/agent/prompt-builder.js +47 -0
  24. package/src/core/agent/prompts/channels/a2a.md +12 -0
  25. package/src/core/agent/prompts/channels/code.md +3 -1
  26. package/src/core/agent/prompts/channels/telegram.md +2 -0
  27. package/src/core/agent/prompts/channels/web_code.md +1 -1
  28. package/src/core/agent/prompts/channels/whatsapp.md +37 -0
  29. package/src/core/agent/prompts/core/agent-base.md +2 -0
  30. package/src/core/agent/prompts/discipline/action.md +1 -0
  31. package/src/core/agent/prompts/discipline/two-segment.md +2 -1
  32. package/src/core/agent/prompts/modes/mobility.md +36 -0
  33. package/src/core/agent/retry.js +42 -0
  34. package/src/core/agent/run-agent.js +322 -25
  35. package/src/core/agent/run-turn.js +253 -0
  36. package/src/core/agent/self-memory.js +18 -52
  37. package/src/core/agent/skills/agent-skills.js +118 -0
  38. package/src/core/agent/skills/catalog.js +1 -1
  39. package/src/core/agent/skills/declared.js +13 -0
  40. package/src/core/agent/skills/index-store.js +8 -1
  41. package/src/core/agent/skills/inspector.js +20 -3
  42. package/src/core/agent/skills/loader.js +18 -3
  43. package/src/core/agent/skills/media.js +121 -0
  44. package/src/core/agent/skills/origin.js +61 -0
  45. package/src/core/agent/skills/sections.js +136 -0
  46. package/src/core/agent/super-agent.js +54 -14
  47. package/src/core/agent/tools/handlers/_calendar.js +50 -0
  48. package/src/core/agent/tools/handlers/add-mcp.js +103 -0
  49. package/src/core/agent/tools/handlers/asana-create-task.js +3 -1
  50. package/src/core/agent/tools/handlers/attach-media.js +56 -0
  51. package/src/core/agent/tools/handlers/calendar-create-event.js +38 -0
  52. package/src/core/agent/tools/handlers/calendar-find-slot.js +39 -0
  53. package/src/core/agent/tools/handlers/calendar-list-events.js +39 -0
  54. package/src/core/agent/tools/handlers/calendar-update-event.js +35 -0
  55. package/src/core/agent/tools/handlers/call-agent.js +14 -6
  56. package/src/core/agent/tools/handlers/call-runtime.js +27 -49
  57. package/src/core/agent/tools/handlers/comment-task.js +78 -0
  58. package/src/core/agent/tools/handlers/complete-task.js +60 -0
  59. package/src/core/agent/tools/handlers/configure-agent.js +70 -0
  60. package/src/core/agent/tools/handlers/create-agent.js +68 -0
  61. package/src/core/agent/tools/handlers/create-task.js +41 -3
  62. package/src/core/agent/tools/handlers/import-agent.js +5 -5
  63. package/src/core/agent/tools/handlers/list-mcp-tools.js +83 -0
  64. package/src/core/agent/tools/handlers/list-routines.js +46 -0
  65. package/src/core/agent/tools/handlers/list-tasks.js +8 -1
  66. package/src/core/agent/tools/handlers/mark-commitment.js +57 -0
  67. package/src/core/agent/tools/handlers/read-skill.js +64 -0
  68. package/src/core/agent/tools/handlers/remember-routine.js +2 -8
  69. package/src/core/agent/tools/handlers/remember.js +82 -6
  70. package/src/core/agent/tools/handlers/remove-agent.js +42 -0
  71. package/src/core/agent/tools/handlers/run-routine.js +94 -0
  72. package/src/core/agent/tools/handlers/run-shell.js +44 -2
  73. package/src/core/agent/tools/handlers/send-telegram.js +41 -19
  74. package/src/core/agent/tools/handlers/set-agent-prompt.js +44 -0
  75. package/src/core/agent/tools/handlers/view-media.js +69 -0
  76. package/src/core/agent/tools/handlers/write-agent-memory.js +52 -0
  77. package/src/core/agent/tools/helpers.js +36 -4
  78. package/src/core/agent/tools/names.js +64 -0
  79. package/src/core/agent/tools/registry-bridge.js +24 -1
  80. package/src/core/agent/tools/registry.js +79 -0
  81. package/src/core/agent/tools/tool-call-parser.js +136 -13
  82. package/src/core/agent/vision-bridge.js +69 -0
  83. package/src/core/apc/agent-identity.js +74 -0
  84. package/src/core/apc/agent-rename-refs.js +187 -0
  85. package/src/core/apc/agent-write.js +312 -0
  86. package/src/core/apc/agents-vault.js +1 -1
  87. package/src/core/apc/blob-keys.js +42 -0
  88. package/src/core/apc/frontmatter.js +166 -10
  89. package/src/core/apc/parser.js +33 -83
  90. package/src/core/apc/paths.js +4 -6
  91. package/src/core/apc/project-memory.js +44 -0
  92. package/src/core/apc/projects-helpers.js +33 -0
  93. package/src/core/apc/scaffold.js +36 -16
  94. package/src/core/channels/telegram/ask-callbacks.js +103 -0
  95. package/src/core/channels/telegram/dispatch.js +154 -30
  96. package/src/core/channels/telegram/helpers.js +59 -7
  97. package/src/core/channels/telegram/inbound/audio.js +26 -30
  98. package/src/core/channels/telegram/inbound/file.js +22 -32
  99. package/src/core/channels/telegram/inbound/photo.js +24 -28
  100. package/src/core/channels/telegram/progress-gate.js +76 -0
  101. package/src/core/channels/telegram/reply.js +151 -43
  102. package/src/core/channels/telegram/voice-note.js +150 -0
  103. package/src/core/config/index.js +53 -54
  104. package/src/core/config/paths.js +117 -31
  105. package/src/core/config/redact.js +89 -4
  106. package/src/core/config/secret-values.js +2 -4
  107. package/src/core/constants/actors.js +5 -0
  108. package/src/core/constants/channels.js +3 -1
  109. package/src/core/constants/task-categories.js +73 -0
  110. package/src/core/constants/task-fields.js +28 -0
  111. package/src/core/engines/_globs.js +33 -0
  112. package/src/core/engines/anthropic.js +19 -1
  113. package/src/core/engines/catalog.js +20 -0
  114. package/src/core/engines/gemini.js +84 -16
  115. package/src/core/engines/index.js +19 -3
  116. package/src/core/engines/mock.js +145 -1
  117. package/src/core/engines/openai-compatible.js +150 -15
  118. package/src/core/engines/presets.js +36 -9
  119. package/src/core/engines/zen.js +131 -0
  120. package/src/core/events/bus.js +140 -0
  121. package/src/core/events/mascot-notify.js +156 -0
  122. package/src/core/http-tools/browser.js +366 -36
  123. package/src/core/http-tools/catalog.js +206 -22
  124. package/src/core/http-tools/grep.js +62 -7
  125. package/src/core/http-tools/registry.js +3 -1
  126. package/src/core/i18n/en.js +34 -3
  127. package/src/core/i18n/es.js +36 -4
  128. package/src/core/i18n/index.js +5 -5
  129. package/src/core/i18n/pt.js +32 -3
  130. package/src/core/identity/self.js +14 -1
  131. package/src/core/images/engines/a1111.js +219 -0
  132. package/src/core/images/engines/index.js +187 -0
  133. package/src/core/images/engines/mock.js +74 -0
  134. package/src/core/images/engines/openai.js +130 -0
  135. package/src/core/images/engines/sdcpp.js +204 -0
  136. package/src/core/images/engines/shared.js +218 -0
  137. package/src/core/images/generate.js +260 -0
  138. package/src/core/integrations/catalog.js +8 -3
  139. package/src/core/integrations/plugins/_google-oauth.js +179 -0
  140. package/src/core/integrations/plugins/asana.js +7 -3
  141. package/src/core/integrations/plugins/calendar.js +404 -0
  142. package/src/core/integrations/store.js +2 -0
  143. package/src/core/memory/broker.js +16 -1
  144. package/src/core/memory/compactor.js +19 -3
  145. package/src/core/memory/consolidate.js +2 -2
  146. package/src/core/memory/dated-log.js +66 -0
  147. package/src/core/memory/embed-engines/custom.js +59 -0
  148. package/src/core/memory/embed-engines/gemini.js +9 -3
  149. package/src/core/memory/embed-engines/index.js +87 -15
  150. package/src/core/memory/embeddings.js +52 -24
  151. package/src/core/memory/index.js +13 -3
  152. package/src/core/memory/indexer.js +83 -27
  153. package/src/core/memory/prune.js +182 -0
  154. package/src/core/memory/store.js +22 -0
  155. package/src/core/memory/summarizer.js +72 -8
  156. package/src/core/mobility/answer.js +80 -0
  157. package/src/core/mobility/geofence.js +667 -0
  158. package/src/core/mobility/osm-route.js +282 -0
  159. package/src/core/mobility/preferences.js +91 -0
  160. package/src/core/mobility/state.js +245 -0
  161. package/src/core/mobility/trip-event.js +384 -0
  162. package/src/core/net/tailscale.js +168 -0
  163. package/src/core/profiles/bundled/secretary/PROFILE.md +32 -42
  164. package/src/core/profiles/bundled/secretary/config.schema.es.json +68 -0
  165. package/src/core/profiles/bundled/secretary/config.schema.json +30 -2
  166. package/src/core/profiles/bundled/secretary/profile.json +3 -1
  167. package/src/core/profiles/bundled/secretary/routines/a2a-sweep.json +20 -0
  168. package/src/core/profiles/bundled/secretary/routines/day-close.json +4 -1
  169. package/src/core/profiles/bundled/secretary/routines/day-open.json +7 -1
  170. package/src/core/profiles/bundled/secretary/routines/watch.json +13 -2
  171. package/src/core/profiles/lifecycle.js +243 -19
  172. package/src/core/profiles/paths.js +12 -0
  173. package/src/core/profiles/store.js +47 -0
  174. package/src/core/routines/active-runs.js +213 -0
  175. package/src/core/routines/delivery.js +612 -0
  176. package/src/core/routines/header.js +88 -0
  177. package/src/core/routines/index.js +26 -0
  178. package/src/core/routines/run-log.js +78 -0
  179. package/src/core/routines/runner.js +629 -66
  180. package/src/core/routines/signals.js +75 -0
  181. package/src/core/runtime-skills/apc-context/SKILL.md +14 -7
  182. package/src/core/runtime-skills/apx/SKILL.md +93 -1
  183. package/src/core/runtime-skills/apx-agency-agents/SKILL.md +2 -2
  184. package/src/core/runtime-skills/apx-agent/SKILL.md +134 -19
  185. package/src/core/runtime-skills/apx-commitment/SKILL.md +134 -0
  186. package/src/core/runtime-skills/apx-image/SKILL.md +60 -0
  187. package/src/core/runtime-skills/apx-integrations/SKILL.md +82 -0
  188. package/src/core/runtime-skills/apx-mcp/SKILL.md +45 -14
  189. package/src/core/runtime-skills/apx-mcp-builder/SKILL.md +4 -4
  190. package/src/core/runtime-skills/apx-profile/SKILL.md +27 -2
  191. package/src/core/runtime-skills/apx-project/SKILL.md +23 -2
  192. package/src/core/runtime-skills/apx-routine/SKILL.md +40 -17
  193. package/src/core/runtime-skills/apx-runtime/SKILL.md +124 -14
  194. package/src/core/runtime-skills/apx-sessions/SKILL.md +7 -6
  195. package/src/core/runtime-skills/apx-skill-builder/SKILL.md +15 -13
  196. package/src/core/runtime-skills/apx-task/SKILL.md +79 -26
  197. package/src/core/runtime-skills/apx-telegram/SKILL.md +12 -12
  198. package/src/core/runtime-skills/apx-transcribe/SKILL.md +56 -0
  199. package/src/core/runtime-skills/apx-voice/SKILL.md +27 -8
  200. package/src/core/runtime-skills/claude-code/SKILL.md +8 -0
  201. package/src/core/runtime-skills/openrouter/SKILL.md +8 -2
  202. package/src/core/runtimes/_spawn.js +9 -1
  203. package/src/core/runtimes/claude-code.js +11 -2
  204. package/src/core/runtimes/codex.js +44 -10
  205. package/src/core/runtimes/index.js +17 -2
  206. package/src/core/runtimes/opencode.js +88 -9
  207. package/src/core/runtimes/outcome.js +119 -0
  208. package/src/core/sessions/index.js +193 -1
  209. package/src/core/stores/agent-inbox.js +109 -23
  210. package/src/core/stores/code-sessions.js +21 -0
  211. package/src/core/stores/commitments.js +27 -3
  212. package/src/core/stores/conversations.js +220 -20
  213. package/src/core/stores/deliveries.js +177 -0
  214. package/src/core/stores/media-archive.js +228 -0
  215. package/src/core/stores/messages.js +1096 -112
  216. package/src/core/stores/organization.js +34 -0
  217. package/src/core/stores/project-memory.js +71 -0
  218. package/src/core/stores/routine-memory.js +16 -8
  219. package/src/core/stores/routines.js +27 -49
  220. package/src/core/stores/sessions.js +4 -8
  221. package/src/core/stores/tasks.js +212 -18
  222. package/src/core/stores/turn-record.js +209 -0
  223. package/src/core/tasks/columns.js +136 -0
  224. package/src/core/tasks/comment-turn.js +258 -0
  225. package/src/core/util/time.js +13 -0
  226. package/src/core/voice/engines/index.js +54 -1
  227. package/src/core/voice/engines/openai.js +50 -4
  228. package/src/core/voice/opus.js +67 -0
  229. package/src/core/voice/pronounceable.js +43 -0
  230. package/src/core/voice/transcription.js +4 -0
  231. package/src/core/voice/tts.js +114 -24
  232. package/src/host/daemon/active-turns.js +188 -0
  233. package/src/host/daemon/api/admin-config.js +9 -0
  234. package/src/host/daemon/api/agents.js +99 -90
  235. package/src/host/daemon/api/artifact-preview.js +7 -6
  236. package/src/host/daemon/api/artifacts.js +3 -2
  237. package/src/host/daemon/api/code.js +55 -9
  238. package/src/host/daemon/api/commitments.js +11 -0
  239. package/src/host/daemon/api/confirm.js +3 -2
  240. package/src/host/daemon/api/conversations.js +543 -35
  241. package/src/host/daemon/api/deck.js +9 -8
  242. package/src/host/daemon/api/deliveries.js +33 -0
  243. package/src/host/daemon/api/desktop.js +5 -4
  244. package/src/host/daemon/api/embeddings.js +15 -6
  245. package/src/host/daemon/api/engines.js +59 -8
  246. package/src/host/daemon/api/exec.js +437 -121
  247. package/src/host/daemon/api/groups.js +215 -0
  248. package/src/host/daemon/api/images.js +107 -0
  249. package/src/host/daemon/api/inbox.js +123 -2
  250. package/src/host/daemon/api/integrations.js +64 -2
  251. package/src/host/daemon/api/mcps.js +7 -6
  252. package/src/host/daemon/api/media.js +283 -0
  253. package/src/host/daemon/api/mobility.js +177 -0
  254. package/src/host/daemon/api/net.js +182 -0
  255. package/src/host/daemon/api/pairing.js +38 -5
  256. package/src/host/daemon/api/profiles.js +30 -2
  257. package/src/host/daemon/api/routines.js +36 -4
  258. package/src/host/daemon/api/runtimes.js +74 -39
  259. package/src/host/daemon/api/sessions-search.js +7 -2
  260. package/src/host/daemon/api/sessions.js +44 -2
  261. package/src/host/daemon/api/shared.js +127 -20
  262. package/src/host/daemon/api/skills.js +11 -4
  263. package/src/host/daemon/api/super-agent.js +384 -53
  264. package/src/host/daemon/api/tasks.js +180 -10
  265. package/src/host/daemon/api/telegram.js +11 -10
  266. package/src/host/daemon/api/thread-faces.js +159 -0
  267. package/src/host/daemon/api/top-level.js +3 -3
  268. package/src/host/daemon/api/transcribe.js +11 -10
  269. package/src/host/daemon/api/tts.js +97 -4
  270. package/src/host/daemon/api/turn-abort.js +23 -0
  271. package/src/host/daemon/api/turns.js +49 -0
  272. package/src/host/daemon/api/voice.js +5 -4
  273. package/src/host/daemon/api/web.js +98 -0
  274. package/src/host/daemon/api.js +39 -0
  275. package/src/host/daemon/delivery-sweep.js +58 -0
  276. package/src/host/daemon/desktop-ws.js +5 -23
  277. package/src/host/daemon/events-ws.js +273 -0
  278. package/src/host/daemon/index.js +59 -15
  279. package/src/host/daemon/plugins/desktop/index.js +17 -4
  280. package/src/host/daemon/plugins/telegram/index.js +78 -40
  281. package/src/host/daemon/pty-bridge.py +99 -0
  282. package/src/host/daemon/routines-scheduler.js +1 -0
  283. package/src/host/daemon/smoke.js +1 -2
  284. package/src/host/daemon/terminal-ws.js +132 -0
  285. package/src/host/daemon/tts-keepwarm.js +87 -0
  286. package/src/host/daemon/wakeup.js +11 -6
  287. package/src/host/daemon/whisper-server.js +44 -0
  288. package/src/host/daemon/whisper-server.py +70 -13
  289. package/src/host/daemon/ws-auth.js +28 -0
  290. package/src/interfaces/android/app/build.gradle +42 -0
  291. package/src/interfaces/android/app/proguard-rules.pro +4 -0
  292. package/src/interfaces/android/app/src/main/AndroidManifest.xml +139 -0
  293. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/AlertScreen.java +139 -0
  294. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/AndroidAutoDetector.java +106 -0
  295. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/ApxCarAppService.java +57 -0
  296. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/ApxPreferences.java +165 -0
  297. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/CarAlertStore.java +84 -0
  298. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/CarMessageActionReceiver.java +47 -0
  299. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/CarMessageNotification.java +152 -0
  300. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/DaemonAddress.java +35 -0
  301. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/DaemonClient.java +339 -0
  302. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/DeviceLocation.java +38 -0
  303. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/ErrandScreen.java +127 -0
  304. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MainActivity.java +872 -0
  305. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MapsNavigationDetector.java +63 -0
  306. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MapsNavigationListenerService.java +453 -0
  307. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MapsShareIntentParser.java +93 -0
  308. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MascotBlobCatalog.java +66 -0
  309. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MascotOverlayService.java +448 -0
  310. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MascotView.java +249 -0
  311. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MessageFrameParser.java +240 -0
  312. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/NotifyChannels.java +69 -0
  313. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/ProximityActionReceiver.java +78 -0
  314. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/ProximityNotification.java +161 -0
  315. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/StartupReceiver.java +57 -0
  316. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/TravelEventGate.java +62 -0
  317. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/TravelStatusBanner.java +116 -0
  318. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/TripLocationService.java +272 -0
  319. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/TripPlacesSheet.java +203 -0
  320. package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/TripScreen.java +182 -0
  321. package/src/interfaces/android/app/src/main/res/drawable/app_background.xml +12 -0
  322. package/src/interfaces/android/app/src/main/res/drawable/ic_apx_app.xml +10 -0
  323. package/src/interfaces/android/app/src/main/res/drawable/ic_apx_launcher_foreground.xml +12 -0
  324. package/src/interfaces/android/app/src/main/res/drawable/ic_apx_notification.xml +22 -0
  325. package/src/interfaces/android/app/src/main/res/drawable/ic_trip_car.xml +37 -0
  326. package/src/interfaces/android/app/src/main/res/drawable-nodpi/apx_logo.png +0 -0
  327. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_amatista.png +0 -0
  328. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_aqua.png +0 -0
  329. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_brasa.png +0 -0
  330. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_campana.png +0 -0
  331. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_caracol.png +0 -0
  332. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_carbon.png +0 -0
  333. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_cobalto.png +0 -0
  334. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_cometa.png +0 -0
  335. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_coral.png +0 -0
  336. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_cubi.png +0 -0
  337. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_eclipse.png +0 -0
  338. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_faro.png +0 -0
  339. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_gajo.png +0 -0
  340. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_iris.png +0 -0
  341. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_kiwi.png +0 -0
  342. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_laguna.png +0 -0
  343. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_lila.png +0 -0
  344. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_menta.png +0 -0
  345. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_nimbo.png +0 -0
  346. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_noche.png +0 -0
  347. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_onyx.png +0 -0
  348. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_orbita.png +0 -0
  349. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_ovni.png +0 -0
  350. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_papa.png +0 -0
  351. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_parche.png +0 -0
  352. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_peon.png +0 -0
  353. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_perla.png +0 -0
  354. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_pino.png +0 -0
  355. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_quad.png +0 -0
  356. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_rubi.png +0 -0
  357. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_saturno.png +0 -0
  358. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_trebol.png +0 -0
  359. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_trino.png +0 -0
  360. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_volcan.png +0 -0
  361. package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_zafiro.png +0 -0
  362. package/src/interfaces/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +5 -0
  363. package/src/interfaces/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +5 -0
  364. package/src/interfaces/android/app/src/main/res/raw/apx_notification.mp3 +0 -0
  365. package/src/interfaces/android/app/src/main/res/values/colors.xml +4 -0
  366. package/src/interfaces/android/app/src/main/res/values/strings.xml +25 -0
  367. package/src/interfaces/android/app/src/main/res/values/styles.xml +10 -0
  368. package/src/interfaces/android/app/src/main/res/values-v31/styles.xml +14 -0
  369. package/src/interfaces/android/app/src/main/res/xml/automotive_app_desc.xml +9 -0
  370. package/src/interfaces/android/app/src/main/res/xml/network_security_config.xml +10 -0
  371. package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/AndroidAutoDetectorTest.java +85 -0
  372. package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/CarAlertStoreTest.java +104 -0
  373. package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/DaemonAddressTest.java +23 -0
  374. package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/MapsNavigationDetectorTest.java +69 -0
  375. package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/MapsShareIntentParserTest.java +31 -0
  376. package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/MessageFrameParserTest.java +180 -0
  377. package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/TravelEventGateTest.java +64 -0
  378. package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/TripPlacesSheetTest.java +66 -0
  379. package/src/interfaces/android/build.gradle +3 -0
  380. package/src/interfaces/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  381. package/src/interfaces/android/gradle/wrapper/gradle-wrapper.properties +9 -0
  382. package/src/interfaces/android/gradle.properties +3 -0
  383. package/src/interfaces/android/gradlew +248 -0
  384. package/src/interfaces/android/gradlew.bat +82 -0
  385. package/src/interfaces/android/settings.gradle +18 -0
  386. package/src/interfaces/cli/commands/a2a.js +145 -4
  387. package/src/interfaces/cli/commands/agent.js +247 -22
  388. package/src/interfaces/cli/commands/code.js +1 -1
  389. package/src/interfaces/cli/commands/commitment.js +5 -0
  390. package/src/interfaces/cli/commands/config.js +85 -6
  391. package/src/interfaces/cli/commands/exec.js +90 -1
  392. package/src/interfaces/cli/commands/image.js +314 -0
  393. package/src/interfaces/cli/commands/mcp.js +87 -23
  394. package/src/interfaces/cli/commands/memory.js +35 -22
  395. package/src/interfaces/cli/commands/panel.js +94 -0
  396. package/src/interfaces/cli/commands/profile.js +14 -0
  397. package/src/interfaces/cli/commands/project.js +29 -0
  398. package/src/interfaces/cli/commands/routine.js +45 -10
  399. package/src/interfaces/cli/commands/runtime.js +30 -7
  400. package/src/interfaces/cli/commands/session.js +90 -38
  401. package/src/interfaces/cli/commands/sessions.js +35 -2
  402. package/src/interfaces/cli/commands/skills.js +15 -9
  403. package/src/interfaces/cli/commands/status.js +2 -3
  404. package/src/interfaces/cli/commands/task.js +54 -5
  405. package/src/interfaces/cli/commands/telegram.js +12 -4
  406. package/src/interfaces/cli/commands/transcribe.js +177 -0
  407. package/src/interfaces/cli/help/index.js +311 -72
  408. package/src/interfaces/cli/http.js +61 -13
  409. package/src/interfaces/cli/index.js +1 -1
  410. package/src/interfaces/cli/routes/agent.js +5 -4
  411. package/src/interfaces/cli/routes/commitment.js +3 -2
  412. package/src/interfaces/cli/routes/config.js +7 -2
  413. package/src/interfaces/cli/routes/image.js +11 -0
  414. package/src/interfaces/cli/routes/index.js +2 -0
  415. package/src/interfaces/cli/routes/log.js +1 -1
  416. package/src/interfaces/cli/routes/memory.js +3 -2
  417. package/src/interfaces/cli/routes/panel.js +15 -2
  418. package/src/interfaces/cli/routes/profile.js +3 -2
  419. package/src/interfaces/cli/routes/transcribe.js +10 -0
  420. package/src/interfaces/cli/stdin.js +29 -0
  421. package/src/interfaces/desktop/assets/blobs/amatista.png +0 -0
  422. package/src/interfaces/desktop/assets/blobs/aqua.png +0 -0
  423. package/src/interfaces/desktop/assets/blobs/brasa.png +0 -0
  424. package/src/interfaces/desktop/assets/blobs/campana.png +0 -0
  425. package/src/interfaces/desktop/assets/blobs/caracol.png +0 -0
  426. package/src/interfaces/desktop/assets/blobs/carbon.png +0 -0
  427. package/src/interfaces/desktop/assets/blobs/cobalto.png +0 -0
  428. package/src/interfaces/desktop/assets/blobs/cometa.png +0 -0
  429. package/src/interfaces/desktop/assets/blobs/coral.png +0 -0
  430. package/src/interfaces/desktop/assets/blobs/cubi.png +0 -0
  431. package/src/interfaces/desktop/assets/blobs/eclipse.png +0 -0
  432. package/src/interfaces/desktop/assets/blobs/faro.png +0 -0
  433. package/src/interfaces/desktop/assets/blobs/gajo.png +0 -0
  434. package/src/interfaces/desktop/assets/blobs/iris.png +0 -0
  435. package/src/interfaces/desktop/assets/blobs/kiwi.png +0 -0
  436. package/src/interfaces/desktop/assets/blobs/laguna.png +0 -0
  437. package/src/interfaces/desktop/assets/blobs/lila.png +0 -0
  438. package/src/interfaces/desktop/assets/blobs/menta.png +0 -0
  439. package/src/interfaces/desktop/assets/blobs/nimbo.png +0 -0
  440. package/src/interfaces/desktop/assets/blobs/noche.png +0 -0
  441. package/src/interfaces/desktop/assets/blobs/onyx.png +0 -0
  442. package/src/interfaces/desktop/assets/blobs/orbita.png +0 -0
  443. package/src/interfaces/desktop/assets/blobs/ovni.png +0 -0
  444. package/src/interfaces/desktop/assets/blobs/papa.png +0 -0
  445. package/src/interfaces/desktop/assets/blobs/parche.png +0 -0
  446. package/src/interfaces/desktop/assets/blobs/peon.png +0 -0
  447. package/src/interfaces/desktop/assets/blobs/perla.png +0 -0
  448. package/src/interfaces/desktop/assets/blobs/pino.png +0 -0
  449. package/src/interfaces/desktop/assets/blobs/quad.png +0 -0
  450. package/src/interfaces/desktop/assets/blobs/rubi.png +0 -0
  451. package/src/interfaces/desktop/assets/blobs/saturno.png +0 -0
  452. package/src/interfaces/desktop/assets/blobs/trebol.png +0 -0
  453. package/src/interfaces/desktop/assets/blobs/trino.png +0 -0
  454. package/src/interfaces/desktop/assets/blobs/volcan.png +0 -0
  455. package/src/interfaces/desktop/assets/blobs/zafiro.png +0 -0
  456. package/src/interfaces/desktop/assets/notification.mp3 +0 -0
  457. package/src/interfaces/desktop/main.js +473 -29
  458. package/src/interfaces/desktop/mascot-blobs.js +41 -0
  459. package/src/interfaces/desktop/mascot-preload.js +32 -0
  460. package/src/interfaces/desktop/mascot.css +186 -0
  461. package/src/interfaces/desktop/mascot.html +40 -0
  462. package/src/interfaces/desktop/mascot.js +163 -0
  463. package/src/interfaces/desktop/preload.js +10 -0
  464. package/src/interfaces/desktop/renderer.js +308 -48
  465. package/src/interfaces/desktop/style.css +34 -0
  466. package/src/interfaces/mcp-server/index.js +87 -0
  467. package/src/interfaces/tui/_shims/util-locale.ts +0 -2
  468. package/src/interfaces/tui/app.tsx +0 -6
  469. package/src/interfaces/tui/attach.ts +0 -1
  470. package/src/interfaces/tui/context/sdk-apx.tsx +0 -4
  471. package/src/interfaces/tui/plugin/api.tsx +0 -3
  472. package/src/interfaces/tui/plugin/runtime.ts +0 -3
  473. package/src/interfaces/tui/thread.ts +0 -16
  474. package/src/interfaces/web/README.md +22 -14
  475. package/src/interfaces/web/dist/assets/claude-XtpLmGtF.webp +0 -0
  476. package/src/interfaces/web/dist/assets/codex-DExPUgvh.webp +0 -0
  477. package/src/interfaces/web/dist/assets/index-BEoRgIqd.css +32 -0
  478. package/src/interfaces/web/dist/assets/index-BEoRgIqd.css.br +0 -0
  479. package/src/interfaces/web/dist/assets/index-BEoRgIqd.css.gz +0 -0
  480. package/src/interfaces/web/dist/assets/index-C6fXHc1p.js +1074 -0
  481. package/src/interfaces/web/dist/assets/index-C6fXHc1p.js.br +0 -0
  482. package/src/interfaces/web/dist/assets/index-C6fXHc1p.js.gz +0 -0
  483. package/src/interfaces/web/dist/assets/index-C6fXHc1p.js.map +1 -0
  484. package/src/interfaces/web/dist/assets/index-C6fXHc1p.js.map.br +0 -0
  485. package/src/interfaces/web/dist/assets/index-C6fXHc1p.js.map.gz +0 -0
  486. package/src/interfaces/web/dist/favicon/dark/site.webmanifest +12 -6
  487. package/src/interfaces/web/dist/favicon/white/site.webmanifest +12 -6
  488. package/src/interfaces/web/dist/index.html +22 -6
  489. package/src/interfaces/web/dist/index.html.br +0 -0
  490. package/src/interfaces/web/dist/index.html.gz +0 -0
  491. package/src/interfaces/web/dist/manifest.webmanifest +25 -0
  492. package/src/interfaces/web/dist/modules/blobs/amatista.png +0 -0
  493. package/src/interfaces/web/dist/modules/blobs/aqua.png +0 -0
  494. package/src/interfaces/web/dist/modules/blobs/brasa.png +0 -0
  495. package/src/interfaces/web/dist/modules/blobs/campana.png +0 -0
  496. package/src/interfaces/web/dist/modules/blobs/caracol.png +0 -0
  497. package/src/interfaces/web/dist/modules/blobs/carbon.png +0 -0
  498. package/src/interfaces/web/dist/modules/blobs/cobalto.png +0 -0
  499. package/src/interfaces/web/dist/modules/blobs/cometa.png +0 -0
  500. package/src/interfaces/web/dist/modules/blobs/coral.png +0 -0
  501. package/src/interfaces/web/dist/modules/blobs/cubi.png +0 -0
  502. package/src/interfaces/web/dist/modules/blobs/eclipse.png +0 -0
  503. package/src/interfaces/web/dist/modules/blobs/faro.png +0 -0
  504. package/src/interfaces/web/dist/modules/blobs/gajo.png +0 -0
  505. package/src/interfaces/web/dist/modules/blobs/iris.png +0 -0
  506. package/src/interfaces/web/dist/modules/blobs/kiwi.png +0 -0
  507. package/src/interfaces/web/dist/modules/blobs/laguna.png +0 -0
  508. package/src/interfaces/web/dist/modules/blobs/lila.png +0 -0
  509. package/src/interfaces/web/dist/modules/blobs/menta.png +0 -0
  510. package/src/interfaces/web/dist/modules/blobs/nimbo.png +0 -0
  511. package/src/interfaces/web/dist/modules/blobs/noche.png +0 -0
  512. package/src/interfaces/web/dist/modules/blobs/onyx.png +0 -0
  513. package/src/interfaces/web/dist/modules/blobs/orbita.png +0 -0
  514. package/src/interfaces/web/dist/modules/blobs/ovni.png +0 -0
  515. package/src/interfaces/web/dist/modules/blobs/papa.png +0 -0
  516. package/src/interfaces/web/dist/modules/blobs/parche.png +0 -0
  517. package/src/interfaces/web/dist/modules/blobs/peon.png +0 -0
  518. package/src/interfaces/web/dist/modules/blobs/perla.png +0 -0
  519. package/src/interfaces/web/dist/modules/blobs/pino.png +0 -0
  520. package/src/interfaces/web/dist/modules/blobs/quad.png +0 -0
  521. package/src/interfaces/web/dist/modules/blobs/rubi.png +0 -0
  522. package/src/interfaces/web/dist/modules/blobs/saturno.png +0 -0
  523. package/src/interfaces/web/dist/modules/blobs/trebol.png +0 -0
  524. package/src/interfaces/web/dist/modules/blobs/trino.png +0 -0
  525. package/src/interfaces/web/dist/modules/blobs/volcan.png +0 -0
  526. package/src/interfaces/web/dist/modules/blobs/zafiro.png +0 -0
  527. package/src/interfaces/web/dist/sw.js +106 -0
  528. package/src/interfaces/web/dist/sw.js.br +0 -0
  529. package/src/interfaces/web/dist/sw.js.gz +0 -0
  530. package/src/interfaces/web/eslint.config.js +83 -0
  531. package/src/interfaces/web/index.html +20 -4
  532. package/src/interfaces/web/package-lock.json +1651 -1280
  533. package/src/interfaces/web/package.json +9 -10
  534. package/src/interfaces/web/pnpm-lock.yaml +867 -869
  535. package/src/interfaces/web/public/favicon/dark/site.webmanifest +12 -6
  536. package/src/interfaces/web/public/favicon/white/site.webmanifest +12 -6
  537. package/src/interfaces/web/public/manifest.webmanifest +25 -0
  538. package/src/interfaces/web/public/modules/blobs/amatista.png +0 -0
  539. package/src/interfaces/web/public/modules/blobs/aqua.png +0 -0
  540. package/src/interfaces/web/public/modules/blobs/brasa.png +0 -0
  541. package/src/interfaces/web/public/modules/blobs/campana.png +0 -0
  542. package/src/interfaces/web/public/modules/blobs/caracol.png +0 -0
  543. package/src/interfaces/web/public/modules/blobs/carbon.png +0 -0
  544. package/src/interfaces/web/public/modules/blobs/cobalto.png +0 -0
  545. package/src/interfaces/web/public/modules/blobs/cometa.png +0 -0
  546. package/src/interfaces/web/public/modules/blobs/coral.png +0 -0
  547. package/src/interfaces/web/public/modules/blobs/cubi.png +0 -0
  548. package/src/interfaces/web/public/modules/blobs/eclipse.png +0 -0
  549. package/src/interfaces/web/public/modules/blobs/faro.png +0 -0
  550. package/src/interfaces/web/public/modules/blobs/gajo.png +0 -0
  551. package/src/interfaces/web/public/modules/blobs/iris.png +0 -0
  552. package/src/interfaces/web/public/modules/blobs/kiwi.png +0 -0
  553. package/src/interfaces/web/public/modules/blobs/laguna.png +0 -0
  554. package/src/interfaces/web/public/modules/blobs/lila.png +0 -0
  555. package/src/interfaces/web/public/modules/blobs/menta.png +0 -0
  556. package/src/interfaces/web/public/modules/blobs/nimbo.png +0 -0
  557. package/src/interfaces/web/public/modules/blobs/noche.png +0 -0
  558. package/src/interfaces/web/public/modules/blobs/onyx.png +0 -0
  559. package/src/interfaces/web/public/modules/blobs/orbita.png +0 -0
  560. package/src/interfaces/web/public/modules/blobs/ovni.png +0 -0
  561. package/src/interfaces/web/public/modules/blobs/papa.png +0 -0
  562. package/src/interfaces/web/public/modules/blobs/parche.png +0 -0
  563. package/src/interfaces/web/public/modules/blobs/peon.png +0 -0
  564. package/src/interfaces/web/public/modules/blobs/perla.png +0 -0
  565. package/src/interfaces/web/public/modules/blobs/pino.png +0 -0
  566. package/src/interfaces/web/public/modules/blobs/quad.png +0 -0
  567. package/src/interfaces/web/public/modules/blobs/rubi.png +0 -0
  568. package/src/interfaces/web/public/modules/blobs/saturno.png +0 -0
  569. package/src/interfaces/web/public/modules/blobs/trebol.png +0 -0
  570. package/src/interfaces/web/public/modules/blobs/trino.png +0 -0
  571. package/src/interfaces/web/public/modules/blobs/volcan.png +0 -0
  572. package/src/interfaces/web/public/modules/blobs/zafiro.png +0 -0
  573. package/src/interfaces/web/public/sw.js +106 -0
  574. package/src/interfaces/web/src/App.tsx +106 -10
  575. package/src/interfaces/web/src/assets/cli/claude.webp +0 -0
  576. package/src/interfaces/web/src/assets/cli/codex.webp +0 -0
  577. package/src/interfaces/web/src/assets/cli/cursor.svg +1 -0
  578. package/src/interfaces/web/src/assets/cli/opencode.png +0 -0
  579. package/src/interfaces/web/src/components/Combobox.tsx +201 -0
  580. package/src/interfaces/web/src/components/MobileHint.tsx +95 -0
  581. package/src/interfaces/web/src/components/MobileLinkDialog.tsx +156 -0
  582. package/src/interfaces/web/src/components/ModelCombobox.tsx +21 -113
  583. package/src/interfaces/web/src/components/{routines/ReadOnlyBlock.tsx → ReadOnlyBlock.tsx} +13 -4
  584. package/src/interfaces/web/src/components/Roby.tsx +5 -3
  585. package/src/interfaces/web/src/components/RobyBubble.tsx +23 -5
  586. package/src/interfaces/web/src/components/RowMenu.tsx +46 -0
  587. package/src/interfaces/web/src/components/TimezoneSelect.tsx +4 -1
  588. package/src/interfaces/web/src/components/Toast.tsx +50 -14
  589. package/src/interfaces/web/src/components/UiSelect.tsx +12 -5
  590. package/src/interfaces/web/src/components/agents/AgentAvatar.tsx +171 -0
  591. package/src/interfaces/web/src/components/agents/AgentFormFields.tsx +42 -11
  592. package/src/interfaces/web/src/components/agents/AgentModelBadge.tsx +157 -0
  593. package/src/interfaces/web/src/components/agents/AgentModelSelect.tsx +57 -0
  594. package/src/interfaces/web/src/components/agents/AgentSkillsPicker.tsx +115 -0
  595. package/src/interfaces/web/src/components/agents/BlobAvatar.tsx +50 -0
  596. package/src/interfaces/web/src/components/agents/blobPresets.ts +50 -0
  597. package/src/interfaces/web/src/components/agents/modelCatalog.ts +66 -0
  598. package/src/interfaces/web/src/components/chat/ActionGroup.tsx +162 -0
  599. package/src/interfaces/web/src/components/chat/AskAnswersCard.tsx +4 -3
  600. package/src/interfaces/web/src/components/chat/AskQuestionsCard.tsx +1 -1
  601. package/src/interfaces/web/src/components/chat/Attachment.tsx +220 -0
  602. package/src/interfaces/web/src/components/chat/ChatList.tsx +226 -51
  603. package/src/interfaces/web/src/components/chat/ChatRowActivity.tsx +45 -0
  604. package/src/interfaces/web/src/components/chat/Composer.tsx +672 -23
  605. package/src/interfaces/web/src/components/chat/ComposerCommands.tsx +175 -0
  606. package/src/interfaces/web/src/components/chat/ComposerSuggest.tsx +215 -0
  607. package/src/interfaces/web/src/components/chat/ContextBar.tsx +133 -65
  608. package/src/interfaces/web/src/components/chat/InlineAskPanel.tsx +107 -47
  609. package/src/interfaces/web/src/components/chat/MessageBubble.tsx +460 -119
  610. package/src/interfaces/web/src/components/chat/MessageList.tsx +165 -18
  611. package/src/interfaces/web/src/components/chat/ReasoningBlock.tsx +58 -0
  612. package/src/interfaces/web/src/components/chat/SessionPicker.tsx +182 -0
  613. package/src/interfaces/web/src/components/chat/SkillTrace.tsx +183 -0
  614. package/src/interfaces/web/src/components/chat/ToolCall.tsx +11 -11
  615. package/src/interfaces/web/src/components/code/CodeContextTab.tsx +87 -53
  616. package/src/interfaces/web/src/components/code/CodeFileTree.tsx +11 -52
  617. package/src/interfaces/web/src/components/code/CodeFileViewer.tsx +5 -4
  618. package/src/interfaces/web/src/components/code/CodeProjectPicker.tsx +31 -7
  619. package/src/interfaces/web/src/components/code/CodeSessionList.tsx +32 -11
  620. package/src/interfaces/web/src/components/code/CodeSidePanel.tsx +33 -3
  621. package/src/interfaces/web/src/components/code/CodeTerminal.tsx +5 -4
  622. package/src/interfaces/web/src/components/code/NewCodeSessionDialog.tsx +164 -0
  623. package/src/interfaces/web/src/components/commitments/CommitmentDetail.tsx +162 -0
  624. package/src/interfaces/web/src/components/commitments/CommitmentFormDialog.tsx +158 -0
  625. package/src/interfaces/web/src/components/commitments/CommitmentList.tsx +194 -0
  626. package/src/interfaces/web/src/components/commitments/commitmentState.tsx +46 -0
  627. package/src/interfaces/web/src/components/common/BulkActionBar.tsx +60 -0
  628. package/src/interfaces/web/src/components/common/ConfirmDialog.tsx +4 -1
  629. package/src/interfaces/web/src/components/common/SelectCheckbox.tsx +40 -0
  630. package/src/interfaces/web/src/components/common/TabNav.tsx +1 -1
  631. package/src/interfaces/web/src/components/cron/CronPicker.tsx +1 -1
  632. package/src/interfaces/web/src/components/files/FileBrowser.tsx +1 -1
  633. package/src/interfaces/web/src/components/files/FileTree.tsx +6 -4
  634. package/src/interfaces/web/src/components/files/FileViewer.tsx +9 -5
  635. package/src/interfaces/web/src/components/files/MarkdownPreview.tsx +45 -9
  636. package/src/interfaces/web/src/components/images/ImageDefaultsCard.tsx +93 -0
  637. package/src/interfaces/web/src/components/images/ImageProviderList.tsx +142 -0
  638. package/src/interfaces/web/src/components/images/ImageProviderModal.tsx +263 -0
  639. package/src/interfaces/web/src/components/images/ImageTestCard.tsx +142 -0
  640. package/src/interfaces/web/src/components/inbox/ChannelFilter.tsx +178 -0
  641. package/src/interfaces/web/src/components/inbox/InboxList.tsx +77 -81
  642. package/src/interfaces/web/src/components/inbox/InboxRowItem.tsx +140 -0
  643. package/src/interfaces/web/src/components/integrations/ComingSoonPlugin.tsx +2 -2
  644. package/src/interfaces/web/src/components/integrations/PluginConnect.tsx +208 -30
  645. package/src/interfaces/web/src/components/integrations/PluginToolsSection.tsx +1 -1
  646. package/src/interfaces/web/src/components/layout/ProjectAvatar.tsx +39 -24
  647. package/src/interfaces/web/src/components/layout/ProjectSidebar.tsx +253 -58
  648. package/src/interfaces/web/src/components/memory/MemoryBrowser.tsx +94 -47
  649. package/src/interfaces/web/src/components/routines/ExecutionsList.tsx +202 -90
  650. package/src/interfaces/web/src/components/routines/RoutineDetail.tsx +28 -6
  651. package/src/interfaces/web/src/components/routines/RoutineEditor.tsx +16 -5
  652. package/src/interfaces/web/src/components/routines/RoutineList.tsx +49 -9
  653. package/src/interfaces/web/src/components/routines/VarTextarea.tsx +1 -1
  654. package/src/interfaces/web/src/components/routines/runChat.ts +121 -0
  655. package/src/interfaces/web/src/components/sessions/SessionDetail.tsx +148 -0
  656. package/src/interfaces/web/src/components/sessions/SessionTerminal.tsx +98 -0
  657. package/src/interfaces/web/src/components/settings/AccessPanel.tsx +205 -0
  658. package/src/interfaces/web/src/components/settings/DefaultRouterCard.tsx +246 -101
  659. package/src/interfaces/web/src/components/settings/DesktopSettingsPanel.tsx +36 -2
  660. package/src/interfaces/web/src/components/settings/DevicesPanel.tsx +19 -6
  661. package/src/interfaces/web/src/components/settings/EmbedProviderList.tsx +274 -0
  662. package/src/interfaces/web/src/components/settings/EnginesPanel.tsx +26 -6
  663. package/src/interfaces/web/src/components/settings/MemoryPanel.tsx +118 -119
  664. package/src/interfaces/web/src/components/settings/NudgePanel.tsx +4 -3
  665. package/src/interfaces/web/src/components/settings/PairDeviceDialog.tsx +15 -4
  666. package/src/interfaces/web/src/components/settings/PanelPrefs.tsx +374 -0
  667. package/src/interfaces/web/src/components/settings/ProfilePanel.tsx +70 -2
  668. package/src/interfaces/web/src/components/settings/RoutingPanel.tsx +2 -1
  669. package/src/interfaces/web/src/components/settings/SkillsInspectorPanel.tsx +102 -43
  670. package/src/interfaces/web/src/components/settings/SkillsManager.tsx +76 -21
  671. package/src/interfaces/web/src/components/settings/SkillsSettings.tsx +10 -3
  672. package/src/interfaces/web/src/components/settings/SuperAgentPanel.tsx +8 -0
  673. package/src/interfaces/web/src/components/settings/TelegramChannelsPanel.tsx +11 -2
  674. package/src/interfaces/web/src/components/settings/TelegramContactsPanel.tsx +19 -8
  675. package/src/interfaces/web/src/components/settings/TelegramRolesPanel.tsx +12 -2
  676. package/src/interfaces/web/src/components/settings/WebPanel.tsx +22 -31
  677. package/src/interfaces/web/src/components/settings/providers/ProviderCard.tsx +15 -3
  678. package/src/interfaces/web/src/components/settings/providers/ProviderModal.tsx +97 -66
  679. package/src/interfaces/web/src/components/settings/providers/ProviderTestDialog.tsx +150 -0
  680. package/src/interfaces/web/src/components/settings/providers/typeStyles.ts +67 -25
  681. package/src/interfaces/web/src/components/settings/providers/types.ts +3 -0
  682. package/src/interfaces/web/src/components/tasks/TaskBoard.tsx +201 -0
  683. package/src/interfaces/web/src/components/tasks/TaskColumnsDialog.tsx +243 -0
  684. package/src/interfaces/web/src/components/tasks/TaskComments.tsx +167 -0
  685. package/src/interfaces/web/src/components/tasks/TaskDetail.tsx +335 -0
  686. package/src/interfaces/web/src/components/tasks/TaskFormDialog.tsx +406 -0
  687. package/src/interfaces/web/src/components/tasks/TaskList.tsx +258 -0
  688. package/src/interfaces/web/src/components/tasks/TaskSubtasks.tsx +126 -0
  689. package/src/interfaces/web/src/components/tasks/columns.ts +51 -0
  690. package/src/interfaces/web/src/components/tasks/taskStatus.tsx +159 -34
  691. package/src/interfaces/web/src/components/tasks/taskViewPrefs.ts +73 -0
  692. package/src/interfaces/web/src/components/tasks/useTaskColumns.ts +31 -0
  693. package/src/interfaces/web/src/components/ui/chat-input.tsx +136 -12
  694. package/src/interfaces/web/src/components/ui/context-menu.tsx +15 -198
  695. package/src/interfaces/web/src/components/ui/dialog.tsx +3 -2
  696. package/src/interfaces/web/src/components/ui/filter-chips.tsx +4 -0
  697. package/src/interfaces/web/src/components/ui/popover.tsx +14 -49
  698. package/src/interfaces/web/src/components/ui/sheet.tsx +2 -1
  699. package/src/interfaces/web/src/components/ui/spinner.tsx +2 -1
  700. package/src/interfaces/web/src/components/ui.tsx +62 -23
  701. package/src/interfaces/web/src/components/voice/VoiceProviderList.tsx +2 -1
  702. package/src/interfaces/web/src/components/voice/VoiceProviderModal.tsx +66 -8
  703. package/src/interfaces/web/src/components/voice/VoiceQvoxInstallCard.tsx +63 -0
  704. package/src/interfaces/web/src/components/voice/VoiceQvoxStatusCard.tsx +51 -0
  705. package/src/interfaces/web/src/components/voice/VoiceSttCard.tsx +4 -3
  706. package/src/interfaces/web/src/components/voice/VoiceTestCard.tsx +38 -4
  707. package/src/interfaces/web/src/constants/index.ts +1 -0
  708. package/src/interfaces/web/src/hooks/useChannelPrefs.ts +39 -0
  709. package/src/interfaces/web/src/hooks/useChat.ts +1092 -85
  710. package/src/interfaces/web/src/hooks/useChatActivity.ts +24 -0
  711. package/src/interfaces/web/src/hooks/useInbox.ts +26 -4
  712. package/src/interfaces/web/src/hooks/useLiveMessages.ts +16 -0
  713. package/src/interfaces/web/src/hooks/useOllamaModels.ts +79 -0
  714. package/src/interfaces/web/src/hooks/useRecorder.ts +198 -0
  715. package/src/interfaces/web/src/hooks/useTokenBootstrap.ts +23 -6
  716. package/src/interfaces/web/src/i18n/en.ts +832 -175
  717. package/src/interfaces/web/src/i18n/es.ts +841 -188
  718. package/src/interfaces/web/src/lib/api/agents.ts +23 -4
  719. package/src/interfaces/web/src/lib/api/code.ts +17 -0
  720. package/src/interfaces/web/src/lib/api/commitments.ts +7 -1
  721. package/src/interfaces/web/src/lib/api/conversations.ts +38 -11
  722. package/src/interfaces/web/src/lib/api/embeddings.ts +3 -0
  723. package/src/interfaces/web/src/lib/api/engines.ts +14 -0
  724. package/src/interfaces/web/src/lib/api/groups.ts +93 -0
  725. package/src/interfaces/web/src/lib/api/images.ts +193 -0
  726. package/src/interfaces/web/src/lib/api/inbox.ts +29 -5
  727. package/src/interfaces/web/src/lib/api/integrations.ts +9 -0
  728. package/src/interfaces/web/src/lib/api/media.ts +126 -0
  729. package/src/interfaces/web/src/lib/api/net.ts +44 -0
  730. package/src/interfaces/web/src/lib/api/profiles.ts +12 -0
  731. package/src/interfaces/web/src/lib/api/projects.ts +9 -1
  732. package/src/interfaces/web/src/lib/api/routines.ts +9 -1
  733. package/src/interfaces/web/src/lib/api/sessions.ts +18 -0
  734. package/src/interfaces/web/src/lib/api/skills.ts +8 -0
  735. package/src/interfaces/web/src/lib/api/super_agent.ts +3 -0
  736. package/src/interfaces/web/src/lib/api/tasks.ts +46 -6
  737. package/src/interfaces/web/src/lib/api/tools.ts +2 -0
  738. package/src/interfaces/web/src/lib/api/transcribe.ts +31 -0
  739. package/src/interfaces/web/src/lib/api/turns.ts +20 -0
  740. package/src/interfaces/web/src/lib/api/voice.ts +72 -2
  741. package/src/interfaces/web/src/lib/api.ts +3 -0
  742. package/src/interfaces/web/src/lib/channels.ts +214 -0
  743. package/src/interfaces/web/src/lib/chat-activity.ts +175 -0
  744. package/src/interfaces/web/src/lib/chat-prefs.ts +48 -0
  745. package/src/interfaces/web/src/lib/code-context.ts +0 -2
  746. package/src/interfaces/web/src/lib/http.ts +51 -17
  747. package/src/interfaces/web/src/lib/inbox-selection.ts +29 -0
  748. package/src/interfaces/web/src/lib/live.ts +252 -0
  749. package/src/interfaces/web/src/lib/net.ts +211 -0
  750. package/src/interfaces/web/src/lib/notify.ts +331 -0
  751. package/src/interfaces/web/src/lib/projectNav.ts +40 -0
  752. package/src/interfaces/web/src/lib/pwa.ts +115 -0
  753. package/src/interfaces/web/src/lib/qvox.ts +24 -0
  754. package/src/interfaces/web/src/lib/slug.ts +3 -0
  755. package/src/interfaces/web/src/lib/tone.ts +150 -0
  756. package/src/interfaces/web/src/main.tsx +15 -4
  757. package/src/interfaces/web/src/screens/ApxAdminScreen.tsx +33 -11
  758. package/src/interfaces/web/src/screens/InboxScreen.tsx +126 -71
  759. package/src/interfaces/web/src/screens/ProjectScreen.tsx +8 -4
  760. package/src/interfaces/web/src/screens/SettingsScreen.tsx +7 -4
  761. package/src/interfaces/web/src/screens/base/AgentDefaultsTab.tsx +30 -9
  762. package/src/interfaces/web/src/screens/base/CommitmentsTab.tsx +150 -168
  763. package/src/interfaces/web/src/screens/base/LogsTab.tsx +8 -7
  764. package/src/interfaces/web/src/screens/base/SessionsTab.tsx +99 -37
  765. package/src/interfaces/web/src/screens/base/WorkspacesTab.tsx +1 -1
  766. package/src/interfaces/web/src/screens/mobile/MobileChat.tsx +85 -0
  767. package/src/interfaces/web/src/screens/mobile/MobileChatList.tsx +244 -0
  768. package/src/interfaces/web/src/screens/mobile/MobileCommitments.tsx +350 -0
  769. package/src/interfaces/web/src/screens/mobile/MobileScreen.tsx +138 -0
  770. package/src/interfaces/web/src/screens/mobile/MobileTabBar.tsx +105 -0
  771. package/src/interfaces/web/src/screens/mobile/MobileTasks.tsx +407 -0
  772. package/src/interfaces/web/src/screens/mobile/NewChatSheet.tsx +313 -0
  773. package/src/interfaces/web/src/screens/mobile/mobileList.tsx +194 -0
  774. package/src/interfaces/web/src/screens/mobile/routes.ts +239 -0
  775. package/src/interfaces/web/src/screens/modules/CodeScreen.tsx +135 -64
  776. package/src/interfaces/web/src/screens/modules/DeckScreen.tsx +0 -1
  777. package/src/interfaces/web/src/screens/modules/ImagesScreen.tsx +172 -0
  778. package/src/interfaces/web/src/screens/modules/VoiceScreen.tsx +36 -4
  779. package/src/interfaces/web/src/screens/project/AgentBrainGraph.tsx +117 -30
  780. package/src/interfaces/web/src/screens/project/AgentDetailScreen.tsx +537 -92
  781. package/src/interfaces/web/src/screens/project/AgentsTab.tsx +344 -93
  782. package/src/interfaces/web/src/screens/project/ArtifactsTab.tsx +1 -1
  783. package/src/interfaces/web/src/screens/project/ChatTab.tsx +1160 -94
  784. package/src/interfaces/web/src/screens/project/IntegrationsTab.tsx +3 -2
  785. package/src/interfaces/web/src/screens/project/McpsTab.tsx +62 -14
  786. package/src/interfaces/web/src/screens/project/Overview.tsx +36 -15
  787. package/src/interfaces/web/src/screens/project/RoutinesTab.tsx +93 -8
  788. package/src/interfaces/web/src/screens/project/SkillsTab.tsx +4 -1
  789. package/src/interfaces/web/src/screens/project/StructureTab.tsx +5 -4
  790. package/src/interfaces/web/src/screens/project/TasksTab.tsx +351 -129
  791. package/src/interfaces/web/src/screens/project/VarsTab.tsx +17 -5
  792. package/src/interfaces/web/src/styles.css +106 -31
  793. package/src/interfaces/web/src/types/daemon.ts +246 -2
  794. package/src/interfaces/web/src/vite-env.d.ts +20 -0
  795. package/src/interfaces/tui/plugin/command-shim.ts +0 -109
  796. package/src/interfaces/web/components.json +0 -25
  797. package/src/interfaces/web/dist/assets/index-CvEoGtTf.js +0 -849
  798. package/src/interfaces/web/dist/assets/index-CvEoGtTf.js.map +0 -1
  799. package/src/interfaces/web/dist/assets/index-DzBBXFaO.css +0 -1
  800. package/src/interfaces/web/src/components/tasks/TaskDetailPanel.tsx +0 -142
  801. package/src/interfaces/web/src/components/ui/accordion.tsx +0 -72
  802. package/src/interfaces/web/src/components/ui/alert-dialog.tsx +0 -187
  803. package/src/interfaces/web/src/components/ui/alert.tsx +0 -76
  804. package/src/interfaces/web/src/components/ui/aspect-ratio.tsx +0 -22
  805. package/src/interfaces/web/src/components/ui/avatar.tsx +0 -107
  806. package/src/interfaces/web/src/components/ui/breadcrumb.tsx +0 -125
  807. package/src/interfaces/web/src/components/ui/button-group.tsx +0 -87
  808. package/src/interfaces/web/src/components/ui/calendar.tsx +0 -221
  809. package/src/interfaces/web/src/components/ui/card.tsx +0 -103
  810. package/src/interfaces/web/src/components/ui/carousel.tsx +0 -242
  811. package/src/interfaces/web/src/components/ui/chart.tsx +0 -371
  812. package/src/interfaces/web/src/components/ui/checkbox.tsx +0 -29
  813. package/src/interfaces/web/src/components/ui/collapsible.tsx +0 -19
  814. package/src/interfaces/web/src/components/ui/combobox.tsx +0 -295
  815. package/src/interfaces/web/src/components/ui/command.tsx +0 -196
  816. package/src/interfaces/web/src/components/ui/direction.tsx +0 -4
  817. package/src/interfaces/web/src/components/ui/drawer.tsx +0 -134
  818. package/src/interfaces/web/src/components/ui/empty.tsx +0 -104
  819. package/src/interfaces/web/src/components/ui/field.tsx +0 -236
  820. package/src/interfaces/web/src/components/ui/hover-card.tsx +0 -51
  821. package/src/interfaces/web/src/components/ui/input-otp.tsx +0 -85
  822. package/src/interfaces/web/src/components/ui/item.tsx +0 -201
  823. package/src/interfaces/web/src/components/ui/menubar.tsx +0 -280
  824. package/src/interfaces/web/src/components/ui/native-select.tsx +0 -61
  825. package/src/interfaces/web/src/components/ui/navigation-menu.tsx +0 -168
  826. package/src/interfaces/web/src/components/ui/pagination.tsx +0 -130
  827. package/src/interfaces/web/src/components/ui/progress.tsx +0 -83
  828. package/src/interfaces/web/src/components/ui/radio-group.tsx +0 -36
  829. package/src/interfaces/web/src/components/ui/resizable.tsx +0 -50
  830. package/src/interfaces/web/src/components/ui/scroll-area.tsx +0 -53
  831. package/src/interfaces/web/src/components/ui/sidebar.tsx +0 -726
  832. package/src/interfaces/web/src/components/ui/slider.tsx +0 -52
  833. package/src/interfaces/web/src/components/ui/sonner.tsx +0 -49
  834. package/src/interfaces/web/src/components/ui/table.tsx +0 -116
  835. package/src/interfaces/web/src/components/ui/toggle-group.tsx +0 -87
  836. package/src/interfaces/web/src/components/ui/toggle.tsx +0 -45
  837. package/src/interfaces/web/src/hooks/use-mobile.ts +0 -19
  838. package/src/interfaces/web/src/screens/base/GlobalTasksTab.tsx +0 -161
@@ -161,10 +161,26 @@ export async function compactChannelIfNeeded(opts = {}) {
161
161
  });
162
162
 
163
163
  const models = resolveCompactModels(config);
164
- const summary = await summarizeStructured({ prompt, models, config });
164
+ const prevSummaryText = prevCompact ? String(prevCompact.body || "").trim() : "";
165
+ const summary = await summarizeStructured({
166
+ prompt,
167
+ models,
168
+ config,
169
+ // The new summary must subsume this one; if it comes back smaller than the
170
+ // state it replaces, it dropped things and is refused.
171
+ prevSummary: prevSummaryText,
172
+ onReject: ({ model, reason }) =>
173
+ log(`memory: compaction for ${channel}/${chat_id} refused ${model}'s summary — ${reason}`),
174
+ });
165
175
  if (!summary) {
166
- log(`memory: compaction for ${channel}/${chat_id} skipped no model available`);
167
- return { skipped: "no model" };
176
+ // Deliberately writing NOTHING. A compact record replaces the turns it
177
+ // covers, so a bad one is permanent amnesia while no record at all just
178
+ // means the raw history stays and the next turn tries again.
179
+ log(
180
+ `memory: compaction for ${channel}/${chat_id} skipped — no usable summary; ` +
181
+ `keeping ${compactedReal} raw turn(s)`
182
+ );
183
+ return { skipped: "no usable summary" };
168
184
  }
169
185
 
170
186
  const totalCovered = prevCount + compactedReal;
@@ -196,8 +196,8 @@ export function revertConsolidation(opts = {}) {
196
196
  return { removed, path: SELF_MEMORY_PATH };
197
197
  }
198
198
 
199
- /** Drop day headings left with no bullets under them. */
200
- function stripEmptyDays(lines) {
199
+ /** Drop day headings left with no bullets under them. Shared with prune.js. */
200
+ export function stripEmptyDays(lines) {
201
201
  const out = [];
202
202
  for (let i = 0; i < lines.length; i++) {
203
203
  const isHeading = /^##\s+\d{4}-\d{2}-\d{2}/.test(lines[i].trim());
@@ -0,0 +1,66 @@
1
+ // The shape every dated note file in APX shares: one `## YYYY-MM-DD` heading
2
+ // per day, one `- [HH:MM][channel] note` bullet per note, oldest day first.
3
+ //
4
+ // Three files are written this way — the super-agent's notebook
5
+ // (`~/.apx/memory.md`), a project's memory (`<repo>/.apc/memory.md`) and a
6
+ // routine's memory — and the fiddly part is the same in all three: a note
7
+ // written today belongs at the END of TODAY'S block, not at the end of the
8
+ // file. Get that wrong and the day splits into two headings the moment
9
+ // anything else is appended. It had been written correctly once and
10
+ // approximately twice more; this is the one copy.
11
+ //
12
+ // Pure — the caller owns the file I/O.
13
+ import { dayStamp, hourStamp } from "#core/util/time.js";
14
+
15
+ /**
16
+ * Append one note to a dated markdown log and return the new body.
17
+ *
18
+ * @param existing current file body ("" for a file that doesn't exist yet)
19
+ * @param note the note; newlines are flattened, one bullet per call
20
+ * @param opts.channel tags the bullet "[HH:MM][channel] …" so the broker and
21
+ * the RAG indexer can attribute it. Omitted → plain "- note".
22
+ * @param opts.time override the HH:MM tag (tests, backfills)
23
+ * @param opts.date override the day heading (tests, backfills)
24
+ * @param opts.header "# …" title used only when creating the file
25
+ */
26
+ export function appendDatedBullet(existing, note, opts = {}) {
27
+ const text = String(note || "").trim();
28
+ if (!text) throw new Error("nothing to remember (empty note)");
29
+
30
+ const { channel = "", time = "", header = "" } = opts;
31
+ const date = opts.date || dayStamp();
32
+ const heading = `## ${date}`;
33
+ const oneLine = text.replace(/\n+/g, " ").trim();
34
+ const ch = String(channel || "").trim().toLowerCase();
35
+ const bullet = `- ${ch ? `[${time || hourStamp()}][${ch}] ` : ""}${oneLine}`;
36
+
37
+ const body = String(existing || "");
38
+ if (!body.trim()) {
39
+ return header ? `${header}\n\n${heading}\n${bullet}\n` : `${heading}\n${bullet}\n`;
40
+ }
41
+
42
+ const lines = body.split("\n");
43
+ // lastIndexOf, not includes(): a heading that merely *contains* today's date
44
+ // ("## 2026-01-01 — release") is not today's block, and treating it as one
45
+ // used to splice the bullet under whatever heading happened to come first.
46
+ const idx = lines.lastIndexOf(heading);
47
+ if (idx < 0) {
48
+ const sep = body.endsWith("\n") ? "" : "\n";
49
+ return `${body}${sep}\n${heading}\n${bullet}\n`;
50
+ }
51
+
52
+ // End of today's block: the next heading, or EOF. Trailing blank lines inside
53
+ // the block stay below the new bullet.
54
+ let insertAt = lines.length;
55
+ for (let i = idx + 1; i < lines.length; i++) {
56
+ if (lines[i].startsWith("## ")) {
57
+ insertAt = i;
58
+ break;
59
+ }
60
+ }
61
+ while (insertAt > idx + 1 && lines[insertAt - 1].trim() === "") insertAt--;
62
+
63
+ lines.splice(insertAt, 0, bullet);
64
+ const next = lines.join("\n");
65
+ return next.endsWith("\n") ? next : `${next}\n`;
66
+ }
@@ -0,0 +1,59 @@
1
+ // Custom OpenAI-compatible embeddings adapter. Backs every user-added provider
2
+ // (config.memory.embeddings.custom.<slug>, surfaced as id "custom:<slug>") — a
3
+ // local or self-hosted /embeddings endpoint such as a Zen server, LiteLLM, or a
4
+ // llama.cpp server. Distinct from the built-in `openai` adapter in two ways:
5
+ // 1. the API key is OPTIONAL (local servers are often keyless), and it never
6
+ // borrows engines.openai.api_key — a custom endpoint gets only its own key;
7
+ // 2. every vector is tagged with the provider's OWN id (`custom:<slug>:model`)
8
+ // so two custom endpoints never collide in the index's cosine space.
9
+
10
+ import { l2normalize } from "../embeddings.js";
11
+
12
+ const DEFAULT_MODEL = "text-embedding-3-small";
13
+
14
+ export default {
15
+ id: "custom",
16
+
17
+ // Available once it has a base_url — the endpoint to POST to. A key is only
18
+ // required if the server itself demands one (checked at call time by the 401).
19
+ async isAvailable(config = {}) {
20
+ return Boolean(config.base_url);
21
+ },
22
+
23
+ async embed({ text, config = {}, timeoutMs = 8000, signal }) {
24
+ const base = String(config.base_url || "").replace(/\/$/, "");
25
+ if (!base) throw new Error("custom embeddings: no base_url");
26
+ const key = config.api_key || "";
27
+ const model = config.model || DEFAULT_MODEL;
28
+ // The provider's own id, injected by the registry — the index-space tag.
29
+ const tag = config._embedder_id || "custom";
30
+ const ctrl = new AbortController();
31
+ const onParentAbort = () => ctrl.abort();
32
+ if (signal) signal.addEventListener("abort", onParentAbort, { once: true });
33
+ const timer = setTimeout(() => ctrl.abort(), timeoutMs);
34
+ try {
35
+ const res = await fetch(`${base}/embeddings`, {
36
+ method: "POST",
37
+ headers: {
38
+ ...(key ? { authorization: `Bearer ${key}` } : {}),
39
+ "content-type": "application/json",
40
+ },
41
+ body: JSON.stringify({ model, input: text }),
42
+ signal: ctrl.signal,
43
+ });
44
+ if (!res.ok) {
45
+ const err = await res.text().catch(() => "");
46
+ throw new Error(`custom embeddings ${res.status}: ${err.slice(0, 200)}`);
47
+ }
48
+ const json = await res.json();
49
+ const vector = json?.data?.[0]?.embedding;
50
+ if (!Array.isArray(vector) || vector.length === 0) {
51
+ throw new Error("custom embeddings: empty vector");
52
+ }
53
+ return { vector: l2normalize(vector), embedder: `${tag}:${model}`, dim: vector.length };
54
+ } finally {
55
+ clearTimeout(timer);
56
+ if (signal) signal.removeEventListener("abort", onParentAbort);
57
+ }
58
+ },
59
+ };
@@ -1,9 +1,15 @@
1
- // Google Gemini embeddings adapter (default: text-embedding-004, 768 dims).
2
- // Free tier available with a Gemini API key. Reuses engines.gemini.api_key.
1
+ // Google Gemini embeddings adapter. Free tier available with a Gemini API key,
2
+ // and it reuses engines.gemini.api_key rather than asking for a second one.
3
+ //
4
+ // The default used to be `text-embedding-004`, which Google has since retired:
5
+ // the endpoint answers 404 "not found for API version v1beta", embedOne swallows
6
+ // it as a provider error, and every vector silently comes back from the offline
7
+ // `tf` fallback instead. Pin a model that is actually served, and override it
8
+ // with memory.embeddings.gemini.model when Google moves again.
3
9
 
4
10
  import { l2normalize } from "../embeddings.js";
5
11
 
6
- const DEFAULT_MODEL = "text-embedding-004";
12
+ const DEFAULT_MODEL = "gemini-embedding-001";
7
13
  const BASE = "https://generativelanguage.googleapis.com/v1beta";
8
14
 
9
15
  function getKey(config = {}, parentEnginesCfg) {
@@ -22,6 +22,7 @@
22
22
  import ollama from "./ollama.js";
23
23
  import openai from "./openai.js";
24
24
  import gemini from "./gemini.js";
25
+ import custom from "./custom.js";
25
26
  import tf from "./tf.js";
26
27
 
27
28
  const ADAPTERS = { ollama, openai, gemini, tf };
@@ -30,7 +31,29 @@ export const EMBED_ENGINE_IDS = Object.keys(ADAPTERS);
30
31
  // Local-first, then free-with-key, then paid, then offline fallback.
31
32
  export const AUTO_PREFERENCE = ["ollama", "gemini", "openai", "tf"];
32
33
 
34
+ // ── Custom providers ────────────────────────────────────────────────────────
35
+ // Users can add any number of OpenAI-compatible /embeddings endpoints (a local
36
+ // Zen / LiteLLM / llama.cpp server). They live under memory.embeddings.custom
37
+ // .<slug> and surface with id "custom:<slug>", all backed by the custom adapter.
38
+ // Mirrors the TTS engine registry (src/core/voice/engines/index.js).
39
+ export const CUSTOM_PREFIX = "custom:";
40
+
41
+ export function isCustomId(id) {
42
+ return typeof id === "string" && id.startsWith(CUSTOM_PREFIX);
43
+ }
44
+ function slugOf(id) {
45
+ return isCustomId(id) ? id.slice(CUSTOM_PREFIX.length) : id;
46
+ }
47
+ function customEngineIds(embedCfg) {
48
+ return Object.keys(embedCfg?.custom || {}).map((slug) => CUSTOM_PREFIX + slug);
49
+ }
50
+ function knownIds(embedCfg) {
51
+ return [...EMBED_ENGINE_IDS, ...customEngineIds(embedCfg)];
52
+ }
53
+
33
54
  export function getEmbedAdapter(provider) {
55
+ // Every custom provider is OpenAI-compatible → the custom adapter.
56
+ if (isCustomId(provider)) return custom;
34
57
  const a = ADAPTERS[provider];
35
58
  if (!a) {
36
59
  throw new Error(
@@ -42,8 +65,8 @@ export function getEmbedAdapter(provider) {
42
65
 
43
66
  export function embeddingsConfig(globalConfig) {
44
67
  const mem = globalConfig?.memory || {};
45
- // Back-compat: older configs only had the flat memory.embed_* keys. Fold them
46
- // into a synthetic ollama engine config so they keep working unchanged.
68
+ // memory.embed_* are the flat defaults every config ships with. With no
69
+ // explicit `embeddings.ollama` block, fold them into a synthetic one.
47
70
  const section = mem.embeddings || {};
48
71
  if (!section.ollama && (mem.embed_model || mem.embed_base_url || mem.embed_timeout_ms)) {
49
72
  return {
@@ -62,10 +85,17 @@ export function embeddingsConfig(globalConfig) {
62
85
  }
63
86
 
64
87
  function providerConfig(globalConfig, provider) {
65
- return embeddingsConfig(globalConfig)?.[provider] || {};
88
+ const emb = embeddingsConfig(globalConfig);
89
+ // A custom provider's config lives under .custom.<slug>; inject its own id so
90
+ // the adapter tags vectors with `custom:<slug>:model` (a distinct cosine space).
91
+ if (isCustomId(provider)) {
92
+ return { ...(emb?.custom?.[slugOf(provider)] || {}), _embedder_id: provider };
93
+ }
94
+ return emb?.[provider] || {};
66
95
  }
67
96
 
68
97
  function isEnabled(embedCfg, id) {
98
+ if (isCustomId(id)) return embedCfg?.custom?.[slugOf(id)]?.enabled !== false;
69
99
  return embedCfg?.[id]?.enabled !== false;
70
100
  }
71
101
 
@@ -76,15 +106,17 @@ export function resolveMode(embedCfg) {
76
106
  return p && p !== "auto" ? "single" : "chain";
77
107
  }
78
108
 
79
- /** Full chain order: user's custom order (known ids only) then the rest; tf last. */
109
+ /** Full chain order: user's saved order (known ids only) then the rest, with tf
110
+ * ALWAYS last (the guaranteed offline floor). "Known" now includes every
111
+ * custom:<slug> so added providers can be reordered. */
80
112
  export function resolveChainOrder(embedCfg) {
81
- const custom = Array.isArray(embedCfg?.order)
82
- ? embedCfg.order.filter((id) => EMBED_ENGINE_IDS.includes(id))
113
+ const known = knownIds(embedCfg);
114
+ const ordered = Array.isArray(embedCfg?.order)
115
+ ? embedCfg.order.filter((id) => known.includes(id) && id !== "tf")
83
116
  : [];
84
- const rest = AUTO_PREFERENCE.filter((id) => !custom.includes(id));
85
- const full = [...custom, ...rest];
86
- if (!full.includes("tf")) full.push("tf");
87
- return full;
117
+ const rest = [...AUTO_PREFERENCE.filter((id) => id !== "tf"), ...customEngineIds(embedCfg)]
118
+ .filter((id) => !ordered.includes(id));
119
+ return [...ordered, ...rest, "tf"];
88
120
  }
89
121
 
90
122
  /**
@@ -115,7 +147,7 @@ export async function selectEmbedEngine({ globalConfig, provider }) {
115
147
  // 3. Chain mode: probe the (enabled) order, first available wins.
116
148
  for (const id of resolveChainOrder(embedCfg)) {
117
149
  if (id !== "tf" && !isEnabled(embedCfg, id)) continue;
118
- const adapter = ADAPTERS[id];
150
+ const adapter = getEmbedAdapter(id);
119
151
  const cfg = providerConfig(globalConfig, id);
120
152
  try {
121
153
  if (await adapter.isAvailable(cfg, globalConfig?.engines)) {
@@ -126,12 +158,50 @@ export async function selectEmbedEngine({ globalConfig, provider }) {
126
158
  return { provider: "tf", adapter: tf, engineConfig: providerConfig(globalConfig, "tf") };
127
159
  }
128
160
 
129
- /** Discover which engines are configured/available right now. */
161
+ /**
162
+ * The ORDERED list of engines embedOne should try this call, first to last,
163
+ * before the guaranteed tf floor. Unlike selectEmbedEngine (which picks ONE by
164
+ * isAvailable), this returns every enabled+available engine in chain order so
165
+ * embedOne can fall through to the next one when a call FAILS at runtime — a
166
+ * rate-limited (429) or down provider, not just a keyless one. tf is omitted
167
+ * here; embedOne appends it as the final fallback.
168
+ * Returns [{ provider, adapter, engineConfig }].
169
+ */
170
+ export async function selectEmbedChain({ globalConfig }) {
171
+ const embedCfg = embeddingsConfig(globalConfig);
172
+
173
+ // Single mode: exactly the configured engine, no fallback (matches the mode's
174
+ // contract; embedOne still drops to tf if that one engine errors).
175
+ if (resolveMode(embedCfg) === "single") {
176
+ const id = embedCfg?.provider;
177
+ if (id && id !== "auto" && (ADAPTERS[id] || isCustomId(id))) {
178
+ return [{ provider: id, adapter: getEmbedAdapter(id), engineConfig: providerConfig(globalConfig, id) }];
179
+ }
180
+ }
181
+
182
+ const out = [];
183
+ for (const id of resolveChainOrder(embedCfg)) {
184
+ if (id === "tf") continue;
185
+ if (!isEnabled(embedCfg, id)) continue;
186
+ let adapter;
187
+ try { adapter = getEmbedAdapter(id); } catch { continue; }
188
+ const cfg = providerConfig(globalConfig, id);
189
+ try {
190
+ if (await adapter.isAvailable(cfg, globalConfig?.engines)) {
191
+ out.push({ provider: id, adapter, engineConfig: cfg });
192
+ }
193
+ } catch { /* probe failures skip the engine */ }
194
+ }
195
+ return out;
196
+ }
197
+
198
+ /** Discover which engines are configured/available right now — built-ins plus
199
+ * every user-added custom:<slug> provider. */
130
200
  export async function listAvailableEmbedEngines(globalConfig) {
131
201
  const embedCfg = embeddingsConfig(globalConfig);
132
202
  const out = [];
133
- for (const id of EMBED_ENGINE_IDS) {
134
- const adapter = ADAPTERS[id];
203
+ for (const id of knownIds(embedCfg)) {
204
+ const adapter = getEmbedAdapter(id);
135
205
  const cfg = providerConfig(globalConfig, id);
136
206
  let available = false;
137
207
  try {
@@ -140,8 +210,10 @@ export async function listAvailableEmbedEngines(globalConfig) {
140
210
  out.push({
141
211
  id,
142
212
  available,
143
- configured: Object.keys(cfg).filter((k) => k !== "enabled").length > 0,
213
+ // `_embedder_id` is injected for custom blocks, not user-set config.
214
+ configured: Object.keys(cfg).filter((k) => k !== "enabled" && k !== "_embedder_id").length > 0,
144
215
  enabled: isEnabled(embedCfg, id),
216
+ ...(isCustomId(id) ? { custom: true, label: cfg.label || slugOf(id) } : {}),
145
217
  });
146
218
  }
147
219
  return out;
@@ -19,7 +19,7 @@
19
19
  // NOTE: this module is the leaf the engine adapters import (l2normalize /
20
20
  // tfEmbed). It also imports the registry — ESM live bindings make this safe
21
21
  // because selectEmbedEngine is only referenced at call time, never at init.
22
- import { selectEmbedEngine } from "./embed-engines/index.js";
22
+ import { selectEmbedEngine, selectEmbedChain } from "./embed-engines/index.js";
23
23
 
24
24
  const TF_DIM = 256;
25
25
 
@@ -85,35 +85,57 @@ function tfResult(text) {
85
85
  return { vector: v, embedder: "tf", dim: v.length };
86
86
  }
87
87
 
88
+ // The provider id inside an embedder tag ("ollama:nomic-embed-text" → "ollama",
89
+ // "tf" → "tf"). Used to pin a batch to whatever actually answered.
90
+ export function embedderProvider(embedder) {
91
+ return String(embedder || "tf").split(":")[0];
92
+ }
93
+
94
+ async function tryEmbed({ adapter, engineConfig }, clean, opts) {
95
+ const timeoutMs = opts.timeoutMs || engineConfig?.timeout_ms || 4000;
96
+ const out = await adapter.embed({
97
+ text: clean,
98
+ config: engineConfig || {},
99
+ parentEnginesCfg: opts.globalConfig?.engines,
100
+ timeoutMs,
101
+ signal: opts.signal,
102
+ });
103
+ if (!out || !Array.isArray(out.vector) || out.vector.length === 0) {
104
+ throw new Error("empty vector");
105
+ }
106
+ return out;
107
+ }
108
+
88
109
  export async function embedOne(text, opts = {}) {
89
110
  const clean = String(text || "").slice(0, 8000);
90
111
  if (!clean.trim()) return tfResult("");
91
112
  if (opts.forceTf) return tfResult(clean);
92
113
 
93
- let adapter, engineConfig;
94
- try {
95
- ({ adapter, engineConfig } = await selectEmbedEngine({
96
- globalConfig: opts.globalConfig,
97
- provider: opts.provider,
98
- }));
99
- } catch {
100
- return tfResult(clean);
114
+ // Explicit provider (test UI, batch fast-path): exactly that engine, no chain
115
+ // fallback — tf only if it errors. This keeps the tester honest about one
116
+ // provider and lets a batch pin to the engine that already won.
117
+ if (opts.provider && opts.provider !== "auto") {
118
+ try {
119
+ const picked = await selectEmbedEngine({ globalConfig: opts.globalConfig, provider: opts.provider });
120
+ return await tryEmbed(picked, clean, opts);
121
+ } catch {
122
+ return tfResult(clean);
123
+ }
101
124
  }
102
125
 
103
- const timeoutMs = opts.timeoutMs || engineConfig?.timeout_ms || 4000;
104
- try {
105
- const out = await adapter.embed({
106
- text: clean,
107
- config: engineConfig || {},
108
- parentEnginesCfg: opts.globalConfig?.engines,
109
- timeoutMs,
110
- signal: opts.signal,
111
- });
112
- if (!out || !Array.isArray(out.vector) || out.vector.length === 0) return tfResult(clean);
113
- return out;
114
- } catch {
115
- return tfResult(clean);
126
+ // Chain: walk every enabled+available engine in order and fall through to the
127
+ // NEXT one whenever a call fails at runtime — a rate-limited (429) or down
128
+ // provider, not just a keyless one. This is the "falls back to the next if one
129
+ // fails" the UI promises; before, a 429 on the first engine dropped straight
130
+ // to the offline tf floor and skipped a working local Ollama behind it.
131
+ let chain = [];
132
+ try { chain = await selectEmbedChain({ globalConfig: opts.globalConfig }); } catch { /* → tf */ }
133
+ for (const engine of chain) {
134
+ try {
135
+ return await tryEmbed(engine, clean, opts);
136
+ } catch { /* try the next engine in the chain */ }
116
137
  }
138
+ return tfResult(clean);
117
139
  }
118
140
 
119
141
  // Embed many strings. Probes the provider once with the first item; if that
@@ -124,9 +146,15 @@ export async function embedBatch(texts, opts = {}) {
124
146
  if (list.length === 0) return [];
125
147
  const first = await embedOne(list[0], opts);
126
148
  const out = [first];
127
- const forceTf = first.embedder === "tf" ? { ...opts, forceTf: true } : opts;
149
+ // Pin the rest to whatever actually answered. Without this, a chain that fell
150
+ // past a rate-limited gemini to ollama would re-probe (and re-429) gemini for
151
+ // every remaining item — slow and quota-burning. tf → forceTf; a real engine →
152
+ // call it directly (no chain re-walk).
153
+ const restOpts = first.embedder === "tf"
154
+ ? { ...opts, forceTf: true }
155
+ : { ...opts, provider: embedderProvider(first.embedder) };
128
156
  for (let i = 1; i < list.length; i++) {
129
- out.push(await embedOne(list[i], forceTf));
157
+ out.push(await embedOne(list[i], restOpts));
130
158
  }
131
159
  return out;
132
160
  }
@@ -41,7 +41,17 @@ let _indexing = false;
41
41
  function indexOnce(note) {
42
42
  if (_indexing || !_store) return Promise.resolve();
43
43
  _indexing = true;
44
- return indexNewMessages(_store, { embed: embedOptsFromConfig(_cfg), projects: _projects, log: note })
44
+ return indexNewMessages(_store, {
45
+ embed: embedOptsFromConfig(_cfg),
46
+ projects: _projects,
47
+ log: note,
48
+ // How many chunks one pass may embed. A local embedder happily takes the
49
+ // default; a hosted one on a free tier does not — 1000 embed calls a minute
50
+ // is a 429, and a rate-limited provider degrades to `tf`, which the
51
+ // consistency guard then (correctly) refuses to write. Smaller passes, more
52
+ // of them: `memory.index_batch`.
53
+ ...(Number(_cfg?.memory?.index_batch) > 0 ? { limit: Number(_cfg.memory.index_batch) } : {}),
54
+ })
45
55
  .catch(() => {})
46
56
  .finally(() => {
47
57
  _indexing = false;
@@ -54,8 +64,8 @@ export function memoryEnabled(config) {
54
64
 
55
65
  // The embeddings provider is resolved from config.memory.embeddings inside
56
66
  // embedOne/embedBatch via the engine registry — so all we forward here is the
57
- // live config. (Legacy memory.embed_* keys are still honored by the registry's
58
- // back-compat shim in embed-engines/index.js.)
67
+ // live config. (The flat memory.embed_* defaults are folded into a synthetic
68
+ // ollama block by embed-engines/index.js.)
59
69
  function embedOptsFromConfig(config) {
60
70
  return { globalConfig: config };
61
71
  }
@@ -238,24 +238,66 @@ function collectAgentMemoryChunks(store, apxHome) {
238
238
 
239
239
  // Project memory (.apc/memory.md) for every registered project. Needs the
240
240
  // registry to map id → repo path. Scoped by channel "project:<id>".
241
+ //
242
+ // BOTH halves are indexed under the same scope: the curated `.apc/memory.md` and
243
+ // the local `~/.apx/projects/<id>/memory.md` the agent writes. They differ in who
244
+ // may write them and whether git carries them — not in whether they are true, and
245
+ // retrieval that saw only the committed half would miss everything the agent has
246
+ // learned since the owner last curated.
241
247
  function collectProjectMemoryChunks(store, projects) {
242
248
  const fresh = [];
243
249
  const list = typeof projects?.list === "function" ? projects.list() : Array.isArray(projects) ? projects : [];
244
250
  for (const entry of list) {
245
251
  const root = entry?.path;
246
252
  if (!root) continue; // the default project (id 0) has no repo root
247
- const body = readIfExists(apcMemoryFile(root));
248
- if (body == null) continue;
253
+ const files = [
254
+ ["projmem", readIfExists(apcMemoryFile(root))],
255
+ ["projlocalmem", entry?.storage_path || entry?.storagePath
256
+ ? readIfExists(path.join(entry.storage_path || entry.storagePath, "memory.md"))
257
+ : null],
258
+ ];
249
259
  const channel = `project:${entry.id}`;
250
- for (const block of chunkFreeMarkdown(body)) {
251
- const id = `projmem:${entry.id}:${fnv1aHex(block)}`;
252
- if (store.hasId(id)) continue;
253
- fresh.push({ id, source: "project-memory", channel, ts: "", tag: "project-memory", text: block });
260
+ for (const [prefix, body] of files) {
261
+ if (body == null) continue;
262
+ for (const block of chunkFreeMarkdown(body)) {
263
+ const id = `${prefix}:${entry.id}:${fnv1aHex(block)}`;
264
+ if (store.hasId(id)) continue;
265
+ fresh.push({ id, source: "project-memory", channel, ts: "", tag: "project-memory", text: block });
266
+ }
254
267
  }
255
268
  }
256
269
  return fresh;
257
270
  }
258
271
 
272
+ // The offline fallback embedder. Everything else is a real provider space.
273
+ const TF_FAMILY = "tf";
274
+
275
+ /**
276
+ * Provider family of an embedder id ("ollama:nomic-embed-text" → "ollama").
277
+ * Vectors are only comparable within one family.
278
+ */
279
+ export function embedderFamily(embedder) {
280
+ const id = String(embedder || TF_FAMILY);
281
+ const cut = id.indexOf(":");
282
+ return cut === -1 ? id : id.slice(0, cut);
283
+ }
284
+
285
+ /**
286
+ * What to do when the live embedder does not match the one the store was built
287
+ * with. Pure, so the policy is testable without a reachable provider.
288
+ *
289
+ * "ok" — same space (or an empty store); index normally.
290
+ * "reindex" — moving into a real provider space; wipe and re-embed history.
291
+ * "skip" — the provider is down and we would fall back to `tf`; leave the
292
+ * good store alone rather than mix spaces.
293
+ *
294
+ * @returns {"ok"|"reindex"|"skip"}
295
+ */
296
+ export function reconcileEmbedder(stored, family, hasRows) {
297
+ if (!stored || stored === family || !hasRows) return "ok";
298
+ return family === TF_FAMILY ? "skip" : "reindex";
299
+ }
300
+
259
301
  // Run one incremental indexing pass. Returns { indexed, backend }.
260
302
  // `opts.embed` overrides embedding options (baseUrl/model/timeoutMs) for tests.
261
303
  export async function indexNewMessages(store, opts = {}) {
@@ -270,32 +312,46 @@ export async function indexNewMessages(store, opts = {}) {
270
312
  cursor.channels = cursor.channels || {};
271
313
 
272
314
  // Embedder-consistency guard. Cosine only works within one embedder space, so
273
- // the whole store must share an embedder family ("ollama" vs "tf"). Probe the
274
- // currently-active embedder and reconcile:
275
- // - upgrade (tf ollama, i.e. Ollama came back): wipe + full re-index so
276
- // the history is re-embedded in the better space.
277
- // - downgrade (ollama tf, i.e. Ollama went down): skip this pass entirely
278
- // so we never pollute a good nomic store with TF rows.
315
+ // the whole store must share an embedder family. Probe the currently-active
316
+ // embedder and reconcile:
317
+ // - moving into a REAL provider space (from tf, or from another provider):
318
+ // wipe + full re-index so the history lives in one comparable space.
319
+ // - falling back to `tf` (the offline embedder the provider is down):
320
+ // skip this pass entirely rather than pollute a good store with tf rows.
321
+ //
322
+ // The family used to be a boolean, "ollama" vs everything-else-is-tf. That
323
+ // made Gemini and OpenAI indistinguishable from the degraded fallback, so
324
+ // putting Gemini first while Ollama was down read as a downgrade and indexing
325
+ // stopped forever instead of re-indexing into the Gemini space.
279
326
  const probe = await embedOne("apx memory embedder probe", embedOpts);
280
- const family = probe.embedder.startsWith("ollama") ? "ollama" : "tf";
327
+ const family = embedderFamily(probe.embedder);
281
328
  const stored = cursor.embedder || null;
282
- if (stored && stored !== family && store.count() > 0) {
283
- if (family === "ollama") {
284
- try {
285
- store.clear?.();
286
- } catch {
287
- /* ignore */
288
- }
289
- cursor.channels = {};
290
- log(`memory: embedder ${stored}→ollama — full re-index`);
291
- } else {
292
- log("memory: Ollama unavailable — skipping index to keep embedder consistent");
293
- cursor.embedder = stored; // keep the good signature
294
- writeCursor(cursorPath, cursor);
295
- return { indexed: 0, backend: store.backend, skipped: "embedder-downgrade" };
329
+ const verdict = reconcileEmbedder(stored, family, store.count() > 0);
330
+ if (verdict === "reindex") {
331
+ try {
332
+ store.clear?.();
333
+ } catch {
334
+ /* ignore */
296
335
  }
336
+ cursor.channels = {};
337
+ log(`memory: embedder ${stored}→${family} — full re-index`);
338
+ } else if (verdict === "skip") {
339
+ log(
340
+ `memory: embedding provider unavailable (would fall back to ${TF_FAMILY}) — ` +
341
+ `skipping index to keep the ${stored} store consistent`
342
+ );
343
+ cursor.embedder = stored; // keep the good signature
344
+ writeCursor(cursorPath, cursor);
345
+ return { indexed: 0, backend: store.backend, skipped: "embedder-downgrade" };
297
346
  }
347
+ // Persist the embedder signature NOW, before doing any work. It is separate
348
+ // state from the per-channel cursors: those may only advance on a complete
349
+ // pass, but the signature describes the store as it already is. Writing it at
350
+ // the end meant a CAPPED pass never recorded it — so the next pass still read
351
+ // the old family, wiped the store, re-embedded the same first `limit` chunks,
352
+ // capped again, and looped forever without the index ever growing.
298
353
  cursor.embedder = family;
354
+ writeCursor(cursorPath, cursor);
299
355
 
300
356
  const { fresh: msgChunks, maxTsByChannel } = collectMessageChunks(store, cursor, messagesDir);
301
357
  const memChunks = collectMemoryChunks(store, memoryPath);