@agentprojectcontext/apx 1.77.3 → 1.79.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/README.md +23 -9
- package/package.json +12 -12
- package/src/core/agent/build-agent-system.js +3 -2
- package/src/core/agent/constants.js +3 -2
- package/src/core/agent/loop/greeting-guard.js +55 -0
- package/src/core/agent/loop/side-effects.js +52 -0
- package/src/core/agent/prompt-builder.js +49 -8
- package/src/core/agent/run-agent.js +43 -53
- package/src/core/agent/security.js +1 -1
- package/src/core/agent/self-memory.js +4 -2
- package/src/core/agent/skills/index-store.js +2 -2
- package/src/core/agent/skills/loader.js +3 -22
- package/src/core/agent/super-agent.js +0 -2
- package/src/core/agent/tool-summary.js +65 -0
- package/src/core/agent/tools/handlers/call-runtime.js +4 -4
- package/src/core/agent/tools/handlers/list-commitments.js +80 -0
- package/src/core/agent/tools/handlers/list-tasks.js +66 -27
- package/src/core/agent/tools/handlers/record-commitment.js +68 -0
- package/src/core/agent/tools/handlers/search-sessions.js +1 -1
- package/src/core/agent/tools/handlers/send-telegram.js +68 -2
- package/src/core/agent/tools/handlers/set-permission-mode.js +11 -4
- package/src/core/agent/tools/names.js +64 -0
- package/src/core/agent/tools/registry.js +9 -0
- package/src/core/agent/tools/tool-call-parser.js +70 -1
- package/src/core/apc/context-copy.js +1 -1
- package/src/core/apc/frontmatter.js +85 -0
- package/src/core/apc/parser.js +9 -21
- package/src/core/apc/paths.js +8 -8
- package/src/core/apc/scaffold.js +5 -4
- package/src/core/channels/telegram/ask-callbacks.js +35 -0
- package/src/core/channels/telegram/config.js +201 -0
- package/src/core/channels/telegram/dispatch.js +3 -0
- package/src/core/channels/telegram/reply.js +30 -5
- package/src/core/config/index.js +25 -183
- package/src/core/config/paths.js +57 -1
- package/src/core/config/redact.js +22 -0
- package/src/core/confirmation/adapters/web.js +1 -1
- package/src/core/daemon/service.js +238 -0
- package/src/core/deck/manifest.js +12 -12
- package/src/core/desktop/autostart.js +3 -2
- package/src/core/desktop/process.js +4 -4
- package/src/core/engines/gemini.js +322 -60
- package/src/core/engines/openai-compatible.js +21 -2
- package/src/core/engines/presets.js +2 -2
- package/src/core/http-tools/browser.js +1 -1
- package/src/core/http-tools/catalog.js +551 -0
- package/src/core/http-tools/fetch.js +1 -1
- package/src/core/http-tools/glob.js +3 -3
- package/src/core/http-tools/grep.js +2 -9
- package/src/core/http-tools/inline-handlers.js +59 -0
- package/src/core/http-tools/registry.js +31 -615
- package/src/core/http-tools/search.js +2 -2
- package/src/core/identity/self.js +6 -7
- package/src/core/identity/telegram.js +3 -3
- package/src/core/logging.js +6 -5
- package/src/core/mcp/sources.js +12 -16
- package/src/core/memory/consolidate.js +225 -0
- package/src/core/memory/indexer.js +2 -5
- package/src/core/nudge/index.js +192 -0
- package/src/core/nudge/policy.js +143 -0
- package/src/core/nudge/store.js +141 -0
- package/src/core/profiles/bundled/secretary/PROFILE.md +8 -9
- package/src/core/profiles/bundled/secretary/config.schema.json +33 -3
- package/src/core/profiles/bundled/secretary/routines/day-close.json +7 -3
- package/src/core/profiles/bundled/secretary/routines/day-open.json +7 -3
- package/src/core/profiles/bundled/secretary/routines/watch.json +13 -0
- package/src/core/profiles/lifecycle.js +0 -2
- package/src/core/profiles/store.js +2 -7
- package/src/core/routines/runner.js +103 -4
- package/src/core/routines/signals.js +270 -0
- package/src/core/runtime-skills/apx-agency-agents/SKILL.md +2 -2
- package/src/core/runtime-skills/apx-profile/SKILL.md +3 -3
- package/src/core/runtime-skills/apx-project/SKILL.md +1 -1
- package/src/core/runtime-skills/apx-task/SKILL.md +8 -8
- package/src/core/runtime-skills/apx-telegram/SKILL.md +4 -4
- package/src/core/runtime-skills/apx-voice/SKILL.md +4 -4
- package/src/core/runtimes/detect.js +2 -2
- package/src/core/sessions/index.js +800 -0
- package/src/core/stores/code-sessions.js +3 -14
- package/src/core/stores/commitments.js +331 -0
- package/src/core/stores/engine-sessions.js +0 -4
- package/src/core/stores/messages.js +4 -5
- package/src/core/stores/organization.js +9 -11
- package/src/core/stores/routines.js +23 -14
- package/src/core/stores/runtime-callbacks.js +2 -1
- package/src/core/util/json-file.js +128 -0
- package/src/core/util/thinking.js +51 -0
- package/src/core/vars/sources.js +14 -21
- package/src/core/voice/stt-models.js +1 -1
- package/src/core/voice/tts.js +5 -4
- package/src/host/daemon/api/admin-config.js +5 -5
- package/src/host/daemon/api/admin.js +8 -7
- package/src/host/daemon/api/agents.js +16 -16
- package/src/host/daemon/api/artifact-preview.js +8 -8
- package/src/host/daemon/api/artifacts.js +7 -7
- package/src/host/daemon/api/code.js +8 -8
- package/src/host/daemon/api/commitments.js +135 -0
- package/src/host/daemon/api/config.js +6 -6
- package/src/host/daemon/api/confirm.js +2 -2
- package/src/host/daemon/api/connections.js +2 -2
- package/src/host/daemon/api/conversations.js +12 -12
- package/src/host/daemon/api/deck.js +6 -6
- package/src/host/daemon/api/desktop.js +8 -8
- package/src/host/daemon/api/embeddings.js +4 -4
- package/src/host/daemon/api/engines.js +8 -7
- package/src/host/daemon/api/exec.js +5 -4
- package/src/host/daemon/api/files-project.js +6 -6
- package/src/host/daemon/api/health.js +2 -2
- package/src/host/daemon/api/identity.js +3 -3
- package/src/host/daemon/api/inbox.js +2 -2
- package/src/host/daemon/api/integrations.js +23 -19
- package/src/host/daemon/api/mcps.js +16 -12
- package/src/host/daemon/api/messages.js +5 -5
- package/src/host/daemon/api/nudges.js +112 -0
- package/src/host/daemon/api/organization.js +8 -8
- package/src/host/daemon/api/pairing.js +6 -6
- package/src/host/daemon/api/plugins.js +3 -3
- package/src/host/daemon/api/prefix.js +20 -0
- package/src/host/daemon/api/profiles.js +9 -9
- package/src/host/daemon/api/projects.js +5 -5
- package/src/host/daemon/api/routines.js +32 -8
- package/src/host/daemon/api/run.js +2 -2
- package/src/host/daemon/api/runtimes.js +6 -6
- package/src/host/daemon/api/self-memory.js +50 -0
- package/src/host/daemon/api/sessions-search.js +18 -11
- package/src/host/daemon/api/sessions.js +6 -6
- package/src/host/daemon/api/shared.js +59 -13
- package/src/host/daemon/api/skills.js +12 -12
- package/src/host/daemon/api/super-agent.js +4 -4
- package/src/host/daemon/api/tasks.js +11 -11
- package/src/host/daemon/api/telegram.js +61 -24
- package/src/host/daemon/api/tools.js +7 -7
- package/src/host/daemon/api/top-level.js +7 -7
- package/src/host/daemon/api/transcribe.js +6 -6
- package/src/host/daemon/api/tts.js +3 -3
- package/src/host/daemon/api/vars.js +11 -8
- package/src/host/daemon/api/voice.js +9 -7
- package/src/host/daemon/api/web.js +28 -32
- package/src/host/daemon/api.js +83 -48
- package/src/host/daemon/callback-reconciler.js +16 -0
- package/src/host/daemon/db.js +1 -1
- package/src/host/daemon/desktop-ws.js +7 -3
- package/src/host/daemon/index.js +11 -5
- package/src/host/daemon/plugins/desktop/index.js +8 -2
- package/src/host/daemon/plugins/telegram/index.js +7 -2
- package/src/host/daemon/stt-venv.js +20 -81
- package/src/host/daemon/wakeup.js +18 -4
- package/src/interfaces/acp/index.js +3 -3
- package/src/interfaces/acp/session.js +2 -2
- package/src/interfaces/cli/commands/a2a.js +2 -2
- package/src/interfaces/cli/commands/agent.js +3 -3
- package/src/interfaces/cli/commands/artifact.js +13 -13
- package/src/interfaces/cli/commands/chat.js +3 -3
- package/src/interfaces/cli/commands/command.js +2 -2
- package/src/interfaces/cli/commands/commitment.js +154 -0
- package/src/interfaces/cli/commands/config.js +4 -4
- package/src/interfaces/cli/commands/daemon.js +74 -18
- package/src/interfaces/cli/commands/desktop.js +7 -14
- package/src/interfaces/cli/commands/exec.js +2 -2
- package/src/interfaces/cli/commands/log.js +3 -4
- package/src/interfaces/cli/commands/mcp.js +9 -9
- package/src/interfaces/cli/commands/memory.js +75 -2
- package/src/interfaces/cli/commands/messages.js +5 -5
- package/src/interfaces/cli/commands/model.js +0 -18
- package/src/interfaces/cli/commands/nudge.js +130 -0
- package/src/interfaces/cli/commands/obsidian.js +5 -5
- package/src/interfaces/cli/commands/org.js +5 -5
- package/src/interfaces/cli/commands/pair.js +6 -6
- package/src/interfaces/cli/commands/plugins.js +2 -2
- package/src/interfaces/cli/commands/profile.js +10 -10
- package/src/interfaces/cli/commands/project-config.js +6 -6
- package/src/interfaces/cli/commands/project.js +10 -10
- package/src/interfaces/cli/commands/routine.js +8 -9
- package/src/interfaces/cli/commands/runtime.js +2 -2
- package/src/interfaces/cli/commands/search.js +0 -1
- package/src/interfaces/cli/commands/session.js +12 -40
- package/src/interfaces/cli/commands/sessions.js +21 -798
- package/src/interfaces/cli/commands/setup.js +16 -6
- package/src/interfaces/cli/commands/skills.js +1 -2
- package/src/interfaces/cli/commands/status.js +3 -3
- package/src/interfaces/cli/commands/task.js +8 -8
- package/src/interfaces/cli/commands/telegram.js +21 -21
- package/src/interfaces/cli/commands/voice.js +6 -6
- package/src/interfaces/cli/help/index.js +2245 -0
- package/src/interfaces/cli/http.js +9 -4
- package/src/interfaces/cli/index.js +14 -2857
- package/src/interfaces/cli/routes/acp.js +13 -0
- package/src/interfaces/cli/routes/agent.js +27 -0
- package/src/interfaces/cli/routes/artifact.js +28 -0
- package/src/interfaces/cli/routes/chat.js +11 -0
- package/src/interfaces/cli/routes/code.js +11 -0
- package/src/interfaces/cli/routes/command.js +21 -0
- package/src/interfaces/cli/routes/commitment.js +19 -0
- package/src/interfaces/cli/routes/config.js +16 -0
- package/src/interfaces/cli/routes/connections.js +11 -0
- package/src/interfaces/cli/routes/conversations.js +21 -0
- package/src/interfaces/cli/routes/daemon.js +25 -0
- package/src/interfaces/cli/routes/desktop.js +20 -0
- package/src/interfaces/cli/routes/env.js +13 -0
- package/src/interfaces/cli/routes/exec.js +11 -0
- package/src/interfaces/cli/routes/identity.js +11 -0
- package/src/interfaces/cli/routes/index.js +75 -0
- package/src/interfaces/cli/routes/init.js +11 -0
- package/src/interfaces/cli/routes/log.js +20 -0
- package/src/interfaces/cli/routes/mcp.js +22 -0
- package/src/interfaces/cli/routes/memory.js +19 -0
- package/src/interfaces/cli/routes/messages.js +16 -0
- package/src/interfaces/cli/routes/model.js +11 -0
- package/src/interfaces/cli/routes/nudge.js +17 -0
- package/src/interfaces/cli/routes/obsidian.js +17 -0
- package/src/interfaces/cli/routes/org.js +34 -0
- package/src/interfaces/cli/routes/overlay.js +10 -0
- package/src/interfaces/cli/routes/pair.js +17 -0
- package/src/interfaces/cli/routes/panel.js +16 -0
- package/src/interfaces/cli/routes/permission.js +11 -0
- package/src/interfaces/cli/routes/plugins.js +21 -0
- package/src/interfaces/cli/routes/profile.js +27 -0
- package/src/interfaces/cli/routes/project.js +41 -0
- package/src/interfaces/cli/routes/restart.js +15 -0
- package/src/interfaces/cli/routes/routine.js +28 -0
- package/src/interfaces/cli/routes/run.js +11 -0
- package/src/interfaces/cli/routes/search.js +11 -0
- package/src/interfaces/cli/routes/send.js +11 -0
- package/src/interfaces/cli/routes/session.js +26 -0
- package/src/interfaces/cli/routes/sessions.js +15 -0
- package/src/interfaces/cli/routes/setup.js +18 -0
- package/src/interfaces/cli/routes/skills.js +20 -0
- package/src/interfaces/cli/routes/status.js +12 -0
- package/src/interfaces/cli/routes/task.js +26 -0
- package/src/interfaces/cli/routes/telegram.js +39 -0
- package/src/interfaces/cli/routes/update.js +24 -0
- package/src/interfaces/cli/routes/voice.js +17 -0
- package/src/interfaces/desktop/main.js +6 -7
- package/src/interfaces/desktop/renderer.js +0 -5
- package/src/interfaces/mcp-server/index.js +15 -14
- package/src/interfaces/tui/_shims/globals.d.ts +8 -0
- package/src/interfaces/tui/tsconfig.json +4 -1
- package/src/interfaces/web/README.md +6 -6
- package/src/interfaces/web/dist/assets/index-CvEoGtTf.js +849 -0
- package/src/interfaces/web/dist/assets/index-CvEoGtTf.js.map +1 -0
- package/src/interfaces/web/dist/assets/index-DzBBXFaO.css +1 -0
- package/src/interfaces/web/dist/index.html +2 -2
- package/src/interfaces/web/package-lock.json +11 -10
- package/src/interfaces/web/src/components/AddProjectDialog.tsx +1 -1
- package/src/interfaces/web/src/components/Section.tsx +18 -3
- package/src/interfaces/web/src/components/agents/AgentFormFields.tsx +1 -1
- package/src/interfaces/web/src/components/chat/ChatList.tsx +2 -2
- package/src/interfaces/web/src/components/chat/MessageBubble.tsx +13 -0
- package/src/interfaces/web/src/components/chat/SkillPicker.tsx +1 -1
- package/src/interfaces/web/src/components/code/CodeFileTree.tsx +1 -1
- package/src/interfaces/web/src/components/code/CodeTerminal.tsx +1 -1
- package/src/interfaces/web/src/components/cron/CronPicker.tsx +196 -0
- package/src/interfaces/web/src/components/desktop/DesktopStatusCard.tsx +1 -1
- package/src/interfaces/web/src/components/files/FileBrowser.tsx +2 -2
- package/src/interfaces/web/src/components/inbox/InboxList.tsx +145 -0
- package/src/interfaces/web/src/components/memory/MemoryBrowser.tsx +36 -6
- package/src/interfaces/web/src/components/routines/ExecutionsList.tsx +1 -1
- package/src/interfaces/web/src/components/routines/RoutineDetail.tsx +16 -4
- package/src/interfaces/web/src/components/routines/RoutineEditor.tsx +14 -4
- package/src/interfaces/web/src/components/routines/shared.ts +14 -5
- package/src/interfaces/web/src/components/settings/DesktopSettingsPanel.tsx +1 -1
- package/src/interfaces/web/src/components/settings/MemoryPanel.tsx +1 -1
- package/src/interfaces/web/src/components/settings/NudgePanel.tsx +183 -0
- package/src/interfaces/web/src/components/settings/ProfilePanel.tsx +36 -12
- package/src/interfaces/web/src/components/settings/SkillsInspectorPanel.tsx +1 -1
- package/src/interfaces/web/src/components/settings/SkillsManager.tsx +2 -2
- package/src/interfaces/web/src/components/tasks/TaskDetailPanel.tsx +1 -1
- package/src/interfaces/web/src/components/ui/filter-chips.tsx +47 -0
- package/src/interfaces/web/src/components/ui.tsx +1 -0
- package/src/interfaces/web/src/constants/index.ts +1 -0
- package/src/interfaces/web/src/hooks/useChat.ts +5 -1
- package/src/interfaces/web/src/hooks/useDaemonStatus.ts +1 -1
- package/src/interfaces/web/src/hooks/useDevices.ts +1 -1
- package/src/interfaces/web/src/hooks/useEngines.ts +1 -1
- package/src/interfaces/web/src/hooks/useGlobalConfig.ts +2 -2
- package/src/interfaces/web/src/hooks/useIdentity.ts +1 -1
- package/src/interfaces/web/src/hooks/useInbox.ts +1 -1
- package/src/interfaces/web/src/hooks/useNudges.ts +38 -0
- package/src/interfaces/web/src/hooks/useProfiles.ts +3 -3
- package/src/interfaces/web/src/hooks/useProjects.ts +1 -1
- package/src/interfaces/web/src/hooks/useTelegram.ts +3 -3
- package/src/interfaces/web/src/hooks/useTokenBootstrap.ts +3 -3
- package/src/interfaces/web/src/i18n/en.ts +127 -0
- package/src/interfaces/web/src/i18n/es.ts +127 -0
- package/src/interfaces/web/src/lib/api/admin.ts +11 -11
- package/src/interfaces/web/src/lib/api/agents.ts +14 -14
- package/src/interfaces/web/src/lib/api/artifacts.ts +11 -11
- package/src/interfaces/web/src/lib/api/code.ts +1 -1
- package/src/interfaces/web/src/lib/api/commitments.ts +57 -0
- package/src/interfaces/web/src/lib/api/conversations.ts +8 -8
- package/src/interfaces/web/src/lib/api/deck.ts +3 -3
- package/src/interfaces/web/src/lib/api/desktop.ts +8 -8
- package/src/interfaces/web/src/lib/api/embeddings.ts +3 -3
- package/src/interfaces/web/src/lib/api/engines.ts +3 -3
- package/src/interfaces/web/src/lib/api/filesystem.ts +2 -2
- package/src/interfaces/web/src/lib/api/health.ts +1 -1
- package/src/interfaces/web/src/lib/api/identity.ts +2 -2
- package/src/interfaces/web/src/lib/api/inbox.ts +1 -1
- package/src/interfaces/web/src/lib/api/integrations.ts +8 -8
- package/src/interfaces/web/src/lib/api/mcps.ts +6 -6
- package/src/interfaces/web/src/lib/api/messages.ts +3 -3
- package/src/interfaces/web/src/lib/api/notebook.ts +23 -0
- package/src/interfaces/web/src/lib/api/nudges.ts +53 -0
- package/src/interfaces/web/src/lib/api/organization.ts +7 -7
- package/src/interfaces/web/src/lib/api/profiles.ts +8 -8
- package/src/interfaces/web/src/lib/api/projectFiles.ts +5 -5
- package/src/interfaces/web/src/lib/api/projects.ts +12 -12
- package/src/interfaces/web/src/lib/api/routines.ts +7 -7
- package/src/interfaces/web/src/lib/api/sessions.ts +2 -2
- package/src/interfaces/web/src/lib/api/skills.ts +11 -11
- package/src/interfaces/web/src/lib/api/super_agent.ts +3 -3
- package/src/interfaces/web/src/lib/api/tasks.ts +12 -12
- package/src/interfaces/web/src/lib/api/telegram.ts +14 -14
- package/src/interfaces/web/src/lib/api/tools.ts +1 -1
- package/src/interfaces/web/src/lib/api/vars.ts +4 -4
- package/src/interfaces/web/src/lib/api/voice.ts +6 -6
- package/src/interfaces/web/src/lib/cron.ts +196 -0
- package/src/interfaces/web/src/lib/when.ts +32 -0
- package/src/interfaces/web/src/screens/InboxScreen.tsx +107 -77
- package/src/interfaces/web/src/screens/ProjectScreen.tsx +5 -2
- package/src/interfaces/web/src/screens/SettingsScreen.tsx +17 -3
- package/src/interfaces/web/src/screens/base/AgentDefaultsTab.tsx +1 -1
- package/src/interfaces/web/src/screens/base/CommitmentsTab.tsx +239 -0
- package/src/interfaces/web/src/screens/base/GlobalTasksTab.tsx +103 -20
- package/src/interfaces/web/src/screens/base/LogsTab.tsx +18 -3
- package/src/interfaces/web/src/screens/base/SessionsTab.tsx +1 -1
- package/src/interfaces/web/src/screens/modules/CodeScreen.tsx +1 -1
- package/src/interfaces/web/src/screens/modules/DeckScreen.tsx +1 -1
- package/src/interfaces/web/src/screens/modules/DesktopScreen.tsx +1 -1
- package/src/interfaces/web/src/screens/modules/VoiceScreen.tsx +1 -1
- package/src/interfaces/web/src/screens/project/AgentDetailScreen.tsx +8 -8
- package/src/interfaces/web/src/screens/project/AgentsTab.tsx +2 -2
- package/src/interfaces/web/src/screens/project/ChatTab.tsx +24 -6
- package/src/interfaces/web/src/screens/project/ConfigTab.tsx +1 -1
- package/src/interfaces/web/src/screens/project/McpsTab.tsx +3 -3
- package/src/interfaces/web/src/screens/project/Overview.tsx +6 -6
- package/src/interfaces/web/src/screens/project/RoutinesTab.tsx +15 -13
- package/src/interfaces/web/src/screens/project/StructureTab.tsx +1 -1
- package/src/interfaces/web/src/screens/project/TasksTab.tsx +1 -1
- package/src/interfaces/web/src/screens/project/VarsTab.tsx +1 -1
- package/src/interfaces/web/src/types/daemon.ts +10 -1
- package/src/interfaces/web/vite.config.ts +13 -18
- package/src/interfaces/web/dist/assets/index-BptlbKjU.js +0 -824
- package/src/interfaces/web/dist/assets/index-BptlbKjU.js.map +0 -1
- package/src/interfaces/web/dist/assets/index-D_EJEA1n.css +0 -1
- package/src/skills/apc-context/SKILL.md +0 -159
- /package/src/{host/daemon → core}/runtimes/_spawn.js +0 -0
- /package/src/{host/daemon → core}/runtimes/aider.js +0 -0
- /package/src/{host/daemon → core}/runtimes/antigravity.js +0 -0
- /package/src/{host/daemon → core}/runtimes/claude-code.js +0 -0
- /package/src/{host/daemon → core}/runtimes/codex.js +0 -0
- /package/src/{host/daemon → core}/runtimes/cursor-agent.js +0 -0
- /package/src/{host/daemon → core}/runtimes/gemini-cli.js +0 -0
- /package/src/{host/daemon → core}/runtimes/index.js +0 -0
- /package/src/{host/daemon → core}/runtimes/opencode.js +0 -0
- /package/src/{host/daemon → core}/runtimes/qwen-code.js +0 -0
|
@@ -6,7 +6,11 @@
|
|
|
6
6
|
// POST /telegram/send_photo { chat_id?, photo, caption?, parse_mode?, channel? }
|
|
7
7
|
// POST /telegram/send_voice { chat_id?, audio, caption?, duration?, channel? }
|
|
8
8
|
// POST /telegram/send_audio { chat_id?, audio, caption?, title?, performer?, channel? }
|
|
9
|
-
// POST /telegram/notify
|
|
9
|
+
// POST /telegram/notify { chat_id?, text, channel?, kind?, project_id?,
|
|
10
|
+
// severity?, unsolicited? }
|
|
11
|
+
// Daemon-initiated pushes. Goes through the
|
|
12
|
+
// interruption budget (core/nudge) and answers
|
|
13
|
+
// 429 with a retry_after_ms when suppressed.
|
|
10
14
|
//
|
|
11
15
|
// GET /telegram/channels — list configured channels
|
|
12
16
|
// POST /telegram/channels { name, ... } — create or replace one channel
|
|
@@ -31,7 +35,6 @@ import {
|
|
|
31
35
|
removeTelegramChannel,
|
|
32
36
|
unsetTelegramChannelFields,
|
|
33
37
|
listContacts,
|
|
34
|
-
findContact,
|
|
35
38
|
upsertContact,
|
|
36
39
|
removeContact,
|
|
37
40
|
listRoles,
|
|
@@ -40,14 +43,15 @@ import {
|
|
|
40
43
|
} from "#core/config/index.js";
|
|
41
44
|
|
|
42
45
|
import { redactChannel, isSecretMarker } from "#core/config/redact.js";
|
|
46
|
+
import { canNudge, recordNudge, nudgeFeedbackKeyboard } from "#core/nudge/index.js";
|
|
43
47
|
|
|
44
|
-
export function register(
|
|
45
|
-
|
|
48
|
+
export function register(api, { telegram }) {
|
|
49
|
+
api.get("/telegram/status", (_req, res) => {
|
|
46
50
|
if (!telegram) return res.json({ enabled: false, channels: [] });
|
|
47
51
|
res.json(telegram.status());
|
|
48
52
|
});
|
|
49
53
|
|
|
50
|
-
|
|
54
|
+
api.post("/telegram/start", (_req, res) => {
|
|
51
55
|
if (!telegram)
|
|
52
56
|
return res.status(503).json({ error: "telegram plugin not loaded" });
|
|
53
57
|
try {
|
|
@@ -58,7 +62,7 @@ export function register(app, { telegram }) {
|
|
|
58
62
|
}
|
|
59
63
|
});
|
|
60
64
|
|
|
61
|
-
|
|
65
|
+
api.post("/telegram/stop", (_req, res) => {
|
|
62
66
|
if (!telegram)
|
|
63
67
|
return res.status(503).json({ error: "telegram plugin not loaded" });
|
|
64
68
|
try {
|
|
@@ -69,7 +73,7 @@ export function register(app, { telegram }) {
|
|
|
69
73
|
}
|
|
70
74
|
});
|
|
71
75
|
|
|
72
|
-
|
|
76
|
+
api.post("/telegram/send", async (req, res) => {
|
|
73
77
|
const { chat_id, text, channel } = req.body || {};
|
|
74
78
|
if (!text) return res.status(400).json({ error: "text required" });
|
|
75
79
|
if (!telegram)
|
|
@@ -82,7 +86,7 @@ export function register(app, { telegram }) {
|
|
|
82
86
|
}
|
|
83
87
|
});
|
|
84
88
|
|
|
85
|
-
|
|
89
|
+
api.post("/telegram/send_photo", async (req, res) => {
|
|
86
90
|
const { chat_id, photo, caption, parse_mode, channel } = req.body || {};
|
|
87
91
|
if (!photo)
|
|
88
92
|
return res.status(400).json({ error: "photo required (path or url)" });
|
|
@@ -102,7 +106,7 @@ export function register(app, { telegram }) {
|
|
|
102
106
|
}
|
|
103
107
|
});
|
|
104
108
|
|
|
105
|
-
|
|
109
|
+
api.post("/telegram/send_voice", async (req, res) => {
|
|
106
110
|
const { chat_id, audio, caption, duration, channel } = req.body || {};
|
|
107
111
|
if (!audio) return res.status(400).json({ error: "audio required (path)" });
|
|
108
112
|
if (!telegram)
|
|
@@ -121,7 +125,7 @@ export function register(app, { telegram }) {
|
|
|
121
125
|
}
|
|
122
126
|
});
|
|
123
127
|
|
|
124
|
-
|
|
128
|
+
api.post("/telegram/send_audio", async (req, res) => {
|
|
125
129
|
const { chat_id, audio, caption, title, performer, channel } =
|
|
126
130
|
req.body || {};
|
|
127
131
|
if (!audio) return res.status(400).json({ error: "audio required (path)" });
|
|
@@ -145,7 +149,7 @@ export function register(app, { telegram }) {
|
|
|
145
149
|
// ── Channel CRUD (config-only; caller must POST /admin/reload to apply) ──
|
|
146
150
|
// We read fresh config from disk on each call so concurrent writes from the
|
|
147
151
|
// CLI and the daemon don't clobber one another via stale closures.
|
|
148
|
-
|
|
152
|
+
api.get("/telegram/channels", (_req, res) => {
|
|
149
153
|
try {
|
|
150
154
|
const cfg = readConfig();
|
|
151
155
|
res.json({ channels: listTelegramChannels(cfg).map(redactChannel) });
|
|
@@ -154,7 +158,7 @@ export function register(app, { telegram }) {
|
|
|
154
158
|
}
|
|
155
159
|
});
|
|
156
160
|
|
|
157
|
-
|
|
161
|
+
api.post("/telegram/channels", (req, res) => {
|
|
158
162
|
const body = req.body || {};
|
|
159
163
|
if (!body.name || typeof body.name !== "string") {
|
|
160
164
|
return res.status(400).json({ error: "name required" });
|
|
@@ -173,7 +177,7 @@ export function register(app, { telegram }) {
|
|
|
173
177
|
}
|
|
174
178
|
});
|
|
175
179
|
|
|
176
|
-
|
|
180
|
+
api.patch("/telegram/channels/:name", (req, res) => {
|
|
177
181
|
const { name } = req.params;
|
|
178
182
|
const body = req.body || {};
|
|
179
183
|
try {
|
|
@@ -202,7 +206,7 @@ export function register(app, { telegram }) {
|
|
|
202
206
|
}
|
|
203
207
|
});
|
|
204
208
|
|
|
205
|
-
|
|
209
|
+
api.delete("/telegram/channels/:name", (req, res) => {
|
|
206
210
|
const { name } = req.params;
|
|
207
211
|
try {
|
|
208
212
|
const cfg = readConfig();
|
|
@@ -219,7 +223,7 @@ export function register(app, { telegram }) {
|
|
|
219
223
|
// channel. These edit ~/.apx/config.json directly; the running poller picks
|
|
220
224
|
// changes up on its next inbound message (it re-reads config per message), so
|
|
221
225
|
// no /admin/reload is required for contact/role edits.
|
|
222
|
-
|
|
226
|
+
api.get("/telegram/contacts", (_req, res) => {
|
|
223
227
|
try {
|
|
224
228
|
const cfg = readConfig();
|
|
225
229
|
res.json({
|
|
@@ -235,7 +239,7 @@ export function register(app, { telegram }) {
|
|
|
235
239
|
}
|
|
236
240
|
});
|
|
237
241
|
|
|
238
|
-
|
|
242
|
+
api.patch("/telegram/contacts/:user_id", (req, res) => {
|
|
239
243
|
const { user_id } = req.params;
|
|
240
244
|
const body = req.body || {};
|
|
241
245
|
try {
|
|
@@ -254,7 +258,7 @@ export function register(app, { telegram }) {
|
|
|
254
258
|
}
|
|
255
259
|
});
|
|
256
260
|
|
|
257
|
-
|
|
261
|
+
api.delete("/telegram/contacts/:user_id", (req, res) => {
|
|
258
262
|
const { user_id } = req.params;
|
|
259
263
|
try {
|
|
260
264
|
const cfg = readConfig();
|
|
@@ -267,7 +271,7 @@ export function register(app, { telegram }) {
|
|
|
267
271
|
});
|
|
268
272
|
|
|
269
273
|
// ── Roles (role → { tools }) ────────────────────────────────────────────
|
|
270
|
-
|
|
274
|
+
api.get("/telegram/roles", (_req, res) => {
|
|
271
275
|
try {
|
|
272
276
|
res.json({ roles: listRoles(readConfig()) });
|
|
273
277
|
} catch (e) {
|
|
@@ -275,7 +279,7 @@ export function register(app, { telegram }) {
|
|
|
275
279
|
}
|
|
276
280
|
});
|
|
277
281
|
|
|
278
|
-
|
|
282
|
+
api.put("/telegram/roles/:name", (req, res) => {
|
|
279
283
|
const { name } = req.params;
|
|
280
284
|
const body = req.body || {};
|
|
281
285
|
try {
|
|
@@ -288,7 +292,7 @@ export function register(app, { telegram }) {
|
|
|
288
292
|
}
|
|
289
293
|
});
|
|
290
294
|
|
|
291
|
-
|
|
295
|
+
api.delete("/telegram/roles/:name", (req, res) => {
|
|
292
296
|
const { name } = req.params;
|
|
293
297
|
try {
|
|
294
298
|
const { removed } = removeRole(readConfig(), name);
|
|
@@ -300,14 +304,47 @@ export function register(app, { telegram }) {
|
|
|
300
304
|
});
|
|
301
305
|
|
|
302
306
|
// Alias for proactive daemon-initiated pushes (routines, error handlers, …).
|
|
303
|
-
|
|
304
|
-
|
|
307
|
+
//
|
|
308
|
+
// PUSH PATH 2 OF 4 — this endpoint exists FOR unrequested messages, so the
|
|
309
|
+
// interruption budget applies by default. A caller delivering something the
|
|
310
|
+
// user is waiting for says so with `unsolicited: false`, and that choice is
|
|
311
|
+
// then visible in its own diff rather than hidden in this handler.
|
|
312
|
+
api.post("/telegram/notify", async (req, res) => {
|
|
313
|
+
const { chat_id, text, channel, kind, project_id, severity, unsolicited } = req.body || {};
|
|
305
314
|
if (!text) return res.status(400).json({ error: "text required" });
|
|
306
315
|
if (!telegram)
|
|
307
316
|
return res.status(503).json({ error: "telegram plugin not loaded" });
|
|
317
|
+
|
|
318
|
+
const gate = canNudge(
|
|
319
|
+
{
|
|
320
|
+
kind: kind || "notify",
|
|
321
|
+
project_id: project_id ?? null,
|
|
322
|
+
severity: severity || "normal",
|
|
323
|
+
unsolicited: unsolicited !== false,
|
|
324
|
+
channel: "telegram",
|
|
325
|
+
},
|
|
326
|
+
readConfig(),
|
|
327
|
+
);
|
|
328
|
+
if (!gate.allowed) {
|
|
329
|
+
// 429, not 500: nothing failed. The caller is being asked to wait, and
|
|
330
|
+
// told for how long, so a routine can decide to try again rather than
|
|
331
|
+
// treating a working guardrail as an outage.
|
|
332
|
+
return res.status(429).json({
|
|
333
|
+
ok: false,
|
|
334
|
+
suppressed: true,
|
|
335
|
+
reason: gate.reason,
|
|
336
|
+
retry_after_ms: gate.retry_after_ms,
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
|
|
308
340
|
try {
|
|
309
|
-
const r = await telegram.send({
|
|
310
|
-
|
|
341
|
+
const r = await telegram.send({
|
|
342
|
+
chat_id, text, channel,
|
|
343
|
+
reply_markup: gate.unsolicited ? nudgeFeedbackKeyboard(gate.nudge_id) : undefined,
|
|
344
|
+
meta: { nudge_id: gate.unsolicited ? gate.nudge_id : undefined, nudge_kind: gate.kind },
|
|
345
|
+
});
|
|
346
|
+
recordNudge(gate, { chat_id, preview: text });
|
|
347
|
+
res.status(202).json({ ok: true, message_id: r.message_id, via: "notify", nudge_id: gate.nudge_id });
|
|
311
348
|
} catch (e) {
|
|
312
349
|
res.status(502).json({ error: e.message });
|
|
313
350
|
}
|
|
@@ -11,11 +11,11 @@ import { buildRegistryRouter } from "#core/http-tools/registry.js";
|
|
|
11
11
|
import { buildGlobRouter } from "#core/http-tools/glob.js";
|
|
12
12
|
import { buildGrepRouter } from "#core/http-tools/grep.js";
|
|
13
13
|
|
|
14
|
-
export function register(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
export function register(api, { express, projects, registries }) {
|
|
15
|
+
api.use("/tools/fetch", buildFetchRouter(express));
|
|
16
|
+
api.use("/tools/browser", buildBrowserRouter(express));
|
|
17
|
+
api.use("/tools/search", buildSearchRouter(express));
|
|
18
|
+
api.use("/tools/glob", buildGlobRouter(express));
|
|
19
|
+
api.use("/tools/grep", buildGrepRouter(express));
|
|
20
|
+
api.use("/tools", buildRegistryRouter(express, { projects, registries }));
|
|
21
21
|
}
|
|
@@ -12,9 +12,9 @@ import fs from "node:fs";
|
|
|
12
12
|
import path from "node:path";
|
|
13
13
|
import { resolveMemoryPath } from "./shared.js";
|
|
14
14
|
|
|
15
|
-
export function register(
|
|
15
|
+
export function register(api, { projects, registries, resolveTopProject }) {
|
|
16
16
|
// ---- /memory ----
|
|
17
|
-
|
|
17
|
+
api.get("/memory", (req, res) => {
|
|
18
18
|
const p = resolveTopProject(req.query);
|
|
19
19
|
if (!p) return res.status(404).json({ error: "no project registered" });
|
|
20
20
|
const memPath = resolveMemoryPath(p);
|
|
@@ -22,7 +22,7 @@ export function register(app, { projects, registries, resolveTopProject }) {
|
|
|
22
22
|
res.json({ project_id: p.id, path: memPath, body });
|
|
23
23
|
});
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
api.post("/memory", (req, res) => {
|
|
26
26
|
const p = resolveTopProject(req.query);
|
|
27
27
|
if (!p) return res.status(404).json({ error: "no project registered" });
|
|
28
28
|
const { body } = req.body || {};
|
|
@@ -42,7 +42,7 @@ export function register(app, { projects, registries, resolveTopProject }) {
|
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
// ---- /files ----
|
|
45
|
-
|
|
45
|
+
api.get("/files", (req, res) => {
|
|
46
46
|
const p = resolveTopProject(req.query);
|
|
47
47
|
if (!p) return res.status(404).json({ error: "no project registered" });
|
|
48
48
|
const rel = req.query.path;
|
|
@@ -90,7 +90,7 @@ export function register(app, { projects, registries, resolveTopProject }) {
|
|
|
90
90
|
});
|
|
91
91
|
});
|
|
92
92
|
|
|
93
|
-
|
|
93
|
+
api.post("/files", (req, res) => {
|
|
94
94
|
const p = resolveTopProject(req.query);
|
|
95
95
|
if (!p) return res.status(404).json({ error: "no project registered" });
|
|
96
96
|
const { path: rel, content } = req.body || {};
|
|
@@ -111,13 +111,13 @@ export function register(app, { projects, registries, resolveTopProject }) {
|
|
|
111
111
|
});
|
|
112
112
|
|
|
113
113
|
// ---- /mcp ----
|
|
114
|
-
|
|
114
|
+
api.get("/mcp", (req, res) => {
|
|
115
115
|
const p = resolveTopProject(req.query);
|
|
116
116
|
if (!p) return res.status(404).json({ error: "no project registered" });
|
|
117
117
|
res.json(registries.for(p).list());
|
|
118
118
|
});
|
|
119
119
|
|
|
120
|
-
|
|
120
|
+
api.post("/mcp/run", async (req, res) => {
|
|
121
121
|
const { project: projectRef, name, tool, params } = req.body || {};
|
|
122
122
|
if (!name) return res.status(400).json({ error: "name required" });
|
|
123
123
|
if (!tool) return res.status(400).json({ error: "tool required" });
|
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
// X-Provider auto | local | openai | custom (overrides config)
|
|
6
6
|
//
|
|
7
7
|
// Shared by overlay, telegram voice messages, and any external caller.
|
|
8
|
-
export function register(
|
|
8
|
+
export function register(api) {
|
|
9
9
|
// GET /transcribe/providers — STT engine list + availability for the web
|
|
10
10
|
// admin (mirror of /tts/providers). local = embedded faster-whisper;
|
|
11
11
|
// openai = cloud Whisper; custom = any OpenAI-compatible server (mlx-audio
|
|
12
12
|
// on Metal, a Radeon/NVIDIA box on the LAN, a remote endpoint).
|
|
13
|
-
|
|
13
|
+
api.get("/transcribe/providers", async (_req, res) => {
|
|
14
14
|
try {
|
|
15
15
|
const { readConfig } = await import("#core/config/index.js");
|
|
16
16
|
const { listSttProviders } = await import("#core/voice/transcription.js");
|
|
@@ -23,7 +23,7 @@ export function register(app) {
|
|
|
23
23
|
// GET /transcribe/hardware — detected machine + the recommended local backend
|
|
24
24
|
// (mlx on Apple Silicon, faster-whisper cuda on NVIDIA, else CPU). Drives the
|
|
25
25
|
// "engine adapts itself" UX in the web admin.
|
|
26
|
-
|
|
26
|
+
api.get("/transcribe/hardware", async (_req, res) => {
|
|
27
27
|
try {
|
|
28
28
|
const { detectHardware, recommendStt } = await import("#core/voice/stt-hardware.js");
|
|
29
29
|
const hw = detectHardware();
|
|
@@ -35,7 +35,7 @@ export function register(app) {
|
|
|
35
35
|
|
|
36
36
|
// GET /transcribe/models?backend=faster|mlx — model catalog with on-disk
|
|
37
37
|
// status (downloaded? size) for the model-manager UI.
|
|
38
|
-
|
|
38
|
+
api.get("/transcribe/models", async (req, res) => {
|
|
39
39
|
try {
|
|
40
40
|
const backend = String(req.query.backend || "faster");
|
|
41
41
|
const { listSttModels } = await import("#core/voice/stt-models.js");
|
|
@@ -48,7 +48,7 @@ export function register(app) {
|
|
|
48
48
|
// GET /transcribe/warmup — load the local whisper model (if needed) and reset
|
|
49
49
|
// its idle watchdog. Callers (e.g. the desktop window) ping this while open so
|
|
50
50
|
// the first real utterance doesn't pay the cold-load cost.
|
|
51
|
-
|
|
51
|
+
api.get("/transcribe/warmup", async (_req, res) => {
|
|
52
52
|
try {
|
|
53
53
|
const { warmupWhisper } = await import("../whisper-server.js");
|
|
54
54
|
res.json(await warmupWhisper());
|
|
@@ -57,7 +57,7 @@ export function register(app) {
|
|
|
57
57
|
}
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
api.post("/transcribe/chunk", async (req, res) => {
|
|
61
61
|
const chunks = [];
|
|
62
62
|
req.on("data", (c) => chunks.push(c));
|
|
63
63
|
req.on("end", async () => {
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
import { synthesize, listProviders } from "#core/voice/tts.js";
|
|
12
12
|
import { readConfig } from "#core/config/index.js";
|
|
13
13
|
|
|
14
|
-
export function register(
|
|
15
|
-
|
|
14
|
+
export function register(api) {
|
|
15
|
+
api.post("/tts/say", async (req, res) => {
|
|
16
16
|
try {
|
|
17
17
|
const { text, voice, language, provider, format, style } = req.body || {};
|
|
18
18
|
if (typeof text !== "string" || !text.trim()) {
|
|
@@ -33,7 +33,7 @@ export function register(app) {
|
|
|
33
33
|
}
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
api.get("/tts/providers", async (_req, res) => {
|
|
37
37
|
try {
|
|
38
38
|
const info = await listProviders(readConfig());
|
|
39
39
|
res.json(info);
|
|
@@ -24,7 +24,10 @@ import {
|
|
|
24
24
|
maskValue,
|
|
25
25
|
} from "#core/vars/index.js";
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
// Var scope vocabulary: project | global. The default depends on whether the
|
|
28
|
+
// request targets the base store. See the note in api/mcps.js: these scope
|
|
29
|
+
// helpers share a shape but not a vocabulary.
|
|
30
|
+
function normalizeVarScope(raw, { isBase }) {
|
|
28
31
|
if (!raw) return isBase ? "global" : "project";
|
|
29
32
|
const s = String(raw).toLowerCase();
|
|
30
33
|
if (s === "project" || s === "global") return s;
|
|
@@ -37,8 +40,8 @@ function maskAll(obj) {
|
|
|
37
40
|
return out;
|
|
38
41
|
}
|
|
39
42
|
|
|
40
|
-
export function register(
|
|
41
|
-
|
|
43
|
+
export function register(api, { project, registries }) {
|
|
44
|
+
api.get("/projects/:pid/vars", (req, res) => {
|
|
42
45
|
const p = project(req, res);
|
|
43
46
|
if (!p) return;
|
|
44
47
|
const reveal = req.query?.reveal === "1" || req.query?.reveal === "true";
|
|
@@ -54,7 +57,7 @@ export function register(app, { project, registries }) {
|
|
|
54
57
|
});
|
|
55
58
|
});
|
|
56
59
|
|
|
57
|
-
|
|
60
|
+
api.get("/projects/:pid/vars/:name", (req, res) => {
|
|
58
61
|
const p = project(req, res);
|
|
59
62
|
if (!p) return;
|
|
60
63
|
const name = req.params.name;
|
|
@@ -80,7 +83,7 @@ export function register(app, { project, registries }) {
|
|
|
80
83
|
});
|
|
81
84
|
});
|
|
82
85
|
|
|
83
|
-
|
|
86
|
+
api.post("/projects/:pid/vars", (req, res) => {
|
|
84
87
|
const p = project(req, res);
|
|
85
88
|
if (!p) return;
|
|
86
89
|
const { name, value } = req.body || {};
|
|
@@ -91,7 +94,7 @@ export function register(app, { project, registries }) {
|
|
|
91
94
|
return res.status(400).json({ error: "value required" });
|
|
92
95
|
}
|
|
93
96
|
const isBase = String(p.id) === "0";
|
|
94
|
-
const scope =
|
|
97
|
+
const scope = normalizeVarScope(req.body?.scope, { isBase });
|
|
95
98
|
if (scope === null) {
|
|
96
99
|
return res
|
|
97
100
|
.status(400)
|
|
@@ -111,11 +114,11 @@ export function register(app, { project, registries }) {
|
|
|
111
114
|
res.status(201).json({ ok: true, name, scope });
|
|
112
115
|
});
|
|
113
116
|
|
|
114
|
-
|
|
117
|
+
api.delete("/projects/:pid/vars/:name", (req, res) => {
|
|
115
118
|
const p = project(req, res);
|
|
116
119
|
if (!p) return;
|
|
117
120
|
const isBase = String(p.id) === "0";
|
|
118
|
-
const scope =
|
|
121
|
+
const scope = normalizeVarScope(req.query?.scope, { isBase });
|
|
119
122
|
if (scope === null) {
|
|
120
123
|
return res
|
|
121
124
|
.status(400)
|
|
@@ -18,8 +18,10 @@
|
|
|
18
18
|
//
|
|
19
19
|
// Domain logic (channel context, suggestion parsing, audio decoding) lives in
|
|
20
20
|
// core/. This file is just glue: parse request → call core → format response.
|
|
21
|
+
import { stripReasoning } from "#core/util/thinking.js";
|
|
21
22
|
import fs from "node:fs";
|
|
22
23
|
import path from "node:path";
|
|
24
|
+
import { TTS_TMP_DIR } from "#core/config/paths.js";
|
|
23
25
|
import { readConfig } from "#core/config/index.js";
|
|
24
26
|
import { synthesize } from "#core/voice/tts.js";
|
|
25
27
|
import { stripEmotionTags } from "#core/voice/emotions.js";
|
|
@@ -31,18 +33,17 @@ import { extractSuggestions } from "#core/agent/suggestions.js";
|
|
|
31
33
|
import { appendGlobalMessage } from "#core/stores/messages.js";
|
|
32
34
|
import { appendErrorTrace, previewText } from "#core/logging.js";
|
|
33
35
|
|
|
34
|
-
export function register(
|
|
36
|
+
export function register(api, { projects, plugins, registries }) {
|
|
35
37
|
// GET /voice/tts?path=<abs>
|
|
36
38
|
//
|
|
37
39
|
// Streams a TTS audio file back to the caller. Sandboxed to the
|
|
38
40
|
// ~/.apx/tmp/tts directory so a client can't request arbitrary
|
|
39
41
|
// filesystem paths through a manifest-leaked reply_audio_path.
|
|
40
|
-
|
|
42
|
+
api.get("/voice/tts", async (req, res) => {
|
|
41
43
|
const rawPath = String(req.query.path || "");
|
|
42
44
|
if (!rawPath) return res.status(400).json({ error: "path required" });
|
|
43
45
|
try {
|
|
44
|
-
const
|
|
45
|
-
const ttsRoot = path.resolve(os.homedir(), ".apx", "tmp", "tts");
|
|
46
|
+
const ttsRoot = path.resolve(TTS_TMP_DIR);
|
|
46
47
|
const resolved = path.resolve(rawPath);
|
|
47
48
|
if (!resolved.startsWith(ttsRoot + path.sep)) {
|
|
48
49
|
return res.status(403).json({ error: "path outside tts dir" });
|
|
@@ -65,7 +66,7 @@ export function register(app, { projects, plugins, registries }) {
|
|
|
65
66
|
}
|
|
66
67
|
});
|
|
67
68
|
|
|
68
|
-
|
|
69
|
+
api.post("/voice/turn", async (req, res) => {
|
|
69
70
|
const body = req.body || {};
|
|
70
71
|
const cfg = readConfig();
|
|
71
72
|
let userText = (body.text || "").trim();
|
|
@@ -103,7 +104,7 @@ export function register(app, { projects, plugins, registries }) {
|
|
|
103
104
|
: [];
|
|
104
105
|
const channel = body.channel || "voice";
|
|
105
106
|
let suggestions = [];
|
|
106
|
-
|
|
107
|
+
const toolsUsed = [];
|
|
107
108
|
// Attribution for the persisted turn (see the appendGlobalMessage below).
|
|
108
109
|
let replyModel = null;
|
|
109
110
|
let replyUsage = null;
|
|
@@ -128,7 +129,8 @@ export function register(app, { projects, plugins, registries }) {
|
|
|
128
129
|
systemSuffix: channelCtx.systemSuffix,
|
|
129
130
|
previousMessages,
|
|
130
131
|
});
|
|
131
|
-
|
|
132
|
+
// Spoken back to the user — never narrate raw planning.
|
|
133
|
+
const raw = stripReasoning(result?.text || "").answer.trim();
|
|
132
134
|
replyModel = result?.model || null;
|
|
133
135
|
replyUsage = result?.usage || null;
|
|
134
136
|
replyName = result?.name || null;
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
//
|
|
3
3
|
// The web bundle is built into src/interfaces/web/dist. When that folder
|
|
4
4
|
// exists, this module mounts it at `/` so users can open
|
|
5
|
-
// http://127.0.0.1:7430 and get the UI.
|
|
6
|
-
//
|
|
7
|
-
//
|
|
5
|
+
// http://127.0.0.1:7430 and get the UI. The root namespace belongs to the
|
|
6
|
+
// panel alone — every data route lives under /api (see ./prefix.js) — so the
|
|
7
|
+
// SPA fallback only has to step aside for that single prefix instead of a
|
|
8
|
+
// hand-maintained list that drifts away from the route registry.
|
|
8
9
|
//
|
|
9
10
|
// During development the user runs `vite dev` on :7431 with proxy → :7430,
|
|
10
11
|
// so this mount is a no-op (dist/ doesn't exist yet).
|
|
@@ -12,29 +13,17 @@ import fs from "node:fs";
|
|
|
12
13
|
import path from "node:path";
|
|
13
14
|
import { fileURLToPath } from "node:url";
|
|
14
15
|
|
|
16
|
+
import { isApiPath } from "./prefix.js";
|
|
17
|
+
|
|
18
|
+
// Re-exported so the SPA/API seam keeps a single import site for callers that
|
|
19
|
+
// reason about "which side of the seam is this path on?" (tests, auth).
|
|
20
|
+
export { isApiPath };
|
|
21
|
+
|
|
15
22
|
const __filename = fileURLToPath(import.meta.url);
|
|
16
23
|
const __dirname = path.dirname(__filename);
|
|
17
24
|
// host/daemon/api/web.js → ../../.. = src/ then interfaces/web/dist
|
|
18
25
|
const WEB_DIST = path.resolve(__dirname, "..", "..", "..", "interfaces", "web", "dist");
|
|
19
26
|
|
|
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", "/skills", "/profiles", "/inbox",
|
|
28
|
-
"/tasks", "/agents", "/plugins", "/previews", "/embeddings",
|
|
29
|
-
];
|
|
30
|
-
|
|
31
|
-
export function isApiPath(p) {
|
|
32
|
-
for (const prefix of API_PREFIXES) {
|
|
33
|
-
if (p === prefix || p.startsWith(prefix + "/")) return true;
|
|
34
|
-
}
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
27
|
// Client-side routes the SPA actually knows how to render. Must mirror the
|
|
39
28
|
// <Routes> registry in src/interfaces/web/src/App.tsx. Anything else still
|
|
40
29
|
// gets the SPA shell (so React Router shows the styled 404 page) but with an
|
|
@@ -51,15 +40,18 @@ export function isKnownSpaRoute(p) {
|
|
|
51
40
|
return SPA_ROUTES.some((re) => re.test(p));
|
|
52
41
|
}
|
|
53
42
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
43
|
+
// /api/admin/web-token: localhost-only endpoint that returns the daemon token
|
|
44
|
+
// so the same-origin admin panel can authenticate every subsequent call.
|
|
45
|
+
// Refuses if the request didn't come from loopback. Also refuses if the
|
|
46
|
+
// request was tunneled in via Cloudflare/ngrok/etc. — those connect from
|
|
47
|
+
// a local agent so the socket IP IS loopback, but tunnel-specific headers
|
|
48
|
+
// give them away. When tunneled, the SPA must instead receive the token
|
|
49
|
+
// via URL fragment (#token=…) that the operator shares out-of-band.
|
|
50
|
+
//
|
|
51
|
+
// Registered on the API router (not the static mount below) so the panel's
|
|
52
|
+
// bootstrap call is a normal /api/* request like every other.
|
|
53
|
+
export function registerWebToken(api, { token }) {
|
|
54
|
+
api.get("/admin/web-token", (req, res) => {
|
|
63
55
|
const ra = req.ip || req.socket?.remoteAddress || "";
|
|
64
56
|
const isLocal =
|
|
65
57
|
ra === "127.0.0.1" ||
|
|
@@ -86,7 +78,11 @@ export function register(app, { express, token }) {
|
|
|
86
78
|
}
|
|
87
79
|
res.json({ token });
|
|
88
80
|
});
|
|
81
|
+
}
|
|
89
82
|
|
|
83
|
+
// Static admin panel at the root. Mounted after `app.use("/api", api)` in
|
|
84
|
+
// api.js, so a stray /api/* request can never reach the SPA fallback.
|
|
85
|
+
export function register(app, { express }) {
|
|
90
86
|
if (!fs.existsSync(WEB_DIST)) {
|
|
91
87
|
// No build present. Expose a hint endpoint so users hitting the daemon
|
|
92
88
|
// root know what to do.
|
|
@@ -112,8 +108,8 @@ export function register(app, { express, token }) {
|
|
|
112
108
|
return;
|
|
113
109
|
}
|
|
114
110
|
|
|
115
|
-
// Serve static assets. NB: we mount AFTER the
|
|
116
|
-
//
|
|
111
|
+
// Serve static assets. NB: we mount AFTER the /api router is mounted in
|
|
112
|
+
// api.js so data routes always win over this catch-all.
|
|
117
113
|
app.use(
|
|
118
114
|
express.static(WEB_DIST, {
|
|
119
115
|
index: false,
|