@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
@@ -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,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() {
@@ -1,5 +1,14 @@
1
+ import fs from "node:fs";
1
2
  import { http } from "../http.js";
2
3
  import { resolveProjectId } from "./project.js";
4
+ import { listRoutines } from "#core/stores/routines.js";
5
+ import { projectStorageRoot } from "#core/config/index.js";
6
+ import {
7
+ routineMemoryPath,
8
+ readRoutineMemory,
9
+ appendRoutineMemory,
10
+ ensureRoutineMemory,
11
+ } from "#core/stores/routine-memory.js";
3
12
 
4
13
  function parseSpec(args) {
5
14
  // Build spec from --spec '<json>' or from --K=V pairs
@@ -120,6 +129,55 @@ export async function cmdRoutineRun(args) {
120
129
  console.log(JSON.stringify(r, null, 2));
121
130
  }
122
131
 
132
+ // Resolve a routine by name OR id to its { id, name, storagePath } so the
133
+ // memory subcommand accepts either argument transparently.
134
+ async function resolveRoutineRef(pid, refRaw) {
135
+ const ref = String(refRaw || "").trim();
136
+ if (!ref) throw new Error("missing <name|id>");
137
+ const projects = await http.get("/projects");
138
+ const project = projects.find((p) => String(p.id) === String(pid));
139
+ if (!project) throw new Error(`project #${pid} not found`);
140
+ const storagePath = project.storage_path || projectStorageRoot(project.apx_id);
141
+ const routines = listRoutines(storagePath);
142
+ const match = routines.find((r) => r.id === ref || r.name === ref);
143
+ if (!match) throw new Error(`routine "${ref}" not found in project #${pid}`);
144
+ return { id: match.id, name: match.name, storagePath };
145
+ }
146
+
147
+ export async function cmdRoutineMemory(args) {
148
+ const sub = args._[0];
149
+ const ref = args._[1];
150
+ const pid = await resolveProjectId(args?.flags?.project);
151
+
152
+ if (!sub || (sub !== "show" && sub !== "add" && sub !== "path")) {
153
+ throw new Error("usage: apx routine memory <show|add|path> <name|id> [note]");
154
+ }
155
+
156
+ const { id, name, storagePath } = await resolveRoutineRef(pid, ref);
157
+ const file = routineMemoryPath(storagePath, id);
158
+
159
+ if (sub === "path") {
160
+ console.log(file);
161
+ return;
162
+ }
163
+ if (sub === "show") {
164
+ ensureRoutineMemory(storagePath, id, name);
165
+ const body = readRoutineMemory(storagePath, id);
166
+ if (!body.trim()) {
167
+ console.log(`(empty — ${file})`);
168
+ return;
169
+ }
170
+ process.stdout.write(body);
171
+ if (!body.endsWith("\n")) console.log("");
172
+ return;
173
+ }
174
+ // add
175
+ const note = args._.slice(2).join(" ").trim();
176
+ if (!note) throw new Error("apx routine memory add: missing <note>");
177
+ const result = appendRoutineMemory(storagePath, id, note, { routineName: name });
178
+ console.log(`appended to ${result.path}`);
179
+ }
180
+
123
181
  export async function cmdRoutineHistory(args) {
124
182
  const name = args._[0];
125
183
  if (!name) throw new Error("apx routine history: missing <name>");
@@ -3,7 +3,7 @@
3
3
  // Uses the daemon's tools/search.js module directly (no HTTP roundtrip,
4
4
  // no need to have `apx daemon start` running).
5
5
 
6
- import { webSearch } from "../../../core/tools/search.js";
6
+ import { webSearch } from "#core/tools/search.js";
7
7
 
8
8
  const DIM = "\x1b[2m";
9
9
  const BOLD = "\x1b[1m";
@@ -1,10 +1,10 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { spawn } from "node:child_process";
4
- import { findApfRoot, readAgents } from "../../../core/parser.js";
5
- import { getOrCreateApxId } from "../../../core/scaffold.js";
6
- import { generateSessionId } from "../../../core/session-store.js";
7
- import { projectStorageRoot, ensureProjectStorage } from "../../../core/config.js";
4
+ import { findApfRoot, readAgents } from "#core/apc/parser.js";
5
+ import { getOrCreateApxId } from "#core/apc/scaffold.js";
6
+ import { generateSessionId } from "#core/stores/sessions.js";
7
+ import { projectStorageRoot, ensureProjectStorage } from "#core/config/index.js";
8
8
  import { http } from "../http.js";
9
9
  import { resolveProjectId } from "./project.js";
10
10
  import {
@@ -7,9 +7,10 @@ import https from "node:https";
7
7
  import http from "node:http";
8
8
  import readline from "node:readline";
9
9
  import { spawnSync } from "node:child_process";
10
- import { readConfig, writeConfig } from "../../../core/config.js";
11
- import { mascot } from "../../../core/mascot.js";
10
+ import { readConfig, writeConfig } from "#core/config/index.js";
11
+ import { mascot } from "#core/mascot.js";
12
12
  import { setupClaudePermissions } from "../claude-permissions.js";
13
+ import { PERMISSION_MODES, DEFAULT_PERMISSION_MODE } from "#core/constants/permissions.js";
13
14
 
14
15
  // ── ANSI helpers ──────────────────────────────────────────────────────────────
15
16
  const c = {
@@ -295,7 +296,7 @@ export async function cmdSetup() {
295
296
  cfg.super_agent.enabled = true;
296
297
  cfg.super_agent.model = chosenModel;
297
298
  cfg.super_agent.system = "";
298
- cfg.super_agent.permission_mode = cfg.super_agent.permission_mode || "automatico";
299
+ cfg.super_agent.permission_mode = cfg.super_agent.permission_mode || DEFAULT_PERMISSION_MODE;
299
300
 
300
301
  if (provider.id === "ollama") {
301
302
  cfg.engines.ollama.base_url = ollamaUrl;