@agentprojectcontext/apx 1.32.0 → 1.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (230) hide show
  1. package/package.json +6 -1
  2. package/skills/apc-context/SKILL.md +5 -2
  3. package/skills/apx/SKILL.md +3 -3
  4. package/skills/apx-agency-agents/SKILL.md +5 -5
  5. package/skills/apx-agent/SKILL.md +7 -7
  6. package/skills/apx-mcp/SKILL.md +6 -4
  7. package/skills/apx-mcp-builder/SKILL.md +4 -7
  8. package/skills/apx-project/SKILL.md +4 -5
  9. package/skills/apx-routine/SKILL.md +14 -12
  10. package/skills/apx-runtime/SKILL.md +5 -3
  11. package/skills/apx-sessions/SKILL.md +5 -5
  12. package/skills/apx-skill-builder/SKILL.md +10 -6
  13. package/skills/apx-task/SKILL.md +8 -8
  14. package/skills/apx-telegram/SKILL.md +23 -7
  15. package/skills/apx-voice/SKILL.md +8 -6
  16. package/src/core/{agent-system.js → agent/build-agent-system.js} +10 -12
  17. package/src/core/agent/index.js +0 -2
  18. package/src/core/{agent-memory.js → agent/memory.js} +2 -2
  19. package/src/core/agent/model-router.js +21 -43
  20. package/src/core/agent/prompt-builder.js +17 -63
  21. package/src/core/agent/prompts/action-discipline.md +24 -0
  22. package/src/core/agent/prompts/channels/code.md +8 -12
  23. package/src/core/agent/prompts/channels/desktop.md +6 -4
  24. package/src/core/agent/prompts/channels/routine.md +10 -1
  25. package/src/core/agent/prompts/channels/telegram.md +10 -1
  26. package/src/core/agent/prompts/channels/web_code.md +20 -0
  27. package/src/core/agent/prompts/modes/voice.md +2 -2
  28. package/src/core/agent/prompts/super-agent-base.md +2 -2
  29. package/src/core/agent/run-agent.js +37 -35
  30. package/src/core/agent/runtime-bridge.js +42 -0
  31. package/src/core/agent/self-memory.js +19 -9
  32. package/src/core/agent/skills/catalog.js +65 -0
  33. package/src/core/agent/skills/index.js +6 -0
  34. package/src/{host/daemon/skills-loader.js → core/agent/skills/loader.js} +3 -3
  35. package/src/core/agent/skills/rag.js +91 -0
  36. package/src/core/agent/skills/trigger.js +71 -0
  37. package/src/{host/daemon → core/agent}/super-agent.js +5 -5
  38. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/add-project.js +3 -4
  39. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/call-agent.js +2 -2
  40. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/call-mcp.js +1 -2
  41. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/call-runtime.js +10 -11
  42. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/create-task.js +1 -1
  43. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/discover-tools.js +1 -1
  44. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/edit-file.js +1 -2
  45. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/import-agent.js +4 -5
  46. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/list-agents.js +1 -1
  47. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/list-skills.js +7 -2
  48. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/list-tasks.js +1 -1
  49. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/list-vault-agents.js +1 -1
  50. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/load-skill.js +1 -1
  51. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/read-agent-memory.js +1 -1
  52. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/read-self-memory.js +1 -1
  53. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/remember.js +1 -1
  54. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/run-shell.js +1 -2
  55. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/search-messages.js +1 -1
  56. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/search-sessions.js +1 -1
  57. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/send-telegram.js +0 -2
  58. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/set-identity.js +1 -3
  59. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/set-permission-mode.js +1 -3
  60. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/tail-messages.js +1 -1
  61. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/transcribe-audio.js +1 -1
  62. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/write-file.js +1 -2
  63. package/src/core/agent/tools/helpers.js +74 -0
  64. package/src/{host/daemon/super-agent-tools → core/agent/tools}/registry-bridge.js +3 -3
  65. package/src/{host/daemon/super-agent-tools/index.js → core/agent/tools/registry.js} +31 -32
  66. package/src/core/apc/agents-vault.js +37 -0
  67. package/src/core/{scaffold.js → apc/scaffold.js} +4 -5
  68. package/src/core/{config.js → config/index.js} +21 -27
  69. package/src/core/config/paths.js +32 -0
  70. package/src/core/constants/actors.js +8 -0
  71. package/src/core/constants/channels.js +19 -0
  72. package/src/core/constants/index.js +5 -0
  73. package/src/core/constants/permissions.js +17 -0
  74. package/src/core/constants/roles.js +9 -0
  75. package/src/core/engines/_streaming.js +63 -0
  76. package/src/core/engines/anthropic.js +11 -22
  77. package/src/core/engines/ollama.js +7 -16
  78. package/src/core/identity/index.js +8 -0
  79. package/src/core/{identity.js → identity/self.js} +5 -5
  80. package/src/core/{telegram-identity.js → identity/telegram.js} +1 -1
  81. package/src/core/logging.js +1 -1
  82. package/src/core/mascot.js +1 -1
  83. package/src/core/memory/active-threads.js +10 -10
  84. package/src/core/memory/broker.js +9 -9
  85. package/src/core/memory/compactor.js +2 -2
  86. package/src/core/memory/index.js +2 -2
  87. package/src/core/memory/indexer.js +1 -1
  88. package/src/core/{code-sessions-store.js → stores/code-sessions.js} +7 -8
  89. package/src/core/{messages-store.js → stores/messages.js} +6 -4
  90. package/src/core/stores/routine-memory.js +71 -0
  91. package/src/core/{routines-store.js → stores/routines.js} +1 -3
  92. package/src/core/stores/runtime-sessions.js +99 -0
  93. package/src/core/{tasks-store.js → stores/tasks.js} +3 -8
  94. package/src/core/update-check.js +1 -1
  95. package/src/core/util/ids.js +14 -0
  96. package/src/core/util/index.js +2 -0
  97. package/src/core/util/time.js +9 -0
  98. package/src/core/voice/tts.js +1 -1
  99. package/src/host/daemon/api/admin-config.js +4 -3
  100. package/src/host/daemon/api/admin.js +1 -1
  101. package/src/host/daemon/api/agents.js +4 -25
  102. package/src/host/daemon/api/artifacts.js +26 -1
  103. package/src/host/daemon/api/code.js +62 -17
  104. package/src/host/daemon/api/confirm.js +1 -1
  105. package/src/host/daemon/api/connections.js +2 -2
  106. package/src/host/daemon/api/conversations.js +2 -2
  107. package/src/host/daemon/api/deck.js +1 -1
  108. package/src/host/daemon/api/desktop.js +1 -1
  109. package/src/host/daemon/api/embeddings.js +4 -4
  110. package/src/host/daemon/api/engines.js +2 -2
  111. package/src/host/daemon/api/exec.js +20 -5
  112. package/src/host/daemon/api/identity.js +1 -1
  113. package/src/host/daemon/api/mcps.js +1 -1
  114. package/src/host/daemon/api/messages.js +1 -1
  115. package/src/host/daemon/api/runtimes.js +9 -8
  116. package/src/host/daemon/api/sessions-search.js +1 -1
  117. package/src/host/daemon/api/sessions.js +2 -2
  118. package/src/host/daemon/api/shared.js +5 -4
  119. package/src/host/daemon/api/skills.js +30 -0
  120. package/src/host/daemon/api/super-agent.js +29 -9
  121. package/src/host/daemon/api/tasks.js +2 -2
  122. package/src/host/daemon/api/telegram.js +1 -1
  123. package/src/host/daemon/api/tools.js +6 -6
  124. package/src/host/daemon/api/tts.js +2 -2
  125. package/src/host/daemon/api/voice.js +14 -12
  126. package/src/host/daemon/api.js +2 -0
  127. package/src/host/daemon/compact.js +1 -1
  128. package/src/host/daemon/db.js +4 -4
  129. package/src/host/daemon/desktop-ws.js +1 -1
  130. package/src/host/daemon/index.js +4 -4
  131. package/src/host/daemon/plugins/{desktop.js → desktop/index.js} +11 -6
  132. package/src/host/daemon/plugins/index.js +2 -2
  133. package/src/host/daemon/plugins/{telegram.js → telegram/index.js} +52 -193
  134. package/src/host/daemon/plugins/telegram/media.js +162 -0
  135. package/src/host/daemon/projects-helpers.js +54 -0
  136. package/src/host/daemon/routines.js +28 -12
  137. package/src/host/daemon/smoke.js +2 -2
  138. package/src/host/daemon/token-store.js +1 -1
  139. package/src/host/daemon/transcription.js +2 -2
  140. package/src/host/daemon/wakeup.js +2 -2
  141. package/src/interfaces/cli/commands/agent.js +3 -3
  142. package/src/interfaces/cli/commands/command.js +1 -1
  143. package/src/interfaces/cli/commands/config.js +3 -2
  144. package/src/interfaces/cli/commands/desktop.js +1 -1
  145. package/src/interfaces/cli/commands/exec.js +2 -1
  146. package/src/interfaces/cli/commands/identity.js +2 -2
  147. package/src/interfaces/cli/commands/init.js +1 -1
  148. package/src/interfaces/cli/commands/mcp.js +1 -1
  149. package/src/interfaces/cli/commands/memory.js +2 -2
  150. package/src/interfaces/cli/commands/model.js +16 -6
  151. package/src/interfaces/cli/commands/project.js +1 -1
  152. package/src/interfaces/cli/commands/routine.js +58 -0
  153. package/src/interfaces/cli/commands/search.js +1 -1
  154. package/src/interfaces/cli/commands/session.js +4 -4
  155. package/src/interfaces/cli/commands/setup.js +4 -3
  156. package/src/interfaces/cli/commands/skills.js +25 -4
  157. package/src/interfaces/cli/commands/status.js +1 -1
  158. package/src/interfaces/cli/commands/sys.js +11 -4
  159. package/src/interfaces/cli/commands/update.js +1 -1
  160. package/src/interfaces/cli/index.js +4 -4
  161. package/src/interfaces/cli/postinstall.js +2 -2
  162. package/src/interfaces/mcp-server/index.js +1 -1
  163. package/src/interfaces/tui/component/prompt/index.tsx +3 -1
  164. package/src/interfaces/tui/context/sdk-apx.tsx +47 -7
  165. package/src/interfaces/tui/context/sync-apx.tsx +20 -2
  166. package/src/interfaces/tui/context/sync.tsx +2 -1
  167. package/src/interfaces/tui/routes/session/index.tsx +151 -136
  168. package/src/interfaces/tui/routes/session/sidebar-apx.tsx +37 -15
  169. package/src/interfaces/tui/run.ts +2 -0
  170. package/src/interfaces/web/dist/assets/index-7dVT2O1S.css +1 -0
  171. package/src/interfaces/web/dist/assets/index-DWsE_8Nz.js +602 -0
  172. package/src/interfaces/web/dist/assets/index-DWsE_8Nz.js.map +1 -0
  173. package/src/interfaces/web/dist/index.html +2 -2
  174. package/src/interfaces/web/package-lock.json +6 -6
  175. package/src/interfaces/web/src/App.tsx +53 -32
  176. package/src/interfaces/web/src/components/RobyBubble.tsx +12 -6
  177. package/src/interfaces/web/src/components/UiSelect.tsx +13 -3
  178. package/src/interfaces/web/src/components/chat/SkillPicker.tsx +77 -0
  179. package/src/interfaces/web/src/components/code/CodeArtifactsTab.tsx +253 -111
  180. package/src/interfaces/web/src/components/code/CodeChangesTab.tsx +10 -8
  181. package/src/interfaces/web/src/components/code/CodeComposer.tsx +20 -17
  182. package/src/interfaces/web/src/components/code/CodeContextTab.tsx +43 -18
  183. package/src/interfaces/web/src/components/code/CodeFileTree.tsx +212 -0
  184. package/src/interfaces/web/src/components/code/CodeFileViewer.tsx +121 -0
  185. package/src/interfaces/web/src/components/code/CodeProjectPicker.tsx +1 -1
  186. package/src/interfaces/web/src/components/code/CodeSessionList.tsx +30 -26
  187. package/src/interfaces/web/src/components/code/CodeSidePanel.tsx +40 -21
  188. package/src/interfaces/web/src/components/code/CodeTerminal.tsx +140 -0
  189. package/src/interfaces/web/src/components/common/TabLayout.tsx +11 -7
  190. package/src/interfaces/web/src/components/common/TabNav.tsx +3 -3
  191. package/src/interfaces/web/src/components/layout/ProjectSidebar.tsx +4 -2
  192. package/src/interfaces/web/src/components/ui/chat-input.tsx +17 -6
  193. package/src/interfaces/web/src/hooks/useChat.ts +48 -2
  194. package/src/interfaces/web/src/hooks/useNavCollapseCtx.tsx +83 -0
  195. package/src/interfaces/web/src/hooks/usePersonaName.ts +11 -0
  196. package/src/interfaces/web/src/i18n/en.ts +7 -7
  197. package/src/interfaces/web/src/i18n/es.ts +8 -8
  198. package/src/interfaces/web/src/lib/api/agents.ts +1 -1
  199. package/src/interfaces/web/src/lib/api/artifacts.ts +10 -0
  200. package/src/interfaces/web/src/lib/api/code.ts +4 -2
  201. package/src/interfaces/web/src/lib/api/skills.ts +25 -0
  202. package/src/interfaces/web/src/lib/api.ts +1 -0
  203. package/src/interfaces/web/src/screens/modules/CodeScreen.tsx +430 -86
  204. package/src/interfaces/web/src/screens/modules/DeckScreen.tsx +5 -18
  205. package/src/interfaces/web/src/screens/modules/DesktopScreen.tsx +1 -8
  206. package/src/interfaces/web/src/screens/modules/VoiceScreen.tsx +39 -40
  207. package/src/interfaces/web/src/screens/project/ChatTab.tsx +16 -16
  208. package/src/skills/apc-context/SKILL.md +159 -0
  209. package/src/core/agent/ghost-guard.js +0 -24
  210. package/src/core/agent/prompts/channels/terminal.md +0 -16
  211. package/src/host/daemon/apc-runtime-context.js +0 -124
  212. package/src/host/daemon/super-agent-tools/helpers.js +0 -124
  213. package/src/host/daemon/tool-call-parser.js +0 -2
  214. package/src/interfaces/web/dist/assets/index-63P_ji1a.js +0 -571
  215. package/src/interfaces/web/dist/assets/index-63P_ji1a.js.map +0 -1
  216. package/src/interfaces/web/dist/assets/index-DLWy6dYz.css +0 -1
  217. /package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/ask-questions.js +0 -0
  218. /package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/list-files.js +0 -0
  219. /package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/list-mcps.js +0 -0
  220. /package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/list-projects.js +0 -0
  221. /package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/read-file.js +0 -0
  222. /package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/search-files.js +0 -0
  223. /package/src/core/agent/{pseudo-tools.js → tools/pseudo-tools.js} +0 -0
  224. /package/src/core/agent/{tool-call-parser.js → tools/tool-call-parser.js} +0 -0
  225. /package/src/core/{parser.js → apc/parser.js} +0 -0
  226. /package/src/core/{apc-skill-sync.js → apc/skill-sync.js} +0 -0
  227. /package/src/core/{artifacts-store.js → stores/artifacts.js} +0 -0
  228. /package/src/{host/daemon → core/stores}/engine-sessions.js +0 -0
  229. /package/src/core/{session-store.js → stores/sessions.js} +0 -0
  230. /package/src/host/daemon/plugins/{telegram-ask.js → telegram/ask.js} +0 -0
@@ -22,6 +22,7 @@ import { register as registerMessages } from "./api/messages.js";
22
22
  import { register as registerTelegram } from "./api/telegram.js";
23
23
  import { register as registerPlugins } from "./api/plugins.js";
24
24
  import { register as registerEngines } from "./api/engines.js";
25
+ import { register as registerSkills } from "./api/skills.js";
25
26
  import { register as registerExec } from "./api/exec.js";
26
27
  import { register as registerSuperAgent } from "./api/super-agent.js";
27
28
  import { register as registerCode } from "./api/code.js";
@@ -107,6 +108,7 @@ export function buildApi({
107
108
  registerMcps(app, ctx);
108
109
  registerMessages(app, ctx);
109
110
  registerEngines(app, ctx);
111
+ registerSkills(app, ctx);
110
112
  registerExec(app, ctx);
111
113
  registerSuperAgent(app, ctx);
112
114
  registerConfirm(app, ctx);
@@ -25,7 +25,7 @@
25
25
  import fs from "node:fs";
26
26
  import path from "node:path";
27
27
  import { parseConversation } from "./conversations.js";
28
- import { callEngine } from "../../core/engines/index.js";
28
+ import { callEngine } from "#core/engines/index.js";
29
29
 
30
30
  const KEEP_LAST = 6;
31
31
 
@@ -2,15 +2,15 @@
2
2
  // Projects are identified by path; no SQLite — filesystem is the source of truth.
3
3
  import fs from "node:fs";
4
4
  import path from "node:path";
5
- import { appendMessageToFs } from "../../core/messages-store.js";
5
+ import { appendMessageToFs } from "#core/stores/messages.js";
6
6
  import { effectiveConfig } from "./project-config.js";
7
- import { readAgents } from "../../core/parser.js";
8
- import { getOrCreateApxId } from "../../core/scaffold.js";
7
+ import { readAgents } from "#core/apc/parser.js";
8
+ import { getOrCreateApxId } from "#core/apc/scaffold.js";
9
9
  import {
10
10
  ensureProjectStorage,
11
11
  DEFAULT_PROJECT_ID,
12
12
  DEFAULT_PROJECT_STORE,
13
- } from "../../core/config.js";
13
+ } from "#core/config/index.js";
14
14
 
15
15
  export class ProjectManager {
16
16
  constructor(globalConfig = {}) {
@@ -1,5 +1,5 @@
1
1
  // Singleton WebSocket hub for the desktop channel.
2
- // Imported by api.js (to register connections) and by plugins/desktop.js (to broadcast).
2
+ // Imported by api.js (to register connections) and by plugins/desktop/index.js (to broadcast).
3
3
 
4
4
  const _clients = new Set(); // Set<WebSocket>
5
5
  let _messageHandler = null; // (ws, data) => void — set by desktop plugin
@@ -14,17 +14,17 @@ import {
14
14
  LOG_PATH,
15
15
  APX_HOME,
16
16
  TOKEN_PATH,
17
- } from "../../core/config.js";
17
+ } from "#core/config/index.js";
18
18
  import { ProjectManager } from "./db.js";
19
- import { McpRegistry } from "../../core/mcp/runner.js";
19
+ import { McpRegistry } from "#core/mcp/runner.js";
20
20
  import { PluginManager } from "./plugins/index.js";
21
21
  import { RoutineScheduler } from "./routines.js";
22
22
  import { buildApi } from "./api.js";
23
23
  import { createTokenStore } from "./token-store.js";
24
24
  import { triggerWakeup } from "./wakeup.js";
25
25
  import { registerDesktopClient } from "./desktop-ws.js";
26
- import { log as logToUnified } from "../../core/logging.js";
27
- import { initMemory, stopMemory } from "../../core/memory/index.js";
26
+ import { log as logToUnified } from "#core/logging.js";
27
+ import { initMemory, stopMemory } from "#core/memory/index.js";
28
28
 
29
29
  const __filename = fileURLToPath(import.meta.url);
30
30
  const __dirname = path.dirname(__filename);
@@ -21,11 +21,13 @@ import {
21
21
  broadcastDesktop,
22
22
  sendToClient,
23
23
  setDesktopMessageHandler,
24
- } from "../desktop-ws.js";
25
- import { runSuperAgent, isSuperAgentEnabled } from "../super-agent.js";
26
- import { appendGlobalMessage } from "../../../core/messages-store.js";
24
+ } from "../../desktop-ws.js";
25
+ import { runSuperAgent, isSuperAgentEnabled } from "#core/agent/super-agent.js";
26
+ import { appendGlobalMessage } from "#core/stores/messages.js";
27
+ import { CHANNELS } from "#core/constants/channels.js";
28
+ import { tryResolveSkillCommand } from "#core/agent/skills/trigger.js";
27
29
 
28
- const CHANNEL = "desktop";
30
+ const CHANNEL = CHANNELS.DESKTOP;
29
31
 
30
32
  export default {
31
33
  id: "desktop",
@@ -128,12 +130,15 @@ async function _handleMessage({ ws, text, previousMessages }, { projects, config
128
130
 
129
131
  log(`desktop: super-agent turn start — model=${cfg.model || config?.super_agent?.model || "(default)"} text="${text.slice(0, 60)}"`);
130
132
  const t0 = Date.now();
133
+ const slashed = tryResolveSkillCommand(text);
134
+ const slashedPrompt = slashed.handled ? slashed.prompt : text;
131
135
  const result = await runSuperAgent({
132
136
  globalConfig: config,
133
137
  projects,
134
138
  plugins,
135
- prompt: text,
136
- channel: "desktop",
139
+ prompt: slashedPrompt,
140
+ channel: CHANNELS.DESKTOP,
141
+ ...(slashed.handled ? { contextNote: slashed.contextNote } : {}),
137
142
  channelMeta: { voice: true }, // desktop module is voice-first → spoken mode
138
143
  previousMessages: history.slice(0, -1),
139
144
  overrideModel: cfg.model || null,
@@ -12,8 +12,8 @@
12
12
  //
13
13
  // Plugins are discovered by static import here. Adding a new plugin = importing
14
14
  // it and pushing into PLUGINS.
15
- import telegramPlugin from "./telegram.js";
16
- import desktopPlugin from "./desktop.js";
15
+ import telegramPlugin from "./telegram/index.js";
16
+ import desktopPlugin from "./desktop/index.js";
17
17
 
18
18
  export const PLUGINS = [telegramPlugin, desktopPlugin];
19
19
 
@@ -29,23 +29,25 @@
29
29
 
30
30
  import fs from "node:fs";
31
31
  import path from "node:path";
32
- import { TELEGRAM_STATE_PATH, APX_HOME } from "../../../core/config.js";
33
- import { callEngine } from "../../../core/engines/index.js";
34
- import { runSuperAgent, isSuperAgentEnabled } from "../super-agent.js";
35
- import { stripThinking } from "../thinking.js";
36
- import { getRecentTelegramTurnsFromFs, appendGlobalMessage } from "../../../core/messages-store.js";
37
- import { compactChannelIfNeeded } from "../../../core/memory/index.js";
38
- import { readAgents } from "../../../core/parser.js";
39
- import { buildAgentSystem } from "../../../core/agent-system.js";
40
- import { transcribe as transcribeAudioFile } from "../transcription.js";
41
- import { resolveAgentName, SUPERAGENT_ACTOR_ID } from "../../../core/identity.js";
42
- import { registerSender, resolveAllowedTools } from "../../../core/telegram-identity.js";
43
- import { buildRelationshipBlock } from "../../../core/agent/index.js";
44
- import { getConfirmationStore as getConfirmStore } from "../../../core/confirmation/pending-store.js";
45
- import { createTelegramConfirmAdapter } from "../../../core/confirmation/adapters/telegram.js";
46
- import * as askFlow from "./telegram-ask.js";
47
-
48
- const API_BASE = "https://api.telegram.org";
32
+ import { TELEGRAM_STATE_PATH, APX_HOME } from "#core/config/index.js";
33
+ import { callEngine } from "#core/engines/index.js";
34
+ import { runSuperAgent, isSuperAgentEnabled } from "#core/agent/super-agent.js";
35
+ import { stripThinking } from "../../thinking.js";
36
+ import { getRecentTelegramTurnsFromFs, appendGlobalMessage } from "#core/stores/messages.js";
37
+ import { compactChannelIfNeeded } from "#core/memory/index.js";
38
+ import { readAgents } from "#core/apc/parser.js";
39
+ import { buildAgentSystem } from "#core/agent/build-agent-system.js";
40
+ import { transcribe as transcribeAudioFile } from "../../transcription.js";
41
+ import { resolveAgentName, SUPERAGENT_ACTOR_ID } from "#core/identity/index.js";
42
+ import { registerSender, resolveAllowedTools } from "#core/identity/telegram.js";
43
+ import { buildRelationshipBlock } from "#core/agent/index.js";
44
+ import { getConfirmationStore as getConfirmStore } from "#core/confirmation/pending-store.js";
45
+ import { CHANNELS } from "#core/constants/channels.js";
46
+ import { tryResolveSkillCommand } from "#core/agent/skills/trigger.js";
47
+ import { createTelegramConfirmAdapter } from "#core/confirmation/adapters/telegram.js";
48
+ import * as askFlow from "./ask.js";
49
+
50
+ // API_BASE re-imported from ./media.js below
49
51
  const nowIso = () => new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
50
52
 
51
53
  // Build the channelMeta passed to the super-agent loop. The prompt template at
@@ -85,159 +87,9 @@ function buildTelegramMeta({ channelName, author, chatId, target, routeToAgent }
85
87
  };
86
88
  }
87
89
 
88
- // ---------- media sending helpers -------------------------------------------
89
-
90
- /**
91
- * Send a photo to a Telegram chat.
92
- * @param {string} token Bot token
93
- * @param {string|number} chatId Telegram chat_id
94
- * @param {string|Buffer} photo Absolute file path OR Buffer of image data
95
- * @param {object} [opts]
96
- * @param {string} [opts.caption]
97
- * @param {string} [opts.parse_mode] "HTML" | "Markdown" | "MarkdownV2"
98
- */
99
- export async function sendPhoto(token, chatId, photo, { caption, parse_mode } = {}) {
100
- const url = `${API_BASE}/bot${token}/sendPhoto`;
101
- const form = new FormData();
102
- form.append("chat_id", String(chatId));
103
- if (caption) form.append("caption", caption);
104
- if (parse_mode) form.append("parse_mode", parse_mode);
105
-
106
- if (typeof photo === "string" && photo.startsWith("http")) {
107
- // Public URL — send as string
108
- form.append("photo", photo);
109
- } else {
110
- // Local file path or Buffer
111
- const buf = Buffer.isBuffer(photo) ? photo : fs.readFileSync(photo);
112
- const name = typeof photo === "string" ? path.basename(photo) : "photo.jpg";
113
- const blob = new Blob([buf], { type: name.endsWith(".png") ? "image/png" : "image/jpeg" });
114
- form.append("photo", blob, name);
115
- }
116
-
117
- const res = await fetch(url, { method: "POST", body: form });
118
- const json = await res.json();
119
- if (!json.ok) throw new Error(`sendPhoto failed: ${json.description || res.status}`);
120
- return json.result;
121
- }
122
-
123
- /**
124
- * Send a voice message (OGG/Opus preferred by Telegram).
125
- * @param {string} token
126
- * @param {string|number} chatId
127
- * @param {string|Buffer} audio Path or Buffer
128
- * @param {object} [opts]
129
- * @param {string} [opts.caption]
130
- * @param {number} [opts.duration]
131
- */
132
- export async function sendVoice(token, chatId, audio, { caption, duration } = {}) {
133
- const url = `${API_BASE}/bot${token}/sendVoice`;
134
- const form = new FormData();
135
- form.append("chat_id", String(chatId));
136
- if (caption) form.append("caption", caption);
137
- if (duration) form.append("duration", String(duration));
138
-
139
- const buf = Buffer.isBuffer(audio) ? audio : fs.readFileSync(audio);
140
- const name = typeof audio === "string" ? path.basename(audio) : "voice.ogg";
141
- const blob = new Blob([buf], { type: "audio/ogg" });
142
- form.append("voice", blob, name);
143
-
144
- const res = await fetch(url, { method: "POST", body: form });
145
- const json = await res.json();
146
- if (!json.ok) throw new Error(`sendVoice failed: ${json.description || res.status}`);
147
- return json.result;
148
- }
149
-
150
- /**
151
- * Send an audio file (MP3, M4A, etc — shown in Telegram music player).
152
- * @param {string} token
153
- * @param {string|number} chatId
154
- * @param {string|Buffer} audio Path or Buffer
155
- * @param {object} [opts]
156
- * @param {string} [opts.caption]
157
- * @param {string} [opts.title]
158
- * @param {string} [opts.performer]
159
- */
160
- /**
161
- * Send any file as a Telegram document (PDF, zip, txt, etc).
162
- * @param {string} token
163
- * @param {string|number} chatId
164
- * @param {string|Buffer} document Path or Buffer of document data
165
- * @param {object} [opts]
166
- * @param {string} [opts.caption]
167
- * @param {string} [opts.filename] override filename for Buffer input
168
- * @param {string} [opts.mime_type]
169
- */
170
- export async function sendDocument(token, chatId, document, { caption, filename, mime_type } = {}) {
171
- const url = `${API_BASE}/bot${token}/sendDocument`;
172
- const form = new FormData();
173
- form.append("chat_id", String(chatId));
174
- if (caption) form.append("caption", caption);
175
-
176
- // URL string → let Telegram fetch it
177
- if (typeof document === "string" && /^https?:\/\//.test(document)) {
178
- form.append("document", document);
179
- } else {
180
- const buf = Buffer.isBuffer(document) ? document : fs.readFileSync(document);
181
- const name =
182
- filename ||
183
- (typeof document === "string" ? path.basename(document) : "document.bin");
184
- const blob = new Blob([buf], { type: mime_type || "application/octet-stream" });
185
- form.append("document", blob, name);
186
- }
187
-
188
- const res = await fetch(url, { method: "POST", body: form });
189
- const json = await res.json();
190
- if (!json.ok) throw new Error(`sendDocument failed: ${json.description || res.status}`);
191
- return json.result;
192
- }
193
-
194
- export async function sendAudio(token, chatId, audio, { caption, title, performer } = {}) {
195
- const url = `${API_BASE}/bot${token}/sendAudio`;
196
- const form = new FormData();
197
- form.append("chat_id", String(chatId));
198
- if (caption) form.append("caption", caption);
199
- if (title) form.append("title", title);
200
- if (performer) form.append("performer", performer);
201
-
202
- const buf = Buffer.isBuffer(audio) ? audio : fs.readFileSync(audio);
203
- const name = typeof audio === "string" ? path.basename(audio) : "audio.mp3";
204
- const blob = new Blob([buf], { type: "audio/mpeg" });
205
- form.append("audio", blob, name);
206
-
207
- const res = await fetch(url, { method: "POST", body: form });
208
- const json = await res.json();
209
- if (!json.ok) throw new Error(`sendAudio failed: ${json.description || res.status}`);
210
- return json.result;
211
- }
212
-
213
- // Audio transcription is delegated to the central dispatcher
214
- // (../transcription.js) which handles local (faster-whisper via Python) +
215
- // OpenAI cloud fallback. See that module for config keys.
216
-
217
- /**
218
- * Download a file from Telegram servers.
219
- * Returns the local file path where it was saved.
220
- */
221
- async function downloadTelegramFile(token, fileId, destDir) {
222
- // Step 1: get file path from Telegram
223
- const infoRes = await fetch(`${API_BASE}/bot${token}/getFile?file_id=${fileId}`);
224
- const infoJson = await infoRes.json();
225
- if (!infoJson.ok) throw new Error(`getFile failed: ${infoJson.description}`);
226
- const filePath = infoJson.result.file_path; // e.g. "photos/file_123.jpg"
227
- const ext = path.extname(filePath) || ".jpg";
228
- const fileName = `tg_${fileId.slice(-8)}_${Date.now()}${ext}`;
229
- const localPath = path.join(destDir, fileName);
230
-
231
- // Step 2: download
232
- const dlRes = await fetch(`${API_BASE}/file/bot${token}/${filePath}`);
233
- if (!dlRes.ok) throw new Error(`download failed: ${dlRes.status}`);
234
- const buf = Buffer.from(await dlRes.arrayBuffer());
235
- fs.writeFileSync(localPath, buf);
236
- return localPath;
237
- }
238
-
239
- // ---------- shared state ----------------------------------------------------
240
-
90
+ // Media sending helpers moved to ./media.js.
91
+ import { sendPhoto, sendVoice, sendDocument, sendAudio, downloadTelegramFile, API_BASE } from "./media.js";
92
+ export { sendPhoto, sendVoice, sendDocument, sendAudio };
241
93
  function loadState() {
242
94
  if (!fs.existsSync(TELEGRAM_STATE_PATH)) return { channels: {} };
243
95
  try {
@@ -485,7 +337,7 @@ class ChannelPoller {
485
337
  const localPath = await downloadTelegramFile(token, bestPhoto.file_id, mediaDir);
486
338
  this.log(`telegram[${this.channel.name}] photo saved: ${localPath}`);
487
339
  appendGlobalMessage({
488
- channel: "telegram",
340
+ channel: CHANNELS.TELEGRAM,
489
341
  direction: "in",
490
342
  type: "photo",
491
343
  actor_id: msg.from?.id ? String(msg.from.id) : author,
@@ -551,7 +403,7 @@ class ChannelPoller {
551
403
  : `[audio] (transcription unavailable${transcribeError ? ": " + transcribeError : ""})`;
552
404
 
553
405
  appendGlobalMessage({
554
- channel: "telegram",
406
+ channel: CHANNELS.TELEGRAM,
555
407
  direction: "in",
556
408
  type: "audio",
557
409
  actor_id: msg.from?.id ? String(msg.from.id) : author,
@@ -584,7 +436,7 @@ class ChannelPoller {
584
436
  if (chat_id && text && await this._maybeConsumeAskTextAnswer({ chat_id, text })) {
585
437
  // Still log the inbound so the chat history records what the user said.
586
438
  appendGlobalMessage({
587
- channel: "telegram",
439
+ channel: CHANNELS.TELEGRAM,
588
440
  direction: "in",
589
441
  type: "user",
590
442
  actor_id: msg.from?.id ? String(msg.from.id) : author,
@@ -623,7 +475,7 @@ class ChannelPoller {
623
475
  // the next turn reads a [RESUMEN COMPACTADO] instead of raw history. Never
624
476
  // awaited — adds zero latency to this reply, degrades gracefully.
625
477
  compactChannelIfNeeded({
626
- channel: "telegram",
478
+ channel: CHANNELS.TELEGRAM,
627
479
  chat_id,
628
480
  config: this.globalConfig,
629
481
  log: this.log,
@@ -647,7 +499,7 @@ class ChannelPoller {
647
499
 
648
500
  // Always log inbound to global store (~/.apx/messages/telegram/)
649
501
  appendGlobalMessage({
650
- channel: "telegram",
502
+ channel: CHANNELS.TELEGRAM,
651
503
  direction: "in",
652
504
  type: "user",
653
505
  actor_id: msg.from?.id ? String(msg.from.id) : author,
@@ -679,7 +531,7 @@ class ChannelPoller {
679
531
  const ack = "Done, context cleared. Starting fresh. What do you need?";
680
532
  await this._send({ chat_id, text: ack });
681
533
  appendGlobalMessage({
682
- channel: "telegram",
534
+ channel: CHANNELS.TELEGRAM,
683
535
  direction: "out",
684
536
  type: "agent",
685
537
  actor_id: SUPERAGENT_ACTOR_ID,
@@ -703,7 +555,7 @@ class ChannelPoller {
703
555
  let replyActorId; // stable id: super_agent | agent slug
704
556
  let replyKind; // actor_kind: superagent | agent
705
557
  const projectCfg = target.config || this.globalConfig;
706
- // Display name for the super-agent persona on this channel (Roby / APX).
558
+ // Display name for the super-agent persona on this channel (from identity.json).
707
559
  const agentDisplay = resolveAgentName(this.globalConfig);
708
560
 
709
561
  // Try the project's chosen agent first (skipped if the legacy
@@ -777,7 +629,7 @@ class ChannelPoller {
777
629
  const heads = headsUpPhrase();
778
630
  await this._send({ chat_id, text: heads });
779
631
  appendGlobalMessage({
780
- channel: "telegram",
632
+ channel: CHANNELS.TELEGRAM,
781
633
  direction: "out",
782
634
  type: "agent",
783
635
  actor_id: SUPERAGENT_ACTOR_ID,
@@ -796,7 +648,7 @@ class ChannelPoller {
796
648
  lastStreamedText = piece;
797
649
  streamedCount += 1;
798
650
  appendGlobalMessage({
799
- channel: "telegram",
651
+ channel: CHANNELS.TELEGRAM,
800
652
  direction: "out",
801
653
  type: "agent",
802
654
  actor_id: SUPERAGENT_ACTOR_ID,
@@ -816,7 +668,7 @@ class ChannelPoller {
816
668
  // Logged for the audit trail / other channels — NOT sent to Telegram.
817
669
  const t = ev.trace;
818
670
  appendGlobalMessage({
819
- channel: "telegram",
671
+ channel: CHANNELS.TELEGRAM,
820
672
  direction: "out",
821
673
  type: "tool",
822
674
  actor_id: t.tool,
@@ -846,17 +698,24 @@ class ChannelPoller {
846
698
  pendingStore: getConfirmStore(),
847
699
  });
848
700
 
701
+ // `/slug ...` shortcut: load the matching skill body into contextNote
702
+ // and strip the prefix from the user prompt before sending to the loop.
703
+ const slashed = tryResolveSkillCommand(text, { projectPath: target?.path });
704
+ const slashedPrompt = slashed.handled ? slashed.prompt : text;
705
+ const slashedContextNote = slashed.handled ? slashed.contextNote : "";
706
+
849
707
  try {
850
708
  const sa = await runSuperAgent({
851
709
  globalConfig: this.globalConfig,
852
710
  projects: this.projects,
853
711
  plugins: this.plugins,
854
712
  registries: this.registries,
855
- prompt: text,
713
+ prompt: slashedPrompt,
856
714
  previousMessages,
857
- channel: "telegram",
715
+ channel: CHANNELS.TELEGRAM,
858
716
  relationshipBlock,
859
717
  allowedTools,
718
+ contextNote: slashedContextNote || undefined,
860
719
  channelMeta: buildTelegramMeta({
861
720
  channelName: this.channel.name,
862
721
  author,
@@ -949,7 +808,7 @@ class ChannelPoller {
949
808
  if (replyText && stripThinking(replyText) !== replyText) meta.thinking_stripped = true;
950
809
  if (saUsage) meta.usage = saUsage;
951
810
  appendGlobalMessage({
952
- channel: "telegram",
811
+ channel: CHANNELS.TELEGRAM,
953
812
  direction: "out",
954
813
  type: "agent",
955
814
  actor_id: replyActorId || SUPERAGENT_ACTOR_ID,
@@ -962,7 +821,7 @@ class ChannelPoller {
962
821
  } catch (e) {
963
822
  this.log(`telegram[${this.channel.name}] send-back error: ${e.message}`);
964
823
  appendGlobalMessage({
965
- channel: "telegram",
824
+ channel: CHANNELS.TELEGRAM,
966
825
  direction: "out",
967
826
  type: "agent",
968
827
  actor_id: replyActorId || SUPERAGENT_ACTOR_ID,
@@ -1138,7 +997,7 @@ class ChannelPoller {
1138
997
  // it up on the NEXT inbound. Mirrors how a normal text reply would be
1139
998
  // recorded.
1140
999
  appendGlobalMessage({
1141
- channel: "telegram",
1000
+ channel: CHANNELS.TELEGRAM,
1142
1001
  direction: "in",
1143
1002
  type: "user",
1144
1003
  actor_id: authorId ? String(authorId) : (author || "ask_flow"),
@@ -1168,10 +1027,10 @@ class ChannelPoller {
1168
1027
  registries: this.registries,
1169
1028
  prompt: compiled,
1170
1029
  previousMessages,
1171
- channel: "telegram",
1030
+ channel: CHANNELS.TELEGRAM,
1172
1031
  relationshipBlock,
1173
1032
  allowedTools,
1174
- channelMeta: { channel: "telegram", chat_id, author, route_to_agent: this.channel.route_to_agent },
1033
+ channelMeta: { channel: CHANNELS.TELEGRAM, chat_id, author, route_to_agent: this.channel.route_to_agent },
1175
1034
  });
1176
1035
  stopTyping();
1177
1036
 
@@ -1198,7 +1057,7 @@ class ChannelPoller {
1198
1057
  if (replyText) {
1199
1058
  await this._send({ chat_id, text: replyText });
1200
1059
  appendGlobalMessage({
1201
- channel: "telegram",
1060
+ channel: CHANNELS.TELEGRAM,
1202
1061
  direction: "out",
1203
1062
  type: "agent",
1204
1063
  actor_id: SUPERAGENT_ACTOR_ID,
@@ -1401,7 +1260,7 @@ export default {
1401
1260
  if (!p) throw new Error("no telegram channel available");
1402
1261
  const result = await p._send({ chat_id, text });
1403
1262
  appendGlobalMessage({
1404
- channel: "telegram",
1263
+ channel: CHANNELS.TELEGRAM,
1405
1264
  direction: "out",
1406
1265
  type: "agent",
1407
1266
  actor_id: SUPERAGENT_ACTOR_ID,
@@ -1431,7 +1290,7 @@ export default {
1431
1290
  if (!p) throw new Error("no telegram channel available");
1432
1291
  const result = await p._sendPhoto({ chat_id, photo, caption, parse_mode });
1433
1292
  appendGlobalMessage({
1434
- channel: "telegram",
1293
+ channel: CHANNELS.TELEGRAM,
1435
1294
  direction: "out",
1436
1295
  type: "photo",
1437
1296
  actor_id: SUPERAGENT_ACTOR_ID,
@@ -1455,7 +1314,7 @@ export default {
1455
1314
  if (!p) throw new Error("no telegram channel available");
1456
1315
  const result = await p._sendVoice({ chat_id, audio, caption, duration });
1457
1316
  appendGlobalMessage({
1458
- channel: "telegram",
1317
+ channel: CHANNELS.TELEGRAM,
1459
1318
  direction: "out",
1460
1319
  type: "voice",
1461
1320
  actor_id: SUPERAGENT_ACTOR_ID,
@@ -1479,7 +1338,7 @@ export default {
1479
1338
  if (!p) throw new Error("no telegram channel available");
1480
1339
  const result = await p._sendDocument({ chat_id, document, caption, filename, mime_type });
1481
1340
  appendGlobalMessage({
1482
- channel: "telegram",
1341
+ channel: CHANNELS.TELEGRAM,
1483
1342
  direction: "out",
1484
1343
  type: "document",
1485
1344
  actor_id: SUPERAGENT_ACTOR_ID,
@@ -1503,7 +1362,7 @@ export default {
1503
1362
  if (!p) throw new Error("no telegram channel available");
1504
1363
  const result = await p._sendAudio({ chat_id, audio, caption, title, performer });
1505
1364
  appendGlobalMessage({
1506
- channel: "telegram",
1365
+ channel: CHANNELS.TELEGRAM,
1507
1366
  direction: "out",
1508
1367
  type: "audio",
1509
1368
  actor_id: SUPERAGENT_ACTOR_ID,