@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
package/src/host/daemon/api.js
CHANGED
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
// Express REST API for APX. See APC docs reference/apx-daemon.
|
|
2
2
|
//
|
|
3
3
|
// Routes are split by domain under ./api/*.js — each module exports
|
|
4
|
-
// `register(
|
|
4
|
+
// `register(router, ctx)`. This file is purely orchestration: middlewares,
|
|
5
5
|
// context construction, mount order, 404 catch-all.
|
|
6
|
+
//
|
|
7
|
+
// EVERY data route lives under /api. Domain modules still declare their paths
|
|
8
|
+
// root-relative (`/projects/:pid/tasks`); they register on an express.Router()
|
|
9
|
+
// that is mounted at API_PREFIX, so the effective URL is /api/projects/:pid/
|
|
10
|
+
// tasks. The root namespace belongs exclusively to the admin panel (static
|
|
11
|
+
// bundle + client-side routes), which is why the "is this an API path?" test
|
|
12
|
+
// is now a single startsWith instead of a hand-maintained prefix list.
|
|
6
13
|
import express from "express";
|
|
7
14
|
|
|
15
|
+
import { API_PREFIX } from "./api/prefix.js";
|
|
16
|
+
import { logError } from "#core/logging.js";
|
|
17
|
+
|
|
8
18
|
import {
|
|
9
19
|
traceIdMiddleware,
|
|
10
20
|
buildAuthMiddleware,
|
|
11
21
|
makeProjectResolver,
|
|
12
22
|
makeTopProjectResolver,
|
|
23
|
+
errorMiddleware,
|
|
13
24
|
} from "./api/shared.js";
|
|
14
25
|
|
|
15
26
|
import { register as registerTools } from "./api/tools.js";
|
|
@@ -35,6 +46,7 @@ import { register as registerRoutines } from "./api/routines.js";
|
|
|
35
46
|
import { register as registerArtifacts } from "./api/artifacts.js";
|
|
36
47
|
import { register as registerArtifactPreview } from "./api/artifact-preview.js";
|
|
37
48
|
import { register as registerTasks } from "./api/tasks.js";
|
|
49
|
+
import { register as registerCommitments } from "./api/commitments.js";
|
|
38
50
|
import { register as registerOrganization } from "./api/organization.js";
|
|
39
51
|
import { register as registerProjectFiles } from "./api/files-project.js";
|
|
40
52
|
import { register as registerConfig } from "./api/config.js";
|
|
@@ -53,7 +65,9 @@ import { register as registerAdminConfig } from "./api/admin-config.js";
|
|
|
53
65
|
import { register as registerIdentity } from "./api/identity.js";
|
|
54
66
|
import { register as registerProfiles } from "./api/profiles.js";
|
|
55
67
|
import { register as registerInbox } from "./api/inbox.js";
|
|
56
|
-
import { register as
|
|
68
|
+
import { register as registerSelfMemory } from "./api/self-memory.js";
|
|
69
|
+
import { register as registerNudges } from "./api/nudges.js";
|
|
70
|
+
import { register as registerWeb, registerWebToken } from "./api/web.js";
|
|
57
71
|
import { register as registerConfirm } from "./api/confirm.js";
|
|
58
72
|
|
|
59
73
|
export function buildApi({
|
|
@@ -70,6 +84,9 @@ export function buildApi({
|
|
|
70
84
|
}) {
|
|
71
85
|
const telegram = plugins?.get("telegram");
|
|
72
86
|
const app = express();
|
|
87
|
+
// Every domain module registers on this router; it is mounted at /api at the
|
|
88
|
+
// bottom of this function. Modules keep declaring root-relative paths.
|
|
89
|
+
const api = express.Router();
|
|
73
90
|
|
|
74
91
|
// ---- Global middleware -------------------------------------------
|
|
75
92
|
app.use(express.json({ limit: "2mb" }));
|
|
@@ -103,62 +120,74 @@ export function buildApi({
|
|
|
103
120
|
};
|
|
104
121
|
|
|
105
122
|
// ---- Tool routers — must mount BEFORE wildcard registry below ----
|
|
106
|
-
registerTools(
|
|
123
|
+
registerTools(api, ctx);
|
|
107
124
|
|
|
108
125
|
// ---- Health (unauthenticated) ------------------------------------
|
|
109
|
-
registerHealth(
|
|
126
|
+
registerHealth(api, ctx);
|
|
110
127
|
|
|
111
128
|
// ---- Projects + per-project surfaces -----------------------------
|
|
112
|
-
registerProjects(
|
|
113
|
-
registerAgents(
|
|
114
|
-
registerSessions(
|
|
115
|
-
registerMcps(
|
|
116
|
-
registerIntegrations(
|
|
117
|
-
registerVars(
|
|
118
|
-
registerMessages(
|
|
119
|
-
registerEngines(
|
|
120
|
-
registerSkills(
|
|
121
|
-
registerExec(
|
|
122
|
-
registerSuperAgent(
|
|
123
|
-
registerConfirm(
|
|
124
|
-
registerCode(
|
|
125
|
-
registerConversations(
|
|
126
|
-
registerConnections(
|
|
127
|
-
registerRuntimes(
|
|
128
|
-
registerRoutines(
|
|
129
|
-
registerArtifacts(
|
|
130
|
-
registerArtifactPreview(
|
|
131
|
-
registerTasks(
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
129
|
+
registerProjects(api, ctx);
|
|
130
|
+
registerAgents(api, ctx);
|
|
131
|
+
registerSessions(api, ctx);
|
|
132
|
+
registerMcps(api, ctx);
|
|
133
|
+
registerIntegrations(api, ctx);
|
|
134
|
+
registerVars(api, ctx);
|
|
135
|
+
registerMessages(api, ctx);
|
|
136
|
+
registerEngines(api, ctx);
|
|
137
|
+
registerSkills(api, ctx);
|
|
138
|
+
registerExec(api, ctx);
|
|
139
|
+
registerSuperAgent(api, ctx);
|
|
140
|
+
registerConfirm(api, ctx);
|
|
141
|
+
registerCode(api, ctx);
|
|
142
|
+
registerConversations(api, ctx);
|
|
143
|
+
registerConnections(api, ctx);
|
|
144
|
+
registerRuntimes(api, ctx);
|
|
145
|
+
registerRoutines(api, ctx);
|
|
146
|
+
registerArtifacts(api, ctx);
|
|
147
|
+
registerArtifactPreview(api, ctx);
|
|
148
|
+
registerTasks(api, ctx);
|
|
149
|
+
registerCommitments(api, ctx);
|
|
150
|
+
registerOrganization(api, ctx);
|
|
151
|
+
registerProjectFiles(api, ctx);
|
|
152
|
+
registerConfig(api, ctx);
|
|
135
153
|
|
|
136
154
|
// ---- Top-level shortcuts (MCP server clients) --------------------
|
|
137
|
-
registerRun(
|
|
138
|
-
registerTopLevel(
|
|
139
|
-
registerSessionsSearch(
|
|
155
|
+
registerRun(api, ctx);
|
|
156
|
+
registerTopLevel(api, ctx);
|
|
157
|
+
registerSessionsSearch(api, ctx);
|
|
140
158
|
|
|
141
159
|
// ---- Channels & plugin surfaces ----------------------------------
|
|
142
|
-
registerTelegram(
|
|
143
|
-
registerPlugins(
|
|
144
|
-
registerTranscribe(
|
|
145
|
-
registerTts(
|
|
146
|
-
registerEmbeddings(
|
|
147
|
-
registerVoice(
|
|
148
|
-
registerDesktop(
|
|
149
|
-
registerDeck(
|
|
150
|
-
registerPairing(
|
|
160
|
+
registerTelegram(api, ctx);
|
|
161
|
+
registerPlugins(api, ctx);
|
|
162
|
+
registerTranscribe(api, ctx);
|
|
163
|
+
registerTts(api, ctx);
|
|
164
|
+
registerEmbeddings(api, ctx);
|
|
165
|
+
registerVoice(api, ctx);
|
|
166
|
+
registerDesktop(api, ctx);
|
|
167
|
+
registerDeck(api, ctx);
|
|
168
|
+
registerPairing(api, ctx);
|
|
151
169
|
|
|
152
170
|
// ---- Admin -------------------------------------------------------
|
|
153
|
-
registerAdmin(
|
|
154
|
-
registerAdminConfig(
|
|
155
|
-
registerIdentity(
|
|
156
|
-
registerProfiles(
|
|
157
|
-
registerInbox(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
171
|
+
registerAdmin(api, ctx);
|
|
172
|
+
registerAdminConfig(api, ctx);
|
|
173
|
+
registerIdentity(api, ctx);
|
|
174
|
+
registerProfiles(api, ctx);
|
|
175
|
+
registerInbox(api, ctx);
|
|
176
|
+
registerSelfMemory(api, ctx);
|
|
177
|
+
registerNudges(api, ctx);
|
|
178
|
+
registerWebToken(api, ctx);
|
|
179
|
+
|
|
180
|
+
// ---- API 404 (MUST be last on the router) ------------------------
|
|
181
|
+
// Terminal for the /api namespace: an unknown /api/* path answers JSON here
|
|
182
|
+
// instead of falling through to the SPA shell below.
|
|
183
|
+
api.use((req, res) =>
|
|
184
|
+
res.status(404).json({ error: `no route ${req.method} ${req.baseUrl}${req.path}` })
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
app.use(API_PREFIX, api);
|
|
188
|
+
|
|
189
|
+
// ---- Web admin panel (static SPA, must mount after /api) ---------
|
|
190
|
+
// Serves src/interfaces/web/dist when present. No-op until the panel is built.
|
|
162
191
|
registerWeb(app, ctx);
|
|
163
192
|
|
|
164
193
|
// ---- 404 catch-all (MUST be last) --------------------------------
|
|
@@ -166,5 +195,11 @@ export function buildApi({
|
|
|
166
195
|
res.status(404).json({ error: `no route ${req.method} ${req.path}` })
|
|
167
196
|
);
|
|
168
197
|
|
|
198
|
+
// ---- Error handler (MUST be after everything) --------------------
|
|
199
|
+
// Express only recognises the 4-argument shape, and only when it is mounted
|
|
200
|
+
// last. Anything an asyncRoute() rejects lands here as JSON instead of an
|
|
201
|
+
// unhandled rejection that kills the daemon.
|
|
202
|
+
app.use(errorMiddleware(logError));
|
|
203
|
+
|
|
169
204
|
return app;
|
|
170
205
|
}
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
// session close captured remains). The rich A2A relay (Roby re-voicing the
|
|
16
16
|
// result) stays the job of the live in-process path.
|
|
17
17
|
import { listPendingCallbacks, deletePendingCallback, readSessionState } from "#core/stores/runtime-callbacks.js";
|
|
18
|
+
import { readConfig } from "#core/config/index.js";
|
|
19
|
+
import { canNudge, recordNudge } from "#core/nudge/index.js";
|
|
18
20
|
|
|
19
21
|
const GRACE_MS = 30_000; // let the live in-process path win a fresh completion
|
|
20
22
|
const STALE_MS = 24 * 60 * 60 * 1000; // drop IOUs for runs that never finished in a day
|
|
@@ -62,11 +64,25 @@ export async function reconcilePendingCallbacks({ plugins, log }) {
|
|
|
62
64
|
if (Number.isFinite(compAge) && compAge < GRACE_MS) continue;
|
|
63
65
|
|
|
64
66
|
if (!telegram) continue; // telegram plugin not up this boot — retry next tick
|
|
67
|
+
|
|
68
|
+
// PUSH PATH 4 OF 4. Declared SOLICITED, and that is a judgement worth
|
|
69
|
+
// stating: the user launched this runtime and is waiting for its result.
|
|
70
|
+
// Arriving late does not make it an interruption, and holding it back
|
|
71
|
+
// for quiet hours would mean losing an answer they asked for. It still
|
|
72
|
+
// passes through the gate so the audit is real and so a future policy
|
|
73
|
+
// can reach it without hunting for a fifth path nobody remembered.
|
|
74
|
+
const gate = canNudge(
|
|
75
|
+
{ kind: "session_result", severity: "normal", unsolicited: false, channel: "telegram" },
|
|
76
|
+
readConfig(),
|
|
77
|
+
);
|
|
78
|
+
if (!gate.allowed) continue; // never today; retry next tick if it ever is
|
|
79
|
+
|
|
65
80
|
await telegram.send({
|
|
66
81
|
channel: entry.tg_channel || undefined,
|
|
67
82
|
chat_id: entry.chat_id,
|
|
68
83
|
text: deliverText(entry, state),
|
|
69
84
|
});
|
|
85
|
+
recordNudge(gate, { chat_id: entry.chat_id });
|
|
70
86
|
deletePendingCallback(entry.session_id);
|
|
71
87
|
log?.(`callback-reconciler: delivered late callback for ${entry.session_id} → chat ${entry.chat_id}`);
|
|
72
88
|
} catch (e) {
|
package/src/host/daemon/db.js
CHANGED
|
@@ -5,7 +5,7 @@ import path from "node:path";
|
|
|
5
5
|
import { appendMessageToFs } from "#core/stores/messages.js";
|
|
6
6
|
import { effectiveConfig } from "./project-config.js";
|
|
7
7
|
import { readAgents } from "#core/apc/parser.js";
|
|
8
|
-
import {
|
|
8
|
+
import { apcProjectFile, apcAgentsDir, apcCommandsDir } from "#core/apc/paths.js";
|
|
9
9
|
import { getOrCreateApxId } from "#core/apc/scaffold.js";
|
|
10
10
|
import {
|
|
11
11
|
ensureProjectStorage,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Singleton WebSocket hub for the desktop channel.
|
|
2
2
|
// Imported by api.js (to register connections) and by plugins/desktop/index.js (to broadcast).
|
|
3
|
+
import { apiPath } from "./api/prefix.js";
|
|
3
4
|
|
|
4
5
|
const _clients = new Set(); // Set<WebSocket>
|
|
5
6
|
let _messageHandler = null; // (ws, data) => void — set by desktop plugin
|
|
@@ -12,7 +13,7 @@ export function setDesktopMessageHandler(fn) {
|
|
|
12
13
|
|
|
13
14
|
// --- WS upgrade auth helpers (shared by the daemon upgrade handler + tests) ---
|
|
14
15
|
//
|
|
15
|
-
// The desktop WS channel must authenticate the same way the HTTP /desktop/*
|
|
16
|
+
// The desktop WS channel must authenticate the same way the HTTP /api/desktop/*
|
|
16
17
|
// routes do: a bearer token (master or paired client) carried on the upgrade
|
|
17
18
|
// request. The legitimate desktop window sends `Authorization: Bearer <token>`
|
|
18
19
|
// (src/interfaces/desktop/main.js); browser clients can pass `?token=`. Without
|
|
@@ -20,11 +21,14 @@ export function setDesktopMessageHandler(fn) {
|
|
|
20
21
|
// when host is set to 0.0.0.0) could open the channel and drive the
|
|
21
22
|
// super-agent. See QA BUG-WS-AUTH.
|
|
22
23
|
|
|
23
|
-
/**
|
|
24
|
+
/** The desktop channel's upgrade path. Lives under /api like every other route. */
|
|
25
|
+
export const DESKTOP_WS_PATH = apiPath("/desktop/ws");
|
|
26
|
+
|
|
27
|
+
/** Path-gate: is this upgrade for the desktop WS channel? */
|
|
24
28
|
export function isDesktopUpgradePath(url) {
|
|
25
29
|
let pathname = url || "";
|
|
26
30
|
try { pathname = new URL(url, "http://localhost").pathname; } catch { /* keep raw */ }
|
|
27
|
-
return pathname ===
|
|
31
|
+
return pathname === DESKTOP_WS_PATH;
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
/** Extract the bearer token from the upgrade request (header first, ?token= fallback). */
|
package/src/host/daemon/index.js
CHANGED
|
@@ -12,12 +12,10 @@ import { randomBytes } from "node:crypto";
|
|
|
12
12
|
const LOOPBACK_HOST = "127.0.0.1";
|
|
13
13
|
import {
|
|
14
14
|
readConfig,
|
|
15
|
-
writeConfig,
|
|
16
15
|
effectiveHost,
|
|
17
16
|
effectivePort,
|
|
18
17
|
addProject as addProjectInConfig,
|
|
19
18
|
PID_PATH,
|
|
20
|
-
LOG_PATH,
|
|
21
19
|
APX_HOME,
|
|
22
20
|
TOKEN_PATH,
|
|
23
21
|
} from "#core/config/index.js";
|
|
@@ -297,12 +295,11 @@ async function main() {
|
|
|
297
295
|
}).catch(() => {});
|
|
298
296
|
});
|
|
299
297
|
|
|
300
|
-
// Attach WebSocket upgrade for the desktop channel on /desktop/ws
|
|
301
|
-
// (legacy /overlay/ws still accepted for one release).
|
|
298
|
+
// Attach WebSocket upgrade for the desktop channel on /api/desktop/ws.
|
|
302
299
|
server.on("upgrade", async (req, socket, head) => {
|
|
303
300
|
if (!isDesktopUpgradePath(req.url)) { socket.destroy(); return; }
|
|
304
301
|
// Auth: the WS upgrade must carry a valid token (master or paired client),
|
|
305
|
-
// matching the HTTP /desktop/* routes. Without this, any client that can
|
|
302
|
+
// matching the HTTP /api/desktop/* routes. Without this, any client that can
|
|
306
303
|
// reach the daemon (host binds 0.0.0.0 → the LAN) could open the desktop
|
|
307
304
|
// channel and drive the super-agent (permission_mode "total"). The
|
|
308
305
|
// legitimate desktop window already sends the bearer token. See QA BUG-WS-AUTH.
|
|
@@ -358,6 +355,15 @@ async function main() {
|
|
|
358
355
|
process.on("uncaughtException", (e) => {
|
|
359
356
|
log(`uncaughtException: ${e.stack || e.message}`);
|
|
360
357
|
});
|
|
358
|
+
// Node >= 15 terminates the process on an unhandled rejection. The CLI has
|
|
359
|
+
// always registered this; the daemon had not, so one un-awaited failure in a
|
|
360
|
+
// route or a plugin took down the process serving the SPA, Telegram polling,
|
|
361
|
+
// voice and the deck at once. Log and keep running — the request-level
|
|
362
|
+
// errorMiddleware already answers the caller.
|
|
363
|
+
process.on("unhandledRejection", (reason) => {
|
|
364
|
+
const e = reason instanceof Error ? reason : new Error(String(reason));
|
|
365
|
+
log(`unhandledRejection: ${e.stack || e.message}`);
|
|
366
|
+
});
|
|
361
367
|
}
|
|
362
368
|
|
|
363
369
|
main().catch((e) => {
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
import { runSuperAgent, isSuperAgentEnabled } from "#core/agent/super-agent.js";
|
|
26
26
|
import { appendGlobalMessage } from "#core/stores/messages.js";
|
|
27
27
|
import { stripEmotionTags } from "#core/voice/emotions.js";
|
|
28
|
+
import { stripReasoning } from "#core/util/thinking.js";
|
|
28
29
|
import { CHANNELS } from "#core/constants/channels.js";
|
|
29
30
|
import { tryResolveSkillCommand } from "#core/agent/skills/trigger.js";
|
|
30
31
|
|
|
@@ -103,7 +104,7 @@ async function _handleMessage({ ws, text, previousMessages }, { projects, config
|
|
|
103
104
|
await appendGlobalMessage({ channel: CHANNEL, direction: "in", type: "user", author: "user", body: text });
|
|
104
105
|
} catch {}
|
|
105
106
|
|
|
106
|
-
|
|
107
|
+
const toolsExecuted = [];
|
|
107
108
|
|
|
108
109
|
// Per-segment streaming: instead of merging the whole turn into one blob, we
|
|
109
110
|
// emit each assistant text piece as its own `segment` (an intro before a tool,
|
|
@@ -123,7 +124,12 @@ async function _handleMessage({ ws, text, previousMessages }, { projects, config
|
|
|
123
124
|
emittedSegments.push(seg);
|
|
124
125
|
// `text` is what the bubble shows (no [tags]); `speak` keeps the inline
|
|
125
126
|
// emotion tags so the renderer's per-segment TTS can use them.
|
|
126
|
-
|
|
127
|
+
// Desktop is voice-first: an unstripped reasoning dump would be spoken
|
|
128
|
+
// aloud, at length, in the wrong language. Drop the segment entirely
|
|
129
|
+
// rather than narrate the model's notes.
|
|
130
|
+
const spoken = stripReasoning(seg).answer;
|
|
131
|
+
if (!spoken.trim()) return;
|
|
132
|
+
_send(ws, { type: "segment", seq: ++segSeq, text: stripEmotionTags(spoken), speak: spoken });
|
|
127
133
|
};
|
|
128
134
|
|
|
129
135
|
try {
|
|
@@ -311,9 +311,13 @@ export default {
|
|
|
311
311
|
// that bot; otherwise first available bot-tokened channel. Always logs
|
|
312
312
|
// the outbound on `messages` of the channel's target project so audit
|
|
313
313
|
// trails are complete.
|
|
314
|
-
|
|
314
|
+
// `reply_markup` carries the interruption-budget feedback keyboard on
|
|
315
|
+
// proactive pushes (core/nudge). It is NOT a gate: the gate belongs at
|
|
316
|
+
// the call sites that decide to speak unprompted, because this method
|
|
317
|
+
// also carries traffic the user asked for.
|
|
318
|
+
async send({ channel: channelName, chat_id, text, reply_markup, author = resolveAgentName(config), meta: extraMeta }) {
|
|
315
319
|
const p = pickPoller(pollers, channelName);
|
|
316
|
-
const result = await p._send({ chat_id, text });
|
|
320
|
+
const result = await p._send({ chat_id, text, reply_markup });
|
|
317
321
|
appendGlobalMessage({
|
|
318
322
|
channel: CHANNELS.TELEGRAM,
|
|
319
323
|
direction: "out",
|
|
@@ -327,6 +331,7 @@ export default {
|
|
|
327
331
|
chat_id: chat_id || resolveChatId(p.channel),
|
|
328
332
|
tg_channel: p.channel.name,
|
|
329
333
|
via: channelName ? "explicit" : "auto",
|
|
334
|
+
...(extraMeta || {}),
|
|
330
335
|
},
|
|
331
336
|
});
|
|
332
337
|
return result;
|
|
@@ -1,27 +1,33 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Which Python runs whisper-server.py.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
3
|
+
// APX owns a dedicated virtualenv at ~/.apx/runtime/whisper-venv rather than
|
|
4
|
+
// installing into the system or user-site Python: mlx-whisper drags in
|
|
4
5
|
// torch/scipy/numba and would pollute (or clash with) the user's other
|
|
5
|
-
// projects.
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
|
|
6
|
+
// projects.
|
|
7
|
+
//
|
|
8
|
+
// This module used to also create that venv and pip-install into it
|
|
9
|
+
// (ensureVenv, pipInstall, venvHasModule, removeVenv, ENGINE_PACKAGES). None of
|
|
10
|
+
// it was ever called — the installer path was built and then superseded — so it
|
|
11
|
+
// was ~120 lines of dead code sitting next to a live one-liner. Removed. If
|
|
12
|
+
// venv provisioning comes back, it belongs in its own module with a caller.
|
|
9
13
|
import path from "node:path";
|
|
10
14
|
import fs from "node:fs";
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
export const VENV_DIR = path.join(os.homedir(), ".apx", "runtime", "whisper-venv");
|
|
15
|
+
import { WHISPER_VENV_DIR } from "#core/config/paths.js";
|
|
14
16
|
|
|
15
17
|
/** Path to the venv's python (…/bin/python on POSIX, …/Scripts on Windows). */
|
|
16
|
-
|
|
18
|
+
function venvPython() {
|
|
17
19
|
return process.platform === "win32"
|
|
18
|
-
? path.join(
|
|
19
|
-
: path.join(
|
|
20
|
+
? path.join(WHISPER_VENV_DIR, "Scripts", "python.exe")
|
|
21
|
+
: path.join(WHISPER_VENV_DIR, "bin", "python");
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
/** True once the venv has a usable interpreter. */
|
|
23
|
-
|
|
24
|
-
try {
|
|
25
|
+
function venvExists() {
|
|
26
|
+
try {
|
|
27
|
+
return fs.existsSync(venvPython());
|
|
28
|
+
} catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
25
31
|
}
|
|
26
32
|
|
|
27
33
|
/**
|
|
@@ -31,70 +37,3 @@ export function venvExists() {
|
|
|
31
37
|
export function pythonForWhisper() {
|
|
32
38
|
return venvExists() ? venvPython() : "python3";
|
|
33
39
|
}
|
|
34
|
-
|
|
35
|
-
function run(cmd, args, onLine) {
|
|
36
|
-
return new Promise((resolve) => {
|
|
37
|
-
let proc;
|
|
38
|
-
try {
|
|
39
|
-
proc = spawn(cmd, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
40
|
-
} catch (e) {
|
|
41
|
-
onLine?.(`spawn failed: ${e.message}`);
|
|
42
|
-
return resolve({ ok: false, code: -1 });
|
|
43
|
-
}
|
|
44
|
-
const pump = (chunk) => {
|
|
45
|
-
for (const line of chunk.toString().split(/\r?\n/)) {
|
|
46
|
-
if (line.trim()) onLine?.(line);
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
proc.stdout.on("data", pump);
|
|
50
|
-
proc.stderr.on("data", pump);
|
|
51
|
-
proc.on("exit", (code) => resolve({ ok: code === 0, code }));
|
|
52
|
-
proc.on("error", (e) => { onLine?.(e.message); resolve({ ok: false, code: -1 }); });
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/** Create the venv (idempotent). Streams progress lines to onLine. */
|
|
57
|
-
export async function ensureVenv(onLine) {
|
|
58
|
-
if (venvExists()) return { ok: true, created: false };
|
|
59
|
-
fs.mkdirSync(path.dirname(VENV_DIR), { recursive: true });
|
|
60
|
-
onLine?.(`creating venv at ${VENV_DIR}…`);
|
|
61
|
-
const r = await run("python3", ["-m", "venv", VENV_DIR], onLine);
|
|
62
|
-
if (!r.ok || !venvExists()) return { ok: false, error: "venv creation failed" };
|
|
63
|
-
// Upgrade pip once so wheel resolution is fast/modern.
|
|
64
|
-
await run(venvPython(), ["-m", "pip", "install", "--upgrade", "pip", "wheel"], onLine);
|
|
65
|
-
return { ok: true, created: true };
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/** Is a module importable inside the venv? */
|
|
69
|
-
export function venvHasModule(mod) {
|
|
70
|
-
return new Promise((resolve) => {
|
|
71
|
-
if (!venvExists()) return resolve(false);
|
|
72
|
-
const p = spawn(venvPython(), ["-c", `import ${mod}`], { stdio: "ignore" });
|
|
73
|
-
p.on("exit", (code) => resolve(code === 0));
|
|
74
|
-
p.on("error", () => resolve(false));
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/** pip install <pkgs> into the venv, streaming progress. Creates the venv first. */
|
|
79
|
-
export async function pipInstall(pkgs, onLine) {
|
|
80
|
-
const ensured = await ensureVenv(onLine);
|
|
81
|
-
if (!ensured.ok) return ensured;
|
|
82
|
-
const list = Array.isArray(pkgs) ? pkgs : [pkgs];
|
|
83
|
-
onLine?.(`installing ${list.join(", ")}…`);
|
|
84
|
-
const r = await run(venvPython(), ["-m", "pip", "install", "--upgrade", ...list], onLine);
|
|
85
|
-
return { ok: r.ok, code: r.code };
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/** Delete the whole venv (so the engine can be reinstalled clean). */
|
|
89
|
-
export async function removeVenv(onLine) {
|
|
90
|
-
if (!fs.existsSync(VENV_DIR)) return { ok: true, removed: false };
|
|
91
|
-
onLine?.(`removing ${VENV_DIR}…`);
|
|
92
|
-
await fs.promises.rm(VENV_DIR, { recursive: true, force: true });
|
|
93
|
-
return { ok: !fs.existsSync(VENV_DIR), removed: true };
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/** Engine → the pip package(s) it needs in the venv. */
|
|
97
|
-
export const ENGINE_PACKAGES = {
|
|
98
|
-
faster: ["faster-whisper"],
|
|
99
|
-
mlx: ["mlx-whisper"],
|
|
100
|
-
};
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import fetch from "node-fetch";
|
|
3
3
|
import { readIdentity, writeIdentity } from "#core/identity/index.js";
|
|
4
4
|
import { resolveProvider, getAdapter } from "#core/engines/index.js";
|
|
5
|
+
import { canNudge, recordNudge, nudgeFeedbackKeyboard } from "#core/nudge/index.js";
|
|
5
6
|
|
|
6
7
|
const WAKEUP_COOLDOWN_MS = 30 * 60 * 1000; // 30 min
|
|
7
8
|
|
|
@@ -18,7 +19,7 @@ export function detectLanguage(identity, config) {
|
|
|
18
19
|
if (cfgLang) return ISO_TO_LANGUAGE[cfgLang.toLowerCase()] || cfgLang;
|
|
19
20
|
if (identity?.language) return identity.language;
|
|
20
21
|
const lang = process.env.LANG || process.env.LC_MESSAGES || process.env.LC_ALL || "";
|
|
21
|
-
const code = lang.split(/[_
|
|
22
|
+
const code = lang.split(/[_.]/)[0].toLowerCase();
|
|
22
23
|
return ISO_TO_LANGUAGE[code] || "English";
|
|
23
24
|
}
|
|
24
25
|
|
|
@@ -50,12 +51,12 @@ async function generateMessage(identity, engineConfig) {
|
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
async function sendTelegram(token, chatId, text) {
|
|
54
|
+
async function sendTelegram(token, chatId, text, reply_markup) {
|
|
54
55
|
const url = `https://api.telegram.org/bot${token}/sendMessage`;
|
|
55
56
|
const res = await fetch(url, {
|
|
56
57
|
method: "POST",
|
|
57
58
|
headers: { "Content-Type": "application/json" },
|
|
58
|
-
body: JSON.stringify({ chat_id: chatId, text }),
|
|
59
|
+
body: JSON.stringify({ chat_id: chatId, text, ...(reply_markup ? { reply_markup } : {}) }),
|
|
59
60
|
});
|
|
60
61
|
const json = await res.json();
|
|
61
62
|
if (!json.ok) throw new Error(json.description || "telegram send failed");
|
|
@@ -75,10 +76,23 @@ export async function triggerWakeup(config, log) {
|
|
|
75
76
|
if (elapsed < WAKEUP_COOLDOWN_MS) return;
|
|
76
77
|
}
|
|
77
78
|
|
|
79
|
+
// PUSH PATH 1 OF 4 — nobody asked for this one. "I restarted" is the least
|
|
80
|
+
// interesting thing APX can say, so it is also the first thing a budget
|
|
81
|
+
// should be allowed to swallow.
|
|
82
|
+
const gate = canNudge(
|
|
83
|
+
{ kind: "wakeup", severity: "low", unsolicited: true, channel: "telegram" },
|
|
84
|
+
config,
|
|
85
|
+
);
|
|
86
|
+
if (!gate.allowed) {
|
|
87
|
+
log?.(`wakeup: suppressed by the interruption budget — ${gate.reason}`);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
78
91
|
try {
|
|
79
92
|
const message = await generateMessage(identity, config);
|
|
80
93
|
const text = message || `${identity.agent_name} online. Ready.`;
|
|
81
|
-
await sendTelegram(tg.bot_token, tg.chat_id, text);
|
|
94
|
+
await sendTelegram(tg.bot_token, tg.chat_id, text, nudgeFeedbackKeyboard(gate.nudge_id));
|
|
95
|
+
recordNudge(gate, { chat_id: tg.chat_id, preview: text });
|
|
82
96
|
writeIdentity({ last_wakeup: new Date().toISOString() });
|
|
83
97
|
log?.(`wakeup: sent to Telegram chat ${tg.chat_id}`);
|
|
84
98
|
} catch (e) {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
//
|
|
7
7
|
// Like the mcp-server surface, this is a thin adapter over the daemon HTTP
|
|
8
8
|
// API: each `session/prompt` becomes a POST to the NDJSON stream endpoint
|
|
9
|
-
// `/projects/:pid/super-agent/chat/stream` and the stream events are mapped
|
|
9
|
+
// `/api/projects/:pid/super-agent/chat/stream` and the stream events are mapped
|
|
10
10
|
// onto ACP `session/update` notifications:
|
|
11
11
|
//
|
|
12
12
|
// assistant_text → agent_message_chunk
|
|
@@ -179,7 +179,7 @@ export class AcpAgentServer {
|
|
|
179
179
|
session.activeTurn = turn;
|
|
180
180
|
try {
|
|
181
181
|
const final = await this.client.streamPost(
|
|
182
|
-
`/projects/${session.project.id}/super-agent/chat/stream`,
|
|
182
|
+
`/api/projects/${session.project.id}/super-agent/chat/stream`,
|
|
183
183
|
{
|
|
184
184
|
prompt: promptText,
|
|
185
185
|
// ACP clients are coding surfaces (IDEs) — the `code` channel gives
|
|
@@ -314,7 +314,7 @@ export class AcpAgentServer {
|
|
|
314
314
|
}
|
|
315
315
|
try {
|
|
316
316
|
await this.client.post(
|
|
317
|
-
`/super-agent/confirm/${encodeURIComponent(event.correlationId)}`,
|
|
317
|
+
`/api/super-agent/confirm/${encodeURIComponent(event.correlationId)}`,
|
|
318
318
|
{ confirmed }
|
|
319
319
|
);
|
|
320
320
|
} catch (e) {
|
|
@@ -129,12 +129,12 @@ export async function resolveProjectForCwd(client, cwd) {
|
|
|
129
129
|
`No APC project found at or above: ${cwd}. Run \`apx init\` in the workspace first.`
|
|
130
130
|
);
|
|
131
131
|
}
|
|
132
|
-
const projects = await client.get("/projects");
|
|
132
|
+
const projects = await client.get("/api/projects");
|
|
133
133
|
const match = (projects || []).find(
|
|
134
134
|
(p) => path.resolve(p.path) === path.resolve(root)
|
|
135
135
|
);
|
|
136
136
|
if (match) return match;
|
|
137
|
-
return client.post("/projects", { path: root });
|
|
137
|
+
return client.post("/api/projects", { path: root });
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
/** Flatten an ACP prompt (ContentBlock[]) into the text the daemon expects. */
|
|
@@ -26,7 +26,7 @@ export async function cmdSend(args) {
|
|
|
26
26
|
if (!body) throw new Error("apx send: body is empty");
|
|
27
27
|
|
|
28
28
|
const pid = await resolveProjectId(args?.flags?.project);
|
|
29
|
-
const result = await http.post(`/projects/${pid}/send`, {
|
|
29
|
+
const result = await http.post(`/api/projects/${pid}/send`, {
|
|
30
30
|
from,
|
|
31
31
|
to,
|
|
32
32
|
body,
|
|
@@ -48,7 +48,7 @@ export async function cmdConnections(args) {
|
|
|
48
48
|
const slug = args._[0];
|
|
49
49
|
if (!slug) throw new Error("apx connections: missing <agent-slug>");
|
|
50
50
|
const pid = await resolveProjectId(args?.flags?.project);
|
|
51
|
-
const peers = await http.get(`/projects/${pid}/agents/${slug}/connections`);
|
|
51
|
+
const peers = await http.get(`/api/projects/${pid}/agents/${slug}/connections`);
|
|
52
52
|
if (peers.length === 0) {
|
|
53
53
|
console.log(`(no connections logged for ${slug} yet)`);
|
|
54
54
|
return;
|
|
@@ -24,9 +24,9 @@ function requireRoot() {
|
|
|
24
24
|
async function nudgeDaemon(root) {
|
|
25
25
|
try {
|
|
26
26
|
if (!(await http.ping())) return;
|
|
27
|
-
const projects = await http.get("/projects", { autoStart: false });
|
|
27
|
+
const projects = await http.get("/api/projects", { autoStart: false });
|
|
28
28
|
const me = projects.find((p) => p.path === root);
|
|
29
|
-
if (me) await http.post(`/projects/${me.id}/rebuild`, undefined, { autoStart: false });
|
|
29
|
+
if (me) await http.post(`/api/projects/${me.id}/rebuild`, undefined, { autoStart: false });
|
|
30
30
|
} catch { /* daemon hiccup */ }
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -116,7 +116,7 @@ export async function cmdAgentRemove(args) {
|
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
const pid = await resolveProjectId(args?.flags?.project);
|
|
119
|
-
await http.delete(`/projects/${pid}/agents/${slug}`);
|
|
119
|
+
await http.delete(`/api/projects/${pid}/agents/${slug}`);
|
|
120
120
|
console.log(`${tag("removed")} ${bold(slug)} ${gray("(agent file + runtime memory deleted)")}`);
|
|
121
121
|
}
|
|
122
122
|
|