@agentprojectcontext/apx 1.33.1 → 1.35.0

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 (208) hide show
  1. package/package.json +1 -1
  2. package/skills/apx/SKILL.md +49 -61
  3. package/src/core/agent/a2a/reply.js +48 -0
  4. package/src/core/agent/build-agent-system.js +136 -59
  5. package/src/core/agent/channels/voice-context.js +98 -0
  6. package/src/core/agent/memory.js +2 -1
  7. package/src/core/agent/prompt-builder.js +178 -124
  8. package/src/core/agent/prompts/channels/code.md +12 -10
  9. package/src/core/agent/prompts/channels/desktop.md +5 -32
  10. package/src/core/agent/prompts/channels/telegram.md +4 -15
  11. package/src/core/agent/prompts/channels/web_code.md +11 -11
  12. package/src/core/agent/prompts/core/agent-base.md +24 -0
  13. package/src/core/agent/prompts/core/project-agent.md +11 -0
  14. package/src/core/agent/prompts/core/super-agent.md +21 -0
  15. package/src/core/agent/prompts/discipline/action.md +10 -0
  16. package/src/core/agent/prompts/discipline/single-segment.md +6 -0
  17. package/src/core/agent/prompts/discipline/two-segment.md +11 -0
  18. package/src/core/agent/prompts/modes/code-build.md +1 -0
  19. package/src/core/agent/prompts/modes/code-plan.md +1 -0
  20. package/src/core/agent/prompts/modes/index.js +28 -0
  21. package/src/core/agent/self-memory.js +43 -1
  22. package/src/core/agent/skills/index-store.js +307 -0
  23. package/src/core/agent/skills/index.js +15 -1
  24. package/src/core/agent/skills/inspector.js +317 -0
  25. package/src/core/agent/skills/loader.js +22 -18
  26. package/src/core/agent/stream/turn-accumulator.js +73 -0
  27. package/src/core/agent/suggestions.js +37 -0
  28. package/src/core/agent/super-agent.js +7 -1
  29. package/src/core/agent/tools/handlers/_git.js +50 -0
  30. package/src/core/agent/tools/handlers/add-project.js +5 -2
  31. package/src/core/agent/tools/handlers/call-runtime.js +3 -2
  32. package/src/core/agent/tools/handlers/git-diff.js +44 -0
  33. package/src/core/agent/tools/handlers/git-log.js +38 -0
  34. package/src/core/agent/tools/handlers/git-show.js +34 -0
  35. package/src/core/agent/tools/handlers/git-status.js +61 -0
  36. package/src/core/agent/tools/handlers/transcribe-audio.js +1 -1
  37. package/src/core/agent/tools/helpers.js +2 -2
  38. package/src/core/agent/tools/names.js +169 -0
  39. package/src/core/agent/tools/registry-bridge.js +6 -14
  40. package/src/core/agent/tools/registry.js +103 -69
  41. package/src/core/apc/context-copy.js +27 -0
  42. package/src/core/apc/notes.js +19 -0
  43. package/src/core/apc/parser.js +12 -5
  44. package/src/core/apc/paths.js +87 -0
  45. package/src/core/apc/scaffold.js +82 -76
  46. package/src/core/apc/skill-sync.js +10 -0
  47. package/src/{host/daemon/plugins → core/channels}/telegram/dispatch.js +38 -16
  48. package/src/core/config/index.js +24 -2
  49. package/src/core/config/redact.js +95 -0
  50. package/src/core/constants/channels.js +2 -0
  51. package/src/core/constants/code-modes.js +10 -0
  52. package/src/core/constants/index.js +1 -0
  53. package/src/core/deck/manifest.js +186 -0
  54. package/src/core/engines/catalog.js +83 -0
  55. package/src/core/{tools → http-tools}/browser.js +0 -1
  56. package/src/core/{tools → http-tools}/fetch.js +0 -1
  57. package/src/core/{tools → http-tools}/glob.js +0 -1
  58. package/src/core/{tools → http-tools}/grep.js +0 -1
  59. package/src/core/{tools → http-tools}/registry.js +0 -1
  60. package/src/core/{tools → http-tools}/search.js +0 -1
  61. package/src/core/i18n/en.js +9 -0
  62. package/src/core/i18n/es.js +12 -0
  63. package/src/core/i18n/index.js +54 -0
  64. package/src/core/i18n/pt.js +9 -0
  65. package/src/core/identity/telegram.js +2 -1
  66. package/src/core/mcp/runner.js +272 -14
  67. package/src/core/mcp/sources.js +3 -2
  68. package/src/core/routines/index.js +16 -0
  69. package/src/{host/daemon/routines.js → core/routines/runner.js} +36 -103
  70. package/src/core/runtime-skills/apc-context/SKILL.md +159 -0
  71. package/src/core/runtime-skills/apx/SKILL.md +83 -0
  72. package/src/core/runtime-skills/apx-agency-agents/SKILL.md +125 -0
  73. package/src/core/runtime-skills/apx-agent/SKILL.md +97 -0
  74. package/src/core/runtime-skills/apx-mcp/SKILL.md +111 -0
  75. package/src/core/runtime-skills/apx-mcp-builder/SKILL.md +169 -0
  76. package/{skills → src/core/runtime-skills}/apx-project/SKILL.md +20 -29
  77. package/src/core/runtime-skills/apx-routine/SKILL.md +127 -0
  78. package/src/core/runtime-skills/apx-runtime/SKILL.md +99 -0
  79. package/src/core/runtime-skills/apx-sessions/SKILL.md +232 -0
  80. package/src/core/runtime-skills/apx-skill-builder/SKILL.md +129 -0
  81. package/{skills → src/core/runtime-skills}/apx-task/SKILL.md +18 -21
  82. package/src/core/runtime-skills/apx-telegram/SKILL.md +120 -0
  83. package/src/core/runtime-skills/apx-voice/SKILL.md +117 -0
  84. package/src/core/runtime-skills/{claude-code.md → claude-code/SKILL.md} +1 -0
  85. package/src/core/runtime-skills/{codex-cli.md → codex-cli/SKILL.md} +1 -0
  86. package/src/core/runtime-skills/{opencode-cli.md → opencode-cli/SKILL.md} +1 -0
  87. package/src/core/runtime-skills/{openrouter.md → openrouter/SKILL.md} +1 -0
  88. package/src/{host/daemon/env-detect.js → core/runtimes/detect.js} +1 -1
  89. package/src/core/stores/code-sessions.js +50 -2
  90. package/src/core/stores/routine-memory.js +1 -1
  91. package/src/core/stores/sessions-search.js +121 -0
  92. package/src/core/stores/sessions.js +38 -0
  93. package/src/core/vars/index.js +14 -0
  94. package/src/core/vars/interpolate.js +86 -0
  95. package/src/core/vars/sources.js +151 -0
  96. package/src/core/voice/audio-decode.js +38 -0
  97. package/src/core/voice/transcription.js +225 -0
  98. package/src/host/daemon/api/admin-config.js +5 -82
  99. package/src/host/daemon/api/agents.js +5 -5
  100. package/src/host/daemon/api/code.js +17 -169
  101. package/src/host/daemon/api/config.js +3 -4
  102. package/src/host/daemon/api/conversations.js +8 -29
  103. package/src/host/daemon/api/deck.js +37 -404
  104. package/src/host/daemon/api/engines.js +1 -80
  105. package/src/host/daemon/api/exec.js +1 -1
  106. package/src/host/daemon/api/mcps.js +32 -0
  107. package/src/host/daemon/api/routines.js +1 -1
  108. package/src/host/daemon/api/runtimes.js +4 -3
  109. package/src/host/daemon/api/sessions-search.js +24 -140
  110. package/src/host/daemon/api/sessions.js +12 -30
  111. package/src/host/daemon/api/shared.js +2 -1
  112. package/src/host/daemon/api/skills.js +140 -6
  113. package/src/host/daemon/api/super-agent.js +56 -1
  114. package/src/host/daemon/api/telegram.js +1 -11
  115. package/src/host/daemon/api/tools.js +6 -6
  116. package/src/host/daemon/api/transcribe.js +2 -2
  117. package/src/host/daemon/api/vars.js +137 -0
  118. package/src/host/daemon/api/voice.js +13 -290
  119. package/src/host/daemon/api.js +2 -0
  120. package/src/host/daemon/db.js +6 -6
  121. package/src/host/daemon/deck-exec.js +148 -0
  122. package/src/host/daemon/index.js +20 -3
  123. package/src/host/daemon/plugins/telegram/index.js +9 -9
  124. package/src/host/daemon/routines-scheduler.js +64 -0
  125. package/src/host/daemon/smoke.js +3 -2
  126. package/src/host/daemon/whisper-server.js +225 -0
  127. package/src/interfaces/cli/branding.js +53 -0
  128. package/src/interfaces/cli/commands/agent.js +3 -2
  129. package/src/interfaces/cli/commands/command.js +2 -3
  130. package/src/interfaces/cli/commands/messages.js +6 -2
  131. package/src/interfaces/cli/commands/pair.js +5 -4
  132. package/src/interfaces/cli/commands/search.js +1 -1
  133. package/src/interfaces/cli/commands/sessions.js +3 -2
  134. package/src/interfaces/cli/commands/skills.js +290 -55
  135. package/src/interfaces/cli/index.js +84 -2
  136. package/src/interfaces/web/dist/assets/index-C0fm31dY.js +618 -0
  137. package/src/interfaces/web/dist/assets/index-C0fm31dY.js.map +1 -0
  138. package/src/interfaces/web/dist/assets/index-UcAqlBO6.css +1 -0
  139. package/src/interfaces/web/dist/index.html +2 -2
  140. package/src/interfaces/web/package-lock.json +182 -182
  141. package/src/interfaces/web/src/components/ModelCombobox.tsx +2 -1
  142. package/src/interfaces/web/src/components/TelegramChannelDialog.tsx +1 -1
  143. package/src/interfaces/web/src/components/chat/AskAnswersCard.tsx +76 -0
  144. package/src/interfaces/web/src/components/chat/MessageBubble.tsx +37 -4
  145. package/src/interfaces/web/src/components/chat/MessageList.tsx +23 -1
  146. package/src/interfaces/web/src/components/chat/ModelPicker.tsx +3 -1
  147. package/src/interfaces/web/src/components/code/CodeArtifactsTab.tsx +4 -4
  148. package/src/interfaces/web/src/components/code/CodeChangesTab.tsx +1 -1
  149. package/src/interfaces/web/src/components/code/CodeFileTree.tsx +3 -2
  150. package/src/interfaces/web/src/components/code/CodeFileViewer.tsx +3 -2
  151. package/src/interfaces/web/src/components/code/CodeTerminal.tsx +3 -2
  152. package/src/interfaces/web/src/components/config/GlobalConfigEditor.tsx +2 -1
  153. package/src/interfaces/web/src/components/deck/WidgetRow.tsx +2 -1
  154. package/src/interfaces/web/src/components/inputs/KeyValueList.tsx +93 -0
  155. package/src/interfaces/web/src/components/inputs/VarTokenInput.tsx +449 -0
  156. package/src/interfaces/web/src/components/settings/DefaultRouterCard.tsx +2 -1
  157. package/src/interfaces/web/src/components/settings/EnginesPanel.tsx +2 -2
  158. package/src/interfaces/web/src/components/settings/MemoryPanel.tsx +73 -4
  159. package/src/interfaces/web/src/components/settings/SkillsInspectorPanel.tsx +222 -0
  160. package/src/interfaces/web/src/components/settings/providers/ProviderCard.tsx +3 -2
  161. package/src/interfaces/web/src/components/settings/providers/ProviderModal.tsx +3 -2
  162. package/src/interfaces/web/src/components/ui/chat-input.tsx +5 -4
  163. package/src/interfaces/web/src/components/ui/sidebar.tsx +3 -2
  164. package/src/interfaces/web/src/components/voice/VoiceProviderModal.tsx +2 -1
  165. package/src/interfaces/web/src/constants/index.ts +1 -1
  166. package/src/interfaces/web/src/hooks/useChat.ts +19 -0
  167. package/src/interfaces/web/src/i18n/en.ts +175 -7
  168. package/src/interfaces/web/src/i18n/es.ts +180 -15
  169. package/src/interfaces/web/src/lib/api/mcps.ts +25 -0
  170. package/src/interfaces/web/src/lib/api/skills.ts +70 -0
  171. package/src/interfaces/web/src/lib/api/vars.ts +38 -0
  172. package/src/interfaces/web/src/lib/api.ts +1 -0
  173. package/src/interfaces/web/src/screens/ProjectScreen.tsx +8 -31
  174. package/src/interfaces/web/src/screens/SettingsScreen.tsx +6 -2
  175. package/src/interfaces/web/src/screens/modules/CodeScreen.tsx +1 -1
  176. package/src/interfaces/web/src/screens/modules/DeckScreen.tsx +4 -3
  177. package/src/interfaces/web/src/screens/modules/DesktopScreen.tsx +7 -6
  178. package/src/interfaces/web/src/screens/modules/VoiceScreen.tsx +4 -3
  179. package/src/interfaces/web/src/screens/project/AgentDetailScreen.tsx +1 -1
  180. package/src/interfaces/web/src/screens/project/ConfigTab.tsx +132 -1
  181. package/src/interfaces/web/src/screens/project/McpsTab.tsx +549 -104
  182. package/src/interfaces/web/src/screens/project/RoutinesTab.tsx +1 -1
  183. package/src/interfaces/web/src/screens/project/VarsTab.tsx +300 -0
  184. package/src/interfaces/web/src/types/daemon.ts +15 -0
  185. package/skills/apx-agency-agents/SKILL.md +0 -141
  186. package/skills/apx-agent/SKILL.md +0 -100
  187. package/skills/apx-mcp-builder/SKILL.md +0 -183
  188. package/skills/apx-routine/SKILL.md +0 -140
  189. package/skills/apx-runtime/SKILL.md +0 -117
  190. package/skills/apx-sessions/SKILL.md +0 -281
  191. package/skills/apx-skill-builder/SKILL.md +0 -153
  192. package/skills/apx-telegram/SKILL.md +0 -131
  193. package/skills/apx-voice/SKILL.md +0 -137
  194. package/src/core/agent/prompts/action-discipline.md +0 -24
  195. package/src/core/agent/prompts/super-agent-base.md +0 -42
  196. package/src/host/daemon/transcription.js +0 -538
  197. package/src/host/daemon/whisper-transcribe.py +0 -73
  198. package/src/interfaces/web/dist/assets/index-Aaiw8BZN.css +0 -1
  199. package/src/interfaces/web/dist/assets/index-DPqtjDjh.js +0 -602
  200. package/src/interfaces/web/dist/assets/index-DPqtjDjh.js.map +0 -1
  201. /package/src/{host/daemon → core/apc}/projects-helpers.js +0 -0
  202. /package/src/{host/daemon/plugins → core/channels}/telegram/ask.js +0 -0
  203. /package/src/{host/daemon/plugins → core/channels}/telegram/helpers.js +0 -0
  204. /package/src/{host/daemon/plugins → core/channels}/telegram/media.js +0 -0
  205. /package/src/core/{tools → http-tools}/index.js +0 -0
  206. /package/src/{host/daemon/compact.js → core/stores/conversations-compactor.js} +0 -0
  207. /package/src/{host/daemon → core/stores}/conversations.js +0 -0
  208. /package/src/{host/daemon → core/util}/thinking.js +0 -0
@@ -254,6 +254,18 @@ export const es = {
254
254
  unregistered: "Desregistrado.",
255
255
  base_subtitle: "Espacio general · super-agente",
256
256
 
257
+ danger: {
258
+ title: "Zona peligrosa",
259
+ subtitle: "Acciones que afectan el registro del proyecto en APX. No tocan archivos del repo.",
260
+ rebuild_desc: "Re-escanea .apc/, MCPs y agents y regenera el contexto del super-agente para este proyecto.",
261
+ unregister_desc: "Quita el proyecto del registry de APX. La carpeta del disco se mantiene intacta.",
262
+ rebuild_confirm_title: "Rebuild context",
263
+ rebuild_confirm_desc: "Regenerar contexto de {label}.",
264
+ rebuild_long: "Vuelve a leer la config APC, lista MCPs y agents disponibles, y reconstruye el system prompt del super-agente. Es seguro de correr — no borra nada. Usalo después de tocar .apc/ a mano o si los cambios no se reflejan.",
265
+ unregister_confirm_title: "Desregistrar proyecto",
266
+ unregister_long: "El proyecto deja de aparecer en `apx`. Los archivos del disco (.apc/, código, todo) se mantienen. Podés volver a registrarlo con `apx project register <path>`.",
267
+ },
268
+
257
269
  nav: {
258
270
  overview: "Overview",
259
271
  chat: "Chat",
@@ -263,6 +275,7 @@ export const es = {
263
275
  routines: "Rutinas",
264
276
  tasks: "Tasks",
265
277
  mcps: "MCPs",
278
+ vars: "Variables",
266
279
  threads: "Chats",
267
280
  logs: "Logs",
268
281
  memories: "Memorias",
@@ -480,31 +493,99 @@ export const es = {
480
493
 
481
494
  mcps: {
482
495
  title: "MCP servers",
483
- subtitle: "3 scopes: runtime > shared > global. Conflictos arriba si los hay.",
496
+ subtitle: "3 scopes: Runtime > Shared > Global. Conflictos arriba si los hay.",
484
497
  empty: "Sin MCPs configurados.",
485
498
  new: "MCP",
486
499
  delete_confirm: "Borrar MCP {name} de scope {scope}?",
487
500
  conflicts: "⚠ Conflictos: {names}",
488
501
  new_title: "Nuevo MCP",
489
- new_desc: "POST /projects/:pid/mcps?scope=…",
490
- scope_label: "scope",
491
- transport_label: "transport",
492
- name_label: "name",
493
- name_ph: "filesystem",
494
- cmd_label: "command",
502
+ edit_title: "Editar MCP",
503
+ new_desc: "Se guarda según el scope elegido. Los valores con ${var.X} se resuelven al arrancar el MCP.",
504
+ scope_label: "Scope",
505
+ scope_runtime: "Runtime",
506
+ scope_shared: "Shared",
507
+ scope_global: "Global",
508
+ scope_runtime_desc: "Solo este proyecto · con secrets · no se commitea (~/.apx/projects/<id>/mcps.json)",
509
+ scope_shared_desc: "Solo este proyecto · committeable · sin secrets (.apc/mcps.json)",
510
+ scope_global_desc: "Todos los proyectos de esta máquina (~/.apx/mcps.json)",
511
+ transport_stdio: "stdio",
512
+ transport_http: "HTTP",
513
+ transport_stdio_desc: "Proceso local — `command` + args",
514
+ transport_http_desc: "Endpoint remoto — URL + headers",
515
+ transport_label: "Transport",
516
+ name_label: "Nombre",
517
+ name_ph: "my-mcp",
518
+ cmd_label: "Comando",
495
519
  cmd_ph: "npx",
496
- args_label: "args",
497
- args_hint: "space-separated",
498
- args_ph: "-y @modelcontextprotocol/server-filesystem /tmp",
499
- env_label: "env (JSON, opcional)",
500
- url_label: "url",
501
- url_ph: "https://example.com/mcp",
520
+ args_label: "Args",
521
+ args_hint_tokens: "Una entrada por argumento. Usá el botón + para insertar variables.",
522
+ env_label: "Env",
523
+ env_hint_tokens: "Pares clave/valor. Los valores aceptan ${var.NOMBRE} (botón + a la derecha).",
524
+ env_empty: "Sin variables de entorno.",
525
+ url_label: "URL",
526
+ url_ph: "https://example.com/v2/mcp",
527
+ headers_label: "Headers",
528
+ headers_hint: "Pares clave/valor — típicamente Authorization: Bearer ${var.TOKEN}.",
529
+ headers_empty: "Sin headers.",
502
530
  enabled_label: "Habilitado",
503
531
  add_btn: "Agregar",
504
- name_required: "name requerido",
505
- env_invalid: "env debe ser JSON válido",
532
+ save_btn: "Guardar",
533
+ add_arg: "Agregar arg",
534
+ edit_btn: "Editar",
535
+ test_btn: "Probar",
536
+ logs_btn: "Logs",
537
+ testing: "Probando…",
538
+ test_ok: "OK · {n} tools disponibles",
539
+ logs_title: "Logs · {name}",
540
+ logs_empty: "Sin logs todavía. Arrancá el MCP llamando un tool o probando.",
541
+ logs_events: "Eventos recientes",
542
+ logs_stderr: "stderr (últimos 4KB)",
543
+ logs_panel_title: "Live logs",
544
+ logs_panel_pick: "elegí un MCP",
545
+ logs_panel_hint: "Click un MCP de la lista para ver lo que está pasando en vivo.",
546
+ logs_panel_idle: "Sin actividad. Apretá Probar para arrancarlo.",
547
+ name_required: "Nombre requerido",
506
548
  removed: "eliminado",
507
549
  added: "MCP agregado.",
550
+ updated: "MCP actualizado.",
551
+ },
552
+
553
+ vars: {
554
+ title: "Variables",
555
+ subtitle_project: "Reemplazan ${var.NOMBRE} al cargar MCPs y plantillas. Las del proyecto ganan sobre las globales. Se guardan fuera del repo (~/.apx/, chmod 0600).",
556
+ subtitle_base: "Variables globales — disponibles para todos los proyectos. Se guardan en ~/.apx/vars.json (chmod 0600).",
557
+ empty: "Sin variables todavía.",
558
+ new: "Variable",
559
+ new_title: "Nueva variable",
560
+ edit_title: "Editar variable",
561
+ new_desc: "Se referencia como ${var.NOMBRE} en cualquier campo que soporte interpolación.",
562
+ reveal_all: "Mostrar valores",
563
+ reveal: "Mostrar",
564
+ hide: "Ocultar",
565
+ filter_label: "Mostrar:",
566
+ filter_all: "Todas",
567
+ filter_project: "Sólo proyecto",
568
+ filter_global: "Sólo globales",
569
+ scope_label: "Scope",
570
+ scope_project: "proyecto",
571
+ scope_project_desc: "Sólo este proyecto. Pisa la global con mismo nombre.",
572
+ scope_global: "global",
573
+ scope_global_desc: "Disponible en todos los proyectos.",
574
+ name_label: "Nombre",
575
+ name_hint: "Mayúsculas, dígitos y _. P. ej.: MY_API_KEY, GITHUB_TOKEN.",
576
+ value_label: "Valor",
577
+ value_hint: "Se guarda en disco con permisos 0600. Nunca se commitea.",
578
+ value_edit_ph: "(dejá vacío para no cambiarlo… aún no soportado, pegá el valor de nuevo)",
579
+ add_btn: "Agregar",
580
+ save_btn: "Guardar",
581
+ edit_btn: "Editar",
582
+ delete_btn: "Borrar",
583
+ delete_confirm: "¿Borrar {name} ({scope})?",
584
+ removed: "Variable eliminada.",
585
+ added: "Variable agregada.",
586
+ updated: "Variable actualizada.",
587
+ name_required: "Nombre requerido.",
588
+ value_required: "Valor requerido.",
508
589
  },
509
590
 
510
591
  threads: {
@@ -784,6 +865,90 @@ export const es = {
784
865
  changes_no_git: "Los cambios necesitan un repo git. Este proyecto no lo es.",
785
866
  changes_files: "{n} archivo(s) cambiados",
786
867
  stopped: "[detenido]",
868
+ close: "Cerrar",
869
+ reload: "Recargar",
870
+ discard_changes: "Descartar cambios",
871
+ save_shortcut_hint: "Guardar (Cmd/Ctrl+S)",
872
+ artifacts_rename: "Renombrar",
873
+ artifacts_view: "Ver contenido",
874
+ artifacts_edit: "Editar contenido",
875
+ tree_collapse_all: "Colapsar todo",
876
+ terminal_clear: "Limpiar",
877
+ terminal_close: "Cerrar terminal",
878
+ },
879
+
880
+ desktop_screen: {
881
+ status_title: "Estado",
882
+ autostart_title: "Arranque automático",
883
+ shortcut_title: "Atajo de teclado",
884
+ appearance_title: "Apariencia",
885
+ activation_title: "Activación + transcripción",
886
+ last_conv_title: "Última conversación",
887
+ },
888
+
889
+ voice_screen: {
890
+ providers_title: "Proveedores de voz (TTS)",
891
+ test_title: "Probar voz",
892
+ stt_title: "Transcripción (STT)",
893
+ configure_provider: "Configurar {name}",
894
+ },
895
+
896
+ deck_screen: {
897
+ widgets_title: "Widgets",
898
+ context_title: "Contexto APX",
899
+ reload_manifest: "Recargar manifest",
900
+ widget_native: "Widget nativo APX",
901
+ widget_external: "Widget externo",
902
+ },
903
+
904
+ memory_panel: {
905
+ embeddings_title: "Embeddings (RAG)",
906
+ ollama_title: "Ollama (local)",
907
+ openai_title: "OpenAI",
908
+ gemini_title: "Gemini",
909
+ compaction_title: "Compactación de historial",
910
+ },
911
+
912
+ router_panel: {
913
+ title: "Router de modelos",
914
+ },
915
+
916
+ engines_panel: {
917
+ title: "Proveedores",
918
+ new_btn: "Nuevo proveedor",
919
+ },
920
+
921
+ providers_modal: {
922
+ new_title: "Nuevo proveedor",
923
+ edit_title: "Editar {name}",
924
+ list_models_hint: "Listar los modelos reales del proveedor",
925
+ toggle_active: "Activo · click para desactivar",
926
+ toggle_inactive: "Inactivo · click para activar",
927
+ delete: "Borrar",
928
+ },
929
+
930
+ chat_ui: {
931
+ copy: "Copiar",
932
+ stop: "Detener",
933
+ send: "Enviar",
934
+ pick_model: "Elegir modelo (o Auto)",
935
+ insert_variable: "Insertar variable",
936
+ },
937
+
938
+ sidebar_ui: {
939
+ toggle: "Mostrar/ocultar sidebar",
940
+ },
941
+
942
+ models_ui: {
943
+ invalid_hint: "Modelo/proveedor no disponible",
944
+ },
945
+
946
+ global_config: {
947
+ title: "Config APX",
948
+ },
949
+
950
+ agent_detail_extra: {
951
+ skills_title: "Skills & tools",
787
952
  },
788
953
  } as const;
789
954
 
@@ -19,6 +19,27 @@ export interface McpAddBody {
19
19
  enabled?: boolean;
20
20
  }
21
21
 
22
+ export interface McpTestResult {
23
+ ok: boolean;
24
+ tool_count?: number;
25
+ tools?: Array<{ name: string; description: string }>;
26
+ error?: string;
27
+ }
28
+
29
+ export interface McpLogsResult {
30
+ transport: "stdio" | "http";
31
+ running?: boolean;
32
+ command?: string;
33
+ args?: string[];
34
+ url?: string;
35
+ started_at?: string | null;
36
+ last_exit_code?: number | null;
37
+ last_error?: string | null;
38
+ stderr_tail?: string;
39
+ events: Array<{ ts: string; level: string; msg: string }>;
40
+ note?: string;
41
+ }
42
+
22
43
  export const Mcps = {
23
44
  list: (pid: string) => http.get<McpEntry[]>(`/projects/${pid}/mcps`),
24
45
  check: (pid: string) => http.get<McpCheck>(`/projects/${pid}/mcps/check`),
@@ -26,4 +47,8 @@ export const Mcps = {
26
47
  http.post<{ ok: true; name: string }>(`/projects/${pid}/mcps?scope=${scope}`, body),
27
48
  remove: (pid: string, name: string, scope: McpScope = "shared") =>
28
49
  http.del<void>(`/projects/${pid}/mcps/${encodeURIComponent(name)}?scope=${scope}`),
50
+ test: (pid: string, name: string) =>
51
+ http.post<McpTestResult>(`/projects/${pid}/mcps/${encodeURIComponent(name)}/test`, {}),
52
+ logs: (pid: string, name: string) =>
53
+ http.get<McpLogsResult>(`/projects/${pid}/mcps/${encodeURIComponent(name)}/logs`),
29
54
  };
@@ -11,6 +11,55 @@ export type SkillsList = {
11
11
  skills: SkillEntry[];
12
12
  };
13
13
 
14
+ export interface InspectorConfig {
15
+ enabled: boolean;
16
+ load_threshold: number;
17
+ hint_threshold: number;
18
+ margin: number;
19
+ max_loaded: number;
20
+ max_hints: number;
21
+ prompt_floor: number;
22
+ body_char_cap: number;
23
+ }
24
+
25
+ export interface IndexStatus {
26
+ count: number;
27
+ embedder: string | null;
28
+ dim: number | null;
29
+ updated_at: string | null;
30
+ }
31
+
32
+ export interface InspectorState {
33
+ config: InspectorConfig;
34
+ defaults: InspectorConfig;
35
+ keys: string[];
36
+ index: IndexStatus;
37
+ }
38
+
39
+ export interface IndexResult {
40
+ ok: boolean;
41
+ embedder: string;
42
+ dim: number;
43
+ planned: { missing: number; stale: number; gone: number; total: number };
44
+ changed: { added: number; refreshed: number; removed: number; kept: number };
45
+ index: IndexStatus;
46
+ }
47
+
48
+ export interface InspectTrace {
49
+ enabled: boolean;
50
+ reason?: string;
51
+ embedder?: string;
52
+ scored?: { slug: string; sim: number }[];
53
+ loaded?: string[];
54
+ hinted?: string[];
55
+ jit?: boolean;
56
+ }
57
+
58
+ export interface InspectResult {
59
+ trace: InspectTrace;
60
+ contextNote: string;
61
+ }
62
+
14
63
  export const Skills = {
15
64
  /**
16
65
  * List installed skills (bundled + user + optional project-scoped). The
@@ -22,4 +71,25 @@ export const Skills = {
22
71
  ? `/skills?project_path=${encodeURIComponent(projectPath)}`
23
72
  : "/skills",
24
73
  ),
74
+
75
+ /** Skill Inspector config + index status. */
76
+ inspector: () => http.get<InspectorState>("/skills/inspector"),
77
+
78
+ /** Patch inspector config (toggle / tune thresholds). */
79
+ updateInspector: (patch: Partial<InspectorConfig>) =>
80
+ http.put<{ ok: boolean; config: InspectorConfig; index: IndexStatus }>(
81
+ "/skills/inspector",
82
+ patch,
83
+ ),
84
+
85
+ /** (Re)build the inspector vector index. */
86
+ index: (body: { project_path?: string; force?: boolean } = {}) =>
87
+ http.post<IndexResult>("/skills/index", body),
88
+
89
+ /** Dry-run the inspector for a prompt (forces enabled). */
90
+ inspect: (prompt: string, projectPath?: string) =>
91
+ http.post<InspectResult>("/skills/inspect", {
92
+ prompt,
93
+ project_path: projectPath,
94
+ }),
25
95
  };
@@ -0,0 +1,38 @@
1
+ import { http } from "../http";
2
+
3
+ export type VarScope = "project" | "global";
4
+
5
+ export interface VarsList {
6
+ scope_hint: VarScope;
7
+ project: Record<string, string>;
8
+ global: Record<string, string>;
9
+ effective: Record<string, string>;
10
+ sources: Record<string, VarScope>;
11
+ }
12
+
13
+ export interface VarDetail {
14
+ name: string;
15
+ scope: VarScope;
16
+ value: string;
17
+ masked: boolean;
18
+ }
19
+
20
+ export const Vars = {
21
+ list: (pid: string, opts: { reveal?: boolean } = {}) =>
22
+ http.get<VarsList>(
23
+ `/projects/${pid}/vars${opts.reveal ? "?reveal=1" : ""}`,
24
+ ),
25
+ get: (pid: string, name: string, opts: { reveal?: boolean } = {}) =>
26
+ http.get<VarDetail>(
27
+ `/projects/${pid}/vars/${encodeURIComponent(name)}${opts.reveal ? "?reveal=1" : ""}`,
28
+ ),
29
+ upsert: (pid: string, body: { name: string; value: string; scope?: VarScope }) =>
30
+ http.post<{ ok: true; name: string; scope: VarScope }>(
31
+ `/projects/${pid}/vars`,
32
+ body,
33
+ ),
34
+ remove: (pid: string, name: string, scope: VarScope = "project") =>
35
+ http.del<void>(
36
+ `/projects/${pid}/vars/${encodeURIComponent(name)}?scope=${scope}`,
37
+ ),
38
+ };
@@ -9,6 +9,7 @@ export * from "./api/conversations";
9
9
  export * from "./api/routines";
10
10
  export * from "./api/tasks";
11
11
  export * from "./api/mcps";
12
+ export * from "./api/vars";
12
13
  export * from "./api/messages";
13
14
  export * from "./api/sessions";
14
15
  export * from "./api/tools";
@@ -1,13 +1,10 @@
1
1
  import { useMemo } from "react";
2
- import { useNavigate, useParams, Routes, Route, useLocation } from "react-router-dom";
2
+ import { useParams, Routes, Route, useLocation, useNavigate } from "react-router-dom";
3
3
  import {
4
4
  Bot, Heart, Zap, Puzzle, FolderKanban, Settings,
5
- RefreshCw, MessagesSquare, Send,
5
+ MessagesSquare, Send, KeyRound,
6
6
  LayoutDashboard, Boxes, Cpu, ScrollText, History, Brain,
7
7
  } from "lucide-react";
8
- import { Projects } from "../lib/api";
9
- import { Button } from "../components/ui";
10
- import { useToast } from "../components/Toast";
11
8
  import { useNavCollapse, type TabSection } from "../components/common/TabNav";
12
9
  import { TabLayout } from "../components/common/TabLayout";
13
10
  import { useProject } from "../hooks/useProjects";
@@ -25,6 +22,7 @@ import { AgentsTab } from "./project/AgentsTab";
25
22
  import { RoutinesTab } from "./project/RoutinesTab";
26
23
  import { TasksTab } from "./project/TasksTab";
27
24
  import { McpsTab } from "./project/McpsTab";
25
+ import { VarsTab } from "./project/VarsTab";
28
26
  import { ThreadsTab } from "./project/ThreadsTab";
29
27
  import { ChatTab } from "./project/ChatTab";
30
28
  import { TelegramTab } from "./project/TelegramTab";
@@ -33,14 +31,13 @@ import { AgentDetailScreen } from "./project/AgentDetailScreen";
33
31
 
34
32
  type NavKey =
35
33
  | "" | "chat" | "config" | "telegram"
36
- | "agents" | "routines" | "tasks" | "mcps" | "threads" | "logs" | "memories";
34
+ | "agents" | "routines" | "tasks" | "mcps" | "vars" | "threads" | "logs" | "memories";
37
35
 
38
36
  export function ProjectScreen() {
39
37
  const navigate = useNavigate();
40
38
  const location = useLocation();
41
- const toast = useToast();
42
39
  const { pid = "" } = useParams();
43
- const { project, mutate } = useProject(pid);
40
+ const { project } = useProject(pid);
44
41
  const { collapsed, toggle } = useNavCollapse(STORAGE.sidebarCollapsed + ".project");
45
42
 
46
43
  const isBase = String(pid) === "0";
@@ -73,6 +70,7 @@ export function ProjectScreen() {
73
70
  { key: "memories", label: t("project.nav.memories"), icon: Brain },
74
71
  { key: "routines", label: t("project.nav.routines"), icon: Heart },
75
72
  { key: "mcps", label: t("project.nav.mcps"), icon: Puzzle },
73
+ { key: "vars", label: t("project.nav.vars"), icon: KeyRound },
76
74
  { key: "config", label: t("project.nav.config"), icon: Settings },
77
75
  ],
78
76
  },
@@ -96,6 +94,7 @@ export function ProjectScreen() {
96
94
  { key: "routines", label: t("project.nav.routines"), icon: Heart },
97
95
  { key: "tasks", label: t("project.nav.tasks"), icon: Zap },
98
96
  { key: "mcps", label: t("project.nav.mcps"), icon: Puzzle },
97
+ { key: "vars", label: t("project.nav.vars"), icon: KeyRound },
99
98
  { key: "logs", label: t("project.nav.logs"), icon: ScrollText },
100
99
  ],
101
100
  },
@@ -116,33 +115,11 @@ export function ProjectScreen() {
116
115
  return <div className="p-8 text-muted-fg">{t("project.not_found", { pid })}</div>;
117
116
  }
118
117
 
119
- const rebuild = async () => {
120
- try { await Projects.rebuild(pid); toast.success(t("project.rebuild_done")); }
121
- catch (e) { toast.error((e as Error).message); }
122
- };
123
- const unregister = async () => {
124
- const label = project.name || project.path;
125
- if (!confirm(t("project.unregister_confirm", { label }))) return;
126
- try { await Projects.remove(pid); toast.success(t("project.unregistered")); mutate(); navigate("/"); }
127
- catch (e) { toast.error((e as Error).message); }
128
- };
129
-
130
118
  const onTabChange = (key: string) => {
131
119
  const url = key ? `/p/${pid}/${key}` : `/p/${pid}`;
132
120
  navigate(url);
133
121
  };
134
122
 
135
- const actions = Number(pid) !== 0 ? (
136
- <>
137
- <Button size="sm" variant="secondary" onClick={rebuild}>
138
- <RefreshCw size={13} /> {t("project.rebuild")}
139
- </Button>
140
- <Button size="sm" variant="destructive" onClick={unregister}>
141
- {t("admin.unregister")}
142
- </Button>
143
- </>
144
- ) : undefined;
145
-
146
123
  return (
147
124
  <TabLayout
148
125
  sections={sections}
@@ -150,7 +127,6 @@ export function ProjectScreen() {
150
127
  onChange={onTabChange}
151
128
  collapsed={collapsed}
152
129
  onToggleCollapse={toggle}
153
- actions={actions}
154
130
  contentClassName="w-full space-y-6 p-6 pt-3"
155
131
  testId={`project-tab-${active || "overview"}`}
156
132
  >
@@ -169,6 +145,7 @@ export function ProjectScreen() {
169
145
  <Route path="routines" element={<RoutinesTab pid={pid} />} />
170
146
  <Route path="tasks" element={isBase ? <GlobalTasksTab /> : <TasksTab pid={pid} />} />
171
147
  <Route path="mcps" element={<McpsTab pid={pid} />} />
148
+ <Route path="vars" element={<VarsTab pid={pid} />} />
172
149
  <Route path="threads" element={<ThreadsTab pid={pid} />} />
173
150
  <Route path="chat" element={<ChatTab pid={pid} />} />
174
151
  <Route path="*" element={<Overview pid={pid} />} />
@@ -1,13 +1,14 @@
1
1
  import { type ReactElement } from "react";
2
2
  import { useLocation, useNavigate } from "react-router-dom";
3
3
  import {
4
- Bot, Cpu, Database, KeyRound, MessageCircle, Palette, ScrollText, Send, Smartphone, User,
4
+ Bot, Cpu, Database, KeyRound, MessageCircle, Palette, ScrollText, Send, Smartphone, Sparkles, User,
5
5
  } from "lucide-react";
6
6
  import { useNavCollapse, type TabSection } from "../components/common/TabNav";
7
7
  import { TabLayout } from "../components/common/TabLayout";
8
8
  import { IdentityPanel } from "../components/settings/IdentityPanel";
9
9
  import { SuperAgentPanel } from "../components/settings/SuperAgentPanel";
10
10
  import { MemoryPanel } from "../components/settings/MemoryPanel";
11
+ import { SkillsInspectorPanel } from "../components/settings/SkillsInspectorPanel";
11
12
  import { ModelsTab } from "./base/ModelsTab";
12
13
  import { TelegramSettingsTabs } from "../components/settings/TelegramSettingsTabs";
13
14
  import { DevicesPanel } from "../components/settings/DevicesPanel";
@@ -17,7 +18,7 @@ import { STORAGE } from "../constants";
17
18
  import { t } from "../i18n";
18
19
 
19
20
  type TabKey =
20
- | "identity" | "super_agent" | "engines" | "memory" | "telegram" | "devices" | "appearance" | "advanced";
21
+ | "identity" | "super_agent" | "engines" | "memory" | "skills" | "telegram" | "devices" | "appearance" | "advanced";
21
22
 
22
23
  const SECTIONS: TabSection[] = [
23
24
  {
@@ -33,6 +34,7 @@ const SECTIONS: TabSection[] = [
33
34
  { key: "super_agent", label: t("settings.tabs.super_agent"), icon: Bot },
34
35
  { key: "engines", label: t("settings.tabs.engines"), icon: Cpu },
35
36
  { key: "memory", label: "Memoria (RAG)", icon: Database },
37
+ { key: "skills", label: "Skills (RAG)", icon: Sparkles },
36
38
  ],
37
39
  },
38
40
  {
@@ -59,6 +61,7 @@ const PANELS: Record<TabKey, () => ReactElement> = {
59
61
  super_agent: () => <SuperAgentPanel />,
60
62
  engines: () => <ModelsTab />,
61
63
  memory: () => <MemoryPanel />,
64
+ skills: () => <SkillsInspectorPanel />,
62
65
  telegram: () => <TelegramSettingsTabs />,
63
66
  devices: () => <DevicesPanel />,
64
67
  appearance: () => <AppearancePanel />,
@@ -95,6 +98,7 @@ function tabFromPath(pathname: string): TabKey {
95
98
  case "super-agent": return "super_agent";
96
99
  case "engines": return "engines";
97
100
  case "memory": return "memory";
101
+ case "skills": return "skills";
98
102
  case "telegram": return "telegram";
99
103
  case "devices": return "devices";
100
104
  case "appearance": return "appearance";
@@ -531,7 +531,7 @@ export function CodeScreen() {
531
531
  {name}
532
532
  </button>
533
533
  </Tip>
534
- <Tip content="Cerrar">
534
+ <Tip content={t("code_module.close")}>
535
535
  <button
536
536
  type="button"
537
537
  onClick={() => closeFile(f.path)}
@@ -7,6 +7,7 @@ import { useToast } from "../../components/Toast";
7
7
  import { DaemonCard } from "../../components/deck/DaemonCard";
8
8
  import { DesktopGroup } from "../../components/deck/DesktopGroup";
9
9
  import type { DeckWidget } from "../../lib/api/deck";
10
+ import { t } from "../../i18n";
10
11
 
11
12
  // Deck module — configure the companion "Deck" app: enable/disable widgets,
12
13
  // view desktops, inspect the daemon manifest.
@@ -81,7 +82,7 @@ export function DeckScreen() {
81
82
 
82
83
  {/* Widgets section */}
83
84
  <Section
84
- title="Widgets"
85
+ title={t("deck_screen.widgets_title")}
85
86
  description={
86
87
  isLoading
87
88
  ? "Cargando manifest…"
@@ -90,7 +91,7 @@ export function DeckScreen() {
90
91
  : `${widgets.length} widgets · ${enabledCount} externos habilitados`
91
92
  }
92
93
  action={
93
- <Button size="sm" variant="ghost" onClick={() => mutate()} disabled={isLoading} title="Recargar manifest">
94
+ <Button size="sm" variant="ghost" onClick={() => mutate()} disabled={isLoading} title={t("deck_screen.reload_manifest")} aria-label={t("deck_screen.reload_manifest")}>
94
95
  <RefreshCw size={14} className={isLoading ? "animate-spin" : ""} />
95
96
  </Button>
96
97
  }
@@ -132,7 +133,7 @@ export function DeckScreen() {
132
133
 
133
134
  {/* Active project + stats (read-only context) */}
134
135
  {data?.apx && (
135
- <Section title="Contexto APX" description="Información que el Deck ve del daemon.">
136
+ <Section title={t("deck_screen.context_title")} description="Información que el Deck ve del daemon.">
136
137
  <div className="space-y-2 text-sm" data-testid="deck-apx-context">
137
138
  <div className="flex items-center gap-2">
138
139
  <span className="text-muted-fg">Proyecto activo:</span>
@@ -7,6 +7,7 @@ import { UiSelect } from "../../components/UiSelect";
7
7
  import { useToast } from "../../components/Toast";
8
8
  import { useGlobalConfig } from "../../hooks/useGlobalConfig";
9
9
  import { Desktop, fetchDesktopMessages, type GlobalMessage } from "../../lib/api/desktop";
10
+ import { t } from "../../i18n";
10
11
 
11
12
  const DEFAULT_SHORTCUT = "CommandOrControl+G";
12
13
  const POSITION_OPTS = [
@@ -100,7 +101,7 @@ export function DesktopScreen() {
100
101
  <div className="grid gap-6 xl:grid-cols-[1fr_1fr]">
101
102
  {/* ── LEFT: configuration + status ─────────────────────────────── */}
102
103
  <div className="space-y-6">
103
- <Section title="Estado" description="La ventana se lanza desde la terminal o por autostart.">
104
+ <Section title={t("desktop_screen.status_title")} description="La ventana se lanza desde la terminal o por autostart.">
104
105
  {stLoading ? <Loading /> : (
105
106
  <div className="flex items-center gap-2 text-sm">
106
107
  <StatusDot ok={running} />
@@ -120,7 +121,7 @@ export function DesktopScreen() {
120
121
  </Section>
121
122
 
122
123
  <Section
123
- title="Arranque automático"
124
+ title={t("desktop_screen.autostart_title")}
124
125
  description="Lanza la ventana al iniciar sesión del usuario. Equivalente a `apx desktop install` (no requiere sudo)."
125
126
  >
126
127
  {!autostart ? <Loading /> : (
@@ -137,7 +138,7 @@ export function DesktopScreen() {
137
138
  </Section>
138
139
 
139
140
  <Section
140
- title="Atajo de teclado"
141
+ title={t("desktop_screen.shortcut_title")}
141
142
  description="Botón de acceso rápido global que muestra/oculta la ventana y arranca a escuchar."
142
143
  >
143
144
  {cfgLoading ? <Loading /> : (
@@ -169,7 +170,7 @@ export function DesktopScreen() {
169
170
  )}
170
171
  </Section>
171
172
 
172
- <Section title="Apariencia" description="Tema y posición de la ventana en la pantalla.">
173
+ <Section title={t("desktop_screen.appearance_title")} description="Tema y posición de la ventana en la pantalla.">
173
174
  {cfgLoading ? <Loading /> : (
174
175
  <div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
175
176
  <Field label="Tema" hint="Reiniciá la ventana para aplicar.">
@@ -193,7 +194,7 @@ export function DesktopScreen() {
193
194
  </Section>
194
195
 
195
196
  <Section
196
- title="Activación + transcripción"
197
+ title={t("desktop_screen.activation_title")}
197
198
  description="El plugin del daemon procesa los mensajes. STT se configura en Voces."
198
199
  >
199
200
  {cfgLoading ? <Loading /> : (
@@ -216,7 +217,7 @@ export function DesktopScreen() {
216
217
  {/* ── RIGHT: last conversation preview ─────────────────────────── */}
217
218
  <div>
218
219
  <Section
219
- title="Última conversación"
220
+ title={t("desktop_screen.last_conv_title")}
220
221
  description="Lo último charlado con el agente desde la ventana flotante."
221
222
  action={
222
223
  <button