@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
@@ -2,7 +2,7 @@ import fs from "node:fs";
2
2
  import os from "node:os";
3
3
  import path from "node:path";
4
4
  import crypto from "node:crypto";
5
- import { transcribe } from "../../transcription.js";
5
+ import { transcribe } from "#host/daemon/transcription.js";
6
6
 
7
7
  export default {
8
8
  name: "transcribe_audio",
@@ -1,6 +1,6 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import { confirmedProperty, resolveProject, safePathJoin } from "../helpers.js";
3
+ import { resolveProject, safePathJoin } from "../helpers.js";
4
4
 
5
5
  export default {
6
6
  name: "write_file",
@@ -15,7 +15,6 @@ export default {
15
15
  project: { type: "string" },
16
16
  path: { type: "string", description: "relative path inside the project" },
17
17
  content: { type: "string" },
18
- confirmed: confirmedProperty("true only after explicit user confirmation for this exact file write"),
19
18
  },
20
19
  required: ["path", "content"],
21
20
  },
@@ -0,0 +1,74 @@
1
+ // Pure / config-only helpers used by tool handlers. Anything that needs the
2
+ // running daemon's projects registry (projectMeta, resolveProject) lives in
3
+ // host/daemon/projects-helpers.js and is re-exported here for back-compat.
4
+ import path from "node:path";
5
+ import { agentSkills, buildAgentSystem as buildCoreAgentSystem } from "#core/agent/build-agent-system.js";
6
+ import { buildConfirmDescription } from "#core/confirmation/index.js";
7
+ import { PERMISSION_MODES, DEFAULT_PERMISSION_MODE } from "#core/constants/permissions.js";
8
+
9
+ export { projectMeta, resolveProject } from "#host/daemon/projects-helpers.js";
10
+
11
+ export function safePathJoin(root, sub = ".") {
12
+ const target = path.resolve(root, sub || ".");
13
+ const rootResolved = path.resolve(root);
14
+ if (target !== rootResolved && !target.startsWith(rootResolved + path.sep)) {
15
+ throw new Error(`path "${sub}" escapes the project root`);
16
+ }
17
+ return target;
18
+ }
19
+
20
+ export function skillsFromFields(fields = {}) {
21
+ return agentSkills({ fields });
22
+ }
23
+
24
+ export function agentRow(agent) {
25
+ return {
26
+ slug: agent.slug,
27
+ role: agent.fields.Role || null,
28
+ model: agent.fields.Model || null,
29
+ language: agent.fields.Language || null,
30
+ description: agent.fields.Description || null,
31
+ skills: skillsFromFields(agent.fields),
32
+ };
33
+ }
34
+
35
+ export function buildAgentSystem(project, agent, opts = {}) {
36
+ return buildCoreAgentSystem(project, agent, opts);
37
+ }
38
+
39
+ export function createPermissionGuard(globalConfig = {}, {
40
+ requestConfirmation = null,
41
+ } = {}) {
42
+ const permissionMode = globalConfig.super_agent?.permission_mode || DEFAULT_PERMISSION_MODE;
43
+ const allowedTools = new Set(globalConfig.super_agent?.allowed_tools || []);
44
+
45
+ // async so tools can `await requirePermission(...)` and the confirmation
46
+ // dialog resolves transparently before execution continues. The model never
47
+ // self-approves: the only path past a blocked call is the interface's own
48
+ // confirmation dialog via the requestConfirmation callback.
49
+ return async function requirePermission(tool, { dangerous = false, args } = {}) {
50
+ if (permissionMode === PERMISSION_MODES.TOTAL) return;
51
+
52
+ const blocked =
53
+ (permissionMode === PERMISSION_MODES.PERMISO && !allowedTools.has(tool)) ||
54
+ (permissionMode === PERMISSION_MODES.AUTOMATICO && dangerous);
55
+
56
+ if (!blocked) return;
57
+
58
+ const description = buildConfirmDescription(tool, args || {});
59
+
60
+ if (!requestConfirmation) {
61
+ // No confirmation channel wired for this invocation context (e.g. routine,
62
+ // autonomous agent). Surface a clear message so the model can explain it.
63
+ throw new Error(`Action requires user confirmation: ${description}`);
64
+ }
65
+
66
+ const userConfirmed = await requestConfirmation(tool, args || {}, description);
67
+
68
+ if (!userConfirmed) {
69
+ throw new Error(`User did not confirm: ${description}`);
70
+ }
71
+ // Confirmed — fall through, tool executes normally.
72
+ };
73
+ }
74
+
@@ -19,13 +19,13 @@
19
19
  // super-agent-tools/tools/, no import in index.js.
20
20
 
21
21
  import fs from "node:fs";
22
- import { TOOL_DEFINITIONS } from "../../../core/tools/registry.js";
23
- import { TOKEN_PATH } from "../../../core/config.js";
22
+ import { TOOL_DEFINITIONS } from "#core/tools/registry.js";
23
+ import { TOKEN_PATH } from "#core/config/index.js";
24
24
 
25
25
  // The bridge POSTs to the daemon's OWN HTTP server, which is behind the bearer
26
26
  // auth middleware (see api/shared.js). Without a token every bridged tool call
27
27
  // (web_search, browser_*, http_*, glob, grep) comes back 401 "unauthorized" —
28
- // which is exactly what Roby hit. We read the daemon's master token from
28
+ // which is exactly what the super-agent hit. We read the daemon's master token from
29
29
  // ~/.apx/daemon.token (the same file the CLI authenticates with) and cache it.
30
30
  let cachedToken = null;
31
31
  function daemonToken() {
@@ -1,34 +1,34 @@
1
- import listProjects from "./tools/list-projects.js";
2
- import listAgents from "./tools/list-agents.js";
3
- import listVaultAgents from "./tools/list-vault-agents.js";
4
- import importAgent from "./tools/import-agent.js";
5
- import addProject from "./tools/add-project.js";
6
- import listMcps from "./tools/list-mcps.js";
7
- import readAgentMemory from "./tools/read-agent-memory.js";
8
- import remember from "./tools/remember.js";
9
- import readSelfMemory from "./tools/read-self-memory.js";
10
- import listFiles from "./tools/list-files.js";
11
- import readFile from "./tools/read-file.js";
12
- import writeFile from "./tools/write-file.js";
13
- import editFile from "./tools/edit-file.js";
14
- import runShell from "./tools/run-shell.js";
15
- import tailMessages from "./tools/tail-messages.js";
16
- import searchMessages from "./tools/search-messages.js";
17
- import searchSessions from "./tools/search-sessions.js";
18
- import callAgent from "./tools/call-agent.js";
19
- import callMcp from "./tools/call-mcp.js";
20
- import callRuntime from "./tools/call-runtime.js";
21
- import sendTelegram from "./tools/send-telegram.js";
22
- import setIdentity from "./tools/set-identity.js";
23
- import setPermissionMode from "./tools/set-permission-mode.js";
24
- import searchFiles from "./tools/search-files.js";
25
- import listSkills from "./tools/list-skills.js";
26
- import loadSkill from "./tools/load-skill.js";
27
- import transcribeAudio from "./tools/transcribe-audio.js";
28
- import askQuestions from "./tools/ask-questions.js";
29
- import createTask from "./tools/create-task.js";
30
- import listTasks from "./tools/list-tasks.js";
31
- import discoverTools from "./tools/discover-tools.js";
1
+ import listProjects from "./handlers/list-projects.js";
2
+ import listAgents from "./handlers/list-agents.js";
3
+ import listVaultAgents from "./handlers/list-vault-agents.js";
4
+ import importAgent from "./handlers/import-agent.js";
5
+ import addProject from "./handlers/add-project.js";
6
+ import listMcps from "./handlers/list-mcps.js";
7
+ import readAgentMemory from "./handlers/read-agent-memory.js";
8
+ import remember from "./handlers/remember.js";
9
+ import readSelfMemory from "./handlers/read-self-memory.js";
10
+ import listFiles from "./handlers/list-files.js";
11
+ import readFile from "./handlers/read-file.js";
12
+ import writeFile from "./handlers/write-file.js";
13
+ import editFile from "./handlers/edit-file.js";
14
+ import runShell from "./handlers/run-shell.js";
15
+ import tailMessages from "./handlers/tail-messages.js";
16
+ import searchMessages from "./handlers/search-messages.js";
17
+ import searchSessions from "./handlers/search-sessions.js";
18
+ import callAgent from "./handlers/call-agent.js";
19
+ import callMcp from "./handlers/call-mcp.js";
20
+ import callRuntime from "./handlers/call-runtime.js";
21
+ import sendTelegram from "./handlers/send-telegram.js";
22
+ import setIdentity from "./handlers/set-identity.js";
23
+ import setPermissionMode from "./handlers/set-permission-mode.js";
24
+ import searchFiles from "./handlers/search-files.js";
25
+ import listSkills from "./handlers/list-skills.js";
26
+ import loadSkill from "./handlers/load-skill.js";
27
+ import transcribeAudio from "./handlers/transcribe-audio.js";
28
+ import askQuestions from "./handlers/ask-questions.js";
29
+ import createTask from "./handlers/create-task.js";
30
+ import listTasks from "./handlers/list-tasks.js";
31
+ import discoverTools from "./handlers/discover-tools.js";
32
32
  import { createPermissionGuard } from "./helpers.js";
33
33
  import { buildBridgedTools, DEFAULT_CATEGORIES } from "./registry-bridge.js";
34
34
 
@@ -334,7 +334,6 @@ export function makeToolHandlers(ctx) {
334
334
  const toolCtx = {
335
335
  ...ctx,
336
336
  requirePermission: createPermissionGuard(ctx.globalConfig || {}, {
337
- implicitConfirmation: !!ctx.implicitConfirmation,
338
337
  requestConfirmation: ctx.requestConfirmation || null,
339
338
  }),
340
339
  };
@@ -0,0 +1,37 @@
1
+ // Vault = global agent templates. Two layers:
2
+ // - bundled defaults shipped with APX (assets/agent-vault-defaults/)
3
+ // - user overrides + brand-new ones in ~/.apx/agents/ (copy-on-write)
4
+ //
5
+ // This module owns the *normalisation* of vault input patches: which fields
6
+ // the public surface accepts, how they get split (skills/tools = arrays),
7
+ // and how casing maps to the on-disk frontmatter convention (Title case).
8
+ // Read/write to disk lives in apc/scaffold.js + apc/parser.js — this is the
9
+ // pure transform layer so HTTP routes, CLI commands, and the super-agent
10
+ // `import_agent` tool all agree on what fields exist.
11
+
12
+ export const VAULT_PATCH_FIELDS = ["role", "model", "language", "description", "skills", "tools", "is_master"];
13
+
14
+ /**
15
+ * Normalize a user-provided vault patch:
16
+ * - accept lowercase OR Title case keys
17
+ * - drop unknown / undefined / null fields
18
+ * - turn skills/tools into a string[] (accepting csv strings or arrays)
19
+ * - emit Title-case keys so the writer doesn't have to guess
20
+ */
21
+ export function normalizeVaultPatch(input = {}) {
22
+ const out = {};
23
+ for (const k of VAULT_PATCH_FIELDS) {
24
+ const lower = k;
25
+ const title = k.charAt(0).toUpperCase() + k.slice(1);
26
+ const v = input[lower] ?? input[title];
27
+ if (v === undefined || v === null) continue;
28
+ if (k === "skills" || k === "tools") {
29
+ out[title] = Array.isArray(v)
30
+ ? v.map(String).map((s) => s.trim()).filter(Boolean)
31
+ : String(v).split(",").map((s) => s.trim()).filter(Boolean);
32
+ } else {
33
+ out[title] = v;
34
+ }
35
+ }
36
+ return out;
37
+ }
@@ -9,10 +9,12 @@ import {
9
9
  readVaultTombstones,
10
10
  writeVaultTombstones,
11
11
  } from "./parser.js";
12
- import { readApcContextSkill } from "./apc-skill-sync.js";
12
+ import { readApcContextSkill } from "./skill-sync.js";
13
+ import { nowIso } from "../util/time.js";
13
14
 
14
15
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
15
- const PACKAGE_ROOT = path.resolve(__dirname, "..", "..");
16
+ // Now under src/core/apc/ — one more "../" to escape than before.
17
+ const PACKAGE_ROOT = path.resolve(__dirname, "..", "..", "..");
16
18
  const BUNDLED_SKILLS_DIR = path.join(PACKAGE_ROOT, "skills");
17
19
  const RUNTIME_SKILLS_DIR = path.join(__dirname, "runtime-skills");
18
20
 
@@ -381,9 +383,6 @@ migrate.md
381
383
  .DS_Store
382
384
  `;
383
385
 
384
- function nowIso() {
385
- return new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
386
- }
387
386
 
388
387
  // Files that carry project context but are IDE-specific — candidates for APC migration.
389
388
  const SCATTERED_CONTEXT_FILES = [
@@ -1,32 +1,26 @@
1
1
  // Global APX config under ~/.apx/config.json. Cross-platform.
2
+ //
3
+ // Filesystem paths live in ./paths.js and are re-exported from here so callers
4
+ // can keep `import { APX_HOME, projectStorageRoot } from ".../config"` working.
2
5
  import fs from "node:fs";
3
- import os from "node:os";
4
6
  import path from "node:path";
5
-
6
- export const APX_HOME = path.join(os.homedir(), ".apx");
7
- export const CONFIG_PATH = path.join(APX_HOME, "config.json");
8
- export const PID_PATH = path.join(APX_HOME, "daemon.pid");
9
- export const LOG_PATH = path.join(APX_HOME, "daemon.log");
10
- export const TELEGRAM_STATE_PATH = path.join(APX_HOME, "telegram-state.json");
11
- export const TOKEN_PATH = path.join(APX_HOME, "daemon.token");
12
- // Global channel messages (telegram, direct, whatsapp, …) live here,
13
- // separated from any project. Structure: ~/.apx/messages/<channel>/YYYY-MM-DD.jsonl
14
- export const GLOBAL_MESSAGES_DIR = path.join(APX_HOME, "messages");
15
- // Per-project runtime storage (conversations, sessions) — never in the repo.
16
- // Structure: ~/.apx/projects/<apx_id>/agents/<slug>/conversations/
17
- export const PROJECT_STORE_ROOT = path.join(APX_HOME, "projects");
18
- export const DEFAULT_PROJECT_ID = "default";
19
- export const DEFAULT_PROJECT_STORE = path.join(PROJECT_STORE_ROOT, DEFAULT_PROJECT_ID);
20
-
21
- export function projectStorageRoot(apxId) {
22
- return path.join(PROJECT_STORE_ROOT, apxId);
23
- }
24
-
25
- export function ensureProjectStorage(apxId) {
26
- const root = projectStorageRoot(apxId);
27
- fs.mkdirSync(root, { recursive: true });
28
- return root;
29
- }
7
+ import { APX_HOME, CONFIG_PATH } from "./paths.js";
8
+ import { PERMISSION_MODES, DEFAULT_PERMISSION_MODE } from "../constants/permissions.js";
9
+
10
+ export {
11
+ APX_HOME,
12
+ CONFIG_PATH,
13
+ PID_PATH,
14
+ LOG_PATH,
15
+ TELEGRAM_STATE_PATH,
16
+ TOKEN_PATH,
17
+ GLOBAL_MESSAGES_DIR,
18
+ PROJECT_STORE_ROOT,
19
+ DEFAULT_PROJECT_ID,
20
+ DEFAULT_PROJECT_STORE,
21
+ projectStorageRoot,
22
+ ensureProjectStorage,
23
+ } from "./paths.js";
30
24
 
31
25
  const DEFAULT_CONFIG = {
32
26
  port: 7430,
@@ -60,7 +54,7 @@ const DEFAULT_CONFIG = {
60
54
  name: "apx",
61
55
  model: "", // e.g. "ollama:llama3.2:3b"
62
56
  system: "", // optional override; defaults in src/core/agent/prompts/
63
- permission_mode: "automatico", // total | automatico | permiso
57
+ permission_mode: PERMISSION_MODES.AUTOMATICO, // total | automatico | permiso
64
58
  allowed_tools: [], // used by permission_mode="permiso"
65
59
  // Model fallback: ordered list. Each item carries its own provider
66
60
  // prefix; the array order IS the attempt order. The router tries the
@@ -0,0 +1,32 @@
1
+ // Filesystem layout for the APX home directory. Re-exported by config/index.js
2
+ // for back-compat; new code can import directly from here for paths-only work.
3
+ import fs from "node:fs";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+
7
+ export const APX_HOME = path.join(os.homedir(), ".apx");
8
+ export const CONFIG_PATH = path.join(APX_HOME, "config.json");
9
+ export const PID_PATH = path.join(APX_HOME, "daemon.pid");
10
+ export const LOG_PATH = path.join(APX_HOME, "daemon.log");
11
+ export const TELEGRAM_STATE_PATH = path.join(APX_HOME, "telegram-state.json");
12
+ export const TOKEN_PATH = path.join(APX_HOME, "daemon.token");
13
+
14
+ // Global channel messages (telegram, direct, whatsapp, …) live here,
15
+ // separated from any project. Structure: ~/.apx/messages/<channel>/YYYY-MM-DD.jsonl
16
+ export const GLOBAL_MESSAGES_DIR = path.join(APX_HOME, "messages");
17
+
18
+ // Per-project runtime storage (conversations, sessions) — never in the repo.
19
+ // Structure: ~/.apx/projects/<apx_id>/agents/<slug>/conversations/
20
+ export const PROJECT_STORE_ROOT = path.join(APX_HOME, "projects");
21
+ export const DEFAULT_PROJECT_ID = "default";
22
+ export const DEFAULT_PROJECT_STORE = path.join(PROJECT_STORE_ROOT, DEFAULT_PROJECT_ID);
23
+
24
+ export function projectStorageRoot(apxId) {
25
+ return path.join(PROJECT_STORE_ROOT, apxId);
26
+ }
27
+
28
+ export function ensureProjectStorage(apxId) {
29
+ const root = projectStorageRoot(apxId);
30
+ fs.mkdirSync(root, { recursive: true });
31
+ return root;
32
+ }
@@ -0,0 +1,8 @@
1
+ // Stable actor identifiers used in message records, sessions, and confirmation
2
+ // flows. These are NEVER the persona name (which can be renamed) — they are
3
+ // machine ids so storage stays stable across persona renames.
4
+
5
+ // The super-agent (the daemon's default action loop, runs when no project agent
6
+ // was named). Identity (display name + personality) lives in identity.json and
7
+ // is resolved at runtime via resolveAgentName().
8
+ export const SUPERAGENT_ACTOR_ID = "super_agent";
@@ -0,0 +1,19 @@
1
+ // Channel names — kept in one place so a typo can't silently route the wrong
2
+ // prompt or skip a logging path. Each value is the exact string the daemon
3
+ // expects in `channel:` fields of API requests, message records, and
4
+ // CHANNEL_PROMPT_FILES keys.
5
+ //
6
+ // Channels are SURFACES (where the user is). Voice is NOT a channel — it's a
7
+ // MODE that layers on top of a surface via channelMeta.voice.
8
+ export const CHANNELS = Object.freeze({
9
+ TELEGRAM: "telegram",
10
+ CLI: "cli",
11
+ ROUTINE: "routine",
12
+ API: "api",
13
+ WEB: "web", // Web admin big chat
14
+ WEB_SIDEBAR: "web_sidebar", // Web admin docked sidebar
15
+ WEB_CODE: "web_code", // Web admin `/m/code` (OpenCode-style)
16
+ DECK: "deck", // Mobile cockpit dashboard
17
+ DESKTOP: "desktop", // Electron capsule (always voice mode)
18
+ CODE: "code", // `apx code` — terminal coding session
19
+ });
@@ -0,0 +1,5 @@
1
+ // Public re-exports — pick the constants you need from a single entry point.
2
+ export * from "./permissions.js";
3
+ export * from "./channels.js";
4
+ export * from "./roles.js";
5
+ export * from "./actors.js";
@@ -0,0 +1,17 @@
1
+ // Permission modes for the super-agent. The value lives in
2
+ // config.super_agent.permission_mode and is read by createPermissionGuard
3
+ // (super-agent-tools/helpers.js).
4
+ //
5
+ // total — execute every tool without confirmation.
6
+ // automatico — read-only / safe shell runs directly; destructive,
7
+ // outbound, runtime, MCP, and filesystem-mutating actions
8
+ // require user confirmation via the interface dialog.
9
+ // permiso — only allowed_tools run directly; everything else requires
10
+ // user confirmation.
11
+ export const PERMISSION_MODES = Object.freeze({
12
+ TOTAL: "total",
13
+ AUTOMATICO: "automatico",
14
+ PERMISO: "permiso",
15
+ });
16
+
17
+ export const DEFAULT_PERMISSION_MODE = PERMISSION_MODES.AUTOMATICO;
@@ -0,0 +1,9 @@
1
+ // Sender roles resolved by core/identity/telegram.js. Owner is whoever owns
2
+ // the channel; contact is a known person added to the roster; guest is anyone
3
+ // else (no permissions, must be claimed by the owner or added by a real user
4
+ // via terminal/web).
5
+ export const SENDER_ROLES = Object.freeze({
6
+ OWNER: "owner",
7
+ CONTACT: "contact",
8
+ GUEST: "guest",
9
+ });
@@ -0,0 +1,63 @@
1
+ // Shared streaming primitives for engine adapters.
2
+ //
3
+ // Every cloud chat API that supports streaming sends a sequence of newline
4
+ // terminated lines: SSE for OpenAI / Anthropic / Groq / OpenRouter, JSONL for
5
+ // Ollama / Gemini. Adapters used to inline the same TextDecoder loop —
6
+ // `streamLines` collapses that into one place; `streamSseDataEvents` walks
7
+ // the SSE-flavoured form on top of it (`data: <json>` lines).
8
+
9
+ /**
10
+ * Yield complete newline-terminated lines from a streaming `fetch` Response.
11
+ * Handles chunk boundaries falling mid-line and never yields empty trailing
12
+ * tokens. The caller decides what each line means (raw JSON, SSE `data:` row,
13
+ * etc.).
14
+ */
15
+ export async function* streamLines(response) {
16
+ if (!response || !response.body) return;
17
+ const decoder = new TextDecoder();
18
+ let buf = "";
19
+ for await (const chunk of response.body) {
20
+ buf += decoder.decode(chunk, { stream: true });
21
+ let nl;
22
+ while ((nl = buf.indexOf("\n")) !== -1) {
23
+ const line = buf.slice(0, nl);
24
+ buf = buf.slice(nl + 1);
25
+ // Yield even empty lines — SSE uses them as event boundaries.
26
+ yield line;
27
+ }
28
+ }
29
+ // Flush whatever the decoder left buffered + the trailing partial line, if any.
30
+ buf += decoder.decode();
31
+ if (buf.length > 0) yield buf;
32
+ }
33
+
34
+ /**
35
+ * Walk an SSE stream and yield the parsed JSON body of each `data: <json>`
36
+ * row. Skips `data: [DONE]` and rows whose body isn't valid JSON. Lines that
37
+ * don't start with `data: ` (comments, event names, blank separators) are
38
+ * ignored — adapters that need those should use streamLines directly.
39
+ */
40
+ export async function* streamSseDataEvents(response) {
41
+ for await (const line of streamLines(response)) {
42
+ if (!line.startsWith("data: ")) continue;
43
+ const raw = line.slice(6).trim();
44
+ if (!raw || raw === "[DONE]") continue;
45
+ let evt;
46
+ try { evt = JSON.parse(raw); } catch { continue; }
47
+ yield evt;
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Walk a newline-delimited JSON stream (Ollama-style) and yield each parsed
53
+ * object. Empty lines and invalid JSON are skipped.
54
+ */
55
+ export async function* streamJsonLines(response) {
56
+ for await (const line of streamLines(response)) {
57
+ const trimmed = line.trim();
58
+ if (!trimmed) continue;
59
+ let obj;
60
+ try { obj = JSON.parse(trimmed); } catch { continue; }
61
+ yield obj;
62
+ }
63
+ }
@@ -1,5 +1,6 @@
1
1
  // Anthropic Messages API adapter (https://docs.anthropic.com/en/api/messages).
2
2
  // No SDK dependency — direct fetch, keeps the daemon lean.
3
+ import { streamSseDataEvents } from "./_streaming.js";
3
4
 
4
5
  const API_BASE = "https://api.anthropic.com/v1/messages";
5
6
  const API_VERSION = "2023-06-01";
@@ -74,33 +75,21 @@ export default {
74
75
  throw new Error(`anthropic ${res.status}: ${err.slice(0, 200)}`);
75
76
  }
76
77
 
77
- const decoder = new TextDecoder();
78
78
  let text = "";
79
79
  let inputTokens = 0;
80
80
  let outputTokens = 0;
81
81
  let stopReason = null;
82
- let buf = "";
83
82
 
84
- for await (const chunk of res.body) {
85
- buf += decoder.decode(chunk, { stream: true });
86
- const lines = buf.split("\n");
87
- buf = lines.pop(); // keep incomplete last line
88
- for (const line of lines) {
89
- if (!line.startsWith("data: ")) continue;
90
- const raw = line.slice(6).trim();
91
- if (raw === "[DONE]") continue;
92
- let evt;
93
- try { evt = JSON.parse(raw); } catch { continue; }
94
- if (evt.type === "content_block_delta" && evt.delta?.type === "text_delta") {
95
- const t = evt.delta.text || "";
96
- if (t) { text += t; onToken(t); }
97
- } else if (evt.type === "message_delta") {
98
- stopReason = evt.delta?.stop_reason || stopReason;
99
- outputTokens = evt.usage?.output_tokens || outputTokens;
100
- } else if (evt.type === "message_start") {
101
- inputTokens = evt.message?.usage?.input_tokens || 0;
102
- outputTokens = evt.message?.usage?.output_tokens || 0;
103
- }
83
+ for await (const evt of streamSseDataEvents(res)) {
84
+ if (evt.type === "content_block_delta" && evt.delta?.type === "text_delta") {
85
+ const t = evt.delta.text || "";
86
+ if (t) { text += t; onToken(t); }
87
+ } else if (evt.type === "message_delta") {
88
+ stopReason = evt.delta?.stop_reason || stopReason;
89
+ outputTokens = evt.usage?.output_tokens || outputTokens;
90
+ } else if (evt.type === "message_start") {
91
+ inputTokens = evt.message?.usage?.input_tokens || 0;
92
+ outputTokens = evt.message?.usage?.output_tokens || 0;
104
93
  }
105
94
  }
106
95
 
@@ -1,6 +1,7 @@
1
1
  // Ollama adapter (https://github.com/ollama/ollama/blob/main/docs/api.md#generate-a-chat-completion).
2
2
  // Local-only. No API key. Default base_url http://localhost:11434.
3
3
  import { pingUrl, fetchJsonWithTimeout, modelInOllamaTags } from "./_health.js";
4
+ import { streamJsonLines } from "./_streaming.js";
4
5
 
5
6
  function baseUrl(config) {
6
7
  return config.base_url || process.env.OLLAMA_HOST || "http://localhost:11434";
@@ -102,25 +103,15 @@ export default {
102
103
  const t = await res.text();
103
104
  throw new Error(`ollama ${res.status}: ${t}`);
104
105
  }
105
- const decoder = new TextDecoder();
106
106
  let text = "";
107
107
  let inputTokens = 0;
108
108
  let outputTokens = 0;
109
- let buf = "";
110
- for await (const chunk of res.body) {
111
- buf += decoder.decode(chunk, { stream: true });
112
- const lines = buf.split("\n");
113
- buf = lines.pop();
114
- for (const line of lines) {
115
- if (!line.trim()) continue;
116
- let evt;
117
- try { evt = JSON.parse(line); } catch { continue; }
118
- const t = evt.message?.content || "";
119
- if (t) { text += t; onToken(t); }
120
- if (evt.done) {
121
- inputTokens = evt.prompt_eval_count || 0;
122
- outputTokens = evt.eval_count || 0;
123
- }
109
+ for await (const evt of streamJsonLines(res)) {
110
+ const t = evt.message?.content || "";
111
+ if (t) { text += t; onToken(t); }
112
+ if (evt.done) {
113
+ inputTokens = evt.prompt_eval_count || 0;
114
+ outputTokens = evt.eval_count || 0;
124
115
  }
125
116
  }
126
117
  return {
@@ -0,0 +1,8 @@
1
+ // Public entry for everything identity-related.
2
+ //
3
+ // self.js → the super-agent's persona (identity.json) — agent_name,
4
+ // personality, owner.
5
+ // telegram.js → sender resolution for inbound Telegram messages — who is
6
+ // writing right now (owner / contact / guest), per-user_id roster.
7
+ export * from "./self.js";
8
+ export * from "./telegram.js";
@@ -1,13 +1,13 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import os from "node:os";
4
+ import { SUPERAGENT_ACTOR_ID } from "../constants/actors.js";
4
5
 
5
- export const IDENTITY_PATH = path.join(os.homedir(), ".apx", "identity.json");
6
+ // Re-export so callers that already imported the actor id from here keep
7
+ // working. The single source of truth lives in core/constants/actors.js.
8
+ export { SUPERAGENT_ACTOR_ID };
6
9
 
7
- // Stable machine id for the daemon-level "super-agent" mode. Never tied to the
8
- // persona name, so renaming the persona (identity.json) doesn't break message
9
- // attribution / history queries.
10
- export const SUPERAGENT_ACTOR_ID = "super_agent";
10
+ export const IDENTITY_PATH = path.join(os.homedir(), ".apx", "identity.json");
11
11
 
12
12
  // Shown when no persona is configured yet. Brand of the app, not a persona.
13
13
  export const SUPERAGENT_DISPLAY_FALLBACK = "APX";
@@ -16,7 +16,7 @@ import {
16
16
  findTelegramChannel,
17
17
  upsertContact,
18
18
  upsertTelegramChannel,
19
- } from "./config.js";
19
+ } from "../config/index.js";
20
20
 
21
21
  function telegramDisplayName(from) {
22
22
  const full = [from?.first_name, from?.last_name].filter(Boolean).join(" ").trim();
@@ -1,6 +1,6 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import { APX_HOME } from "./config.js";
3
+ import { APX_HOME } from "./config/index.js";
4
4
 
5
5
  export const LOG_DIR = path.join(APX_HOME, "logs");
6
6
  export const ERROR_TRACE_PATH = path.join(LOG_DIR, "errors.jsonl");
@@ -1,5 +1,5 @@
1
1
  // APX mascot — a panda that appears in different moods across the CLI.
2
- // Usage: import { mascot } from '../core/mascot.js'; mascot('happy');
2
+ // Usage: import { mascot } from '#core/mascot.js'; mascot('happy');
3
3
 
4
4
  const R = "\x1b[0m";
5
5
  const B = "\x1b[1m";