@agentprojectcontext/apx 1.33.0 → 1.34.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 (172) hide show
  1. package/package.json +1 -1
  2. package/skills/apc-context/SKILL.md +2 -5
  3. package/skills/apx/SKILL.md +49 -61
  4. package/src/core/agent/a2a/reply.js +48 -0
  5. package/src/core/agent/build-agent-system.js +4 -3
  6. package/src/core/agent/channels/voice-context.js +98 -0
  7. package/src/core/agent/memory.js +2 -1
  8. package/src/core/agent/prompt-builder.js +2 -1
  9. package/src/core/agent/prompts/modes/code-build.md +1 -0
  10. package/src/core/agent/prompts/modes/code-plan.md +1 -0
  11. package/src/core/agent/prompts/modes/index.js +28 -0
  12. package/src/core/agent/skills/loader.js +22 -18
  13. package/src/core/agent/stream/turn-accumulator.js +73 -0
  14. package/src/core/agent/suggestions.js +37 -0
  15. package/src/core/agent/tools/handlers/add-project.js +5 -2
  16. package/src/core/agent/tools/handlers/call-runtime.js +3 -2
  17. package/src/core/agent/tools/handlers/transcribe-audio.js +1 -1
  18. package/src/core/agent/tools/helpers.js +2 -2
  19. package/src/core/agent/tools/names.js +138 -0
  20. package/src/core/agent/tools/registry-bridge.js +6 -14
  21. package/src/core/agent/tools/registry.js +68 -65
  22. package/src/core/apc/context-copy.js +27 -0
  23. package/src/core/apc/notes.js +19 -0
  24. package/src/core/apc/parser.js +13 -6
  25. package/src/core/apc/paths.js +87 -0
  26. package/src/core/apc/scaffold.js +82 -74
  27. package/src/core/apc/skill-sync.js +13 -1
  28. package/src/core/channels/telegram/dispatch.js +595 -0
  29. package/src/core/channels/telegram/helpers.js +130 -0
  30. package/src/core/config/index.js +3 -2
  31. package/src/core/config/redact.js +95 -0
  32. package/src/core/constants/channels.js +2 -0
  33. package/src/core/constants/code-modes.js +10 -0
  34. package/src/core/constants/index.js +1 -0
  35. package/src/core/deck/manifest.js +186 -0
  36. package/src/core/engines/catalog.js +83 -0
  37. package/src/core/engines/gemini.js +28 -11
  38. package/src/core/engines/index.js +11 -1
  39. package/src/core/{tools → http-tools}/browser.js +0 -1
  40. package/src/core/{tools → http-tools}/fetch.js +0 -1
  41. package/src/core/{tools → http-tools}/glob.js +0 -1
  42. package/src/core/{tools → http-tools}/grep.js +0 -1
  43. package/src/core/{tools → http-tools}/registry.js +0 -1
  44. package/src/core/{tools → http-tools}/search.js +0 -1
  45. package/src/core/i18n/en.js +9 -0
  46. package/src/core/i18n/es.js +12 -0
  47. package/src/core/i18n/index.js +54 -0
  48. package/src/core/i18n/pt.js +9 -0
  49. package/src/core/identity/telegram.js +2 -1
  50. package/src/core/mcp/runner.js +272 -14
  51. package/src/core/mcp/sources.js +3 -2
  52. package/src/core/routines/index.js +16 -0
  53. package/src/{host/daemon/routines.js → core/routines/runner.js} +36 -103
  54. package/src/core/runtime-skills/apc-context/SKILL.md +159 -0
  55. package/src/core/runtime-skills/apx/SKILL.md +95 -0
  56. package/src/core/runtime-skills/apx-mcp/SKILL.md +116 -0
  57. package/src/core/runtime-skills/{claude-code.md → claude-code/SKILL.md} +1 -0
  58. package/src/core/runtime-skills/{codex-cli.md → codex-cli/SKILL.md} +1 -0
  59. package/src/core/runtime-skills/{opencode-cli.md → opencode-cli/SKILL.md} +1 -0
  60. package/src/core/runtime-skills/{openrouter.md → openrouter/SKILL.md} +1 -0
  61. package/src/{host/daemon/env-detect.js → core/runtimes/detect.js} +1 -1
  62. package/src/core/stores/code-sessions.js +50 -2
  63. package/src/core/stores/routine-memory.js +1 -1
  64. package/src/core/stores/sessions-search.js +121 -0
  65. package/src/core/stores/sessions.js +38 -0
  66. package/src/core/vars/index.js +14 -0
  67. package/src/core/vars/interpolate.js +86 -0
  68. package/src/core/vars/sources.js +151 -0
  69. package/src/core/voice/audio-decode.js +38 -0
  70. package/src/core/voice/transcription.js +225 -0
  71. package/src/host/daemon/api/admin-config.js +5 -82
  72. package/src/host/daemon/api/agents.js +5 -5
  73. package/src/host/daemon/api/code.js +17 -169
  74. package/src/host/daemon/api/config.js +3 -4
  75. package/src/host/daemon/api/conversations.js +8 -29
  76. package/src/host/daemon/api/deck.js +37 -404
  77. package/src/host/daemon/api/engines.js +1 -50
  78. package/src/host/daemon/api/exec.js +1 -1
  79. package/src/host/daemon/api/mcps.js +32 -0
  80. package/src/host/daemon/api/routines.js +1 -1
  81. package/src/host/daemon/api/runtimes.js +4 -3
  82. package/src/host/daemon/api/sessions-search.js +24 -140
  83. package/src/host/daemon/api/sessions.js +12 -30
  84. package/src/host/daemon/api/shared.js +2 -1
  85. package/src/host/daemon/api/telegram.js +1 -11
  86. package/src/host/daemon/api/tools.js +6 -6
  87. package/src/host/daemon/api/transcribe.js +2 -2
  88. package/src/host/daemon/api/vars.js +137 -0
  89. package/src/host/daemon/api/voice.js +13 -290
  90. package/src/host/daemon/api.js +2 -0
  91. package/src/host/daemon/db.js +6 -6
  92. package/src/host/daemon/deck-exec.js +148 -0
  93. package/src/host/daemon/index.js +3 -3
  94. package/src/host/daemon/plugins/telegram/index.js +24 -687
  95. package/src/host/daemon/routines-scheduler.js +64 -0
  96. package/src/host/daemon/smoke.js +3 -2
  97. package/src/host/daemon/whisper-server.js +225 -0
  98. package/src/interfaces/cli/commands/agent.js +3 -2
  99. package/src/interfaces/cli/commands/command.js +2 -3
  100. package/src/interfaces/cli/commands/messages.js +6 -2
  101. package/src/interfaces/cli/commands/pair.js +5 -4
  102. package/src/interfaces/cli/commands/search.js +1 -1
  103. package/src/interfaces/cli/commands/sessions.js +3 -2
  104. package/src/interfaces/cli/commands/skills.js +36 -55
  105. package/src/interfaces/web/dist/assets/index-DdmSRtsz.css +1 -0
  106. package/src/interfaces/web/dist/assets/index-M4FspaCH.js +613 -0
  107. package/src/interfaces/web/dist/assets/index-M4FspaCH.js.map +1 -0
  108. package/src/interfaces/web/dist/index.html +2 -2
  109. package/src/interfaces/web/package-lock.json +182 -182
  110. package/src/interfaces/web/src/components/ModelCombobox.tsx +44 -8
  111. package/src/interfaces/web/src/components/TelegramChannelDialog.tsx +1 -1
  112. package/src/interfaces/web/src/components/chat/AskAnswersCard.tsx +76 -0
  113. package/src/interfaces/web/src/components/chat/MessageBubble.tsx +16 -3
  114. package/src/interfaces/web/src/components/chat/MessageList.tsx +23 -1
  115. package/src/interfaces/web/src/components/chat/ModelPicker.tsx +3 -1
  116. package/src/interfaces/web/src/components/code/CodeArtifactsTab.tsx +4 -4
  117. package/src/interfaces/web/src/components/code/CodeChangesTab.tsx +1 -1
  118. package/src/interfaces/web/src/components/code/CodeFileTree.tsx +3 -2
  119. package/src/interfaces/web/src/components/code/CodeFileViewer.tsx +3 -2
  120. package/src/interfaces/web/src/components/code/CodeTerminal.tsx +3 -2
  121. package/src/interfaces/web/src/components/config/GlobalConfigEditor.tsx +2 -1
  122. package/src/interfaces/web/src/components/deck/WidgetRow.tsx +2 -1
  123. package/src/interfaces/web/src/components/inputs/KeyValueList.tsx +93 -0
  124. package/src/interfaces/web/src/components/inputs/VarTokenInput.tsx +449 -0
  125. package/src/interfaces/web/src/components/settings/DefaultRouterCard.tsx +2 -1
  126. package/src/interfaces/web/src/components/settings/EnginesPanel.tsx +2 -2
  127. package/src/interfaces/web/src/components/settings/MemoryPanel.tsx +5 -4
  128. package/src/interfaces/web/src/components/settings/providers/ProviderCard.tsx +3 -2
  129. package/src/interfaces/web/src/components/settings/providers/ProviderModal.tsx +3 -2
  130. package/src/interfaces/web/src/components/ui/chat-input.tsx +5 -4
  131. package/src/interfaces/web/src/components/ui/sidebar.tsx +3 -2
  132. package/src/interfaces/web/src/components/voice/VoiceProviderModal.tsx +2 -1
  133. package/src/interfaces/web/src/constants/index.ts +1 -1
  134. package/src/interfaces/web/src/i18n/en.ts +174 -7
  135. package/src/interfaces/web/src/i18n/es.ts +179 -15
  136. package/src/interfaces/web/src/lib/api/mcps.ts +25 -0
  137. package/src/interfaces/web/src/lib/api/vars.ts +38 -0
  138. package/src/interfaces/web/src/lib/api.ts +1 -0
  139. package/src/interfaces/web/src/screens/ProjectScreen.tsx +8 -31
  140. package/src/interfaces/web/src/screens/modules/CodeScreen.tsx +1 -1
  141. package/src/interfaces/web/src/screens/modules/DeckScreen.tsx +4 -3
  142. package/src/interfaces/web/src/screens/modules/DesktopScreen.tsx +7 -6
  143. package/src/interfaces/web/src/screens/modules/VoiceScreen.tsx +4 -3
  144. package/src/interfaces/web/src/screens/project/AgentDetailScreen.tsx +1 -1
  145. package/src/interfaces/web/src/screens/project/ConfigTab.tsx +132 -1
  146. package/src/interfaces/web/src/screens/project/McpsTab.tsx +549 -104
  147. package/src/interfaces/web/src/screens/project/RoutinesTab.tsx +1 -1
  148. package/src/interfaces/web/src/screens/project/VarsTab.tsx +300 -0
  149. package/src/interfaces/web/src/types/daemon.ts +5 -0
  150. package/src/host/daemon/transcription.js +0 -538
  151. package/src/host/daemon/whisper-transcribe.py +0 -73
  152. package/src/interfaces/web/dist/assets/index-7dVT2O1S.css +0 -1
  153. package/src/interfaces/web/dist/assets/index-DWsE_8Nz.js +0 -602
  154. package/src/interfaces/web/dist/assets/index-DWsE_8Nz.js.map +0 -1
  155. /package/src/{host/daemon → core/apc}/projects-helpers.js +0 -0
  156. /package/src/{host/daemon/plugins → core/channels}/telegram/ask.js +0 -0
  157. /package/src/{host/daemon/plugins → core/channels}/telegram/media.js +0 -0
  158. /package/src/core/{tools → http-tools}/index.js +0 -0
  159. /package/{skills → src/core/runtime-skills}/apx-agency-agents/SKILL.md +0 -0
  160. /package/{skills → src/core/runtime-skills}/apx-agent/SKILL.md +0 -0
  161. /package/{skills → src/core/runtime-skills}/apx-mcp-builder/SKILL.md +0 -0
  162. /package/{skills → src/core/runtime-skills}/apx-project/SKILL.md +0 -0
  163. /package/{skills → src/core/runtime-skills}/apx-routine/SKILL.md +0 -0
  164. /package/{skills → src/core/runtime-skills}/apx-runtime/SKILL.md +0 -0
  165. /package/{skills → src/core/runtime-skills}/apx-sessions/SKILL.md +0 -0
  166. /package/{skills → src/core/runtime-skills}/apx-skill-builder/SKILL.md +0 -0
  167. /package/{skills → src/core/runtime-skills}/apx-task/SKILL.md +0 -0
  168. /package/{skills → src/core/runtime-skills}/apx-telegram/SKILL.md +0 -0
  169. /package/{skills → src/core/runtime-skills}/apx-voice/SKILL.md +0 -0
  170. /package/src/{host/daemon/compact.js → core/stores/conversations-compactor.js} +0 -0
  171. /package/src/{host/daemon → core/stores}/conversations.js +0 -0
  172. /package/src/{host/daemon → core/util}/thinking.js +0 -0
@@ -253,6 +253,18 @@ export const en = {
253
253
  unregistered: "Unregistered.",
254
254
  base_subtitle: "General workspace · super-agent",
255
255
 
256
+ danger: {
257
+ title: "Danger zone",
258
+ subtitle: "Actions that affect APX's project registry. They do not touch repo files.",
259
+ rebuild_desc: "Re-scans .apc/, MCPs and agents and regenerates the super-agent context for this project.",
260
+ unregister_desc: "Removes the project from APX's registry. The folder on disk stays intact.",
261
+ rebuild_confirm_title: "Rebuild context",
262
+ rebuild_confirm_desc: "Regenerate context for {label}.",
263
+ rebuild_long: "Re-reads APC config, lists available MCPs and agents, and rebuilds the super-agent system prompt. Safe to run — nothing is deleted. Use it after editing .apc/ by hand or if changes are not being picked up.",
264
+ unregister_confirm_title: "Unregister project",
265
+ unregister_long: "The project disappears from `apx`. Files on disk (.apc/, code, everything) stay. You can re-register it with `apx project register <path>`.",
266
+ },
267
+
256
268
  nav: {
257
269
  overview: "Overview",
258
270
  chat: "Chat",
@@ -262,6 +274,7 @@ export const en = {
262
274
  routines: "Routines",
263
275
  tasks: "Tasks",
264
276
  mcps: "MCPs",
277
+ vars: "Variables",
265
278
  threads: "Chats",
266
279
  logs: "Logs",
267
280
  memories: "Memories",
@@ -486,17 +499,17 @@ export const en = {
486
499
  conflicts: "⚠ Conflicts: {names}",
487
500
  new_title: "New MCP",
488
501
  new_desc: "POST /projects/:pid/mcps?scope=…",
489
- scope_label: "scope",
490
- transport_label: "transport",
491
- name_label: "name",
502
+ scope_label: "Scope",
503
+ transport_label: "Transport",
504
+ name_label: "Name",
492
505
  name_ph: "filesystem",
493
- cmd_label: "command",
506
+ cmd_label: "Command",
494
507
  cmd_ph: "npx",
495
- args_label: "args",
508
+ args_label: "Args",
496
509
  args_hint: "space-separated",
497
510
  args_ph: "-y @modelcontextprotocol/server-filesystem /tmp",
498
- env_label: "env (JSON, optional)",
499
- url_label: "url",
511
+ env_label: "Env (JSON, optional)",
512
+ url_label: "URL",
500
513
  url_ph: "https://example.com/mcp",
501
514
  enabled_label: "Enabled",
502
515
  add_btn: "Add",
@@ -504,6 +517,77 @@ export const en = {
504
517
  env_invalid: "env must be valid JSON",
505
518
  removed: "removed",
506
519
  added: "MCP added.",
520
+ updated: "MCP updated.",
521
+ edit_title: "Edit MCP",
522
+ save_btn: "Save",
523
+ add_arg: "Add arg",
524
+ edit_btn: "Edit",
525
+ test_btn: "Test",
526
+ logs_btn: "Logs",
527
+ testing: "Testing…",
528
+ test_ok: "OK · {n} tools available",
529
+ logs_title: "Logs · {name}",
530
+ logs_empty: "No logs yet. Start the MCP by calling a tool or running Test.",
531
+ logs_events: "Recent events",
532
+ logs_stderr: "stderr (last 4KB)",
533
+ logs_panel_title: "Live logs",
534
+ logs_panel_pick: "pick an MCP",
535
+ logs_panel_hint: "Click an MCP in the list to see what's happening live.",
536
+ logs_panel_idle: "No activity. Hit Test to spin it up.",
537
+ scope_runtime: "Runtime",
538
+ scope_shared: "Shared",
539
+ scope_global: "Global",
540
+ scope_runtime_desc: "This project only · with secrets · not committed (~/.apx/projects/<id>/mcps.json)",
541
+ scope_shared_desc: "This project only · committeable · no secrets (.apc/mcps.json)",
542
+ scope_global_desc: "All projects on this machine (~/.apx/mcps.json)",
543
+ transport_stdio: "stdio",
544
+ transport_http: "HTTP",
545
+ transport_stdio_desc: "Local process — `command` + args",
546
+ transport_http_desc: "Remote endpoint — URL + headers",
547
+ args_hint_tokens: "One entry per arg. Use the + button to insert a variable.",
548
+ env_hint_tokens: "Key/value pairs. Values accept ${var.NAME} (+ button on the right).",
549
+ env_empty: "No env vars.",
550
+ headers_label: "Headers",
551
+ headers_hint: "Key/value pairs — typically Authorization: Bearer ${var.TOKEN}.",
552
+ headers_empty: "No headers.",
553
+ },
554
+
555
+ vars: {
556
+ title: "Variables",
557
+ subtitle_project: "Replace ${var.NAME} when loading MCPs and templates. Project vars beat globals. Stored outside the repo (~/.apx/, chmod 0600).",
558
+ subtitle_base: "Global variables — available to every project. Stored in ~/.apx/vars.json (chmod 0600).",
559
+ empty: "No variables yet.",
560
+ new: "Variable",
561
+ new_title: "New variable",
562
+ edit_title: "Edit variable",
563
+ new_desc: "Referenced as ${var.NAME} in any field that supports interpolation.",
564
+ reveal_all: "Show values",
565
+ reveal: "Show",
566
+ hide: "Hide",
567
+ filter_label: "Show:",
568
+ filter_all: "All",
569
+ filter_project: "Project only",
570
+ filter_global: "Globals only",
571
+ scope_label: "Scope",
572
+ scope_project: "project",
573
+ scope_project_desc: "This project only. Beats the global with the same name.",
574
+ scope_global: "global",
575
+ scope_global_desc: "Available to every project.",
576
+ name_label: "Name",
577
+ name_hint: "Uppercase, digits and _. E.g. MY_API_KEY, GITHUB_TOKEN.",
578
+ value_label: "Value",
579
+ value_hint: "Stored on disk with 0600 perms. Never committed.",
580
+ value_edit_ph: "(leave empty to keep current… not yet supported, paste the value again)",
581
+ add_btn: "Add",
582
+ save_btn: "Save",
583
+ edit_btn: "Edit",
584
+ delete_btn: "Delete",
585
+ delete_confirm: "Delete {name} ({scope})?",
586
+ removed: "Variable removed.",
587
+ added: "Variable added.",
588
+ updated: "Variable updated.",
589
+ name_required: "Name required.",
590
+ value_required: "Value required.",
507
591
  },
508
592
 
509
593
  threads: {
@@ -783,5 +867,88 @@ export const en = {
783
867
  changes_no_git: "Changes need a git repository. This project isn't one.",
784
868
  changes_files: "{n} file(s) changed",
785
869
  stopped: "[stopped]",
870
+ close: "Close",
871
+ reload: "Reload",
872
+ discard_changes: "Discard changes",
873
+ save_shortcut_hint: "Save (Cmd/Ctrl+S)",
874
+ artifacts_rename: "Rename",
875
+ artifacts_view: "View contents",
876
+ artifacts_edit: "Edit contents",
877
+ tree_collapse_all: "Collapse all",
878
+ terminal_clear: "Clear",
879
+ terminal_close: "Close terminal",
880
+ },
881
+
882
+ desktop_screen: {
883
+ status_title: "Status",
884
+ autostart_title: "Auto-start",
885
+ shortcut_title: "Keyboard shortcut",
886
+ appearance_title: "Appearance",
887
+ activation_title: "Activation + transcription",
888
+ last_conv_title: "Last conversation",
889
+ },
890
+
891
+ voice_screen: {
892
+ providers_title: "Voice providers (TTS)",
893
+ test_title: "Test voice",
894
+ stt_title: "Transcription (STT)",
895
+ configure_provider: "Configure {name}",
896
+ },
897
+
898
+ deck_screen: {
899
+ widgets_title: "Widgets",
900
+ context_title: "APX context",
901
+ reload_manifest: "Reload manifest",
902
+ widget_native: "Native APX widget",
903
+ widget_external: "External widget",
904
+ },
905
+
906
+ memory_panel: {
907
+ embeddings_title: "Embeddings (RAG)",
908
+ ollama_title: "Ollama (local)",
909
+ openai_title: "OpenAI",
910
+ gemini_title: "Gemini",
911
+ },
912
+
913
+ router_panel: {
914
+ title: "Model router",
915
+ },
916
+
917
+ engines_panel: {
918
+ title: "Providers",
919
+ new_btn: "New provider",
920
+ },
921
+
922
+ providers_modal: {
923
+ new_title: "New provider",
924
+ edit_title: "Edit {name}",
925
+ list_models_hint: "List the provider's actual models",
926
+ toggle_active: "Active · click to deactivate",
927
+ toggle_inactive: "Inactive · click to activate",
928
+ delete: "Delete",
929
+ },
930
+
931
+ chat_ui: {
932
+ copy: "Copy",
933
+ stop: "Stop",
934
+ send: "Send",
935
+ pick_model: "Pick model (or Auto)",
936
+ insert_variable: "Insert variable",
937
+ },
938
+
939
+ sidebar_ui: {
940
+ toggle: "Toggle sidebar",
941
+ },
942
+
943
+ models_ui: {
944
+ invalid_hint: "Model/provider unavailable",
945
+ },
946
+
947
+ global_config: {
948
+ title: "APX config",
949
+ },
950
+
951
+ agent_detail_extra: {
952
+ skills_title: "Skills & tools",
786
953
  },
787
954
  } as const;
@@ -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,89 @@ 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
+ },
910
+
911
+ router_panel: {
912
+ title: "Router de modelos",
913
+ },
914
+
915
+ engines_panel: {
916
+ title: "Proveedores",
917
+ new_btn: "Nuevo proveedor",
918
+ },
919
+
920
+ providers_modal: {
921
+ new_title: "Nuevo proveedor",
922
+ edit_title: "Editar {name}",
923
+ list_models_hint: "Listar los modelos reales del proveedor",
924
+ toggle_active: "Activo · click para desactivar",
925
+ toggle_inactive: "Inactivo · click para activar",
926
+ delete: "Borrar",
927
+ },
928
+
929
+ chat_ui: {
930
+ copy: "Copiar",
931
+ stop: "Detener",
932
+ send: "Enviar",
933
+ pick_model: "Elegir modelo (o Auto)",
934
+ insert_variable: "Insertar variable",
935
+ },
936
+
937
+ sidebar_ui: {
938
+ toggle: "Mostrar/ocultar sidebar",
939
+ },
940
+
941
+ models_ui: {
942
+ invalid_hint: "Modelo/proveedor no disponible",
943
+ },
944
+
945
+ global_config: {
946
+ title: "Config APX",
947
+ },
948
+
949
+ agent_detail_extra: {
950
+ skills_title: "Skills & tools",
787
951
  },
788
952
  } as const;
789
953
 
@@ -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
  };
@@ -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} />} />
@@ -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)}