@agentprojectcontext/apx 1.33.1 → 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.
- package/package.json +1 -1
- package/skills/apx/SKILL.md +49 -61
- package/src/core/agent/a2a/reply.js +48 -0
- package/src/core/agent/build-agent-system.js +4 -3
- package/src/core/agent/channels/voice-context.js +98 -0
- package/src/core/agent/memory.js +2 -1
- package/src/core/agent/prompt-builder.js +2 -1
- package/src/core/agent/prompts/modes/code-build.md +1 -0
- package/src/core/agent/prompts/modes/code-plan.md +1 -0
- package/src/core/agent/prompts/modes/index.js +28 -0
- package/src/core/agent/skills/loader.js +22 -18
- package/src/core/agent/stream/turn-accumulator.js +73 -0
- package/src/core/agent/suggestions.js +37 -0
- package/src/core/agent/tools/handlers/add-project.js +5 -2
- package/src/core/agent/tools/handlers/call-runtime.js +3 -2
- package/src/core/agent/tools/handlers/transcribe-audio.js +1 -1
- package/src/core/agent/tools/helpers.js +2 -2
- package/src/core/agent/tools/names.js +138 -0
- package/src/core/agent/tools/registry-bridge.js +6 -14
- package/src/core/agent/tools/registry.js +68 -65
- package/src/core/apc/context-copy.js +27 -0
- package/src/core/apc/notes.js +19 -0
- package/src/core/apc/parser.js +12 -5
- package/src/core/apc/paths.js +87 -0
- package/src/core/apc/scaffold.js +82 -76
- package/src/core/apc/skill-sync.js +10 -0
- package/src/{host/daemon/plugins → core/channels}/telegram/dispatch.js +38 -16
- package/src/core/config/index.js +3 -2
- package/src/core/config/redact.js +95 -0
- package/src/core/constants/channels.js +2 -0
- package/src/core/constants/code-modes.js +10 -0
- package/src/core/constants/index.js +1 -0
- package/src/core/deck/manifest.js +186 -0
- package/src/core/engines/catalog.js +83 -0
- package/src/core/{tools → http-tools}/browser.js +0 -1
- package/src/core/{tools → http-tools}/fetch.js +0 -1
- package/src/core/{tools → http-tools}/glob.js +0 -1
- package/src/core/{tools → http-tools}/grep.js +0 -1
- package/src/core/{tools → http-tools}/registry.js +0 -1
- package/src/core/{tools → http-tools}/search.js +0 -1
- package/src/core/i18n/en.js +9 -0
- package/src/core/i18n/es.js +12 -0
- package/src/core/i18n/index.js +54 -0
- package/src/core/i18n/pt.js +9 -0
- package/src/core/identity/telegram.js +2 -1
- package/src/core/mcp/runner.js +272 -14
- package/src/core/mcp/sources.js +3 -2
- package/src/core/routines/index.js +16 -0
- package/src/{host/daemon/routines.js → core/routines/runner.js} +36 -103
- package/src/core/runtime-skills/apc-context/SKILL.md +159 -0
- package/src/core/runtime-skills/apx/SKILL.md +95 -0
- package/src/core/runtime-skills/apx-mcp/SKILL.md +116 -0
- package/src/core/runtime-skills/{claude-code.md → claude-code/SKILL.md} +1 -0
- package/src/core/runtime-skills/{codex-cli.md → codex-cli/SKILL.md} +1 -0
- package/src/core/runtime-skills/{opencode-cli.md → opencode-cli/SKILL.md} +1 -0
- package/src/core/runtime-skills/{openrouter.md → openrouter/SKILL.md} +1 -0
- package/src/{host/daemon/env-detect.js → core/runtimes/detect.js} +1 -1
- package/src/core/stores/code-sessions.js +50 -2
- package/src/core/stores/routine-memory.js +1 -1
- package/src/core/stores/sessions-search.js +121 -0
- package/src/core/stores/sessions.js +38 -0
- package/src/core/vars/index.js +14 -0
- package/src/core/vars/interpolate.js +86 -0
- package/src/core/vars/sources.js +151 -0
- package/src/core/voice/audio-decode.js +38 -0
- package/src/core/voice/transcription.js +225 -0
- package/src/host/daemon/api/admin-config.js +5 -82
- package/src/host/daemon/api/agents.js +5 -5
- package/src/host/daemon/api/code.js +17 -169
- package/src/host/daemon/api/config.js +3 -4
- package/src/host/daemon/api/conversations.js +8 -29
- package/src/host/daemon/api/deck.js +37 -404
- package/src/host/daemon/api/engines.js +1 -80
- package/src/host/daemon/api/exec.js +1 -1
- package/src/host/daemon/api/mcps.js +32 -0
- package/src/host/daemon/api/routines.js +1 -1
- package/src/host/daemon/api/runtimes.js +4 -3
- package/src/host/daemon/api/sessions-search.js +24 -140
- package/src/host/daemon/api/sessions.js +12 -30
- package/src/host/daemon/api/shared.js +2 -1
- package/src/host/daemon/api/telegram.js +1 -11
- package/src/host/daemon/api/tools.js +6 -6
- package/src/host/daemon/api/transcribe.js +2 -2
- package/src/host/daemon/api/vars.js +137 -0
- package/src/host/daemon/api/voice.js +13 -290
- package/src/host/daemon/api.js +2 -0
- package/src/host/daemon/db.js +6 -6
- package/src/host/daemon/deck-exec.js +148 -0
- package/src/host/daemon/index.js +3 -3
- package/src/host/daemon/plugins/telegram/index.js +9 -9
- package/src/host/daemon/routines-scheduler.js +64 -0
- package/src/host/daemon/smoke.js +3 -2
- package/src/host/daemon/whisper-server.js +225 -0
- package/src/interfaces/cli/commands/agent.js +3 -2
- package/src/interfaces/cli/commands/command.js +2 -3
- package/src/interfaces/cli/commands/messages.js +6 -2
- package/src/interfaces/cli/commands/pair.js +5 -4
- package/src/interfaces/cli/commands/search.js +1 -1
- package/src/interfaces/cli/commands/sessions.js +3 -2
- package/src/interfaces/cli/commands/skills.js +36 -55
- package/src/interfaces/web/dist/assets/index-DdmSRtsz.css +1 -0
- package/src/interfaces/web/dist/assets/index-M4FspaCH.js +613 -0
- package/src/interfaces/web/dist/assets/index-M4FspaCH.js.map +1 -0
- package/src/interfaces/web/dist/index.html +2 -2
- package/src/interfaces/web/package-lock.json +182 -182
- package/src/interfaces/web/src/components/ModelCombobox.tsx +2 -1
- package/src/interfaces/web/src/components/TelegramChannelDialog.tsx +1 -1
- package/src/interfaces/web/src/components/chat/AskAnswersCard.tsx +76 -0
- package/src/interfaces/web/src/components/chat/MessageBubble.tsx +16 -3
- package/src/interfaces/web/src/components/chat/MessageList.tsx +23 -1
- package/src/interfaces/web/src/components/chat/ModelPicker.tsx +3 -1
- package/src/interfaces/web/src/components/code/CodeArtifactsTab.tsx +4 -4
- package/src/interfaces/web/src/components/code/CodeChangesTab.tsx +1 -1
- package/src/interfaces/web/src/components/code/CodeFileTree.tsx +3 -2
- package/src/interfaces/web/src/components/code/CodeFileViewer.tsx +3 -2
- package/src/interfaces/web/src/components/code/CodeTerminal.tsx +3 -2
- package/src/interfaces/web/src/components/config/GlobalConfigEditor.tsx +2 -1
- package/src/interfaces/web/src/components/deck/WidgetRow.tsx +2 -1
- package/src/interfaces/web/src/components/inputs/KeyValueList.tsx +93 -0
- package/src/interfaces/web/src/components/inputs/VarTokenInput.tsx +449 -0
- package/src/interfaces/web/src/components/settings/DefaultRouterCard.tsx +2 -1
- package/src/interfaces/web/src/components/settings/EnginesPanel.tsx +2 -2
- package/src/interfaces/web/src/components/settings/MemoryPanel.tsx +5 -4
- package/src/interfaces/web/src/components/settings/providers/ProviderCard.tsx +3 -2
- package/src/interfaces/web/src/components/settings/providers/ProviderModal.tsx +3 -2
- package/src/interfaces/web/src/components/ui/chat-input.tsx +5 -4
- package/src/interfaces/web/src/components/ui/sidebar.tsx +3 -2
- package/src/interfaces/web/src/components/voice/VoiceProviderModal.tsx +2 -1
- package/src/interfaces/web/src/constants/index.ts +1 -1
- package/src/interfaces/web/src/i18n/en.ts +174 -7
- package/src/interfaces/web/src/i18n/es.ts +179 -15
- package/src/interfaces/web/src/lib/api/mcps.ts +25 -0
- package/src/interfaces/web/src/lib/api/vars.ts +38 -0
- package/src/interfaces/web/src/lib/api.ts +1 -0
- package/src/interfaces/web/src/screens/ProjectScreen.tsx +8 -31
- package/src/interfaces/web/src/screens/modules/CodeScreen.tsx +1 -1
- package/src/interfaces/web/src/screens/modules/DeckScreen.tsx +4 -3
- package/src/interfaces/web/src/screens/modules/DesktopScreen.tsx +7 -6
- package/src/interfaces/web/src/screens/modules/VoiceScreen.tsx +4 -3
- package/src/interfaces/web/src/screens/project/AgentDetailScreen.tsx +1 -1
- package/src/interfaces/web/src/screens/project/ConfigTab.tsx +132 -1
- package/src/interfaces/web/src/screens/project/McpsTab.tsx +549 -104
- package/src/interfaces/web/src/screens/project/RoutinesTab.tsx +1 -1
- package/src/interfaces/web/src/screens/project/VarsTab.tsx +300 -0
- package/src/interfaces/web/src/types/daemon.ts +5 -0
- package/src/host/daemon/transcription.js +0 -538
- package/src/host/daemon/whisper-transcribe.py +0 -73
- package/src/interfaces/web/dist/assets/index-Aaiw8BZN.css +0 -1
- package/src/interfaces/web/dist/assets/index-DPqtjDjh.js +0 -602
- package/src/interfaces/web/dist/assets/index-DPqtjDjh.js.map +0 -1
- /package/src/{host/daemon → core/apc}/projects-helpers.js +0 -0
- /package/src/{host/daemon/plugins → core/channels}/telegram/ask.js +0 -0
- /package/src/{host/daemon/plugins → core/channels}/telegram/helpers.js +0 -0
- /package/src/{host/daemon/plugins → core/channels}/telegram/media.js +0 -0
- /package/src/core/{tools → http-tools}/index.js +0 -0
- /package/{skills → src/core/runtime-skills}/apx-agency-agents/SKILL.md +0 -0
- /package/{skills → src/core/runtime-skills}/apx-agent/SKILL.md +0 -0
- /package/{skills → src/core/runtime-skills}/apx-mcp-builder/SKILL.md +0 -0
- /package/{skills → src/core/runtime-skills}/apx-project/SKILL.md +0 -0
- /package/{skills → src/core/runtime-skills}/apx-routine/SKILL.md +0 -0
- /package/{skills → src/core/runtime-skills}/apx-runtime/SKILL.md +0 -0
- /package/{skills → src/core/runtime-skills}/apx-sessions/SKILL.md +0 -0
- /package/{skills → src/core/runtime-skills}/apx-skill-builder/SKILL.md +0 -0
- /package/{skills → src/core/runtime-skills}/apx-task/SKILL.md +0 -0
- /package/{skills → src/core/runtime-skills}/apx-telegram/SKILL.md +0 -0
- /package/{skills → src/core/runtime-skills}/apx-voice/SKILL.md +0 -0
- /package/src/{host/daemon/compact.js → core/stores/conversations-compactor.js} +0 -0
- /package/src/{host/daemon → core/stores}/conversations.js +0 -0
- /package/src/{host/daemon → core/util}/thinking.js +0 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
// APX Deck manifest — the data model the companion clients (deck, desktop
|
|
2
|
+
// capsule) read on boot. Pure data + decoration; no HTTP or filesystem.
|
|
3
|
+
// host/daemon/api/deck.js wraps this for the /deck/manifest endpoint.
|
|
4
|
+
|
|
5
|
+
export const CORE_WIDGETS = [
|
|
6
|
+
{
|
|
7
|
+
id: "apx-current-project",
|
|
8
|
+
title: "Proyecto actual",
|
|
9
|
+
source: "apx",
|
|
10
|
+
desktop: "project",
|
|
11
|
+
kind: "context",
|
|
12
|
+
status: "available",
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
id: "apx-voice",
|
|
16
|
+
title: "Voz APX",
|
|
17
|
+
source: "apx",
|
|
18
|
+
desktop: "general",
|
|
19
|
+
kind: "voice",
|
|
20
|
+
status: "available",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: "apx-agents",
|
|
24
|
+
title: "Agentes APX",
|
|
25
|
+
source: "apx",
|
|
26
|
+
desktop: "ai",
|
|
27
|
+
kind: "agents",
|
|
28
|
+
status: "available",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: "apx-notes",
|
|
32
|
+
title: "Notas APX",
|
|
33
|
+
source: "apx",
|
|
34
|
+
desktop: "project",
|
|
35
|
+
kind: "capture",
|
|
36
|
+
status: "available",
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
export const EXTERNAL_WIDGETS = [
|
|
41
|
+
["docker", "Docker", "infra"],
|
|
42
|
+
["dokploy", "Dokploy", "infra"],
|
|
43
|
+
["factorial", "Factorial", "work"],
|
|
44
|
+
["telegram", "Telegram", "comms"],
|
|
45
|
+
["gmail", "Gmail", "comms"],
|
|
46
|
+
["outlook", "Outlook", "comms"],
|
|
47
|
+
["teams", "Teams", "comms"],
|
|
48
|
+
["whatsapp", "WhatsApp", "comms"],
|
|
49
|
+
["zen", "Zen Browser", "ai"],
|
|
50
|
+
["claude", "Claude", "ai"],
|
|
51
|
+
["chatgpt", "ChatGPT", "ai"],
|
|
52
|
+
["cursor", "Cursor", "ai"],
|
|
53
|
+
["codex", "Codex", "ai"],
|
|
54
|
+
].map(([id, title, desktop]) => ({
|
|
55
|
+
id,
|
|
56
|
+
title,
|
|
57
|
+
source: "external",
|
|
58
|
+
desktop,
|
|
59
|
+
kind: "plugin",
|
|
60
|
+
status: "not_configured",
|
|
61
|
+
}));
|
|
62
|
+
|
|
63
|
+
export const DESKTOPS = [
|
|
64
|
+
{ id: "general", title: "Hoy" },
|
|
65
|
+
{ id: "project", title: "Proyecto" },
|
|
66
|
+
{ id: "ai", title: "IA" },
|
|
67
|
+
{ id: "comms", title: "Comunicaciones" },
|
|
68
|
+
{ id: "infra", title: "Infra" },
|
|
69
|
+
{ id: "work", title: "Tiempo laboral" },
|
|
70
|
+
{ id: "plugins", title: "Plugins" },
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
export const SAFE_ACTIONS = [
|
|
74
|
+
{
|
|
75
|
+
id: "apx.copy_context",
|
|
76
|
+
title: "Copiar contexto APX",
|
|
77
|
+
risk: "safe",
|
|
78
|
+
endpoint: "/projects/:pid/agents",
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
id: "apx.voice_turn",
|
|
82
|
+
title: "Hablar con APX",
|
|
83
|
+
risk: "safe",
|
|
84
|
+
endpoint: "/voice/turn",
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
id: "apx.super_agent",
|
|
88
|
+
title: "Pedir acción a APX",
|
|
89
|
+
risk: "confirm",
|
|
90
|
+
endpoint: "/projects/:pid/super-agent/chat",
|
|
91
|
+
},
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
// Widget ids the user is allowed to override. Keeps a rogue client from
|
|
95
|
+
// writing arbitrary keys into the global config under deck.widget_overrides.
|
|
96
|
+
// CORE_WIDGETS are intentionally NOT in here — they're built-in APX surfaces
|
|
97
|
+
// and don't make sense to disable.
|
|
98
|
+
export const TOGGLEABLE_WIDGETS = new Set(EXTERNAL_WIDGETS.map((w) => w.id));
|
|
99
|
+
|
|
100
|
+
function pickActiveProject(projectList) {
|
|
101
|
+
return projectList.find((project) => Number(project.id) !== 0) || projectList[0] || null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Apply runtime status + user overrides to the static EXTERNAL_WIDGETS list.
|
|
106
|
+
*
|
|
107
|
+
* 1. user explicitly disabled it → "disabled" (sticky, regardless of plugin
|
|
108
|
+
* auto-detect)
|
|
109
|
+
* 2. daemon has a running plugin → "available"
|
|
110
|
+
* 3. user toggled it on but no plugin backing → "configured"
|
|
111
|
+
* 4. nothing → leave the static "not_configured" default
|
|
112
|
+
*/
|
|
113
|
+
export function decorateExternalWidgets(pluginStatus = {}, overrides = {}) {
|
|
114
|
+
return EXTERNAL_WIDGETS.map((widget) => {
|
|
115
|
+
const override = overrides[widget.id];
|
|
116
|
+
const status = pluginStatus[widget.id];
|
|
117
|
+
const decorated = { ...widget };
|
|
118
|
+
if (status) decorated.daemon_status = status;
|
|
119
|
+
if (override?.enabled === false) {
|
|
120
|
+
decorated.status = "disabled";
|
|
121
|
+
} else if (status) {
|
|
122
|
+
decorated.status = status.enabled === false ? "disabled" : "available";
|
|
123
|
+
} else if (override?.enabled === true) {
|
|
124
|
+
decorated.status = "configured";
|
|
125
|
+
}
|
|
126
|
+
// Always echo the user-toggle so the app can render the switch
|
|
127
|
+
// independently of the running/available bit.
|
|
128
|
+
decorated.user_enabled = override?.enabled ?? null;
|
|
129
|
+
return decorated;
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Build the full /deck/manifest response body.
|
|
135
|
+
*
|
|
136
|
+
* Inputs are *resolved* runtime values, not the live managers — caller is
|
|
137
|
+
* responsible for catching errors in projects.list()/plugins.status() and
|
|
138
|
+
* passing the resulting arrays/maps in (or empty defaults).
|
|
139
|
+
*/
|
|
140
|
+
export function buildDeckManifest({
|
|
141
|
+
projectList = [],
|
|
142
|
+
pluginStatus = {},
|
|
143
|
+
overrides = {},
|
|
144
|
+
version,
|
|
145
|
+
startedAt,
|
|
146
|
+
config,
|
|
147
|
+
}) {
|
|
148
|
+
const activeProject = pickActiveProject(projectList);
|
|
149
|
+
return {
|
|
150
|
+
status: "ok",
|
|
151
|
+
daemon: {
|
|
152
|
+
name: "apx",
|
|
153
|
+
version,
|
|
154
|
+
host: config?.host || "127.0.0.1",
|
|
155
|
+
port: config?.port || 7430,
|
|
156
|
+
uptime_s: Math.round((Date.now() - startedAt) / 1000),
|
|
157
|
+
started_at: new Date(startedAt).toISOString(),
|
|
158
|
+
},
|
|
159
|
+
deck: {
|
|
160
|
+
name: "apx-deck",
|
|
161
|
+
desktops: DESKTOPS,
|
|
162
|
+
widgets: [...CORE_WIDGETS, ...decorateExternalWidgets(pluginStatus, overrides)],
|
|
163
|
+
suggested_actions: SAFE_ACTIONS,
|
|
164
|
+
},
|
|
165
|
+
apx: {
|
|
166
|
+
active_project: activeProject,
|
|
167
|
+
projects: projectList,
|
|
168
|
+
plugins: pluginStatus,
|
|
169
|
+
endpoints: {
|
|
170
|
+
health: "/health",
|
|
171
|
+
projects: "/projects",
|
|
172
|
+
plugins: "/plugins",
|
|
173
|
+
voice_turn: "/voice/turn",
|
|
174
|
+
transcribe_chunk: "/transcribe/chunk",
|
|
175
|
+
super_agent_chat: "/projects/:pid/super-agent/chat",
|
|
176
|
+
super_agent_stream: "/projects/:pid/super-agent/chat/stream",
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
safety: {
|
|
180
|
+
direct_shell: false,
|
|
181
|
+
arbitrary_commands: false,
|
|
182
|
+
dangerous_actions_require_confirmation: true,
|
|
183
|
+
allowed_actions_only: true,
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// Live model catalogs per engine. Wraps each provider's "list models" endpoint
|
|
2
|
+
// behind one signature: listModels(engine, baseUrl?, apiKey?) → { models } or
|
|
3
|
+
// { error }. Pure transport — no daemon dependencies. Both the daemon HTTP
|
|
4
|
+
// adapter and CLI commands can reuse this.
|
|
5
|
+
import { fetchJsonWithTimeout } from "./_health.js";
|
|
6
|
+
|
|
7
|
+
export const DEFAULT_BASE = {
|
|
8
|
+
openai: "https://api.openai.com/v1",
|
|
9
|
+
groq: "https://api.groq.com/openai/v1",
|
|
10
|
+
openrouter: "https://openrouter.ai/api/v1",
|
|
11
|
+
gemini: "https://generativelanguage.googleapis.com/v1beta/openai",
|
|
12
|
+
anthropic: "https://api.anthropic.com/v1",
|
|
13
|
+
ollama: "http://localhost:11434",
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// Gemini's native models endpoint returns a much richer catalog than the
|
|
17
|
+
// OpenAI-compat shim (which only echoes back a handful). We always query the
|
|
18
|
+
// native URL regardless of the user's configured base_url.
|
|
19
|
+
const GEMINI_NATIVE_BASE = "https://generativelanguage.googleapis.com/v1beta";
|
|
20
|
+
|
|
21
|
+
export async function listModels(engine, baseUrl, apiKey) {
|
|
22
|
+
const base = String(baseUrl || DEFAULT_BASE[engine] || "").replace(/\/$/, "");
|
|
23
|
+
|
|
24
|
+
if (engine === "ollama") {
|
|
25
|
+
const b = base || process.env.OLLAMA_HOST || "http://localhost:11434";
|
|
26
|
+
const r = await fetchJsonWithTimeout(`${b}/api/tags`, { timeoutMs: 2500 });
|
|
27
|
+
if (!r.ok) return { error: r.reason || "no se pudo contactar Ollama" };
|
|
28
|
+
const list = Array.isArray(r.json?.models) ? r.json.models : [];
|
|
29
|
+
return { models: list.map((m) => m?.name).filter((n) => typeof n === "string" && n) };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (engine === "anthropic") {
|
|
33
|
+
if (!apiKey) return { error: "falta api_key" };
|
|
34
|
+
const b = base || DEFAULT_BASE.anthropic;
|
|
35
|
+
const r = await fetchJsonWithTimeout(`${b}/models?limit=100`, {
|
|
36
|
+
timeoutMs: 5000,
|
|
37
|
+
headers: { "x-api-key": apiKey, "anthropic-version": "2023-06-01" },
|
|
38
|
+
});
|
|
39
|
+
if (!r.ok) return { error: r.reason || `HTTP ${r.status}` };
|
|
40
|
+
const data = Array.isArray(r.json?.data) ? r.json.data : [];
|
|
41
|
+
return { models: data.map((m) => m?.id).filter(Boolean) };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (engine === "gemini") {
|
|
45
|
+
if (!apiKey) return { error: "falta api_key" };
|
|
46
|
+
// Native Gemini API returns rich metadata, including supportedGenerationMethods
|
|
47
|
+
// so we can drop embeddings/vision-only entries. Names come back as
|
|
48
|
+
// "models/<id>"; strip the prefix.
|
|
49
|
+
const r = await fetchJsonWithTimeout(
|
|
50
|
+
`${GEMINI_NATIVE_BASE}/models?key=${encodeURIComponent(apiKey)}&pageSize=200`,
|
|
51
|
+
{ timeoutMs: 5000 },
|
|
52
|
+
);
|
|
53
|
+
if (!r.ok) return { error: r.reason || `HTTP ${r.status}` };
|
|
54
|
+
const data = Array.isArray(r.json?.models) ? r.json.models : [];
|
|
55
|
+
const models = data
|
|
56
|
+
.filter((m) => {
|
|
57
|
+
const methods = m?.supportedGenerationMethods;
|
|
58
|
+
if (!Array.isArray(methods)) return true;
|
|
59
|
+
return methods.includes("generateContent");
|
|
60
|
+
})
|
|
61
|
+
.map((m) => {
|
|
62
|
+
const name = typeof m?.name === "string" ? m.name : "";
|
|
63
|
+
return name.startsWith("models/") ? name.slice("models/".length) : name;
|
|
64
|
+
})
|
|
65
|
+
.filter(Boolean);
|
|
66
|
+
return { models };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// openai-compatible family: openai, groq, openrouter, azure, custom
|
|
70
|
+
if (!apiKey) return { error: "falta api_key" };
|
|
71
|
+
if (!base) return { error: "falta base_url" };
|
|
72
|
+
const r = await fetchJsonWithTimeout(`${base}/models`, {
|
|
73
|
+
timeoutMs: 5000,
|
|
74
|
+
headers: { authorization: `Bearer ${apiKey}` },
|
|
75
|
+
});
|
|
76
|
+
if (!r.ok) return { error: r.reason || `HTTP ${r.status}` };
|
|
77
|
+
const data = Array.isArray(r.json?.data)
|
|
78
|
+
? r.json.data
|
|
79
|
+
: Array.isArray(r.json?.models)
|
|
80
|
+
? r.json.models
|
|
81
|
+
: [];
|
|
82
|
+
return { models: data.map((m) => m?.id || m?.name).filter(Boolean) };
|
|
83
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Backend strings — English (en).
|
|
2
|
+
export default {
|
|
3
|
+
"telegram.heads_up": "On it — working on that… 🛠️",
|
|
4
|
+
"telegram.reset_ack": "Done, context cleared. Starting fresh. What do you need?",
|
|
5
|
+
"telegram.error_generic": "Something broke on my side — already logged.",
|
|
6
|
+
"telegram.fallback_listo": "Done.",
|
|
7
|
+
|
|
8
|
+
"common.unknown_error": "Something went wrong.",
|
|
9
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Backend strings — Spanish (es). Keep this file flat dot-paths only; the
|
|
2
|
+
// web admin has its own i18n tree.
|
|
3
|
+
export default {
|
|
4
|
+
// Telegram channel
|
|
5
|
+
"telegram.heads_up": "Dale, estoy con eso… 🛠️",
|
|
6
|
+
"telegram.reset_ack": "Listo, contexto borrado. Arranco un hilo nuevo, ¿qué necesitás?",
|
|
7
|
+
"telegram.error_generic": "Algo se rompió de mi lado — ya lo registré.",
|
|
8
|
+
"telegram.fallback_listo": "Listo.",
|
|
9
|
+
|
|
10
|
+
// Generic helpers reused from several surfaces
|
|
11
|
+
"common.unknown_error": "Algo salió mal.",
|
|
12
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Backend i18n for daemon-side messages (Telegram heads-up, system replies,
|
|
2
|
+
// any other user-facing string emitted from the host/core layer). The web
|
|
3
|
+
// admin has its own dict tree under src/interfaces/web/src/i18n/ — that one
|
|
4
|
+
// stays separate, this is for what the daemon sends back.
|
|
5
|
+
//
|
|
6
|
+
// Usage:
|
|
7
|
+
// import { t, resolveLang } from "#core/i18n/index.js";
|
|
8
|
+
// const lang = resolveLang(globalConfig);
|
|
9
|
+
// await sendTelegram(t("telegram.heads_up", { lang }));
|
|
10
|
+
//
|
|
11
|
+
// Adding a key: pick a clear dotted path, add it to every locale dict, and
|
|
12
|
+
// the unit test in tests/i18n.test.js will assert parity (no missing
|
|
13
|
+
// translations). Values can include {var} placeholders that t() will fill.
|
|
14
|
+
import en from "./en.js";
|
|
15
|
+
import es from "./es.js";
|
|
16
|
+
import pt from "./pt.js";
|
|
17
|
+
|
|
18
|
+
const DICTS = Object.freeze({ en, es, pt });
|
|
19
|
+
const DEFAULT_LANG = "es";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Pull the user's preferred language code from a globalConfig snapshot.
|
|
23
|
+
* Falls back to DEFAULT_LANG when nothing is set. The 2-char slice keeps
|
|
24
|
+
* "es-AR" / "en-US" / "pt-BR" working without per-region dicts.
|
|
25
|
+
*/
|
|
26
|
+
export function resolveLang(globalConfig) {
|
|
27
|
+
const raw = globalConfig?.user?.language;
|
|
28
|
+
return String(raw || DEFAULT_LANG).slice(0, 2).toLowerCase();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function format(s, vars) {
|
|
32
|
+
if (!vars) return s;
|
|
33
|
+
return s.replace(/\{(\w+)\}/g, (_m, k) => (k in vars ? String(vars[k]) : `{${k}}`));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Translate a key into the active locale. Missing keys fall back through:
|
|
38
|
+
* requested lang → DEFAULT_LANG → the key itself (as a last-resort
|
|
39
|
+
* placeholder so the caller can spot the gap).
|
|
40
|
+
*/
|
|
41
|
+
export function t(key, { lang = DEFAULT_LANG, vars } = {}) {
|
|
42
|
+
const code = String(lang || DEFAULT_LANG).slice(0, 2).toLowerCase();
|
|
43
|
+
const dict = DICTS[code] || DICTS[DEFAULT_LANG];
|
|
44
|
+
const value = dict?.[key] ?? DICTS[DEFAULT_LANG]?.[key] ?? key;
|
|
45
|
+
return format(value, vars);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Lower-level: get the active dict, e.g. for bulk lookups in a loop. */
|
|
49
|
+
export function getDict(lang) {
|
|
50
|
+
const code = String(lang || DEFAULT_LANG).slice(0, 2).toLowerCase();
|
|
51
|
+
return DICTS[code] || DICTS[DEFAULT_LANG];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export { DICTS, DEFAULT_LANG };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Backend strings — Portuguese (pt).
|
|
2
|
+
export default {
|
|
3
|
+
"telegram.heads_up": "Já estou nisso… 🛠️",
|
|
4
|
+
"telegram.reset_ack": "Pronto, contexto limpo. Começando do zero — do que você precisa?",
|
|
5
|
+
"telegram.error_generic": "Algo quebrou do meu lado — já registrei.",
|
|
6
|
+
"telegram.fallback_listo": "Pronto.",
|
|
7
|
+
|
|
8
|
+
"common.unknown_error": "Algo deu errado.",
|
|
9
|
+
};
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
upsertContact,
|
|
18
18
|
upsertTelegramChannel,
|
|
19
19
|
} from "../config/index.js";
|
|
20
|
+
import { SENDER_ROLES } from "../constants/roles.js";
|
|
20
21
|
|
|
21
22
|
function telegramDisplayName(from) {
|
|
22
23
|
const full = [from?.first_name, from?.last_name].filter(Boolean).join(" ").trim();
|
|
@@ -62,7 +63,7 @@ export function resolveAllowedTools(cfg, sender) {
|
|
|
62
63
|
if (sender?.isOwner) return "*";
|
|
63
64
|
const def = cfg?.telegram?.roles?.[sender?.role];
|
|
64
65
|
if (def && def.tools !== undefined) return def.tools;
|
|
65
|
-
if (sender?.role ===
|
|
66
|
+
if (sender?.role === SENDER_ROLES.GUEST) return [];
|
|
66
67
|
return "*";
|
|
67
68
|
}
|
|
68
69
|
|