@agentprojectcontext/apx 1.25.0 → 1.27.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +42 -12
- package/skills/apx/SKILL.md +50 -119
- package/skills/apx-agency-agents/SKILL.md +141 -0
- package/skills/apx-agent/SKILL.md +100 -0
- package/skills/apx-mcp/SKILL.md +114 -0
- package/skills/apx-mcp-builder/SKILL.md +186 -0
- package/skills/apx-project/SKILL.md +100 -0
- package/skills/apx-routine/SKILL.md +138 -0
- package/skills/apx-runtime/SKILL.md +115 -0
- package/skills/apx-sessions/SKILL.md +281 -0
- package/skills/apx-skill-builder/SKILL.md +149 -0
- package/skills/apx-task/SKILL.md +95 -0
- package/skills/apx-telegram/SKILL.md +115 -0
- package/skills/apx-voice/SKILL.md +135 -0
- package/src/core/agent/constants.js +3 -0
- package/src/core/agent/ghost-guard.js +24 -0
- package/src/core/agent/index.js +35 -0
- package/src/core/agent/model-router.js +257 -0
- package/src/core/agent/prompt-builder.js +313 -0
- package/src/core/agent/prompts/channels/api.md +7 -0
- package/src/core/agent/prompts/channels/cli.md +6 -0
- package/src/core/agent/prompts/channels/code.md +20 -0
- package/src/core/agent/prompts/channels/deck.md +6 -0
- package/src/core/agent/prompts/channels/desktop.md +24 -0
- package/src/core/agent/prompts/channels/routine.md +9 -0
- package/src/core/agent/prompts/channels/telegram.md +9 -0
- package/src/core/agent/prompts/channels/terminal.md +16 -0
- package/src/core/agent/prompts/channels/web.md +7 -0
- package/src/core/agent/prompts/channels/web_sidebar.md +7 -0
- package/src/core/agent/prompts/modes/voice.md +4 -0
- package/src/core/agent/prompts/super-agent-base.md +42 -0
- package/src/core/agent/pseudo-tools.js +40 -0
- package/src/core/agent/retry.js +85 -0
- package/src/core/agent/run-agent.js +423 -0
- package/src/core/agent/self-memory.js +155 -0
- package/src/{daemon → core/agent}/tool-call-parser.js +83 -10
- package/src/core/agent/tools-overlap.js +66 -0
- package/src/core/apc-skill-sync.js +97 -0
- package/src/core/code-sessions-store.js +150 -0
- package/src/core/config.js +473 -11
- package/src/core/desktop/autostart.js +162 -0
- package/src/core/engines/_health.js +63 -0
- package/src/{daemon → core}/engines/anthropic.js +16 -0
- package/src/core/engines/gemini.js +168 -0
- package/src/core/engines/groq.js +8 -0
- package/src/{daemon → core}/engines/index.js +3 -1
- package/src/{daemon → core}/engines/mock.js +22 -0
- package/src/{daemon → core}/engines/ollama.js +35 -0
- package/src/core/engines/openai-compatible.js +145 -0
- package/src/core/engines/openai.js +8 -0
- package/src/core/engines/openrouter.js +8 -0
- package/src/core/git-baseline.js +147 -0
- package/src/core/identity.js +21 -0
- package/src/core/logging.js +1 -1
- package/src/core/mcp/index.js +14 -0
- package/src/{daemon/mcp-runner.js → core/mcp/runner.js} +18 -6
- package/src/core/mcp/sources.js +246 -0
- package/src/core/memory/active-threads.js +124 -0
- package/src/core/memory/broker.js +144 -0
- package/src/core/memory/compactor.js +186 -0
- package/src/core/memory/embed-engines/gemini.js +62 -0
- package/src/core/memory/embed-engines/index.js +148 -0
- package/src/core/memory/embed-engines/ollama.js +55 -0
- package/src/core/memory/embed-engines/openai.js +64 -0
- package/src/core/memory/embed-engines/tf.js +20 -0
- package/src/core/memory/embeddings.js +132 -0
- package/src/core/memory/index.js +161 -0
- package/src/core/memory/indexer.js +257 -0
- package/src/core/memory/store.js +231 -0
- package/src/core/messages-store.js +143 -25
- package/src/core/parser.js +78 -16
- package/src/core/scaffold.js +175 -79
- package/src/core/tasks-store.js +264 -0
- package/src/core/telegram-identity.js +126 -0
- package/src/core/tools/index.js +6 -0
- package/src/core/voice/engines/elevenlabs.js +96 -0
- package/src/core/voice/engines/gemini.js +148 -0
- package/src/core/voice/engines/index.js +144 -0
- package/src/core/voice/engines/mock.js +59 -0
- package/src/core/voice/engines/openai.js +82 -0
- package/src/core/voice/engines/piper.js +93 -0
- package/src/core/voice/index.js +3 -0
- package/src/core/voice/tts.js +89 -0
- package/src/{daemon → host/daemon}/apc-runtime-context.js +1 -1
- package/src/host/daemon/api/admin-config.js +159 -0
- package/src/host/daemon/api/admin.js +72 -0
- package/src/host/daemon/api/agents.js +284 -0
- package/src/host/daemon/api/artifacts.js +52 -0
- package/src/host/daemon/api/code.js +351 -0
- package/src/host/daemon/api/config.js +104 -0
- package/src/host/daemon/api/connections.js +42 -0
- package/src/host/daemon/api/conversations.js +161 -0
- package/src/host/daemon/api/deck.js +511 -0
- package/src/host/daemon/api/desktop.js +71 -0
- package/src/host/daemon/api/embeddings.js +65 -0
- package/src/host/daemon/api/engines.js +80 -0
- package/src/host/daemon/api/exec.js +193 -0
- package/src/host/daemon/api/health.js +10 -0
- package/src/host/daemon/api/identity.js +36 -0
- package/src/host/daemon/api/mcps.js +205 -0
- package/src/host/daemon/api/messages.js +83 -0
- package/src/host/daemon/api/pairing.js +194 -0
- package/src/host/daemon/api/plugins.js +19 -0
- package/src/host/daemon/api/projects.js +35 -0
- package/src/host/daemon/api/routines.js +84 -0
- package/src/host/daemon/api/run.js +55 -0
- package/src/host/daemon/api/runtimes.js +219 -0
- package/src/host/daemon/api/sessions-search.js +177 -0
- package/src/host/daemon/api/sessions.js +115 -0
- package/src/host/daemon/api/shared.js +217 -0
- package/src/host/daemon/api/super-agent.js +208 -0
- package/src/host/daemon/api/tasks.js +118 -0
- package/src/host/daemon/api/telegram.js +325 -0
- package/src/host/daemon/api/tools.js +21 -0
- package/src/host/daemon/api/top-level.js +131 -0
- package/src/host/daemon/api/transcribe.js +35 -0
- package/src/host/daemon/api/tts.js +44 -0
- package/src/host/daemon/api/voice.js +519 -0
- package/src/host/daemon/api/web.js +123 -0
- package/src/host/daemon/api.js +152 -0
- package/src/{daemon → host/daemon}/compact.js +1 -1
- package/src/{daemon → host/daemon}/db.js +19 -5
- package/src/{daemon/overlay-ws.js → host/daemon/desktop-ws.js} +7 -7
- package/src/host/daemon/engine-sessions.js +245 -0
- package/src/{daemon → host/daemon}/index.js +27 -10
- package/src/{daemon/plugins/overlay.js → host/daemon/plugins/desktop.js} +36 -28
- package/src/{daemon → host/daemon}/plugins/index.js +2 -2
- package/src/{daemon → host/daemon}/plugins/telegram.js +165 -33
- package/src/{daemon → host/daemon}/project-config.js +31 -7
- package/src/{daemon → host/daemon}/routines.js +27 -8
- package/src/{daemon → host/daemon}/skills-loader.js +4 -2
- package/src/{daemon → host/daemon}/smoke.js +9 -5
- package/src/{daemon → host/daemon}/super-agent-tools/helpers.js +1 -1
- package/src/host/daemon/super-agent-tools/index.js +157 -0
- package/src/{daemon → host/daemon}/super-agent-tools/registry-bridge.js +1 -1
- package/src/{daemon → host/daemon}/super-agent-tools/tools/add-project.js +2 -2
- package/src/{daemon → host/daemon}/super-agent-tools/tools/ask-questions.js +4 -0
- package/src/{daemon → host/daemon}/super-agent-tools/tools/call-agent.js +5 -2
- package/src/{daemon → host/daemon}/super-agent-tools/tools/call-runtime.js +117 -8
- package/src/host/daemon/super-agent-tools/tools/create-task.js +52 -0
- package/src/{daemon → host/daemon}/super-agent-tools/tools/import-agent.js +2 -3
- package/src/{daemon → host/daemon}/super-agent-tools/tools/list-agents.js +1 -1
- package/src/host/daemon/super-agent-tools/tools/list-tasks.js +52 -0
- package/src/{daemon → host/daemon}/super-agent-tools/tools/list-vault-agents.js +1 -1
- package/src/host/daemon/super-agent-tools/tools/read-self-memory.js +21 -0
- package/src/host/daemon/super-agent-tools/tools/remember.js +40 -0
- package/src/{daemon → host/daemon}/super-agent-tools/tools/search-messages.js +1 -1
- package/src/host/daemon/super-agent-tools/tools/search-sessions.js +134 -0
- package/src/{daemon → host/daemon}/super-agent-tools/tools/send-telegram.js +2 -2
- package/src/{daemon → host/daemon}/super-agent-tools/tools/set-identity.js +1 -1
- package/src/{daemon → host/daemon}/super-agent-tools/tools/set-permission-mode.js +1 -1
- package/src/{daemon → host/daemon}/super-agent-tools/tools/tail-messages.js +1 -1
- package/src/host/daemon/super-agent.js +129 -0
- package/src/host/daemon/token-store.js +118 -0
- package/src/host/daemon/tool-call-parser.js +2 -0
- package/src/{daemon → host/daemon}/transcription.js +1 -1
- package/src/{daemon → host/daemon}/wakeup.js +2 -2
- package/src/interfaces/cli/claude-permissions.js +33 -0
- package/src/{cli → interfaces/cli}/commands/agent.js +43 -8
- package/src/{cli → interfaces/cli}/commands/command.js +1 -1
- package/src/{cli → interfaces/cli}/commands/config.js +1 -1
- package/src/{cli → interfaces/cli}/commands/daemon.js +67 -0
- package/src/interfaces/cli/commands/desktop.js +335 -0
- package/src/interfaces/cli/commands/exec.js +92 -0
- package/src/{cli → interfaces/cli}/commands/identity.js +6 -63
- package/src/{cli → interfaces/cli}/commands/init.js +1 -1
- package/src/{cli → interfaces/cli}/commands/mcp.js +69 -10
- package/src/{cli → interfaces/cli}/commands/memory.js +2 -2
- package/src/interfaces/cli/commands/model.js +136 -0
- package/src/interfaces/cli/commands/pair.js +170 -0
- package/src/interfaces/cli/commands/project-config.js +131 -0
- package/src/{cli → interfaces/cli}/commands/project.js +1 -1
- package/src/{cli → interfaces/cli}/commands/search.js +1 -1
- package/src/interfaces/cli/commands/session.js +892 -0
- package/src/interfaces/cli/commands/sessions.js +997 -0
- package/src/{cli → interfaces/cli}/commands/setup.js +98 -4
- package/src/{cli → interfaces/cli}/commands/skills.js +117 -9
- package/src/{cli → interfaces/cli}/commands/status.js +9 -1
- package/src/{cli → interfaces/cli}/commands/sys.js +96 -17
- package/src/interfaces/cli/commands/task.js +179 -0
- package/src/interfaces/cli/commands/telegram.js +366 -0
- package/src/{cli → interfaces/cli}/commands/update.js +1 -1
- package/src/interfaces/cli/commands/voice.js +258 -0
- package/src/{cli → interfaces/cli}/http.js +6 -2
- package/src/{cli → interfaces/cli}/index.js +955 -63
- package/src/interfaces/cli/postinstall.js +34 -0
- package/src/interfaces/desktop/assets/app-icon-180.png +0 -0
- package/src/interfaces/desktop/assets/app-icon-32.png +0 -0
- package/src/interfaces/desktop/assets/app-icon.png +0 -0
- package/src/interfaces/desktop/assets/apx-logo.png +0 -0
- package/src/interfaces/desktop/assets/superagent.png +0 -0
- package/src/interfaces/desktop/assets/tray-icon.png +0 -0
- package/src/interfaces/desktop/index.html +18 -0
- package/src/interfaces/desktop/main.js +652 -0
- package/src/interfaces/desktop/preload.js +48 -0
- package/src/interfaces/desktop/renderer.js +1006 -0
- package/src/interfaces/desktop/style.css +400 -0
- package/src/{mcp → interfaces/mcp-server}/index.js +2 -2
- package/src/interfaces/tui/_shims/util-which.ts +53 -0
- package/src/{tui → interfaces/tui}/app.tsx +2 -2
- package/src/{tui → interfaces/tui}/component/prompt/index.tsx +4 -1
- package/src/{tui → interfaces/tui}/context/sdk-apx.tsx +84 -16
- package/src/interfaces/tui/context/sync-apx.tsx +398 -0
- package/src/interfaces/tui/routes/session/index.tsx +368 -0
- package/src/interfaces/tui/routes/session/message-actions.tsx +58 -0
- package/src/interfaces/tui/routes/session/sidebar-apx.tsx +114 -0
- package/src/{tui → interfaces/tui}/tsconfig.json +1 -0
- package/src/{tui → interfaces/tui}/util/clipboard.ts +1 -1
- package/src/interfaces/web/README.md +102 -0
- package/src/interfaces/web/coming-soon.html +65 -0
- package/src/interfaces/web/components.json +25 -0
- package/src/interfaces/web/dist/assets/index-BDUsA6L6.css +1 -0
- package/src/interfaces/web/dist/assets/index-CfWyjPBa.js +548 -0
- package/src/interfaces/web/dist/assets/index-CfWyjPBa.js.map +1 -0
- package/src/interfaces/web/dist/favicon/dark/android-chrome-192x192.png +0 -0
- package/src/interfaces/web/dist/favicon/dark/android-chrome-512x512.png +0 -0
- package/src/interfaces/web/dist/favicon/dark/apple-touch-icon.png +0 -0
- package/src/interfaces/web/dist/favicon/dark/favicon-16x16.png +0 -0
- package/src/interfaces/web/dist/favicon/dark/favicon-32x32.png +0 -0
- package/src/interfaces/web/dist/favicon/dark/favicon-48x48.png +0 -0
- package/src/interfaces/web/dist/favicon/dark/favicon.ico +0 -0
- package/src/interfaces/web/dist/favicon/dark/favicon.webp +0 -0
- package/src/interfaces/web/dist/favicon/dark/site.webmanifest +18 -0
- package/src/interfaces/web/dist/favicon/white/android-chrome-192x192.png +0 -0
- package/src/interfaces/web/dist/favicon/white/android-chrome-512x512.png +0 -0
- package/src/interfaces/web/dist/favicon/white/apple-touch-icon.png +0 -0
- package/src/interfaces/web/dist/favicon/white/favicon-16x16.png +0 -0
- package/src/interfaces/web/dist/favicon/white/favicon-32x32.png +0 -0
- package/src/interfaces/web/dist/favicon/white/favicon-48x48.png +0 -0
- package/src/interfaces/web/dist/favicon/white/favicon.ico +0 -0
- package/src/interfaces/web/dist/favicon/white/favicon.webp +0 -0
- package/src/interfaces/web/dist/favicon/white/site.webmanifest +18 -0
- package/src/interfaces/web/dist/index.html +27 -0
- package/src/interfaces/web/dist/logo/logo_dark.webp +0 -0
- package/src/interfaces/web/dist/logo/logo_only_dark.webp +0 -0
- package/src/interfaces/web/dist/logo/logo_only_white.webp +0 -0
- package/src/interfaces/web/dist/logo/logo_vertical_dark.webp +0 -0
- package/src/interfaces/web/dist/logo/logo_vertical_white.webp +0 -0
- package/src/interfaces/web/dist/logo/logo_white.webp +0 -0
- package/src/interfaces/web/dist/modules/superagent.png +0 -0
- package/src/interfaces/web/index.html +26 -0
- package/src/interfaces/web/package-lock.json +4253 -0
- package/src/interfaces/web/package.json +55 -0
- package/src/interfaces/web/playwright.config.ts +45 -0
- package/src/interfaces/web/pnpm-lock.yaml +2946 -0
- package/src/interfaces/web/public/favicon/dark/android-chrome-192x192.png +0 -0
- package/src/interfaces/web/public/favicon/dark/android-chrome-512x512.png +0 -0
- package/src/interfaces/web/public/favicon/dark/apple-touch-icon.png +0 -0
- package/src/interfaces/web/public/favicon/dark/favicon-16x16.png +0 -0
- package/src/interfaces/web/public/favicon/dark/favicon-32x32.png +0 -0
- package/src/interfaces/web/public/favicon/dark/favicon-48x48.png +0 -0
- package/src/interfaces/web/public/favicon/dark/favicon.ico +0 -0
- package/src/interfaces/web/public/favicon/dark/favicon.webp +0 -0
- package/src/interfaces/web/public/favicon/dark/site.webmanifest +18 -0
- package/src/interfaces/web/public/favicon/white/android-chrome-192x192.png +0 -0
- package/src/interfaces/web/public/favicon/white/android-chrome-512x512.png +0 -0
- package/src/interfaces/web/public/favicon/white/apple-touch-icon.png +0 -0
- package/src/interfaces/web/public/favicon/white/favicon-16x16.png +0 -0
- package/src/interfaces/web/public/favicon/white/favicon-32x32.png +0 -0
- package/src/interfaces/web/public/favicon/white/favicon-48x48.png +0 -0
- package/src/interfaces/web/public/favicon/white/favicon.ico +0 -0
- package/src/interfaces/web/public/favicon/white/favicon.webp +0 -0
- package/src/interfaces/web/public/favicon/white/site.webmanifest +18 -0
- package/src/interfaces/web/public/logo/logo_dark.webp +0 -0
- package/src/interfaces/web/public/logo/logo_only_dark.webp +0 -0
- package/src/interfaces/web/public/logo/logo_only_white.webp +0 -0
- package/src/interfaces/web/public/logo/logo_vertical_dark.webp +0 -0
- package/src/interfaces/web/public/logo/logo_vertical_white.webp +0 -0
- package/src/interfaces/web/public/logo/logo_white.webp +0 -0
- package/src/interfaces/web/public/modules/superagent.png +0 -0
- package/src/interfaces/web/src/App.tsx +199 -0
- package/src/interfaces/web/src/components/AddProjectDialog.tsx +121 -0
- package/src/interfaces/web/src/components/ModelCombobox.tsx +96 -0
- package/src/interfaces/web/src/components/RobyBubble.tsx +213 -0
- package/src/interfaces/web/src/components/Section.tsx +44 -0
- package/src/interfaces/web/src/components/TelegramChannelDialog.tsx +97 -0
- package/src/interfaces/web/src/components/TelegramSendDialog.tsx +48 -0
- package/src/interfaces/web/src/components/Toast.tsx +84 -0
- package/src/interfaces/web/src/components/UiSelect.tsx +74 -0
- package/src/interfaces/web/src/components/chat/Composer.tsx +43 -0
- package/src/interfaces/web/src/components/chat/ContextBar.tsx +111 -0
- package/src/interfaces/web/src/components/chat/MessageBubble.tsx +95 -0
- package/src/interfaces/web/src/components/chat/MessageList.tsx +35 -0
- package/src/interfaces/web/src/components/chat/ModelPicker.tsx +145 -0
- package/src/interfaces/web/src/components/chat/ToolCall.tsx +141 -0
- package/src/interfaces/web/src/components/code/CodeChangesTab.tsx +87 -0
- package/src/interfaces/web/src/components/code/CodeComposer.tsx +87 -0
- package/src/interfaces/web/src/components/code/CodeContextTab.tsx +83 -0
- package/src/interfaces/web/src/components/code/CodeProjectPicker.tsx +39 -0
- package/src/interfaces/web/src/components/code/CodeSessionList.tsx +97 -0
- package/src/interfaces/web/src/components/code/CodeSidePanel.tsx +44 -0
- package/src/interfaces/web/src/components/code/CodeToolTrail.tsx +29 -0
- package/src/interfaces/web/src/components/code/DiffView.tsx +67 -0
- package/src/interfaces/web/src/components/common/Qr.tsx +27 -0
- package/src/interfaces/web/src/components/common/TabLayout.tsx +46 -0
- package/src/interfaces/web/src/components/common/TabNav.tsx +113 -0
- package/src/interfaces/web/src/components/config/ConfigTabsEditor.tsx +202 -0
- package/src/interfaces/web/src/components/config/GlobalConfigEditor.tsx +42 -0
- package/src/interfaces/web/src/components/config/global-config-sections.ts +60 -0
- package/src/interfaces/web/src/components/config/project-config-sections.ts +58 -0
- package/src/interfaces/web/src/components/deck/DaemonCard.tsx +58 -0
- package/src/interfaces/web/src/components/deck/DesktopGroup.tsx +33 -0
- package/src/interfaces/web/src/components/deck/WidgetRow.tsx +100 -0
- package/src/interfaces/web/src/components/layout/Logo.tsx +59 -0
- package/src/interfaces/web/src/components/layout/ProjectAvatar.tsx +116 -0
- package/src/interfaces/web/src/components/layout/ProjectSidebar.tsx +151 -0
- package/src/interfaces/web/src/components/settings/AdvancedPanel.tsx +45 -0
- package/src/interfaces/web/src/components/settings/AppearancePanel.tsx +72 -0
- package/src/interfaces/web/src/components/settings/DefaultRouterCard.tsx +232 -0
- package/src/interfaces/web/src/components/settings/DevicesPanel.tsx +60 -0
- package/src/interfaces/web/src/components/settings/EnginesPanel.tsx +127 -0
- package/src/interfaces/web/src/components/settings/IdentityPanel.tsx +69 -0
- package/src/interfaces/web/src/components/settings/MemoryPanel.tsx +226 -0
- package/src/interfaces/web/src/components/settings/PairDeviceDialog.tsx +175 -0
- package/src/interfaces/web/src/components/settings/SuperAgentPanel.tsx +93 -0
- package/src/interfaces/web/src/components/settings/TelegramChannelsPanel.tsx +90 -0
- package/src/interfaces/web/src/components/settings/TelegramContactsPanel.tsx +101 -0
- package/src/interfaces/web/src/components/settings/TelegramGlobalPanel.tsx +100 -0
- package/src/interfaces/web/src/components/settings/TelegramRolesPanel.tsx +108 -0
- package/src/interfaces/web/src/components/settings/TelegramSettingsTabs.tsx +55 -0
- package/src/interfaces/web/src/components/settings/providers/ProviderCard.tsx +95 -0
- package/src/interfaces/web/src/components/settings/providers/ProviderModal.tsx +405 -0
- package/src/interfaces/web/src/components/settings/providers/typeStyles.ts +155 -0
- package/src/interfaces/web/src/components/settings/providers/types.ts +26 -0
- package/src/interfaces/web/src/components/ui/accordion.tsx +72 -0
- package/src/interfaces/web/src/components/ui/alert-dialog.tsx +187 -0
- package/src/interfaces/web/src/components/ui/alert.tsx +76 -0
- package/src/interfaces/web/src/components/ui/aspect-ratio.tsx +22 -0
- package/src/interfaces/web/src/components/ui/avatar.tsx +107 -0
- package/src/interfaces/web/src/components/ui/badge.tsx +52 -0
- package/src/interfaces/web/src/components/ui/breadcrumb.tsx +125 -0
- package/src/interfaces/web/src/components/ui/button-group.tsx +87 -0
- package/src/interfaces/web/src/components/ui/button.tsx +58 -0
- package/src/interfaces/web/src/components/ui/calendar.tsx +221 -0
- package/src/interfaces/web/src/components/ui/card.tsx +103 -0
- package/src/interfaces/web/src/components/ui/carousel.tsx +242 -0
- package/src/interfaces/web/src/components/ui/chart.tsx +371 -0
- package/src/interfaces/web/src/components/ui/chat-input.tsx +122 -0
- package/src/interfaces/web/src/components/ui/checkbox.tsx +29 -0
- package/src/interfaces/web/src/components/ui/collapsible.tsx +19 -0
- package/src/interfaces/web/src/components/ui/combobox.tsx +295 -0
- package/src/interfaces/web/src/components/ui/command.tsx +196 -0
- package/src/interfaces/web/src/components/ui/context-menu.tsx +271 -0
- package/src/interfaces/web/src/components/ui/dialog.tsx +158 -0
- package/src/interfaces/web/src/components/ui/direction.tsx +4 -0
- package/src/interfaces/web/src/components/ui/drawer.tsx +134 -0
- package/src/interfaces/web/src/components/ui/dropdown-menu.tsx +266 -0
- package/src/interfaces/web/src/components/ui/empty.tsx +104 -0
- package/src/interfaces/web/src/components/ui/field.tsx +236 -0
- package/src/interfaces/web/src/components/ui/hover-card.tsx +51 -0
- package/src/interfaces/web/src/components/ui/input-group.tsx +158 -0
- package/src/interfaces/web/src/components/ui/input-otp.tsx +85 -0
- package/src/interfaces/web/src/components/ui/input.tsx +20 -0
- package/src/interfaces/web/src/components/ui/item.tsx +201 -0
- package/src/interfaces/web/src/components/ui/kbd.tsx +26 -0
- package/src/interfaces/web/src/components/ui/label.tsx +20 -0
- package/src/interfaces/web/src/components/ui/menubar.tsx +280 -0
- package/src/interfaces/web/src/components/ui/native-select.tsx +61 -0
- package/src/interfaces/web/src/components/ui/navigation-menu.tsx +168 -0
- package/src/interfaces/web/src/components/ui/pagination.tsx +130 -0
- package/src/interfaces/web/src/components/ui/popover.tsx +88 -0
- package/src/interfaces/web/src/components/ui/progress.tsx +83 -0
- package/src/interfaces/web/src/components/ui/radio-group.tsx +36 -0
- package/src/interfaces/web/src/components/ui/resizable.tsx +50 -0
- package/src/interfaces/web/src/components/ui/scroll-area.tsx +53 -0
- package/src/interfaces/web/src/components/ui/select.tsx +201 -0
- package/src/interfaces/web/src/components/ui/separator.tsx +23 -0
- package/src/interfaces/web/src/components/ui/sheet.tsx +138 -0
- package/src/interfaces/web/src/components/ui/sidebar.tsx +723 -0
- package/src/interfaces/web/src/components/ui/skeleton.tsx +13 -0
- package/src/interfaces/web/src/components/ui/slider.tsx +52 -0
- package/src/interfaces/web/src/components/ui/sonner.tsx +49 -0
- package/src/interfaces/web/src/components/ui/spinner.tsx +10 -0
- package/src/interfaces/web/src/components/ui/switch.tsx +30 -0
- package/src/interfaces/web/src/components/ui/table.tsx +116 -0
- package/src/interfaces/web/src/components/ui/tabs.tsx +72 -0
- package/src/interfaces/web/src/components/ui/textarea.tsx +18 -0
- package/src/interfaces/web/src/components/ui/tip.tsx +21 -0
- package/src/interfaces/web/src/components/ui/toggle-group.tsx +87 -0
- package/src/interfaces/web/src/components/ui/toggle.tsx +45 -0
- package/src/interfaces/web/src/components/ui/tooltip.tsx +64 -0
- package/src/interfaces/web/src/components/ui.tsx +211 -0
- package/src/interfaces/web/src/components/voice/VoiceProviderList.tsx +197 -0
- package/src/interfaces/web/src/components/voice/VoiceProviderModal.tsx +213 -0
- package/src/interfaces/web/src/components/voice/VoiceSttCard.tsx +72 -0
- package/src/interfaces/web/src/components/voice/VoiceTestCard.tsx +112 -0
- package/src/interfaces/web/src/components/voice/useTtsPlayer.ts +59 -0
- package/src/interfaces/web/src/constants/index.ts +91 -0
- package/src/interfaces/web/src/hooks/use-mobile.ts +19 -0
- package/src/interfaces/web/src/hooks/useChat.ts +276 -0
- package/src/interfaces/web/src/hooks/useDaemonStatus.ts +12 -0
- package/src/interfaces/web/src/hooks/useDevices.ts +12 -0
- package/src/interfaces/web/src/hooks/useEngines.ts +10 -0
- package/src/interfaces/web/src/hooks/useGlobalConfig.ts +24 -0
- package/src/interfaces/web/src/hooks/useIdentity.ts +16 -0
- package/src/interfaces/web/src/hooks/useProjects.ts +27 -0
- package/src/interfaces/web/src/hooks/useTelegram.ts +35 -0
- package/src/interfaces/web/src/hooks/useTheme.tsx +57 -0
- package/src/interfaces/web/src/hooks/useTokenBootstrap.ts +122 -0
- package/src/interfaces/web/src/i18n/en.ts +767 -0
- package/src/interfaces/web/src/i18n/es.ts +770 -0
- package/src/interfaces/web/src/i18n/index.ts +86 -0
- package/src/interfaces/web/src/lib/api/admin.ts +30 -0
- package/src/interfaces/web/src/lib/api/agents.ts +46 -0
- package/src/interfaces/web/src/lib/api/code.ts +122 -0
- package/src/interfaces/web/src/lib/api/conversations.ts +16 -0
- package/src/interfaces/web/src/lib/api/deck.ts +106 -0
- package/src/interfaces/web/src/lib/api/desktop.ts +54 -0
- package/src/interfaces/web/src/lib/api/embeddings.ts +44 -0
- package/src/interfaces/web/src/lib/api/engines.ts +17 -0
- package/src/interfaces/web/src/lib/api/filesystem.ts +12 -0
- package/src/interfaces/web/src/lib/api/health.ts +6 -0
- package/src/interfaces/web/src/lib/api/identity.ts +7 -0
- package/src/interfaces/web/src/lib/api/mcps.ts +29 -0
- package/src/interfaces/web/src/lib/api/messages.ts +24 -0
- package/src/interfaces/web/src/lib/api/projects.ts +29 -0
- package/src/interfaces/web/src/lib/api/routines.ts +14 -0
- package/src/interfaces/web/src/lib/api/sessions.ts +16 -0
- package/src/interfaces/web/src/lib/api/super_agent.ts +29 -0
- package/src/interfaces/web/src/lib/api/tasks.ts +19 -0
- package/src/interfaces/web/src/lib/api/telegram.ts +57 -0
- package/src/interfaces/web/src/lib/api/tools.ts +13 -0
- package/src/interfaces/web/src/lib/api/voice.ts +169 -0
- package/src/interfaces/web/src/lib/api.ts +48 -0
- package/src/interfaces/web/src/lib/cn.ts +6 -0
- package/src/interfaces/web/src/lib/code-context.ts +83 -0
- package/src/interfaces/web/src/lib/config-values.ts +29 -0
- package/src/interfaces/web/src/lib/device.ts +10 -0
- package/src/interfaces/web/src/lib/http.ts +104 -0
- package/src/interfaces/web/src/lib/secrets.ts +15 -0
- package/src/interfaces/web/src/lib/utils.ts +6 -0
- package/src/interfaces/web/src/main.tsx +16 -0
- package/src/interfaces/web/src/screens/ApxAdminScreen.tsx +174 -0
- package/src/interfaces/web/src/screens/PairingScreen.tsx +105 -0
- package/src/interfaces/web/src/screens/ProjectScreen.tsx +178 -0
- package/src/interfaces/web/src/screens/SettingsScreen.tsx +111 -0
- package/src/interfaces/web/src/screens/base/AgentDefaultsTab.tsx +274 -0
- package/src/interfaces/web/src/screens/base/ComingSoon.tsx +16 -0
- package/src/interfaces/web/src/screens/base/GlobalTasksTab.tsx +53 -0
- package/src/interfaces/web/src/screens/base/LogsTab.tsx +188 -0
- package/src/interfaces/web/src/screens/base/ModelsTab.tsx +13 -0
- package/src/interfaces/web/src/screens/base/SessionsTab.tsx +58 -0
- package/src/interfaces/web/src/screens/base/WorkspacesTab.tsx +49 -0
- package/src/interfaces/web/src/screens/modules/CodeScreen.tsx +295 -0
- package/src/interfaces/web/src/screens/modules/DeckScreen.tsx +173 -0
- package/src/interfaces/web/src/screens/modules/DesktopScreen.tsx +304 -0
- package/src/interfaces/web/src/screens/modules/VoiceScreen.tsx +174 -0
- package/src/interfaces/web/src/screens/project/AgentBrainGraph.tsx +152 -0
- package/src/interfaces/web/src/screens/project/AgentDetailScreen.tsx +455 -0
- package/src/interfaces/web/src/screens/project/AgentsTab.tsx +364 -0
- package/src/interfaces/web/src/screens/project/ChatTab.tsx +198 -0
- package/src/interfaces/web/src/screens/project/ConfigTab.tsx +94 -0
- package/src/interfaces/web/src/screens/project/McpsTab.tsx +149 -0
- package/src/interfaces/web/src/screens/project/MemoriesTab.tsx +134 -0
- package/src/interfaces/web/src/screens/project/Overview.tsx +37 -0
- package/src/interfaces/web/src/screens/project/RoutinesTab.tsx +386 -0
- package/src/interfaces/web/src/screens/project/TasksTab.tsx +116 -0
- package/src/interfaces/web/src/screens/project/TelegramTab.tsx +126 -0
- package/src/interfaces/web/src/screens/project/ThreadsTab.tsx +100 -0
- package/src/interfaces/web/src/styles.css +128 -0
- package/src/interfaces/web/src/types/daemon.ts +289 -0
- package/src/interfaces/web/tailwind.config.js +53 -0
- package/src/interfaces/web/tsconfig.json +24 -0
- package/src/interfaces/web/vite.config.ts +50 -0
- package/src/cli/commands/exec.js +0 -56
- package/src/cli/commands/overlay.js +0 -253
- package/src/cli/commands/session.js +0 -395
- package/src/cli/commands/sessions.js +0 -517
- package/src/cli/commands/telegram.js +0 -77
- package/src/cli/postinstall.js +0 -75
- package/src/cli-ts/commands/agent.ts +0 -173
- package/src/cli-ts/commands/chat.ts +0 -119
- package/src/cli-ts/commands/daemon.ts +0 -112
- package/src/cli-ts/commands/exec.ts +0 -109
- package/src/cli-ts/commands/mcp.ts +0 -235
- package/src/cli-ts/commands/session.ts +0 -224
- package/src/cli-ts/commands/status.ts +0 -61
- package/src/cli-ts/http.ts +0 -36
- package/src/cli-ts/index.ts +0 -73
- package/src/cli-ts/ui.ts +0 -107
- package/src/daemon/api.js +0 -1558
- package/src/daemon/engines/gemini.js +0 -56
- package/src/daemon/engines/openai.js +0 -79
- package/src/daemon/mcp-sources.js +0 -114
- package/src/daemon/super-agent-tools/index.js +0 -84
- package/src/daemon/super-agent-tools.js +0 -1
- package/src/daemon/super-agent.js +0 -541
- package/src/overlay/index.html +0 -44
- package/src/overlay/main.js +0 -480
- package/src/overlay/preload.js +0 -34
- package/src/overlay/renderer.js +0 -371
- package/src/overlay/style.css +0 -250
- package/src/tui/context/sync-apx.tsx +0 -284
- package/src/tui/routes/session/index.tsx +0 -274
- package/src/tui/routes/session/sidebar-apx.tsx +0 -90
- /package/src/{daemon → core}/tools/browser.js +0 -0
- /package/src/{daemon → core}/tools/fetch.js +0 -0
- /package/src/{daemon → core}/tools/glob.js +0 -0
- /package/src/{daemon → core}/tools/grep.js +0 -0
- /package/src/{daemon → core}/tools/registry.js +0 -0
- /package/src/{daemon → core}/tools/search.js +0 -0
- /package/src/{daemon → host/daemon}/conversations.js +0 -0
- /package/src/{daemon → host/daemon}/env-detect.js +0 -0
- /package/src/{daemon → host/daemon}/runtimes/_spawn.js +0 -0
- /package/src/{daemon → host/daemon}/runtimes/aider.js +0 -0
- /package/src/{daemon → host/daemon}/runtimes/claude-code.js +0 -0
- /package/src/{daemon → host/daemon}/runtimes/codex.js +0 -0
- /package/src/{daemon → host/daemon}/runtimes/cursor-agent.js +0 -0
- /package/src/{daemon → host/daemon}/runtimes/gemini-cli.js +0 -0
- /package/src/{daemon → host/daemon}/runtimes/index.js +0 -0
- /package/src/{daemon → host/daemon}/runtimes/opencode.js +0 -0
- /package/src/{daemon → host/daemon}/runtimes/qwen-code.js +0 -0
- /package/src/{daemon → host/daemon}/super-agent-tools/tools/call-mcp.js +0 -0
- /package/src/{daemon → host/daemon}/super-agent-tools/tools/edit-file.js +0 -0
- /package/src/{daemon → host/daemon}/super-agent-tools/tools/list-files.js +0 -0
- /package/src/{daemon → host/daemon}/super-agent-tools/tools/list-mcps.js +0 -0
- /package/src/{daemon → host/daemon}/super-agent-tools/tools/list-projects.js +0 -0
- /package/src/{daemon → host/daemon}/super-agent-tools/tools/list-skills.js +0 -0
- /package/src/{daemon → host/daemon}/super-agent-tools/tools/load-skill.js +0 -0
- /package/src/{daemon → host/daemon}/super-agent-tools/tools/read-agent-memory.js +0 -0
- /package/src/{daemon → host/daemon}/super-agent-tools/tools/read-file.js +0 -0
- /package/src/{daemon → host/daemon}/super-agent-tools/tools/run-shell.js +0 -0
- /package/src/{daemon → host/daemon}/super-agent-tools/tools/search-files.js +0 -0
- /package/src/{daemon → host/daemon}/super-agent-tools/tools/transcribe-audio.js +0 -0
- /package/src/{daemon → host/daemon}/super-agent-tools/tools/write-file.js +0 -0
- /package/src/{daemon → host/daemon}/thinking.js +0 -0
- /package/src/{daemon → host/daemon}/whisper-server.py +0 -0
- /package/src/{daemon → host/daemon}/whisper-transcribe.py +0 -0
- /package/src/{cli → interfaces/cli}/commands/a2a.js +0 -0
- /package/src/{cli → interfaces/cli}/commands/artifact.js +0 -0
- /package/src/{cli → interfaces/cli}/commands/chat.js +0 -0
- /package/src/{cli → interfaces/cli}/commands/log.js +0 -0
- /package/src/{cli → interfaces/cli}/commands/messages.js +0 -0
- /package/src/{cli → interfaces/cli}/commands/plugins.js +0 -0
- /package/src/{cli → interfaces/cli}/commands/routine.js +0 -0
- /package/src/{cli → interfaces/cli}/commands/runtime.js +0 -0
- /package/src/{cli → interfaces/cli}/terminal-chat/renderer.js +0 -0
- /package/src/{overlay → interfaces/desktop}/package.json +0 -0
- /package/src/{tui → interfaces/tui}/_shims/cli-error.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/cli-logo.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/cli-ui.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/config-console-state.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/core-any.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/core-binary.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/core-flag.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/core-log.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/lsp-language.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/opencode-any.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/opencode-sdk-v2.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/plugin-tui.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/prompt-display.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/provider-provider.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/session-retry.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/session-schema.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/session-session.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/snapshot.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/tool-any.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/util-error.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/util-filesystem.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/util-format.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/util-iife.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/util-locale.ts +0 -0
- /package/src/{tui → interfaces/tui}/_shims/util-process.ts +0 -0
- /package/src/{tui → interfaces/tui}/asset/charge.wav +0 -0
- /package/src/{tui → interfaces/tui}/asset/pulse-a.wav +0 -0
- /package/src/{tui → interfaces/tui}/asset/pulse-b.wav +0 -0
- /package/src/{tui → interfaces/tui}/asset/pulse-c.wav +0 -0
- /package/src/{tui → interfaces/tui}/attach.ts +0 -0
- /package/src/{tui → interfaces/tui}/component/bg-pulse-render.ts +0 -0
- /package/src/{tui → interfaces/tui}/component/bg-pulse.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/border.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-agent.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-console-org.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-mcp.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-model.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-provider.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-retry-action.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-session-delete-failed.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-session-list.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-session-rename.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-skill.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-stash.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-status.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-tag.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-theme-list.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-variant.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-workspace-create.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-workspace-file-changes.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/dialog-workspace-unavailable.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/error-component.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/logo.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/plugin-route-missing.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/prompt/autocomplete.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/prompt/cwd.ts +0 -0
- /package/src/{tui → interfaces/tui}/component/prompt/frecency.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/prompt/history.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/prompt/part.ts +0 -0
- /package/src/{tui → interfaces/tui}/component/prompt/stash.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/prompt/traits.ts +0 -0
- /package/src/{tui → interfaces/tui}/component/spinner.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/startup-loading.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/todo-item.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/use-connected.tsx +0 -0
- /package/src/{tui → interfaces/tui}/component/workspace-label.tsx +0 -0
- /package/src/{tui → interfaces/tui}/config/cwd.ts +0 -0
- /package/src/{tui → interfaces/tui}/config/keybind.ts +0 -0
- /package/src/{tui → interfaces/tui}/config/tui-migrate.ts +0 -0
- /package/src/{tui → interfaces/tui}/config/tui-schema.ts +0 -0
- /package/src/{tui → interfaces/tui}/config/tui.ts +0 -0
- /package/src/{tui → interfaces/tui}/context/aggregate-failures.ts +0 -0
- /package/src/{tui → interfaces/tui}/context/args.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/command-palette.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/directory.ts +0 -0
- /package/src/{tui → interfaces/tui}/context/editor-zed.ts +0 -0
- /package/src/{tui → interfaces/tui}/context/editor.ts +0 -0
- /package/src/{tui → interfaces/tui}/context/event-apx.ts +0 -0
- /package/src/{tui → interfaces/tui}/context/event.ts +0 -0
- /package/src/{tui → interfaces/tui}/context/exit.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/helper.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/kv.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/local.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/path-format.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/project-apx.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/project.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/prompt.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/route.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/sdk.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/sync-v2.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/sync.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/aura.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/ayu.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/carbonfox.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/catppuccin-frappe.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/catppuccin-macchiato.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/catppuccin.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/cobalt2.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/cursor.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/dracula.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/everforest.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/flexoki.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/github.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/gruvbox.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/kanagawa.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/lucent-orng.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/material.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/matrix.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/mercury.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/monokai.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/nightowl.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/nord.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/one-dark.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/opencode.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/orng.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/osaka-jade.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/palenight.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/rosepine.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/solarized.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/synthwave84.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/tokyonight.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/vercel.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/vesper.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme/zenburn.json +0 -0
- /package/src/{tui → interfaces/tui}/context/theme.tsx +0 -0
- /package/src/{tui → interfaces/tui}/context/tui-config.tsx +0 -0
- /package/src/{tui → interfaces/tui}/event.ts +0 -0
- /package/src/{tui → interfaces/tui}/feature-plugins/home/footer.tsx +0 -0
- /package/src/{tui → interfaces/tui}/feature-plugins/home/tips-view.tsx +0 -0
- /package/src/{tui → interfaces/tui}/feature-plugins/home/tips.tsx +0 -0
- /package/src/{tui → interfaces/tui}/feature-plugins/sidebar/context.tsx +0 -0
- /package/src/{tui → interfaces/tui}/feature-plugins/sidebar/files.tsx +0 -0
- /package/src/{tui → interfaces/tui}/feature-plugins/sidebar/footer.tsx +0 -0
- /package/src/{tui → interfaces/tui}/feature-plugins/sidebar/lsp.tsx +0 -0
- /package/src/{tui → interfaces/tui}/feature-plugins/sidebar/mcp.tsx +0 -0
- /package/src/{tui → interfaces/tui}/feature-plugins/sidebar/todo.tsx +0 -0
- /package/src/{tui → interfaces/tui}/feature-plugins/system/plugins.tsx +0 -0
- /package/src/{tui → interfaces/tui}/feature-plugins/system/session-v2.tsx +0 -0
- /package/src/{tui → interfaces/tui}/feature-plugins/system/which-key.tsx +0 -0
- /package/src/{tui → interfaces/tui}/keymap.tsx +0 -0
- /package/src/{tui → interfaces/tui}/layer.ts +0 -0
- /package/src/{tui → interfaces/tui}/plugin/api.tsx +0 -0
- /package/src/{tui → interfaces/tui}/plugin/command-shim.ts +0 -0
- /package/src/{tui → interfaces/tui}/plugin/internal.ts +0 -0
- /package/src/{tui → interfaces/tui}/plugin/runtime.ts +0 -0
- /package/src/{tui → interfaces/tui}/plugin/slots.tsx +0 -0
- /package/src/{tui → interfaces/tui}/routes/home.tsx +0 -0
- /package/src/{tui → interfaces/tui}/routes/session/dialog-fork-from-timeline.tsx +0 -0
- /package/src/{tui → interfaces/tui}/routes/session/dialog-message.tsx +0 -0
- /package/src/{tui → interfaces/tui}/routes/session/dialog-subagent.tsx +0 -0
- /package/src/{tui → interfaces/tui}/routes/session/dialog-timeline.tsx +0 -0
- /package/src/{tui → interfaces/tui}/routes/session/footer.tsx +0 -0
- /package/src/{tui → interfaces/tui}/routes/session/permission.tsx +0 -0
- /package/src/{tui → interfaces/tui}/routes/session/question.tsx +0 -0
- /package/src/{tui → interfaces/tui}/routes/session/sidebar.tsx +0 -0
- /package/src/{tui → interfaces/tui}/routes/session/subagent-footer.tsx +0 -0
- /package/src/{tui → interfaces/tui}/run.ts +0 -0
- /package/src/{tui → interfaces/tui}/thread.ts +0 -0
- /package/src/{tui → interfaces/tui}/ui/dialog-alert.tsx +0 -0
- /package/src/{tui → interfaces/tui}/ui/dialog-confirm.tsx +0 -0
- /package/src/{tui → interfaces/tui}/ui/dialog-export-options.tsx +0 -0
- /package/src/{tui → interfaces/tui}/ui/dialog-help.tsx +0 -0
- /package/src/{tui → interfaces/tui}/ui/dialog-prompt.tsx +0 -0
- /package/src/{tui → interfaces/tui}/ui/dialog-select.tsx +0 -0
- /package/src/{tui → interfaces/tui}/ui/dialog.tsx +0 -0
- /package/src/{tui → interfaces/tui}/ui/link.tsx +0 -0
- /package/src/{tui → interfaces/tui}/ui/spinner.ts +0 -0
- /package/src/{tui → interfaces/tui}/ui/toast.tsx +0 -0
- /package/src/{tui → interfaces/tui}/util/editor.ts +0 -0
- /package/src/{tui → interfaces/tui}/util/model.ts +0 -0
- /package/src/{tui → interfaces/tui}/util/provider-origin.ts +0 -0
- /package/src/{tui → interfaces/tui}/util/revert-diff.ts +0 -0
- /package/src/{tui → interfaces/tui}/util/scroll.ts +0 -0
- /package/src/{tui → interfaces/tui}/util/selection.ts +0 -0
- /package/src/{tui → interfaces/tui}/util/signal.ts +0 -0
- /package/src/{tui → interfaces/tui}/util/sound.ts +0 -0
- /package/src/{tui → interfaces/tui}/util/transcript.ts +0 -0
- /package/src/{tui → interfaces/tui}/validate-session.ts +0 -0
- /package/src/{tui → interfaces/tui}/win32.ts +0 -0
- /package/src/{tui → interfaces/tui}/worker.ts +0 -0
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
// Daemon HTTP routes for the unified "voice" channel.
|
|
2
|
+
//
|
|
3
|
+
// POST /voice/turn { audio?: <base64 or path>, format?, text?, agent?,
|
|
4
|
+
// channel?, provider?, voice?, language?,
|
|
5
|
+
// previousMessages? }
|
|
6
|
+
// → { user_text, reply_text, reply_audio_path,
|
|
7
|
+
// reply_duration_s, reply_mime, provider,
|
|
8
|
+
// conversation_id? }
|
|
9
|
+
//
|
|
10
|
+
// Conceptually a single bidirectional turn:
|
|
11
|
+
// 1. STT on the incoming audio (or use `text` if provided directly).
|
|
12
|
+
// 2. Run the APX default agent (super-agent mode) — or an explicit agent
|
|
13
|
+
// slug — on the transcribed text.
|
|
14
|
+
// 3. TTS on the reply text → playable audio file.
|
|
15
|
+
//
|
|
16
|
+
// Channel/agent surfaces (Telegram plugin, overlay) can keep their own
|
|
17
|
+
// pipelines, but they should delegate here when they want a hablada reply.
|
|
18
|
+
import fs from "node:fs";
|
|
19
|
+
import path from "node:path";
|
|
20
|
+
import os from "node:os";
|
|
21
|
+
import { randomUUID } from "node:crypto";
|
|
22
|
+
import { readConfig } from "../../../core/config.js";
|
|
23
|
+
import { synthesize } from "../../../core/voice/tts.js";
|
|
24
|
+
import { transcribe } from "../transcription.js";
|
|
25
|
+
import { runSuperAgent, isSuperAgentEnabled } from "../super-agent.js";
|
|
26
|
+
import { appendGlobalMessage } from "../../../core/messages-store.js";
|
|
27
|
+
import { appendErrorTrace, previewText } from "../../../core/logging.js";
|
|
28
|
+
|
|
29
|
+
// ── Channel-aware pre-processor ────────────────────────────────────
|
|
30
|
+
//
|
|
31
|
+
// Each surface that talks to the super-agent (voice overlay on the
|
|
32
|
+
// deck, deck buttons, telegram, raw API) has different ergonomics:
|
|
33
|
+
// what the response will look like, how long it can be, whether the
|
|
34
|
+
// UI can render structured suggestions. `buildChannelContext` is the
|
|
35
|
+
// single place where those decisions live — voice.js passes the
|
|
36
|
+
// channel string from the request body and gets back the context
|
|
37
|
+
// note + system suffix to feed into the super-agent.
|
|
38
|
+
//
|
|
39
|
+
// The shape is intentionally tiny: contextNote becomes the
|
|
40
|
+
// `contextNote` field on the super-agent call (gets prepended to the
|
|
41
|
+
// prompt), systemSuffix is concatenated onto the system prompt to
|
|
42
|
+
// teach the model surface-specific output rules (e.g. trailing
|
|
43
|
+
// ```suggestions JSON block``` on voice/deck).
|
|
44
|
+
function buildChannelContext(channel, { projectId, language = "es" } = {}) {
|
|
45
|
+
const base = {
|
|
46
|
+
contextNote: "",
|
|
47
|
+
systemSuffix: "",
|
|
48
|
+
wantsSuggestions: false,
|
|
49
|
+
// Channel id forwarded to runSuperAgent so the matching channels/*.md
|
|
50
|
+
// formatting block is injected.
|
|
51
|
+
channel: "",
|
|
52
|
+
// Forwarded as channelMeta; { voice: true } flags spoken mode.
|
|
53
|
+
channelMeta: {},
|
|
54
|
+
};
|
|
55
|
+
// Project resolution hint.
|
|
56
|
+
// - per-project mic (projectId set): use it imperatively, don't ask.
|
|
57
|
+
// - global deck mic (no projectId): default to project id=0
|
|
58
|
+
// ("default") for actions unless the user names a project out
|
|
59
|
+
// loud. Either way, don't pester the user with "¿en qué
|
|
60
|
+
// proyecto?" — pick a sensible default and act.
|
|
61
|
+
const projectHint = projectId
|
|
62
|
+
? `\nThe active project is id=${projectId}. For ANY task/note/list ` +
|
|
63
|
+
`action, pass project_id=${projectId} automatically. Do NOT ask the ` +
|
|
64
|
+
`user which project — only switch if they explicitly name another.`
|
|
65
|
+
: `\nThis is the GLOBAL mic (no project in focus). For task/note/list ` +
|
|
66
|
+
`actions, default to project_id=0 ("default") UNLESS the user names ` +
|
|
67
|
+
`a project out loud (e.g. "en evolution-registry…", "en el proyecto ` +
|
|
68
|
+
`apx…") — then resolve that project by name. Never ask "¿en qué ` +
|
|
69
|
+
`proyecto?"; pick the default and act.`;
|
|
70
|
+
// Hard language directive — without this the model defaults to its
|
|
71
|
+
// training-bias English on short Spanish prompts, especially when
|
|
72
|
+
// the user mixes English-ish product names ("aicrm").
|
|
73
|
+
const langDirective = language === "es"
|
|
74
|
+
? "IMPORTANT: Reply ALWAYS in Spanish (rioplatense/Argentina). The user speaks Spanish."
|
|
75
|
+
: `IMPORTANT: Reply in language "${language}".`;
|
|
76
|
+
|
|
77
|
+
// Surface mapping. Channels are surfaces; "voice" is NOT a surface — it's the
|
|
78
|
+
// spoken MODE of the deck. All channel FORMATTING now lives in the
|
|
79
|
+
// channels/*.md + modes/voice.md blocks (injected by buildSuperAgentSystem);
|
|
80
|
+
// contextNote here carries ONLY per-request dynamic bits (language + project).
|
|
81
|
+
// incoming "voice" → deck surface, spoken
|
|
82
|
+
// incoming "deck" → deck surface, text card
|
|
83
|
+
// incoming "desktop" → desktop module, spoken (voice-first)
|
|
84
|
+
const dynamicNote = `${langDirective}${projectHint}`;
|
|
85
|
+
switch (channel) {
|
|
86
|
+
case "voice":
|
|
87
|
+
return { ...base, contextNote: dynamicNote, systemSuffix: SUGGESTIONS_INSTRUCTION, wantsSuggestions: true, channel: "deck", channelMeta: { voice: true } };
|
|
88
|
+
case "deck":
|
|
89
|
+
return { ...base, contextNote: dynamicNote, systemSuffix: SUGGESTIONS_INSTRUCTION, wantsSuggestions: true, channel: "deck", channelMeta: {} };
|
|
90
|
+
case "desktop":
|
|
91
|
+
return { ...base, contextNote: dynamicNote, systemSuffix: SUGGESTIONS_INSTRUCTION, wantsSuggestions: true, channel: "desktop", channelMeta: { voice: true } };
|
|
92
|
+
case "telegram":
|
|
93
|
+
// Format rules live in channels/telegram.md; keep only the dynamic note.
|
|
94
|
+
return { ...base, contextNote: dynamicNote, channel: "telegram", channelMeta: {} };
|
|
95
|
+
default:
|
|
96
|
+
return { ...base, contextNote: dynamicNote, channel: channel || "api", channelMeta: {} };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Balanced suffix. An earlier, more aggressive version ("EJECUTA, no
|
|
101
|
+
// narres — LLAMÁ A LA TOOL") made Gemini call tools for EVERYTHING,
|
|
102
|
+
// even "hola" → it fired send_telegram("hola"). The rule below gates
|
|
103
|
+
// tool use on a *clear* action request and explicitly tells the model
|
|
104
|
+
// to just talk for chit-chat.
|
|
105
|
+
const SUGGESTIONS_INSTRUCTION = `
|
|
106
|
+
|
|
107
|
+
# Cuándo usar tools
|
|
108
|
+
SOLO llamá una tool cuando el usuario pide CLARAMENTE una acción
|
|
109
|
+
concreta: "creá una tarea …", "mandá un telegram …", "listá …",
|
|
110
|
+
"abrí …", "marcá como hecha …". En esos casos ejecutá la tool (no
|
|
111
|
+
digas "lo voy a hacer" — hacelo) y después confirmá en una frase corta
|
|
112
|
+
en castellano lo que YA hiciste.
|
|
113
|
+
|
|
114
|
+
Si el mensaje es un saludo, una pregunta, o charla ("hola", "cómo
|
|
115
|
+
andás", "qué podés hacer") NO llames ninguna tool: respondé en texto,
|
|
116
|
+
breve, en castellano.
|
|
117
|
+
|
|
118
|
+
Nunca llames la misma tool dos veces en el mismo turno.
|
|
119
|
+
|
|
120
|
+
# Sugerencias (opcional)
|
|
121
|
+
Al final, en su propia línea, podés agregar un bloque fenced
|
|
122
|
+
\`suggestions\` con 2-3 próximos pasos. El usuario NO lo ve (la deck lo
|
|
123
|
+
quita):
|
|
124
|
+
\`\`\`suggestions
|
|
125
|
+
[{"label":"Ver tareas","command":"deck.view:tasks"}]
|
|
126
|
+
\`\`\`
|
|
127
|
+
Si no hay próximos pasos útiles, omití el bloque.`;
|
|
128
|
+
|
|
129
|
+
// Pull the trailing ```suggestions ... ``` block off the agent's
|
|
130
|
+
// reply. Returns { cleanText, suggestions[] } — cleanText is the
|
|
131
|
+
// reply with the block removed so the user (and TTS) never sees it.
|
|
132
|
+
const SUGGESTIONS_BLOCK_RE = /\n*```\s*suggestions\s*\n([\s\S]*?)\n?```\s*$/i;
|
|
133
|
+
|
|
134
|
+
function extractSuggestions(text) {
|
|
135
|
+
if (typeof text !== "string" || !text) return { cleanText: text || "", suggestions: [] };
|
|
136
|
+
const m = SUGGESTIONS_BLOCK_RE.exec(text);
|
|
137
|
+
if (!m) return { cleanText: text, suggestions: [] };
|
|
138
|
+
const cleanText = text.slice(0, m.index).trim();
|
|
139
|
+
let suggestions = [];
|
|
140
|
+
try {
|
|
141
|
+
const parsed = JSON.parse(m[1]);
|
|
142
|
+
if (Array.isArray(parsed)) {
|
|
143
|
+
suggestions = parsed
|
|
144
|
+
.filter((s) => s && typeof s === "object" && typeof s.label === "string")
|
|
145
|
+
.slice(0, 4)
|
|
146
|
+
.map((s) => ({
|
|
147
|
+
label: String(s.label).slice(0, 48),
|
|
148
|
+
...(typeof s.command === "string" ? { command: s.command.slice(0, 96) } : {}),
|
|
149
|
+
}));
|
|
150
|
+
}
|
|
151
|
+
} catch {
|
|
152
|
+
// Malformed JSON — drop suggestions silently rather than fail the
|
|
153
|
+
// turn. Better UX to show the reply without chips than an error.
|
|
154
|
+
}
|
|
155
|
+
return { cleanText, suggestions };
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ── Voice intent classifier ────────────────────────────────────────
|
|
159
|
+
//
|
|
160
|
+
// A very small, regex-based router that catches a handful of "verb-y"
|
|
161
|
+
// utterances we want to short-circuit instead of sending to the LLM.
|
|
162
|
+
// Right now: "crear tarea ...", "creá tarea ...", "agregá una tarea ...",
|
|
163
|
+
// "nueva tarea ..." → POST to the project's tasks store directly.
|
|
164
|
+
//
|
|
165
|
+
// The classifier always returns either:
|
|
166
|
+
// - { handled: false } → caller falls through to the super-agent
|
|
167
|
+
// - { handled: true, reply: "...", meta?: {...} } → caller returns
|
|
168
|
+
//
|
|
169
|
+
// Keeping it dependency-free + sync lets us run it before any heavy
|
|
170
|
+
// work in the voice handler.
|
|
171
|
+
const TASK_INTENT_RE = new RegExp(
|
|
172
|
+
// Optional polite preamble: "podés / por favor"
|
|
173
|
+
"^\\s*(?:por favor|porfa|porfis|dale|che|apx)?[,!\\s]*" +
|
|
174
|
+
// Either:
|
|
175
|
+
// (a) verb cluster + optional article + "tarea"
|
|
176
|
+
// (b) standalone "nueva|otra tarea" (no verb)
|
|
177
|
+
"(?:" +
|
|
178
|
+
// (a) verbs — with optional clitic pronouns (-me, -te, -le)
|
|
179
|
+
"(?:crea[r]?|cre[áa]|agreg[áa](?:me|le)?|agreg[uú]e|sum[áa](?:me)?|" +
|
|
180
|
+
"anot[áa](?:me)?|an[oó]tame|guard[áa](?:me)?|met[ée](?:me)?|" +
|
|
181
|
+
"añad[íi]|añad[ée]|pone(?:me|le)?|recor[dáa]me)" +
|
|
182
|
+
"\\s+(?:una|la|el|esta|ese|otra|otro)?\\s*" +
|
|
183
|
+
"(?:tarea|task|pendiente|todo|to-do)" +
|
|
184
|
+
"|" +
|
|
185
|
+
// (b) "nueva tarea X" / "otra tarea X" without a verb
|
|
186
|
+
"(?:nueva|otra|nuevo)\\s+(?:tarea|task|pendiente)" +
|
|
187
|
+
")" +
|
|
188
|
+
// Optional connectors before the title
|
|
189
|
+
"\\s+(?:que\\s+(?:diga|sea|es)|para|de|sobre|llamada|titulada|titul[áa]da|:|-|de:)?\\s*",
|
|
190
|
+
"i"
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
function extractTaskTitle(text) {
|
|
194
|
+
if (typeof text !== "string") return null;
|
|
195
|
+
const cleaned = text.trim().replace(/^[«"']|[«"'.,;:!?]+$/g, "");
|
|
196
|
+
if (!cleaned) return null;
|
|
197
|
+
const m = TASK_INTENT_RE.exec(cleaned);
|
|
198
|
+
if (!m) return null;
|
|
199
|
+
const title = cleaned.slice(m[0].length).trim().replace(/[.!?]+$/, "");
|
|
200
|
+
if (!title) return null;
|
|
201
|
+
return title;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function pickIntentProject({ projects, hintId }) {
|
|
205
|
+
if (!projects?.list) return null;
|
|
206
|
+
const list = projects.list();
|
|
207
|
+
if (hintId !== undefined && hintId !== null) {
|
|
208
|
+
const hit = list.find((p) => String(p.id) === String(hintId));
|
|
209
|
+
if (hit) return hit;
|
|
210
|
+
}
|
|
211
|
+
// Prefer the first non-default real project; fall back to default.
|
|
212
|
+
return list.find((p) => Number(p.id) !== 0) || list[0] || null;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async function tryVoiceTaskIntent({ projects, userText, hintProjectId }) {
|
|
216
|
+
const title = extractTaskTitle(userText);
|
|
217
|
+
if (!title) return { handled: false };
|
|
218
|
+
const listEntry = pickIntentProject({ projects, hintId: hintProjectId });
|
|
219
|
+
if (!listEntry) {
|
|
220
|
+
return {
|
|
221
|
+
handled: true,
|
|
222
|
+
reply: "No hay proyectos APX registrados. Agregá uno con `apx project add` y volvé a intentar.",
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
// projects.list() returns flat entries without storagePath; the
|
|
226
|
+
// resolver returns the full record. We need that for the JSONL store.
|
|
227
|
+
const project = projects.get(listEntry.id) || listEntry;
|
|
228
|
+
if (!project?.storagePath) {
|
|
229
|
+
return {
|
|
230
|
+
handled: true,
|
|
231
|
+
reply: `No pude crear la tarea: no encuentro el storage del proyecto ${project?.name || listEntry.name}.`,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
try {
|
|
235
|
+
const { createTask } = await import("../../../core/tasks-store.js");
|
|
236
|
+
const task = createTask(project.storagePath, {
|
|
237
|
+
title,
|
|
238
|
+
source: "voice",
|
|
239
|
+
});
|
|
240
|
+
// Resolver may strip the human-readable name; fall back to the
|
|
241
|
+
// list entry which always has it.
|
|
242
|
+
const displayName = project.name || listEntry.name || `proyecto #${project.id}`;
|
|
243
|
+
return {
|
|
244
|
+
handled: true,
|
|
245
|
+
reply: `Listo. Anoté "${title}" en ${displayName}.`,
|
|
246
|
+
meta: { task_id: task.id, project_id: project.id },
|
|
247
|
+
};
|
|
248
|
+
} catch (e) {
|
|
249
|
+
return {
|
|
250
|
+
handled: true,
|
|
251
|
+
reply: `No pude crear la tarea: ${e.message || "error desconocido"}`,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async function decodeAudioInput({ audio, format = "webm" }) {
|
|
257
|
+
if (!audio) return null;
|
|
258
|
+
// A short string that starts with "/" and exists on disk is treated as a
|
|
259
|
+
// path. Anything else is interpreted as base64 (optionally with a data:
|
|
260
|
+
// prefix).
|
|
261
|
+
if (
|
|
262
|
+
typeof audio === "string" &&
|
|
263
|
+
audio.length < 1024 &&
|
|
264
|
+
audio.startsWith("/") &&
|
|
265
|
+
fs.existsSync(audio)
|
|
266
|
+
) {
|
|
267
|
+
return { path: audio, cleanup: false };
|
|
268
|
+
}
|
|
269
|
+
let b64 = audio;
|
|
270
|
+
const m = /^data:[^;]+;base64,(.+)$/.exec(b64);
|
|
271
|
+
if (m) b64 = m[1];
|
|
272
|
+
const buf = Buffer.from(b64, "base64");
|
|
273
|
+
if (!buf.length) throw new Error("voice/turn: decoded audio is empty");
|
|
274
|
+
const ext = String(format || "webm").replace(/^\./, "");
|
|
275
|
+
const tmp = path.join(os.tmpdir(), `apx-voice-${Date.now()}-${randomUUID()}.${ext}`);
|
|
276
|
+
fs.writeFileSync(tmp, buf);
|
|
277
|
+
return { path: tmp, cleanup: true };
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export function register(app, { projects, plugins, registries }) {
|
|
281
|
+
// GET /voice/tts?path=<abs>
|
|
282
|
+
//
|
|
283
|
+
// Streams a TTS audio file back to the caller. Sandboxed to the
|
|
284
|
+
// ~/.apx/tmp/tts directory so a client can't request arbitrary
|
|
285
|
+
// filesystem paths through a manifest-leaked reply_audio_path.
|
|
286
|
+
app.get("/voice/tts", async (req, res) => {
|
|
287
|
+
const rawPath = String(req.query.path || "");
|
|
288
|
+
if (!rawPath) return res.status(400).json({ error: "path required" });
|
|
289
|
+
try {
|
|
290
|
+
const os = await import("node:os");
|
|
291
|
+
const ttsRoot = path.resolve(os.homedir(), ".apx", "tmp", "tts");
|
|
292
|
+
const resolved = path.resolve(rawPath);
|
|
293
|
+
if (!resolved.startsWith(ttsRoot + path.sep)) {
|
|
294
|
+
return res.status(403).json({ error: "path outside tts dir" });
|
|
295
|
+
}
|
|
296
|
+
if (!fs.existsSync(resolved)) return res.status(404).json({ error: "not found" });
|
|
297
|
+
const ext = path.extname(resolved).toLowerCase();
|
|
298
|
+
const mime =
|
|
299
|
+
ext === ".wav" ? "audio/wav" :
|
|
300
|
+
ext === ".mp3" ? "audio/mpeg" :
|
|
301
|
+
ext === ".m4a" || ext === ".aac" ? "audio/mp4" :
|
|
302
|
+
ext === ".ogg" || ext === ".opus" ? "audio/ogg" :
|
|
303
|
+
"application/octet-stream";
|
|
304
|
+
res.setHeader("Content-Type", mime);
|
|
305
|
+
// Cache for a minute — the client fetches each reply once anyway,
|
|
306
|
+
// but a retry shouldn't re-hit disk if it's the same file.
|
|
307
|
+
res.setHeader("Cache-Control", "private, max-age=60");
|
|
308
|
+
fs.createReadStream(resolved).pipe(res);
|
|
309
|
+
} catch (e) {
|
|
310
|
+
res.status(500).json({ error: e?.message || "tts read failed" });
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
app.post("/voice/turn", async (req, res) => {
|
|
315
|
+
const body = req.body || {};
|
|
316
|
+
const cfg = readConfig();
|
|
317
|
+
let userText = (body.text || "").trim();
|
|
318
|
+
let decoded = null;
|
|
319
|
+
|
|
320
|
+
try {
|
|
321
|
+
// ── 1. STT (skip if caller already gave us text) ─────────────────
|
|
322
|
+
if (!userText) {
|
|
323
|
+
decoded = await decodeAudioInput({
|
|
324
|
+
audio: body.audio,
|
|
325
|
+
format: body.format,
|
|
326
|
+
});
|
|
327
|
+
if (!decoded) {
|
|
328
|
+
return res.status(400).json({ error: "audio or text required" });
|
|
329
|
+
}
|
|
330
|
+
const sttResult = await transcribe(decoded.path, {
|
|
331
|
+
language:
|
|
332
|
+
body.language && body.language !== "auto" ? body.language : undefined,
|
|
333
|
+
});
|
|
334
|
+
userText = (sttResult.text || "").trim();
|
|
335
|
+
}
|
|
336
|
+
if (!userText) {
|
|
337
|
+
return res.json({
|
|
338
|
+
user_text: "",
|
|
339
|
+
reply_text: "",
|
|
340
|
+
reply_audio_path: null,
|
|
341
|
+
provider: null,
|
|
342
|
+
empty: true,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// ── 1.5 Intent classifier (DISABLED) ──────────────────────────
|
|
347
|
+
// We used to regex-match "creá una tarea X" here and short-circuit
|
|
348
|
+
// the LLM. That fired far too eagerly — any sentence containing
|
|
349
|
+
// those words became a task title, even when the user's actual
|
|
350
|
+
// intent was different ("explicame cómo funciona crear una
|
|
351
|
+
// tarea" would create a bogus task).
|
|
352
|
+
//
|
|
353
|
+
// The right path is the agent's own tool calling: the super-agent
|
|
354
|
+
// already has `create_task`, `send_telegram`, `list_tasks`, etc.
|
|
355
|
+
// in its CORE schema (see super-agent-tools/index.js). We let
|
|
356
|
+
// the model decide; the system prompt below pushes it toward
|
|
357
|
+
// tool use instead of narrating the action in prose.
|
|
358
|
+
const intentResult = { handled: false };
|
|
359
|
+
let replyText = "";
|
|
360
|
+
const previousMessages = Array.isArray(body.previousMessages)
|
|
361
|
+
? body.previousMessages
|
|
362
|
+
: [];
|
|
363
|
+
const channel = body.channel || "voice";
|
|
364
|
+
|
|
365
|
+
let intentMeta = null;
|
|
366
|
+
let suggestions = [];
|
|
367
|
+
let toolsUsed = [];
|
|
368
|
+
const channelCtx = buildChannelContext(channel, {
|
|
369
|
+
projectId: body.projectId,
|
|
370
|
+
language: body.language && body.language !== "auto" ? body.language : "es",
|
|
371
|
+
});
|
|
372
|
+
if (intentResult.handled) {
|
|
373
|
+
replyText = intentResult.reply;
|
|
374
|
+
intentMeta = intentResult.meta || null;
|
|
375
|
+
// Intent shortcut bypasses the LLM, so no model-generated
|
|
376
|
+
// suggestions either; we hand-craft a couple based on the
|
|
377
|
+
// outcome so the chips area isn't empty.
|
|
378
|
+
if (intentMeta?.task_id) {
|
|
379
|
+
suggestions = [
|
|
380
|
+
{ label: "Ver tareas", command: "deck.view:tasks" },
|
|
381
|
+
{ label: "Anotar otra", command: "voice.again" },
|
|
382
|
+
];
|
|
383
|
+
}
|
|
384
|
+
} else if (isSuperAgentEnabled(cfg)) {
|
|
385
|
+
try {
|
|
386
|
+
const result = await runSuperAgent({
|
|
387
|
+
globalConfig: cfg,
|
|
388
|
+
projects,
|
|
389
|
+
plugins,
|
|
390
|
+
registries,
|
|
391
|
+
prompt: userText,
|
|
392
|
+
contextNote: channelCtx.contextNote,
|
|
393
|
+
channel: channelCtx.channel || "",
|
|
394
|
+
channelMeta: channelCtx.channelMeta || {},
|
|
395
|
+
systemSuffix: channelCtx.systemSuffix,
|
|
396
|
+
previousMessages,
|
|
397
|
+
});
|
|
398
|
+
const raw = (result?.text || "").trim();
|
|
399
|
+
// Surface the tools the agent actually executed this turn so
|
|
400
|
+
// the overlay can show "lo que APX hizo" instead of echoing
|
|
401
|
+
// the transcript. Dedup by name (a list_tasks before+after
|
|
402
|
+
// shows once) and map to a compact {name, summary} shape.
|
|
403
|
+
if (Array.isArray(result?.trace)) {
|
|
404
|
+
const seen = new Set();
|
|
405
|
+
for (const t of result.trace) {
|
|
406
|
+
const name = t?.tool;
|
|
407
|
+
if (!name || seen.has(name)) continue;
|
|
408
|
+
seen.add(name);
|
|
409
|
+
toolsUsed.push({
|
|
410
|
+
name,
|
|
411
|
+
ok: !(t?.result && t.result.error),
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
if (channelCtx.wantsSuggestions) {
|
|
416
|
+
const parsed = extractSuggestions(raw);
|
|
417
|
+
replyText = parsed.cleanText;
|
|
418
|
+
suggestions = parsed.suggestions;
|
|
419
|
+
// Safety net: small models sometimes return ONLY the
|
|
420
|
+
// suggestions block (no visible reply). Don't ship empty
|
|
421
|
+
// text to TTS — synthesize a generic confirmation so the
|
|
422
|
+
// user gets audible feedback that something happened.
|
|
423
|
+
if (!replyText && raw) {
|
|
424
|
+
replyText = suggestions.length
|
|
425
|
+
? "Listo."
|
|
426
|
+
: raw;
|
|
427
|
+
}
|
|
428
|
+
} else {
|
|
429
|
+
replyText = raw;
|
|
430
|
+
}
|
|
431
|
+
} catch (e) {
|
|
432
|
+
appendErrorTrace({
|
|
433
|
+
trace_id: req.apxTraceId,
|
|
434
|
+
surface: "daemon_api",
|
|
435
|
+
route: "POST /voice/turn",
|
|
436
|
+
channel,
|
|
437
|
+
error: { message: e.message, stack: e.stack },
|
|
438
|
+
prompt_preview: previewText(userText),
|
|
439
|
+
});
|
|
440
|
+
return res.status(500).json({
|
|
441
|
+
user_text: userText,
|
|
442
|
+
reply_text: "",
|
|
443
|
+
error: `agent failed: ${e.message}`,
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
} else {
|
|
447
|
+
// No super-agent configured: still useful as a STT+TTS echo.
|
|
448
|
+
replyText = userText;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Persist the turn to the cross-channel store (feeds RAG index,
|
|
452
|
+
// search_messages, and the "active threads" block). channelCtx.channel is
|
|
453
|
+
// the resolved surface ("deck"/"desktop"). Best-effort.
|
|
454
|
+
try {
|
|
455
|
+
const logCh = channelCtx.channel || channel;
|
|
456
|
+
if (logCh && logCh !== "api") {
|
|
457
|
+
appendGlobalMessage({ channel: logCh, direction: "in", type: "user", author: "user", body: userText });
|
|
458
|
+
if (replyText) appendGlobalMessage({ channel: logCh, direction: "out", type: "agent", body: replyText });
|
|
459
|
+
}
|
|
460
|
+
} catch { /* best-effort */ }
|
|
461
|
+
|
|
462
|
+
// ── 3. TTS on the reply ──────────────────────────────────────────
|
|
463
|
+
let tts = { audio_path: null, duration_s: null, mime: null, provider: null };
|
|
464
|
+
if (replyText) {
|
|
465
|
+
try {
|
|
466
|
+
tts = await synthesize({
|
|
467
|
+
text: replyText,
|
|
468
|
+
voice: body.voice,
|
|
469
|
+
language: body.language,
|
|
470
|
+
provider: body.provider,
|
|
471
|
+
format: body.format_out,
|
|
472
|
+
globalConfig: cfg,
|
|
473
|
+
});
|
|
474
|
+
} catch (e) {
|
|
475
|
+
// Don't fail the whole turn just because TTS broke; return text.
|
|
476
|
+
tts = {
|
|
477
|
+
audio_path: null,
|
|
478
|
+
duration_s: null,
|
|
479
|
+
mime: null,
|
|
480
|
+
provider: null,
|
|
481
|
+
error: e.message,
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
res.json({
|
|
487
|
+
user_text: userText,
|
|
488
|
+
reply_text: replyText,
|
|
489
|
+
reply_audio_path: tts.audio_path,
|
|
490
|
+
reply_duration_s: tts.duration_s,
|
|
491
|
+
reply_mime: tts.mime,
|
|
492
|
+
provider: tts.provider,
|
|
493
|
+
tts_error: tts.error || undefined,
|
|
494
|
+
intent: intentMeta || undefined,
|
|
495
|
+
suggestions: suggestions.length ? suggestions : undefined,
|
|
496
|
+
tools_used: toolsUsed.length ? toolsUsed : undefined,
|
|
497
|
+
channel: channel,
|
|
498
|
+
});
|
|
499
|
+
} catch (e) {
|
|
500
|
+
res.status(500).json({ error: e.message });
|
|
501
|
+
} finally {
|
|
502
|
+
if (decoded?.cleanup) {
|
|
503
|
+
try {
|
|
504
|
+
fs.unlinkSync(decoded.path);
|
|
505
|
+
} catch {
|
|
506
|
+
/* ignore */
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Note for plugin authors:
|
|
514
|
+
// Desktop (src/host/daemon/plugins/desktop.js) and Telegram
|
|
515
|
+
// (src/host/daemon/plugins/telegram.js) currently implement their own
|
|
516
|
+
// STT → agent → render pipelines. To get spoken replies via APX they can
|
|
517
|
+
// POST to /voice/turn (or call `synthesize()` directly) instead of
|
|
518
|
+
// re-implementing TTS. This module intentionally does NOT migrate them —
|
|
519
|
+
// it only exposes the unified channel.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// Serve the local admin panel from the daemon.
|
|
2
|
+
//
|
|
3
|
+
// The web bundle is built into src/interfaces/web/dist. When that folder
|
|
4
|
+
// exists, this module mounts it at `/` so users can open
|
|
5
|
+
// http://127.0.0.1:7430 and get the UI. SPA fallback: anything that isn't an
|
|
6
|
+
// /api/* / /projects/* / etc. route falls through to index.html so React
|
|
7
|
+
// Router can resolve client-side paths like /p/1/tasks.
|
|
8
|
+
//
|
|
9
|
+
// During development the user runs `vite dev` on :7431 with proxy → :7430,
|
|
10
|
+
// so this mount is a no-op (dist/ doesn't exist yet).
|
|
11
|
+
import fs from "node:fs";
|
|
12
|
+
import path from "node:path";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
|
|
15
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
16
|
+
const __dirname = path.dirname(__filename);
|
|
17
|
+
// host/daemon/api/web.js → ../../.. = src/ then interfaces/web/dist
|
|
18
|
+
const WEB_DIST = path.resolve(__dirname, "..", "..", "..", "interfaces", "web", "dist");
|
|
19
|
+
|
|
20
|
+
// Paths the panel is NOT allowed to swallow with its SPA fallback. These are
|
|
21
|
+
// the real API surfaces; we keep the list flat so it doesn't drift away from
|
|
22
|
+
// the actual route registry.
|
|
23
|
+
const API_PREFIXES = [
|
|
24
|
+
"/health", "/admin", "/projects", "/telegram", "/engines", "/runtimes",
|
|
25
|
+
"/messages", "/sessions", "/tools", "/mcp", "/voice", "/tts", "/desktop", "/overlay",
|
|
26
|
+
"/transcribe", "/run", "/files", "/memory", "/env", "/pair", "/deck",
|
|
27
|
+
"/super-agent", "/identity",
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
function isApiPath(p) {
|
|
31
|
+
for (const prefix of API_PREFIXES) {
|
|
32
|
+
if (p === prefix || p.startsWith(prefix + "/")) return true;
|
|
33
|
+
}
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function register(app, { express, token }) {
|
|
38
|
+
// /admin/web-token: localhost-only endpoint that returns the daemon token
|
|
39
|
+
// so the same-origin admin panel can authenticate every subsequent call.
|
|
40
|
+
// Refuses if the request didn't come from loopback. Also refuses if the
|
|
41
|
+
// request was tunneled in via Cloudflare/ngrok/etc. — those connect from
|
|
42
|
+
// a local agent so the socket IP IS loopback, but tunnel-specific headers
|
|
43
|
+
// give them away. When tunneled, the SPA must instead receive the token
|
|
44
|
+
// via URL fragment (#token=…) that the operator shares out-of-band.
|
|
45
|
+
app.get("/admin/web-token", (req, res) => {
|
|
46
|
+
const ra = req.ip || req.socket?.remoteAddress || "";
|
|
47
|
+
const isLocal =
|
|
48
|
+
ra === "127.0.0.1" ||
|
|
49
|
+
ra === "::1" ||
|
|
50
|
+
ra === "::ffff:127.0.0.1" ||
|
|
51
|
+
ra === "" /* in-process */;
|
|
52
|
+
if (!isLocal) {
|
|
53
|
+
return res.status(403).json({ error: "web-token is loopback-only" });
|
|
54
|
+
}
|
|
55
|
+
const tunneledHeaders = [
|
|
56
|
+
"cf-connecting-ip",
|
|
57
|
+
"cf-ray",
|
|
58
|
+
"x-forwarded-for",
|
|
59
|
+
"x-real-ip",
|
|
60
|
+
"x-forwarded-host",
|
|
61
|
+
"ngrok-trace-id",
|
|
62
|
+
];
|
|
63
|
+
for (const h of tunneledHeaders) {
|
|
64
|
+
if (req.headers[h]) {
|
|
65
|
+
return res.status(403).json({
|
|
66
|
+
error: "web-token disabled for tunneled requests — share #token=… in URL fragment instead",
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
res.json({ token });
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
if (!fs.existsSync(WEB_DIST)) {
|
|
74
|
+
// No build present. Expose a hint endpoint so users hitting the daemon
|
|
75
|
+
// root know what to do.
|
|
76
|
+
app.get("/", (_req, res) => {
|
|
77
|
+
res
|
|
78
|
+
.status(200)
|
|
79
|
+
.type("text/html")
|
|
80
|
+
.send(
|
|
81
|
+
[
|
|
82
|
+
"<!doctype html><meta charset='utf-8'><title>APX</title>",
|
|
83
|
+
"<style>body{font:14px/1.5 system-ui;padding:2rem;color:#aab2bc;background:#0b0d10}",
|
|
84
|
+
"code{background:#1a1e23;padding:.1em .4em;border-radius:3px;font-family:ui-monospace,Menlo}</style>",
|
|
85
|
+
"<h1 style='color:#d6dbe1'>APX daemon</h1>",
|
|
86
|
+
"<p>The admin panel hasn't been built on this install.</p>",
|
|
87
|
+
"<p>From the apx repo run:</p>",
|
|
88
|
+
"<pre><code>cd src/interfaces/web && pnpm install && pnpm build</code></pre>",
|
|
89
|
+
"<p>Or develop with hot reload:</p>",
|
|
90
|
+
"<pre><code>cd src/interfaces/web && pnpm dev</code></pre>",
|
|
91
|
+
"<p>Meanwhile the CLI works as usual — try <code>apx status</code>.</p>",
|
|
92
|
+
].join("")
|
|
93
|
+
);
|
|
94
|
+
});
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Serve static assets. NB: we mount AFTER the API routes are registered
|
|
99
|
+
// in api.js so /projects, /admin/*, etc. always win over the catch-all.
|
|
100
|
+
app.use(
|
|
101
|
+
express.static(WEB_DIST, {
|
|
102
|
+
index: false,
|
|
103
|
+
etag: true,
|
|
104
|
+
maxAge: "1h",
|
|
105
|
+
setHeaders(res, filePath) {
|
|
106
|
+
// index.html is the SPA shell — never cache it, so a redeploy is
|
|
107
|
+
// picked up on the next reload.
|
|
108
|
+
if (filePath.endsWith("index.html")) {
|
|
109
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
})
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
// SPA fallback: anything that isn't an API path AND doesn't have a file
|
|
116
|
+
// extension goes to index.html. The web app handles routing.
|
|
117
|
+
app.get("*", (req, res, next) => {
|
|
118
|
+
if (req.method !== "GET") return next();
|
|
119
|
+
if (isApiPath(req.path)) return next();
|
|
120
|
+
if (path.extname(req.path)) return next(); // let static handle 404 of /foo.png
|
|
121
|
+
res.sendFile(path.join(WEB_DIST, "index.html"));
|
|
122
|
+
});
|
|
123
|
+
}
|