@agentprojectcontext/apx 1.31.2 → 1.32.2

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 (229) 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/constants.js +5 -0
  18. package/src/core/agent/index.js +0 -2
  19. package/src/core/{agent-memory.js → agent/memory.js} +2 -2
  20. package/src/core/agent/model-router.js +21 -43
  21. package/src/core/agent/prompt-builder.js +17 -63
  22. package/src/core/agent/prompts/action-discipline.md +17 -0
  23. package/src/core/agent/prompts/channels/code.md +8 -12
  24. package/src/core/agent/prompts/channels/desktop.md +6 -4
  25. package/src/core/agent/prompts/channels/routine.md +10 -1
  26. package/src/core/agent/prompts/channels/telegram.md +5 -0
  27. package/src/core/agent/prompts/channels/web_code.md +20 -0
  28. package/src/core/agent/prompts/modes/voice.md +2 -2
  29. package/src/core/agent/prompts/super-agent-base.md +2 -2
  30. package/src/core/agent/run-agent.js +66 -36
  31. package/src/core/agent/runtime-bridge.js +42 -0
  32. package/src/core/agent/self-memory.js +19 -9
  33. package/src/core/agent/skills/catalog.js +65 -0
  34. package/src/core/agent/skills/index.js +6 -0
  35. package/src/{host/daemon/skills-loader.js → core/agent/skills/loader.js} +3 -3
  36. package/src/core/agent/skills/rag.js +91 -0
  37. package/src/core/agent/skills/trigger.js +71 -0
  38. package/src/{host/daemon → core/agent}/super-agent.js +5 -5
  39. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/add-project.js +3 -4
  40. package/src/core/agent/tools/handlers/ask-questions.js +115 -0
  41. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/call-agent.js +2 -2
  42. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/call-mcp.js +1 -2
  43. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/call-runtime.js +10 -11
  44. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/create-task.js +1 -1
  45. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/discover-tools.js +1 -1
  46. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/edit-file.js +1 -2
  47. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/import-agent.js +4 -5
  48. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/list-agents.js +1 -1
  49. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/list-skills.js +7 -2
  50. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/list-tasks.js +1 -1
  51. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/list-vault-agents.js +1 -1
  52. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/load-skill.js +1 -1
  53. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/read-agent-memory.js +1 -1
  54. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/read-self-memory.js +1 -1
  55. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/remember.js +1 -1
  56. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/run-shell.js +1 -2
  57. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/search-messages.js +1 -1
  58. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/search-sessions.js +1 -1
  59. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/send-telegram.js +0 -2
  60. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/set-identity.js +1 -3
  61. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/set-permission-mode.js +1 -3
  62. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/tail-messages.js +1 -1
  63. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/transcribe-audio.js +1 -1
  64. package/src/{host/daemon/super-agent-tools/tools → core/agent/tools/handlers}/write-file.js +1 -2
  65. package/src/core/agent/tools/helpers.js +74 -0
  66. package/src/{host/daemon/super-agent-tools → core/agent/tools}/registry-bridge.js +3 -3
  67. package/src/{host/daemon/super-agent-tools/index.js → core/agent/tools/registry.js} +31 -32
  68. package/src/core/apc/agents-vault.js +37 -0
  69. package/src/core/{scaffold.js → apc/scaffold.js} +4 -5
  70. package/src/core/{config.js → config/index.js} +21 -27
  71. package/src/core/config/paths.js +32 -0
  72. package/src/core/constants/actors.js +8 -0
  73. package/src/core/constants/channels.js +19 -0
  74. package/src/core/constants/index.js +5 -0
  75. package/src/core/constants/permissions.js +17 -0
  76. package/src/core/constants/roles.js +9 -0
  77. package/src/core/engines/_streaming.js +63 -0
  78. package/src/core/engines/anthropic.js +11 -22
  79. package/src/core/engines/ollama.js +7 -16
  80. package/src/core/identity/index.js +8 -0
  81. package/src/core/{identity.js → identity/self.js} +5 -5
  82. package/src/core/{telegram-identity.js → identity/telegram.js} +1 -1
  83. package/src/core/logging.js +1 -1
  84. package/src/core/mascot.js +1 -1
  85. package/src/core/memory/active-threads.js +10 -10
  86. package/src/core/memory/broker.js +9 -9
  87. package/src/core/memory/compactor.js +2 -2
  88. package/src/core/memory/index.js +2 -2
  89. package/src/core/memory/indexer.js +1 -1
  90. package/src/core/{code-sessions-store.js → stores/code-sessions.js} +3 -7
  91. package/src/core/{messages-store.js → stores/messages.js} +6 -4
  92. package/src/core/stores/routine-memory.js +71 -0
  93. package/src/core/{routines-store.js → stores/routines.js} +1 -3
  94. package/src/core/stores/runtime-sessions.js +99 -0
  95. package/src/core/{tasks-store.js → stores/tasks.js} +3 -8
  96. package/src/core/update-check.js +1 -1
  97. package/src/core/util/ids.js +14 -0
  98. package/src/core/util/index.js +2 -0
  99. package/src/core/util/text-similarity.js +52 -0
  100. package/src/core/util/time.js +9 -0
  101. package/src/core/voice/tts.js +1 -1
  102. package/src/host/daemon/api/admin-config.js +4 -3
  103. package/src/host/daemon/api/admin.js +1 -1
  104. package/src/host/daemon/api/agents.js +4 -25
  105. package/src/host/daemon/api/artifacts.js +118 -1
  106. package/src/host/daemon/api/code.js +60 -16
  107. package/src/host/daemon/api/confirm.js +1 -1
  108. package/src/host/daemon/api/connections.js +2 -2
  109. package/src/host/daemon/api/conversations.js +2 -2
  110. package/src/host/daemon/api/deck.js +1 -1
  111. package/src/host/daemon/api/desktop.js +1 -1
  112. package/src/host/daemon/api/embeddings.js +4 -4
  113. package/src/host/daemon/api/engines.js +2 -2
  114. package/src/host/daemon/api/exec.js +3 -3
  115. package/src/host/daemon/api/identity.js +1 -1
  116. package/src/host/daemon/api/mcps.js +1 -1
  117. package/src/host/daemon/api/messages.js +1 -1
  118. package/src/host/daemon/api/runtimes.js +9 -8
  119. package/src/host/daemon/api/sessions-search.js +1 -1
  120. package/src/host/daemon/api/sessions.js +2 -2
  121. package/src/host/daemon/api/shared.js +5 -4
  122. package/src/host/daemon/api/skills.js +30 -0
  123. package/src/host/daemon/api/super-agent.js +29 -9
  124. package/src/host/daemon/api/tasks.js +2 -2
  125. package/src/host/daemon/api/telegram.js +1 -1
  126. package/src/host/daemon/api/tools.js +6 -6
  127. package/src/host/daemon/api/tts.js +2 -2
  128. package/src/host/daemon/api/voice.js +14 -12
  129. package/src/host/daemon/api.js +2 -0
  130. package/src/host/daemon/compact.js +1 -1
  131. package/src/host/daemon/db.js +4 -4
  132. package/src/host/daemon/desktop-ws.js +1 -1
  133. package/src/host/daemon/index.js +4 -4
  134. package/src/host/daemon/plugins/{desktop.js → desktop/index.js} +45 -6
  135. package/src/host/daemon/plugins/index.js +2 -2
  136. package/src/host/daemon/plugins/telegram/ask.js +309 -0
  137. package/src/host/daemon/plugins/{telegram.js → telegram/index.js} +390 -191
  138. package/src/host/daemon/plugins/telegram/media.js +162 -0
  139. package/src/host/daemon/projects-helpers.js +54 -0
  140. package/src/host/daemon/routines.js +28 -12
  141. package/src/host/daemon/smoke.js +2 -2
  142. package/src/host/daemon/token-store.js +1 -1
  143. package/src/host/daemon/transcription.js +2 -2
  144. package/src/host/daemon/wakeup.js +2 -2
  145. package/src/interfaces/cli/commands/agent.js +3 -3
  146. package/src/interfaces/cli/commands/artifact.js +99 -0
  147. package/src/interfaces/cli/commands/command.js +1 -1
  148. package/src/interfaces/cli/commands/config.js +3 -2
  149. package/src/interfaces/cli/commands/desktop.js +1 -1
  150. package/src/interfaces/cli/commands/exec.js +2 -1
  151. package/src/interfaces/cli/commands/identity.js +2 -2
  152. package/src/interfaces/cli/commands/init.js +1 -1
  153. package/src/interfaces/cli/commands/mcp.js +1 -1
  154. package/src/interfaces/cli/commands/memory.js +2 -2
  155. package/src/interfaces/cli/commands/model.js +16 -6
  156. package/src/interfaces/cli/commands/project.js +1 -1
  157. package/src/interfaces/cli/commands/routine.js +58 -0
  158. package/src/interfaces/cli/commands/search.js +1 -1
  159. package/src/interfaces/cli/commands/session.js +4 -4
  160. package/src/interfaces/cli/commands/setup.js +4 -3
  161. package/src/interfaces/cli/commands/skills.js +25 -4
  162. package/src/interfaces/cli/commands/status.js +1 -1
  163. package/src/interfaces/cli/commands/sys.js +11 -4
  164. package/src/interfaces/cli/commands/update.js +1 -1
  165. package/src/interfaces/cli/index.js +8 -4
  166. package/src/interfaces/cli/postinstall.js +2 -2
  167. package/src/interfaces/cli/terminal-chat/renderer.js +22 -2
  168. package/src/interfaces/mcp-server/index.js +1 -1
  169. package/src/interfaces/tui/component/prompt/index.tsx +3 -1
  170. package/src/interfaces/tui/context/sdk-apx.tsx +47 -7
  171. package/src/interfaces/tui/context/sync-apx.tsx +20 -2
  172. package/src/interfaces/tui/context/sync.tsx +2 -1
  173. package/src/interfaces/tui/routes/session/index.tsx +151 -136
  174. package/src/interfaces/tui/routes/session/sidebar-apx.tsx +37 -15
  175. package/src/interfaces/tui/run.ts +2 -0
  176. package/src/interfaces/web/dist/assets/index-34U_Mp1M.css +1 -0
  177. package/src/interfaces/web/dist/assets/index-BkybwwRn.js +570 -0
  178. package/src/interfaces/web/dist/assets/index-BkybwwRn.js.map +1 -0
  179. package/src/interfaces/web/dist/index.html +2 -2
  180. package/src/interfaces/web/package-lock.json +9 -9
  181. package/src/interfaces/web/src/App.tsx +51 -32
  182. package/src/interfaces/web/src/components/RobyBubble.tsx +12 -6
  183. package/src/interfaces/web/src/components/UiSelect.tsx +1 -1
  184. package/src/interfaces/web/src/components/chat/AskQuestionsCard.tsx +72 -0
  185. package/src/interfaces/web/src/components/chat/InlineAskPanel.tsx +399 -0
  186. package/src/interfaces/web/src/components/chat/MessageBubble.tsx +16 -3
  187. package/src/interfaces/web/src/components/chat/MessageList.tsx +2 -1
  188. package/src/interfaces/web/src/components/chat/SkillPicker.tsx +77 -0
  189. package/src/interfaces/web/src/components/code/CodeArtifactsTab.tsx +230 -0
  190. package/src/interfaces/web/src/components/code/CodeProjectPicker.tsx +1 -1
  191. package/src/interfaces/web/src/components/code/CodeSidePanel.tsx +40 -17
  192. package/src/interfaces/web/src/components/common/TabLayout.tsx +9 -5
  193. package/src/interfaces/web/src/components/common/TabNav.tsx +3 -3
  194. package/src/interfaces/web/src/components/layout/ProjectSidebar.tsx +4 -2
  195. package/src/interfaces/web/src/hooks/useChat.ts +47 -2
  196. package/src/interfaces/web/src/hooks/useNavCollapseCtx.tsx +59 -0
  197. package/src/interfaces/web/src/hooks/usePersonaName.ts +11 -0
  198. package/src/interfaces/web/src/i18n/en.ts +27 -7
  199. package/src/interfaces/web/src/i18n/es.ts +27 -7
  200. package/src/interfaces/web/src/lib/api/artifacts.ts +47 -0
  201. package/src/interfaces/web/src/lib/api/skills.ts +25 -0
  202. package/src/interfaces/web/src/lib/api.ts +2 -0
  203. package/src/interfaces/web/src/screens/modules/CodeScreen.tsx +41 -20
  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 +27 -9
  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/super-agent-tools/tools/ask-questions.js +0 -32
  214. package/src/host/daemon/tool-call-parser.js +0 -2
  215. package/src/interfaces/web/dist/assets/index-BDUsA6L6.css +0 -1
  216. package/src/interfaces/web/dist/assets/index-BV615I9p.js +0 -548
  217. package/src/interfaces/web/dist/assets/index-BV615I9p.js.map +0 -1
  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
@@ -0,0 +1,162 @@
1
+ // Telegram media helpers: send photo/voice/document/audio + download a remote
2
+ // file. Auto-extracted from plugins/telegram/index.js — these used to live
3
+ // inline next to the poll loop and the super-agent dispatch.
4
+ //
5
+ // Each helper takes the bot token and chat id explicitly so they can be used
6
+ // from any code path (tests, other plugins, future agents). Buffer or
7
+ // absolute path input is accepted for media; for URLs the helpers pass them
8
+ // through to Telegram and let the API fetch them.
9
+ import fs from "node:fs";
10
+ import path from "node:path";
11
+
12
+ export const API_BASE = "https://api.telegram.org";
13
+
14
+ /**
15
+ * Send a photo to a Telegram chat.
16
+ * @param {string} token Bot token
17
+ * @param {string|number} chatId Telegram chat_id
18
+ * @param {string|Buffer} photo Absolute file path OR Buffer of image data
19
+ * @param {object} [opts]
20
+ * @param {string} [opts.caption]
21
+ * @param {string} [opts.parse_mode] "HTML" | "Markdown" | "MarkdownV2"
22
+ */
23
+ export async function sendPhoto(token, chatId, photo, { caption, parse_mode } = {}) {
24
+ const url = `${API_BASE}/bot${token}/sendPhoto`;
25
+ const form = new FormData();
26
+ form.append("chat_id", String(chatId));
27
+ if (caption) form.append("caption", caption);
28
+ if (parse_mode) form.append("parse_mode", parse_mode);
29
+
30
+ if (typeof photo === "string" && photo.startsWith("http")) {
31
+ // Public URL — send as string
32
+ form.append("photo", photo);
33
+ } else {
34
+ // Local file path or Buffer
35
+ const buf = Buffer.isBuffer(photo) ? photo : fs.readFileSync(photo);
36
+ const name = typeof photo === "string" ? path.basename(photo) : "photo.jpg";
37
+ const blob = new Blob([buf], { type: name.endsWith(".png") ? "image/png" : "image/jpeg" });
38
+ form.append("photo", blob, name);
39
+ }
40
+
41
+ const res = await fetch(url, { method: "POST", body: form });
42
+ const json = await res.json();
43
+ if (!json.ok) throw new Error(`sendPhoto failed: ${json.description || res.status}`);
44
+ return json.result;
45
+ }
46
+
47
+ /**
48
+ * Send a voice message (OGG/Opus preferred by Telegram).
49
+ * @param {string} token
50
+ * @param {string|number} chatId
51
+ * @param {string|Buffer} audio Path or Buffer
52
+ * @param {object} [opts]
53
+ * @param {string} [opts.caption]
54
+ * @param {number} [opts.duration]
55
+ */
56
+ export async function sendVoice(token, chatId, audio, { caption, duration } = {}) {
57
+ const url = `${API_BASE}/bot${token}/sendVoice`;
58
+ const form = new FormData();
59
+ form.append("chat_id", String(chatId));
60
+ if (caption) form.append("caption", caption);
61
+ if (duration) form.append("duration", String(duration));
62
+
63
+ const buf = Buffer.isBuffer(audio) ? audio : fs.readFileSync(audio);
64
+ const name = typeof audio === "string" ? path.basename(audio) : "voice.ogg";
65
+ const blob = new Blob([buf], { type: "audio/ogg" });
66
+ form.append("voice", blob, name);
67
+
68
+ const res = await fetch(url, { method: "POST", body: form });
69
+ const json = await res.json();
70
+ if (!json.ok) throw new Error(`sendVoice failed: ${json.description || res.status}`);
71
+ return json.result;
72
+ }
73
+
74
+ /**
75
+ * Send an audio file (MP3, M4A, etc — shown in Telegram music player).
76
+ * @param {string} token
77
+ * @param {string|number} chatId
78
+ * @param {string|Buffer} audio Path or Buffer
79
+ * @param {object} [opts]
80
+ * @param {string} [opts.caption]
81
+ * @param {string} [opts.title]
82
+ * @param {string} [opts.performer]
83
+ */
84
+ /**
85
+ * Send any file as a Telegram document (PDF, zip, txt, etc).
86
+ * @param {string} token
87
+ * @param {string|number} chatId
88
+ * @param {string|Buffer} document Path or Buffer of document data
89
+ * @param {object} [opts]
90
+ * @param {string} [opts.caption]
91
+ * @param {string} [opts.filename] override filename for Buffer input
92
+ * @param {string} [opts.mime_type]
93
+ */
94
+ export async function sendDocument(token, chatId, document, { caption, filename, mime_type } = {}) {
95
+ const url = `${API_BASE}/bot${token}/sendDocument`;
96
+ const form = new FormData();
97
+ form.append("chat_id", String(chatId));
98
+ if (caption) form.append("caption", caption);
99
+
100
+ // URL string → let Telegram fetch it
101
+ if (typeof document === "string" && /^https?:\/\//.test(document)) {
102
+ form.append("document", document);
103
+ } else {
104
+ const buf = Buffer.isBuffer(document) ? document : fs.readFileSync(document);
105
+ const name =
106
+ filename ||
107
+ (typeof document === "string" ? path.basename(document) : "document.bin");
108
+ const blob = new Blob([buf], { type: mime_type || "application/octet-stream" });
109
+ form.append("document", blob, name);
110
+ }
111
+
112
+ const res = await fetch(url, { method: "POST", body: form });
113
+ const json = await res.json();
114
+ if (!json.ok) throw new Error(`sendDocument failed: ${json.description || res.status}`);
115
+ return json.result;
116
+ }
117
+
118
+ export async function sendAudio(token, chatId, audio, { caption, title, performer } = {}) {
119
+ const url = `${API_BASE}/bot${token}/sendAudio`;
120
+ const form = new FormData();
121
+ form.append("chat_id", String(chatId));
122
+ if (caption) form.append("caption", caption);
123
+ if (title) form.append("title", title);
124
+ if (performer) form.append("performer", performer);
125
+
126
+ const buf = Buffer.isBuffer(audio) ? audio : fs.readFileSync(audio);
127
+ const name = typeof audio === "string" ? path.basename(audio) : "audio.mp3";
128
+ const blob = new Blob([buf], { type: "audio/mpeg" });
129
+ form.append("audio", blob, name);
130
+
131
+ const res = await fetch(url, { method: "POST", body: form });
132
+ const json = await res.json();
133
+ if (!json.ok) throw new Error(`sendAudio failed: ${json.description || res.status}`);
134
+ return json.result;
135
+ }
136
+
137
+ // Audio transcription is delegated to the central dispatcher
138
+ // (../transcription.js) which handles local (faster-whisper via Python) +
139
+ // OpenAI cloud fallback. See that module for config keys.
140
+
141
+ /**
142
+ * Download a file from Telegram servers.
143
+ * Returns the local file path where it was saved.
144
+ */
145
+ export async function downloadTelegramFile(token, fileId, destDir) {
146
+ // Step 1: get file path from Telegram
147
+ const infoRes = await fetch(`${API_BASE}/bot${token}/getFile?file_id=${fileId}`);
148
+ const infoJson = await infoRes.json();
149
+ if (!infoJson.ok) throw new Error(`getFile failed: ${infoJson.description}`);
150
+ const filePath = infoJson.result.file_path; // e.g. "photos/file_123.jpg"
151
+ const ext = path.extname(filePath) || ".jpg";
152
+ const fileName = `tg_${fileId.slice(-8)}_${Date.now()}${ext}`;
153
+ const localPath = path.join(destDir, fileName);
154
+
155
+ // Step 2: download
156
+ const dlRes = await fetch(`${API_BASE}/file/bot${token}/${filePath}`);
157
+ if (!dlRes.ok) throw new Error(`download failed: ${dlRes.status}`);
158
+ const buf = Buffer.from(await dlRes.arrayBuffer());
159
+ fs.writeFileSync(localPath, buf);
160
+ return localPath;
161
+ }
162
+
@@ -0,0 +1,54 @@
1
+ // Helpers that need the running daemon's projects registry to do their work
2
+ // (projectMeta + resolveProject). Pure / config-only helpers live in
3
+ // core/agent/tools/helpers.js.
4
+ import path from "node:path";
5
+
6
+ export function projectMeta(projects, entry) {
7
+ const meta = projects.list().find((p) => p.id === entry.id);
8
+ return {
9
+ id: entry.id,
10
+ name: meta?.name || path.basename(entry.path),
11
+ path: entry.path,
12
+ };
13
+ }
14
+
15
+ export function resolveProject(projects, target, { allowMulti = false } = {}) {
16
+ if (target === undefined || target === null || target === "") {
17
+ if (allowMulti) return null;
18
+ const defaultProject = projects.get(0);
19
+ if (defaultProject) return defaultProject;
20
+ const all = projects.list();
21
+ if (all.length === 1) return projects.get(all[0].id);
22
+ throw new Error(`multiple projects registered (${all.length}); specify project=<id|name|path>`);
23
+ }
24
+
25
+ const tgt = String(target);
26
+ if (tgt.toLowerCase() === "default") {
27
+ const defaultProject = projects.get(0);
28
+ if (!defaultProject) throw new Error("default project not available");
29
+ return defaultProject;
30
+ }
31
+
32
+ if (typeof target === "number" || /^\d+$/.test(tgt)) {
33
+ const entry = projects.get(parseInt(tgt, 10));
34
+ if (!entry) throw new Error(`project id ${target} not found`);
35
+ return entry;
36
+ }
37
+
38
+ const all = projects.list();
39
+ const byPath = all.find((p) => p.path === path.resolve(tgt));
40
+ if (byPath) return projects.get(byPath.id);
41
+
42
+ const byName = all.find((p) => p.name === tgt);
43
+ if (byName) return projects.get(byName.id);
44
+
45
+ const tgtLow = tgt.toLowerCase();
46
+ const fuzzy = all.filter(
47
+ (p) => p.name.toLowerCase().includes(tgtLow) || p.path.toLowerCase().includes(tgtLow)
48
+ );
49
+ if (fuzzy.length === 1) return projects.get(fuzzy[0].id);
50
+ if (fuzzy.length > 1) {
51
+ throw new Error(`project "${tgt}" is ambiguous; matches: ${fuzzy.map((p) => p.name).join(", ")}`);
52
+ }
53
+ throw new Error(`project "${tgt}" not found`);
54
+ }
@@ -15,13 +15,15 @@ import { execFile } from "node:child_process";
15
15
  import os from "node:os";
16
16
  import fs from "node:fs";
17
17
  import path from "node:path";
18
- import { callEngine } from "../../core/engines/index.js";
19
- import { runSuperAgent } from "./super-agent.js";
20
- import { computeSuppressedTools } from "../../core/agent/index.js";
21
- import { readAgents } from "../../core/parser.js";
22
- import { buildAgentSystem } from "../../core/agent-system.js";
23
- import { resolveAgentName, SUPERAGENT_ACTOR_ID } from "../../core/identity.js";
24
- import { resolveArtifactRef, ARTIFACTS_SKIP_SIGNAL } from "../../core/artifacts-store.js";
18
+ import { callEngine } from "#core/engines/index.js";
19
+ import { runSuperAgent } from "#core/agent/super-agent.js";
20
+ import { computeSuppressedTools } from "#core/agent/index.js";
21
+ import { readAgents } from "#core/apc/parser.js";
22
+ import { buildAgentSystem } from "#core/agent/build-agent-system.js";
23
+ import { resolveAgentName, SUPERAGENT_ACTOR_ID } from "#core/identity/index.js";
24
+ import { resolveArtifactRef, ARTIFACTS_SKIP_SIGNAL } from "#core/stores/artifacts.js";
25
+ import { ensureRoutineMemory, readRoutineMemoryForPrompt, routineMemoryPath } from "#core/stores/routine-memory.js";
26
+ import { CHANNELS } from "#core/constants/channels.js";
25
27
  import {
26
28
  listRoutines,
27
29
  getRoutine,
@@ -32,7 +34,7 @@ import {
32
34
  getDueRoutines,
33
35
  parseSchedule,
34
36
  computeNextRun,
35
- } from "../../core/routines-store.js";
37
+ } from "#core/stores/routines.js";
36
38
 
37
39
  export {
38
40
  listRoutines,
@@ -89,7 +91,7 @@ async function handleExecAgent(ctx, routine) {
89
91
 
90
92
  project.logMessage({
91
93
  agent_slug: slug,
92
- channel: "routine",
94
+ channel: CHANNELS.ROUTINE,
93
95
  direction: "out",
94
96
  type: "agent",
95
97
  actor_id: slug,
@@ -129,16 +131,30 @@ async function handleSuperAgent(ctx, routine) {
129
131
  plugins,
130
132
  registries,
131
133
  prompt,
132
- channel: "routine",
134
+ channel: CHANNELS.ROUTINE,
133
135
  channelMeta: {
134
136
  routineName: routine.name,
137
+ routineId: routine.id || "",
138
+ routineSchedule: routine.schedule || "",
139
+ routineLastRun: routine.last_run || "",
140
+ routineMemoryPath: (() => {
141
+ try {
142
+ ensureRoutineMemory(project.storagePath || project.path, routine.id, routine.name);
143
+ return routineMemoryPath(project.storagePath || project.path, routine.id);
144
+ } catch { return ""; }
145
+ })(),
146
+ routineMemory: (() => {
147
+ try {
148
+ return readRoutineMemoryForPrompt(project.storagePath || project.path, routine.id);
149
+ } catch { return ""; }
150
+ })(),
135
151
  projectPath: project.path,
136
152
  },
137
153
  suppressTools: suppressTools.length > 0 ? suppressTools : null,
138
154
  });
139
155
 
140
156
  project.logMessage({
141
- channel: "routine",
157
+ channel: CHANNELS.ROUTINE,
142
158
  direction: "out",
143
159
  type: "agent",
144
160
  actor_id: SUPERAGENT_ACTOR_ID,
@@ -346,7 +362,7 @@ export async function runRoutineNow(ctx, routine) {
346
362
  disable: isOnce,
347
363
  });
348
364
  ctx.project.logMessage?.({
349
- channel: "routine",
365
+ channel: CHANNELS.ROUTINE,
350
366
  direction: "out",
351
367
  type: "system",
352
368
  actor_id: "apx:routine",
@@ -8,8 +8,8 @@ import path from "node:path";
8
8
  import fs from "node:fs";
9
9
  import { fileURLToPath } from "node:url";
10
10
  import { ProjectManager } from "./db.js";
11
- import { McpRegistry } from "../../core/mcp/runner.js";
12
- import { readAgents } from "../../core/parser.js";
11
+ import { McpRegistry } from "#core/mcp/runner.js";
12
+ import { readAgents } from "#core/apc/parser.js";
13
13
 
14
14
  const __filename = fileURLToPath(import.meta.url);
15
15
  const __dirname = path.dirname(__filename);
@@ -23,7 +23,7 @@
23
23
  import fs from "node:fs";
24
24
  import path from "node:path";
25
25
  import { randomBytes, randomUUID, createHash } from "node:crypto";
26
- import { APX_HOME } from "../../core/config.js";
26
+ import { APX_HOME } from "#core/config/index.js";
27
27
 
28
28
  export const CLIENTS_PATH = path.join(APX_HOME, "clients.json");
29
29
 
@@ -30,7 +30,7 @@ import fs from "node:fs";
30
30
  import path from "node:path";
31
31
  import { spawn, exec } from "node:child_process";
32
32
  import { fileURLToPath } from "node:url";
33
- import { logInfo, logWarn, logError } from "../../core/logging.js";
33
+ import { logInfo, logWarn, logError } from "#core/logging.js";
34
34
 
35
35
  const __filename = fileURLToPath(import.meta.url);
36
36
  const __dirname = path.dirname(__filename);
@@ -76,7 +76,7 @@ export function resolveTranscriptionLanguage(localCfg, userLang) {
76
76
 
77
77
  async function getConfig() {
78
78
  try {
79
- const { readConfig } = await import("../core/config.js");
79
+ const { readConfig } = await import("#core/config/index.js");
80
80
  const cfg = readConfig() || {};
81
81
  const t = cfg.transcription || {};
82
82
  const openaiKey = cfg.engines?.openai?.api_key || process.env.OPENAI_API_KEY || "";
@@ -1,7 +1,7 @@
1
1
  // Wake-up message — sent via Telegram once per daemon restart (with cooldown).
2
2
  import fetch from "node-fetch";
3
- import { readIdentity, writeIdentity } from "../../core/identity.js";
4
- import { resolveProvider, getAdapter } from "../../core/engines/index.js";
3
+ import { readIdentity, writeIdentity } from "#core/identity/index.js";
4
+ import { resolveProvider, getAdapter } from "#core/engines/index.js";
5
5
 
6
6
  const WAKEUP_COOLDOWN_MS = 30 * 60 * 1000; // 30 min
7
7
 
@@ -1,8 +1,8 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import { findApfRoot, readAgents, readVaultAgents, readVaultAgent, VAULT_DIR, SLUG_RE } from "../../../core/parser.js";
4
- import { writeAgentFile, writeVaultAgentFile, removeVaultAgent, restoreVaultAgent, addImportedAgent, ensureAgentDir } from "../../../core/scaffold.js";
5
- import { ensureAgentRuntimeDir, agentMemoryPath } from "../../../core/agent-memory.js";
3
+ import { findApfRoot, readAgents, readVaultAgents, readVaultAgent, VAULT_DIR, SLUG_RE } from "#core/apc/parser.js";
4
+ import { writeAgentFile, writeVaultAgentFile, removeVaultAgent, restoreVaultAgent, addImportedAgent, ensureAgentDir } from "#core/apc/scaffold.js";
5
+ import { ensureAgentRuntimeDir, agentMemoryPath } from "#core/agent/memory.js";
6
6
  import { http } from "../http.js";
7
7
 
8
8
  // ── ANSI ──────────────────────────────────────────────────────────────────────
@@ -1,6 +1,40 @@
1
+ import fs from "node:fs";
2
+ import { spawn } from "node:child_process";
3
+ import path from "node:path";
1
4
  import { http } from "../http.js";
2
5
  import { resolveProjectId } from "./project.js";
3
6
 
7
+ // First two bytes of an executable script. Used as a hint when the file
8
+ // doesn't have the exec bit but clearly intends to run (shebang line).
9
+ const SHEBANG = "#!";
10
+
11
+ // Decide if an artifact is "runnable": exec bit on the file, OR the file
12
+ // starts with a shebang. If shebang-but-not-exec, we set the bit before
13
+ // spawning so `./script` works without the user having to chmod +x.
14
+ function detectRunnable(absPath) {
15
+ let stat;
16
+ try {
17
+ stat = fs.statSync(absPath);
18
+ } catch {
19
+ return { runnable: false, reason: "not_found" };
20
+ }
21
+ if (!stat.isFile()) return { runnable: false, reason: "not_a_file" };
22
+ const execBit = (stat.mode & 0o111) !== 0;
23
+ let hasShebang = false;
24
+ try {
25
+ const fd = fs.openSync(absPath, "r");
26
+ const buf = Buffer.alloc(2);
27
+ fs.readSync(fd, buf, 0, 2, 0);
28
+ fs.closeSync(fd);
29
+ hasShebang = buf.toString("utf8") === SHEBANG;
30
+ } catch {
31
+ // ignore: hasShebang stays false
32
+ }
33
+ if (execBit) return { runnable: true, reason: "exec_bit", autoChmod: false };
34
+ if (hasShebang) return { runnable: true, reason: "shebang", autoChmod: true };
35
+ return { runnable: false, reason: "no_exec_no_shebang" };
36
+ }
37
+
4
38
  export async function cmdArtifactCreate(args) {
5
39
  const name = args._[0];
6
40
  if (!name) throw new Error("apx artifact create: missing <name>");
@@ -43,3 +77,68 @@ export async function cmdArtifactRemove(args) {
43
77
  await http.delete(`/projects/${pid}/artifacts/${encodeURIComponent(name)}`);
44
78
  console.log(`removed artifact "${name}"`);
45
79
  }
80
+
81
+ // `apx artifact run <name> [-- args...]`
82
+ //
83
+ // Resolves the artifact's absolute path via the daemon (single source of
84
+ // truth for project storage), then spawns the file LOCALLY with stdio
85
+ // inherited so the caller sees output as if they typed `./artifact <args>`.
86
+ // Detection is lenient: exec bit OR shebang → runnable; shebang-only files
87
+ // get a one-shot chmod +x so the user doesn't have to do it themselves.
88
+ //
89
+ // The remaining argv after `run <name>` is passed straight to the script,
90
+ // so `apx artifact run hello.sh -- hola mundo` becomes `./hello.sh hola mundo`.
91
+ export async function cmdArtifactRun(args) {
92
+ const name = args._[0];
93
+ if (!name) throw new Error("apx artifact run: missing <name>");
94
+ const pid = await resolveProjectId(args?.flags?.project);
95
+ // Pull the artifact record from the daemon for the absolute path.
96
+ let entry;
97
+ try {
98
+ entry = await http.get(`/projects/${pid}/artifacts/${encodeURIComponent(name)}`);
99
+ } catch (e) {
100
+ throw new Error(`artifact "${name}" not found in project #${pid}: ${e.message}`);
101
+ }
102
+ const absPath = entry.path;
103
+ if (!absPath || !fs.existsSync(absPath)) {
104
+ throw new Error(`artifact "${name}" path missing on disk: ${absPath}`);
105
+ }
106
+
107
+ const detection = detectRunnable(absPath);
108
+ if (!detection.runnable) {
109
+ const hint = detection.reason === "no_exec_no_shebang"
110
+ ? "no es ejecutable (sin shebang ni bit +x). Probá `apx artifact show` para ver el contenido."
111
+ : `no se puede ejecutar (${detection.reason}).`;
112
+ throw new Error(`artifact "${name}" ${hint}`);
113
+ }
114
+ if (detection.autoChmod) {
115
+ try {
116
+ const st = fs.statSync(absPath);
117
+ fs.chmodSync(absPath, st.mode | 0o111);
118
+ } catch (e) {
119
+ throw new Error(`could not chmod +x ${absPath}: ${e.message}`);
120
+ }
121
+ }
122
+
123
+ // Everything after the artifact name is forwarded to the script. The CLI's
124
+ // argv parser already strips top-level flags; what's left in `_` is name
125
+ // + script args.
126
+ const scriptArgs = (args._ || []).slice(1);
127
+ const cwd = path.dirname(absPath);
128
+ const child = spawn(absPath, scriptArgs, { stdio: "inherit", cwd });
129
+
130
+ await new Promise((resolve) => {
131
+ child.on("exit", (code, signal) => {
132
+ if (signal) {
133
+ // Killed by signal — surface a non-zero exit for shell pipelines.
134
+ process.exit(128 + (signal === "SIGINT" ? 2 : 15));
135
+ }
136
+ process.exit(code ?? 0);
137
+ });
138
+ child.on("error", (err) => {
139
+ console.error(`apx artifact run: spawn failed — ${err.message}`);
140
+ resolve();
141
+ process.exit(1);
142
+ });
143
+ });
144
+ }
@@ -1,7 +1,7 @@
1
1
  // apx command — list and show workflow commands from .apc/commands/
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
- import { findApfRoot } from "../../../core/parser.js";
4
+ import { findApfRoot } from "#core/apc/parser.js";
5
5
  import { http } from "../http.js";
6
6
  import { resolveProjectId } from "./project.js";
7
7
 
@@ -1,6 +1,7 @@
1
1
  import { http } from "../http.js";
2
2
  import { resolveProjectId } from "./project.js";
3
- import { readConfig, writeConfig } from "../../../core/config.js";
3
+ import { readConfig, writeConfig } from "#core/config/index.js";
4
+ import { PERMISSION_MODES, DEFAULT_PERMISSION_MODE } from "#core/constants/permissions.js";
4
5
 
5
6
  function parseValue(raw) {
6
7
  // best-effort: try JSON first (covers numbers, bools, objects, arrays, null,
@@ -61,7 +62,7 @@ export function cmdPermission(args = {}) {
61
62
  const cfg = readConfig();
62
63
  cfg.super_agent = cfg.super_agent || {};
63
64
  if (sub === "show" || sub === "get" || sub === "ls") {
64
- console.log(`permission_mode=${cfg.super_agent.permission_mode || "automatico"}`);
65
+ console.log(`permission_mode=${cfg.super_agent.permission_mode || DEFAULT_PERMISSION_MODE}`);
65
66
  console.log(`allowed_tools=${(cfg.super_agent.allowed_tools || []).join(",") || "(none)"}`);
66
67
  return;
67
68
  }
@@ -15,7 +15,7 @@ import {
15
15
  LINUX_DESKTOP_PATH,
16
16
  WIN_RUN_KEY,
17
17
  WIN_RUN_NAME,
18
- } from "../../../core/desktop/autostart.js";
18
+ } from "#core/desktop/autostart.js";
19
19
 
20
20
  // Re-exports — kept so existing tests (tests/desktop-autostart.test.js)
21
21
  // can still import these directly from the CLI module.
@@ -1,5 +1,6 @@
1
1
  import { http } from "../http.js";
2
2
  import { resolveProjectId } from "./project.js";
3
+ import { CHANNELS } from "#core/constants/channels.js";
3
4
 
4
5
  /**
5
6
  * Resolve exec target from CLI args.
@@ -63,7 +64,7 @@ export async function cmdExec(args) {
63
64
  const pid = await resolveProjectId(args?.flags?.project);
64
65
  const body = {
65
66
  prompt,
66
- channel: "cli",
67
+ channel: CHANNELS.CLI,
67
68
  channelMeta: { cwd: process.cwd() },
68
69
  };
69
70
  if (args.flags.model && args.flags.model !== true) body.model = args.flags.model;
@@ -1,5 +1,5 @@
1
1
  import readline from "node:readline";
2
- import { readIdentity, writeIdentity } from "../../../core/identity.js";
2
+ import { readIdentity, writeIdentity } from "#core/identity/index.js";
3
3
 
4
4
  function ask(rl, question, defaultVal) {
5
5
  return new Promise((resolve) => {
@@ -22,7 +22,7 @@ export async function cmdIdentity(args) {
22
22
  console.log("No identity configured. Run: apx identity wizard");
23
23
  return;
24
24
  }
25
- const { readConfig } = await import("../../../core/config.js");
25
+ const { readConfig } = await import("#core/config/index.js");
26
26
  const cfg = readConfig();
27
27
  console.log("");
28
28
  console.log(` Agent name : ${id.agent_name}`);
@@ -1,5 +1,5 @@
1
1
  import path from "node:path";
2
- import { initApf } from "../../../core/scaffold.js";
2
+ import { initApf } from "#core/apc/scaffold.js";
3
3
 
4
4
  export function cmdInit(args) {
5
5
  const dir = args._[0] || ".";
@@ -1,6 +1,6 @@
1
1
  import { http } from "../http.js";
2
2
  import { resolveProjectId } from "./project.js";
3
- import { findApfRoot } from "../../../core/parser.js";
3
+ import { findApfRoot } from "#core/apc/parser.js";
4
4
 
5
5
  const VALID_SCOPES = new Set(["shared", "runtime", "global", "all"]);
6
6
 
@@ -1,6 +1,6 @@
1
1
  import fs from "node:fs";
2
- import { findApfRoot } from "../../../core/parser.js";
3
- import { agentMemoryPath, readAgentMemory, writeAgentMemory, ensureAgentRuntimeDir } from "../../../core/agent-memory.js";
2
+ import { findApfRoot } from "#core/apc/parser.js";
3
+ import { agentMemoryPath, readAgentMemory, writeAgentMemory, ensureAgentRuntimeDir } from "#core/agent/memory.js";
4
4
  import { http } from "../http.js";
5
5
 
6
6
  function requireRoot() {
@@ -1,11 +1,22 @@
1
- import { readConfig, writeConfig } from "../../../core/config.js";
1
+ import { readConfig, writeConfig } from "#core/config/index.js";
2
2
  import {
3
3
  probeAllProviders,
4
4
  resolveActiveModel,
5
- fallbackOrder,
6
- modelForProvider,
5
+ fallbackModels,
6
+ parseModelId,
7
7
  DEFAULT_FALLBACK_ORDER,
8
- } from "../../../core/agent/model-router.js";
8
+ } from "#core/agent/model-router.js";
9
+
10
+ function providersFromFallback(cfg) {
11
+ const seen = [];
12
+ for (const m of fallbackModels(cfg)) {
13
+ try {
14
+ const p = parseModelId(m).provider;
15
+ if (!seen.includes(p)) seen.push(p);
16
+ } catch { /* skip malformed */ }
17
+ }
18
+ return seen.length ? seen : [...DEFAULT_FALLBACK_ORDER];
19
+ }
9
20
 
10
21
  function parseValue(raw) {
11
22
  try {
@@ -56,7 +67,7 @@ export async function cmdModel(args = {}) {
56
67
  console.log("Model router");
57
68
  console.log(` primary: ${cfg.super_agent.model || "(not set)"}`);
58
69
  console.log(` fallback: ${cfg.super_agent.model_fallback.enabled !== false ? "on" : "off"}`);
59
- console.log(` order: ${fallbackOrder(cfg).join(" → ")}`);
70
+ console.log(` order: ${providersFromFallback(cfg).join(" → ")}`);
60
71
  if (active) {
61
72
  console.log(` active: ${active.modelId}${active.fromFallback ? " (fallback)" : ""}`);
62
73
  }
@@ -133,4 +144,3 @@ export async function cmdModel(args = {}) {
133
144
  throw new Error(`unknown model subcommand: ${sub}. Try: status | order | key | set | test | enable | disable`);
134
145
  }
135
146
 
136
- export { modelForProvider, fallbackOrder };
@@ -1,5 +1,5 @@
1
1
  import path from "node:path";
2
- import { findApfRoot } from "../../../core/parser.js";
2
+ import { findApfRoot } from "#core/apc/parser.js";
3
3
  import { http } from "../http.js";
4
4
 
5
5
  function requireRoot() {