@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
@@ -0,0 +1,222 @@
1
+ import { useState } from "react";
2
+ import useSWR from "swr";
3
+ import { Sparkles, RefreshCw, Wand2 } from "lucide-react";
4
+ import { Section } from "../Section";
5
+ import { Button, Field, Input, Loading, Badge, Switch } from "../ui";
6
+ import { useToast } from "../Toast";
7
+ import { Skills, type InspectTrace } from "../../lib/api/skills";
8
+
9
+ // Skill Inspector — per-turn skill RAG middleware. When ON, the static
10
+ // "available skills" slug-dump is removed from the agent's system prompt and a
11
+ // local RAG injects, per turn, only the skill(s) the user's message actually
12
+ // needs. This panel toggles the feature, tunes its thresholds, (re)builds the
13
+ // vector index, and offers a live dry-run so you can see what it would surface.
14
+ //
15
+ // Mirrors MemoryPanel (RAG embeddings): same Section/Field/Button idiom. Config
16
+ // persists under config.skills.inspector.* via the inspector PUT endpoint, so
17
+ // no separate global-config patch is needed.
18
+
19
+ // Numeric knobs with human labels + sane ranges. We keep them as plain number
20
+ // inputs (same idiom as the embeddings model fields) rather than sliders so the
21
+ // values are explicit and copy-pasteable.
22
+ const KNOBS: { key: keyof NumericKnobs; label: string; hint: string; step: number; min: number; max: number }[] = [
23
+ { key: "load_threshold", label: "Umbral de carga", hint: "Similitud mínima para inyectar el CUERPO de la skill (alto = más estricto).", step: 0.01, min: 0, max: 1 },
24
+ { key: "hint_threshold", label: "Umbral de sugerencia", hint: "Similitud mínima para solo SUGERIR la skill (que el agente la cargue si quiere).", step: 0.01, min: 0, max: 1 },
25
+ { key: "margin", label: "Margen sobre el 2º", hint: "El top debe superar al segundo por este margen para cargar su cuerpo (evita empates flojos).", step: 0.01, min: 0, max: 1 },
26
+ { key: "max_loaded", label: "Máx. cuerpos cargados", hint: "Cuántas skills se inyectan completas por turno.", step: 1, min: 0, max: 5 },
27
+ { key: "max_hints", label: "Máx. sugerencias", hint: "Cuántas skills extra se nombran como sugerencia.", step: 1, min: 0, max: 8 },
28
+ { key: "prompt_floor", label: "Largo mínimo del prompt", hint: "Mensajes más cortos que esto se ignoran (evita 'ok', 'hola').", step: 1, min: 0, max: 40 },
29
+ { key: "body_char_cap", label: "Tope de chars del cuerpo", hint: "Recorta cuerpos de skill largos para no inflar el contexto.", step: 500, min: 500, max: 20000 },
30
+ ];
31
+
32
+ type NumericKnobs = {
33
+ load_threshold: number; hint_threshold: number; margin: number;
34
+ max_loaded: number; max_hints: number; prompt_floor: number; body_char_cap: number;
35
+ };
36
+
37
+ export function SkillsInspectorPanel() {
38
+ const toast = useToast();
39
+ const { data, mutate, isLoading } = useSWR("/skills/inspector", () => Skills.inspector());
40
+ const [busy, setBusy] = useState(false);
41
+ const [probe, setProbe] = useState("");
42
+ const [probeResult, setProbeResult] = useState<InspectTrace | null>(null);
43
+
44
+ if (isLoading || !data) return <Loading />;
45
+
46
+ const cfg = data.config;
47
+ const idx = data.index;
48
+
49
+ const apply = async (patch: Record<string, unknown>) => {
50
+ setBusy(true);
51
+ try {
52
+ await Skills.updateInspector(patch);
53
+ await mutate();
54
+ } catch (e) {
55
+ toast.error(`No se pudo guardar: ${(e as Error).message}`);
56
+ } finally {
57
+ setBusy(false);
58
+ }
59
+ };
60
+
61
+ const runIndex = async (force = false) => {
62
+ setBusy(true);
63
+ try {
64
+ const r = await Skills.index({ force });
65
+ toast.success(
66
+ `Indexado con ${r.embedder} (dim ${r.dim}): +${r.changed.added} ~${r.changed.refreshed} -${r.changed.removed}.`,
67
+ );
68
+ await mutate();
69
+ } catch (e) {
70
+ toast.error(`Falló el index: ${(e as Error).message}`);
71
+ } finally {
72
+ setBusy(false);
73
+ }
74
+ };
75
+
76
+ const runProbe = async () => {
77
+ if (!probe.trim()) return;
78
+ setBusy(true);
79
+ setProbeResult(null);
80
+ try {
81
+ const r = await Skills.inspect(probe.trim());
82
+ setProbeResult(r.trace);
83
+ } catch (e) {
84
+ toast.error(`Falló el dry-run: ${(e as Error).message}`);
85
+ } finally {
86
+ setBusy(false);
87
+ }
88
+ };
89
+
90
+ return (
91
+ <div className="space-y-6">
92
+ <Section
93
+ title="Skill Inspector (RAG por turno)"
94
+ description="Función experimental. Cuando está activa, el agente NO recibe la lista completa de skills en su prompt; en cada mensaje un RAG local decide qué skill(s) cargar — el cuerpo completo si hay match fuerte, una sugerencia si hay match medio, nada si no aplica. Se reevalúa cada turno: una skill que dejó de ser relevante desaparece del contexto."
95
+ >
96
+ <div className="space-y-4">
97
+ <Field
98
+ label="Activar inspector"
99
+ hint="Apagado = comportamiento clásico (lista de slugs + sugerencia pasiva). Encendido = el RAG decide por turno."
100
+ >
101
+ <Switch
102
+ checked={cfg.enabled}
103
+ disabled={busy}
104
+ onChange={(v) => apply({ enabled: v })}
105
+ label={cfg.enabled ? "Encendido" : "Apagado"}
106
+ />
107
+ </Field>
108
+
109
+ <div className="flex flex-wrap items-center gap-2 pt-1">
110
+ <Badge tone={idx.count > 0 ? "success" : "warning"}>
111
+ Índice: {idx.count} skills
112
+ </Badge>
113
+ <Badge tone="muted">{idx.embedder || "sin indexar"}</Badge>
114
+ {idx.dim ? <Badge tone="muted">dim {idx.dim}</Badge> : null}
115
+ {idx.updated_at ? (
116
+ <span className="text-xs text-muted-foreground">
117
+ actualizado {new Date(idx.updated_at).toLocaleString()}
118
+ </span>
119
+ ) : null}
120
+ </div>
121
+
122
+ <div className="flex flex-wrap items-center gap-3 pt-1">
123
+ <Button variant="secondary" onClick={() => runIndex(false)} loading={busy}>
124
+ <RefreshCw size={14} /> Reindexar
125
+ </Button>
126
+ <Button variant="secondary" onClick={() => runIndex(true)} loading={busy}>
127
+ <RefreshCw size={14} /> Reindexar (forzado)
128
+ </Button>
129
+ <span className="text-xs text-muted-foreground">
130
+ El embedder sale de Memoria (RAG). Local con Ollama, u offline si no hay proveedor.
131
+ </span>
132
+ </div>
133
+ </div>
134
+ </Section>
135
+
136
+ <Section
137
+ title="Umbrales y límites"
138
+ description="Ajustá qué tan agresivo es el inspector. Subir los umbrales = menos falsos positivos pero más riesgo de perderse una skill; bajarlos = lo contrario."
139
+ >
140
+ <div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
141
+ {KNOBS.map((k) => (
142
+ <Field key={k.key} label={k.label} hint={k.hint}>
143
+ <Input
144
+ type="number"
145
+ step={k.step}
146
+ min={k.min}
147
+ max={k.max}
148
+ defaultValue={String(cfg[k.key])}
149
+ disabled={busy}
150
+ onBlur={(ev) => {
151
+ const n = Number(ev.target.value);
152
+ if (Number.isFinite(n) && n !== cfg[k.key]) apply({ [k.key]: n });
153
+ }}
154
+ className="max-w-[12rem]"
155
+ />
156
+ </Field>
157
+ ))}
158
+ </div>
159
+ </Section>
160
+
161
+ <Section
162
+ title="Probar (dry-run)"
163
+ description="Escribí un mensaje como lo haría un usuario y mirá qué skills cargaría/sugeriría el inspector — sin llamar al modelo. Fuerza el inspector activo aunque esté apagado arriba."
164
+ >
165
+ <div className="space-y-3">
166
+ <div className="flex flex-wrap items-center gap-2">
167
+ <Input
168
+ value={probe}
169
+ placeholder="ej: necesito crear un video promocional con voz en off"
170
+ disabled={busy}
171
+ onChange={(ev) => setProbe(ev.target.value)}
172
+ onKeyDown={(ev) => { if (ev.key === "Enter") runProbe(); }}
173
+ className="max-w-xl flex-1"
174
+ />
175
+ <Button variant="primary" onClick={runProbe} loading={busy}>
176
+ <Wand2 size={14} /> Probar
177
+ </Button>
178
+ </div>
179
+
180
+ {probeResult && (
181
+ <div className="rounded-md border border-border/60 bg-muted/30 p-3 text-sm">
182
+ <div className="mb-2 flex flex-wrap items-center gap-2">
183
+ <Sparkles size={14} className="text-muted-foreground" />
184
+ <span className="text-muted-foreground">{probeResult.embedder || "—"}</span>
185
+ {probeResult.jit ? <Badge tone="warning">JIT (índice vacío)</Badge> : null}
186
+ {probeResult.reason && !probeResult.loaded?.length && !probeResult.hinted?.length ? (
187
+ <Badge tone="muted">{probeResult.reason}</Badge>
188
+ ) : null}
189
+ </div>
190
+
191
+ {probeResult.loaded?.length ? (
192
+ <div className="mb-1">
193
+ <span className="text-muted-foreground">Cargadas: </span>
194
+ {probeResult.loaded.map((s) => (
195
+ <Badge key={s} tone="success" className="mr-1">{s}</Badge>
196
+ ))}
197
+ </div>
198
+ ) : null}
199
+
200
+ {probeResult.hinted?.length ? (
201
+ <div className="mb-1">
202
+ <span className="text-muted-foreground">Sugeridas: </span>
203
+ {probeResult.hinted.map((s) => (
204
+ <Badge key={s} tone="info" className="mr-1">{s}</Badge>
205
+ ))}
206
+ </div>
207
+ ) : null}
208
+
209
+ {probeResult.scored?.length ? (
210
+ <div className="mt-2 space-y-0.5 font-mono text-xs text-muted-foreground">
211
+ {probeResult.scored.map((s) => (
212
+ <div key={s.slug}>{s.sim.toFixed(3)} {s.slug}</div>
213
+ ))}
214
+ </div>
215
+ ) : null}
216
+ </div>
217
+ )}
218
+ </div>
219
+ </Section>
220
+ </div>
221
+ );
222
+ }
@@ -4,6 +4,7 @@ import { Tip } from "../../ui/tip";
4
4
  import { secretSuffix } from "../../../lib/secrets";
5
5
  import { ENGINE_BADGES, ENGINE_GRADIENTS, ENGINE_ICONS, ENGINE_OPTIONS, engineStyle } from "./typeStyles";
6
6
  import type { Provider } from "./types";
7
+ import { t } from "../../../i18n";
7
8
 
8
9
  export function ProviderCard({
9
10
  provider,
@@ -42,7 +43,7 @@ export function ProviderCard({
42
43
  </span>
43
44
  </div>
44
45
  <div className="flex shrink-0 items-center gap-1">
45
- <Tip content={active ? "Activo · click para desactivar" : "Inactivo · click para activar"}>
46
+ <Tip content={active ? t("providers_modal.toggle_active") : t("providers_modal.toggle_inactive")}>
46
47
  <button
47
48
  type="button"
48
49
  onClick={(e) => { e.stopPropagation(); onToggle(); }}
@@ -57,7 +58,7 @@ export function ProviderCard({
57
58
  {active ? "Active" : "Off"}
58
59
  </button>
59
60
  </Tip>
60
- <Tip content="Borrar">
61
+ <Tip content={t("providers_modal.delete")}>
61
62
  <button
62
63
  type="button"
63
64
  onClick={(e) => { e.stopPropagation(); onDelete(); }}
@@ -7,6 +7,7 @@ import { Engines } from "../../../lib/api";
7
7
  import { isSecretMarker, secretSuffix } from "../../../lib/secrets";
8
8
  import { ENGINE_ICONS, ENGINE_OPTIONS, ENGINE_PRESETS, type EngineType } from "./typeStyles";
9
9
  import type { Provider } from "./types";
10
+ import { t } from "../../../i18n";
10
11
 
11
12
  export interface ProviderSaveResult {
12
13
  provider: Provider;
@@ -269,7 +270,7 @@ export function ProviderModal({ open, initial, existingSlugs, onClose, onSave }:
269
270
  <Dialog
270
271
  open={open}
271
272
  onClose={onClose}
272
- title={isEdit ? `Editar ${initial?.name || initial?.slug}` : "Nuevo provider"}
273
+ title={isEdit ? t("providers_modal.edit_title", { name: initial?.name || initial?.slug || "" }) : t("providers_modal.new_title")}
273
274
  description="Proveedor LLM. El motor (engine) define qué adapter usa (openai, ollama, …)."
274
275
  size="lg"
275
276
  footer={
@@ -362,7 +363,7 @@ export function ProviderModal({ open, initial, existingSlugs, onClose, onSave }:
362
363
  options={modelOptions}
363
364
  className="flex-1"
364
365
  />
365
- <Button size="sm" variant="secondary" onClick={loadModels} disabled={loadingModels} title="Lista los modelos reales del proveedor">
366
+ <Button size="sm" variant="secondary" onClick={loadModels} disabled={loadingModels} title={t("providers_modal.list_models_hint")} aria-label={t("providers_modal.list_models_hint")}>
366
367
  {loadingModels ? <Loader2 className="size-3.5 animate-spin" /> : <RefreshCw className="size-3.5" />}
367
368
  Cargar modelos
368
369
  </Button>
@@ -5,6 +5,7 @@ import { ArrowUp, Square } from "lucide-react"
5
5
 
6
6
  import { cn } from "@/lib/utils"
7
7
  import { Button } from "@/components/ui/button"
8
+ import { t } from "@/i18n"
8
9
 
9
10
  interface ChatInputProps {
10
11
  value: string
@@ -109,8 +110,8 @@ export function ChatInput({
109
110
  size="icon-sm"
110
111
  variant="destructive"
111
112
  onClick={onStop}
112
- aria-label="Detener"
113
- title="Detener"
113
+ aria-label={t("chat_ui.stop")}
114
+ title={t("chat_ui.stop")}
114
115
  >
115
116
  <Square className="size-3.5" fill="currentColor" />
116
117
  </Button>
@@ -121,8 +122,8 @@ export function ChatInput({
121
122
  variant="default"
122
123
  onClick={onSubmit}
123
124
  disabled={!canSend}
124
- aria-label="Enviar"
125
- title="Enviar"
125
+ aria-label={t("chat_ui.send")}
126
+ title={t("chat_ui.send")}
126
127
  >
127
128
  <ArrowUp className="size-4" />
128
129
  </Button>
@@ -9,6 +9,7 @@ import { useIsMobile } from "@/hooks/use-mobile"
9
9
  import { cn } from "@/lib/utils"
10
10
  import { Button } from "@/components/ui/button"
11
11
  import { Input } from "@/components/ui/input"
12
+ import { t } from "@/i18n"
12
13
  import { Separator } from "@/components/ui/separator"
13
14
  import {
14
15
  Sheet,
@@ -284,10 +285,10 @@ function SidebarRail({ className, ...props }: React.ComponentProps<"button">) {
284
285
  <button
285
286
  data-sidebar="rail"
286
287
  data-slot="sidebar-rail"
287
- aria-label="Toggle Sidebar"
288
+ aria-label={t("sidebar_ui.toggle")}
288
289
  tabIndex={-1}
289
290
  onClick={toggleSidebar}
290
- title="Toggle Sidebar"
291
+ title={t("sidebar_ui.toggle")}
291
292
  className={cn(
292
293
  "absolute inset-y-0 z-20 hidden w-4 transition-all ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:start-1/2 after:w-[2px] hover:after:bg-sidebar-border sm:flex ltr:-translate-x-1/2 rtl:-translate-x-1/2",
293
294
  "in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize",
@@ -13,6 +13,7 @@ import {
13
13
  type OpenAiTtsConfig,
14
14
  type PiperConfig,
15
15
  } from "../../lib/api/voice";
16
+ import { t } from "../../i18n";
16
17
 
17
18
  // Per-provider settings. Saved as dotted-key patches under voice.tts.<id>.
18
19
  // Secrets (api_key) follow the EnginesPanel convention: a blank field keeps
@@ -124,7 +125,7 @@ export function VoiceProviderModal({ open, providerId, config, onClose, onSave }
124
125
  <Dialog
125
126
  open={open}
126
127
  onClose={onClose}
127
- title={`Configurar ${meta?.name || providerId}`}
128
+ title={t("voice_screen.configure_provider", { name: meta?.name || providerId || "" })}
128
129
  description={meta?.note}
129
130
  size="md"
130
131
  footer={
@@ -58,7 +58,7 @@ export const ENGINE_ORDER = [
58
58
  export const PERMISSION_MODES = ["total", "automatico", "permiso"] as const;
59
59
  export type PermissionMode = (typeof PERMISSION_MODES)[number];
60
60
 
61
- /** Routine kinds — must match src/host/daemon/routines.js. */
61
+ /** Routine kinds — must match src/core/routines/runner.js. */
62
62
  export const ROUTINE_KINDS = [
63
63
  "heartbeat",
64
64
  "exec_agent",
@@ -32,6 +32,13 @@ export interface ChatMsg {
32
32
  usage?: ChatUsage;
33
33
  /** Operational notes (engine fallbacks, retries, suppressions). */
34
34
  notes?: string[];
35
+ /** Skill Inspector decision for this turn (when the feature is on): which
36
+ * skills the per-turn RAG loaded inline vs merely hinted. */
37
+ inspector?: {
38
+ embedder?: string;
39
+ loaded?: string[];
40
+ hinted?: string[];
41
+ };
35
42
  }
36
43
 
37
44
  export interface SendOptions {
@@ -138,6 +145,18 @@ export function applyStreamEvent(turn: ChatMsg, ev: ChatStreamEvent): ChatMsg {
138
145
  return withNote(`retry (${ev.reason || "?"})`);
139
146
  case "tools_suppressed":
140
147
  return withNote(`tools suppressed: ${(ev.tools || []).join(", ")}`);
148
+ case "skill_inspector": {
149
+ const insp = ev.inspector;
150
+ if (!insp || (!insp.loaded?.length && !insp.hinted?.length)) return turn;
151
+ return {
152
+ ...turn,
153
+ inspector: {
154
+ embedder: insp.embedder,
155
+ loaded: insp.loaded || [],
156
+ hinted: insp.hinted || [],
157
+ },
158
+ };
159
+ }
141
160
  case "assistant_text":
142
161
  return ev.text ? { ...turn, parts: [...turn.parts, { kind: "text", text: ev.text }] } : turn;
143
162
  case "tool_start":
@@ -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,89 @@ 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
+ compaction_title: "History compaction",
912
+ },
913
+
914
+ router_panel: {
915
+ title: "Model router",
916
+ },
917
+
918
+ engines_panel: {
919
+ title: "Providers",
920
+ new_btn: "New provider",
921
+ },
922
+
923
+ providers_modal: {
924
+ new_title: "New provider",
925
+ edit_title: "Edit {name}",
926
+ list_models_hint: "List the provider's actual models",
927
+ toggle_active: "Active · click to deactivate",
928
+ toggle_inactive: "Inactive · click to activate",
929
+ delete: "Delete",
930
+ },
931
+
932
+ chat_ui: {
933
+ copy: "Copy",
934
+ stop: "Stop",
935
+ send: "Send",
936
+ pick_model: "Pick model (or Auto)",
937
+ insert_variable: "Insert variable",
938
+ },
939
+
940
+ sidebar_ui: {
941
+ toggle: "Toggle sidebar",
942
+ },
943
+
944
+ models_ui: {
945
+ invalid_hint: "Model/provider unavailable",
946
+ },
947
+
948
+ global_config: {
949
+ title: "APX config",
950
+ },
951
+
952
+ agent_detail_extra: {
953
+ skills_title: "Skills & tools",
786
954
  },
787
955
  } as const;