@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
@@ -1,4 +1,4 @@
1
- // "Hilos activos en otros canales" — a tiny, recency-based awareness block.
1
+ // "Active threads on other channels" — a tiny, recency-based awareness block.
2
2
  //
3
3
  // Unlike the Memory Broker (semantic RAG + remembered notes), this reads the
4
4
  // raw cross-channel message log and surfaces the most recent turn from every
@@ -11,8 +11,8 @@
11
11
 
12
12
  import fs from "node:fs";
13
13
  import path from "node:path";
14
- import { GLOBAL_MESSAGES_DIR } from "../config.js";
15
- import { parseDayJsonl } from "../messages-store.js";
14
+ import { GLOBAL_MESSAGES_DIR } from "../config/index.js";
15
+ import { parseDayJsonl } from "../stores/messages.js";
16
16
 
17
17
  const BODY_CAP = 70;
18
18
 
@@ -20,9 +20,9 @@ function ago(ts) {
20
20
  const then = Date.parse(ts);
21
21
  if (!Number.isFinite(then)) return "";
22
22
  const mins = Math.max(0, Math.round((Date.now() - then) / 60000));
23
- if (mins < 60) return `hace ${mins} min`;
23
+ if (mins < 60) return `${mins} min ago`;
24
24
  const hrs = Math.round(mins / 60);
25
- return `hace ${hrs} h`;
25
+ return `${hrs} h ago`;
26
26
  }
27
27
 
28
28
  // Prefer the last USER turn (most recognizable for "lo de antes"); fall back to
@@ -69,7 +69,7 @@ function readChannelRecentTurn(baseDir, channel, sinceMs) {
69
69
  return turn;
70
70
  }
71
71
 
72
- // Build the "# Hilos activos en otros canales" block. Returns "" when there's
72
+ // Build the "# Active threads on other channels" block. Returns "" when there's
73
73
  // nothing recent on another channel (or the feature is disabled).
74
74
  export function buildActiveThreadsBlock(currentChannel, { config, messagesDir } = {}) {
75
75
  try {
@@ -111,10 +111,10 @@ export function buildActiveThreadsBlock(currentChannel, { config, messagesDir }
111
111
  });
112
112
 
113
113
  return [
114
- "# Hilos activos en otros canales",
115
- 'Charlas recientes en otras superficies (NO es este chat). Si el usuario dice',
116
- '"seguimos" / "lo de antes" / "lo de telegram", probablemente sea uno de estos —',
117
- "retomalo naturalmente; usá search_messages para el detalle exacto.",
114
+ "# Active threads on other channels",
115
+ "Recent chatter on other surfaces (NOT this chat). If the user says",
116
+ '"let\'s continue" / "the thing from before" / "the telegram one" it\'s probably',
117
+ "one of these — pick it up naturally; use search_messages for exact detail.",
118
118
  "",
119
119
  ...lines,
120
120
  ].join("\n");
@@ -1,10 +1,10 @@
1
1
  // Memory Broker (Pieza 4) — runs before each super-agent turn and assembles the
2
- // [MEMORIA RELEVANTE] block injected into the system prompt.
2
+ // [RELEVANT MEMORY] block injected into the system prompt.
3
3
  //
4
4
  // incoming message
5
5
  // → embed it + RAG retriever (Pieza 2) → top-K relevant chunks
6
6
  // → read the last N entries of memory.md
7
- // → merge, dedupe, format as a [MEMORIA RELEVANTE] block
7
+ // → merge, dedupe, format as a [RELEVANT MEMORY] block
8
8
  //
9
9
  // Silent and bounded: the whole thing races an 800 ms budget. If RAG is slow
10
10
  // (Ollama lagging), the block still returns with whatever memory.md gave us —
@@ -79,7 +79,7 @@ function bulletFor({ date, channel, text }) {
79
79
  return `• ${d}${c} ${trunc(text)}`.replace(/\s+/g, " ").trim();
80
80
  }
81
81
 
82
- // Build the [MEMORIA RELEVANTE] block. Returns "" when there's nothing useful.
82
+ // Build the [RELEVANT MEMORY] block. Returns "" when there's nothing useful.
83
83
  //
84
84
  // opts: { store, config, memoryPath, budgetMs, topK, channel, embed }
85
85
  export async function buildMemoryBlock(message, opts = {}) {
@@ -132,13 +132,13 @@ export async function buildMemoryBlock(message, opts = {}) {
132
132
  if (bullets.length === 0) return "";
133
133
 
134
134
  return [
135
- "# Memoria relevante (cross-channel)",
136
- "Contexto recuperado de tu memoria y del historial de todos los canales. Tratá",
137
- "como hechos conocidos; si arrancás una sesión nueva y algo de acá sigue abierto,",
138
- "mencionalo naturalmente (\"ayer estuvimos con X, ¿seguimos?\") sin que te pregunten.",
135
+ "# Relevant memory (cross-channel)",
136
+ "Context recovered from your notebook and from the message log across channels.",
137
+ "Treat these as known facts. If a fresh session opens and something here is still",
138
+ "open, bring it up naturally in the user's language (e.g. \"yesterday we were on X — shall we continue?\") without being asked.",
139
139
  "",
140
- "[MEMORIA RELEVANTE]",
140
+ "[RELEVANT MEMORY]",
141
141
  ...bullets,
142
- "[/MEMORIA RELEVANTE]",
142
+ "[/RELEVANT MEMORY]",
143
143
  ].join("\n");
144
144
  }
@@ -15,8 +15,8 @@
15
15
 
16
16
  import fs from "node:fs";
17
17
  import path from "node:path";
18
- import { GLOBAL_MESSAGES_DIR } from "../config.js";
19
- import { parseDayJsonl, appendGlobalMessage } from "../messages-store.js";
18
+ import { GLOBAL_MESSAGES_DIR } from "../config/index.js";
19
+ import { parseDayJsonl, appendGlobalMessage } from "../stores/messages.js";
20
20
  import { callEngine } from "../engines/index.js";
21
21
 
22
22
  const DEFAULT_MAX_TURNS = 60;
@@ -12,7 +12,7 @@
12
12
  // affected piece simply contributes nothing.
13
13
 
14
14
  import path from "node:path";
15
- import { APX_HOME } from "../config.js";
15
+ import { APX_HOME } from "../config/index.js";
16
16
  import { ensureSelfMemoryFile } from "../agent/self-memory.js";
17
17
  import fs from "node:fs";
18
18
  import { openMemoryStore } from "./store.js";
@@ -141,7 +141,7 @@ export function stopMemory() {
141
141
  _ready = null;
142
142
  }
143
143
 
144
- // Build the [MEMORIA RELEVANTE] block for a turn (Pieza 4). Never throws —
144
+ // Build the [RELEVANT MEMORY] block for a turn (Pieza 4). Never throws —
145
145
  // returns "" on any failure so the prompt builder can drop the block.
146
146
  export async function memoryBlockFor(message, { config, channel, budgetMs } = {}) {
147
147
  try {
@@ -19,7 +19,7 @@
19
19
 
20
20
  import fs from "node:fs";
21
21
  import path from "node:path";
22
- import { GLOBAL_MESSAGES_DIR, APX_HOME } from "../config.js";
22
+ import { GLOBAL_MESSAGES_DIR, APX_HOME } from "../config/index.js";
23
23
  import { SELF_MEMORY_PATH, parseSelfMemoryEntries } from "../agent/self-memory.js";
24
24
  import { embedBatch, embedOne } from "./embeddings.js";
25
25
 
@@ -11,7 +11,8 @@
11
11
  // translation.
12
12
  import fs from "node:fs";
13
13
  import path from "node:path";
14
- import { randomUUID } from "node:crypto";
14
+ import { nowIso } from "../util/time.js";
15
+ import { shortId as makeShortId } from "../util/ids.js";
15
16
 
16
17
  function sessionsDir(storagePath) {
17
18
  return path.join(storagePath, "code-sessions");
@@ -21,13 +22,8 @@ function sessionFile(storagePath, id) {
21
22
  return path.join(sessionsDir(storagePath), `${id}.json`);
22
23
  }
23
24
 
24
- function nowIso() {
25
- return new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
26
- }
27
-
28
25
  function shortId() {
29
- const hex = randomUUID().replace(/-/g, "").slice(0, 8);
30
- return "cs_" + parseInt(hex, 16).toString(36).padStart(6, "0").slice(-6);
26
+ return makeShortId("cs");
31
27
  }
32
28
 
33
29
  // Atomic write: tmp file + rename so a crash mid-write can't corrupt a session.
@@ -53,6 +49,7 @@ function toRow(s) {
53
49
  title: s.title,
54
50
  mode: s.mode,
55
51
  model: s.model || null,
52
+ agentSlug: s.agentSlug || null,
56
53
  createdAt: s.createdAt,
57
54
  updatedAt: s.updatedAt,
58
55
  messageCount: Array.isArray(s.messages) ? s.messages.length : 0,
@@ -82,7 +79,7 @@ export function getCodeSession(storagePath, id) {
82
79
 
83
80
  /**
84
81
  * Create a new session.
85
- * fields: { projectId, title?, model?, mode?, git? }
82
+ * fields: { projectId, title?, model?, mode?, git?, agentSlug? }
86
83
  */
87
84
  export function createCodeSession(storagePath, fields = {}) {
88
85
  const id = shortId();
@@ -95,6 +92,7 @@ export function createCodeSession(storagePath, fields = {}) {
95
92
  updatedAt: ts,
96
93
  model: fields.model || null,
97
94
  mode: fields.mode === "plan" ? "plan" : "build",
95
+ agentSlug: fields.agentSlug || null,
98
96
  git: fields.git && typeof fields.git === "object" ? fields.git : null,
99
97
  messages: [],
100
98
  };
@@ -112,6 +110,7 @@ export function updateCodeSession(storagePath, id, patch = {}) {
112
110
  if (patch.title != null) session.title = String(patch.title).trim() || session.title;
113
111
  if (patch.model !== undefined) session.model = patch.model || null;
114
112
  if (patch.mode === "plan" || patch.mode === "build") session.mode = patch.mode;
113
+ if (patch.agentSlug !== undefined) session.agentSlug = patch.agentSlug || null;
115
114
  if (patch.git !== undefined) session.git = patch.git;
116
115
  session.updatedAt = nowIso();
117
116
  writeJson(sessionFile(storagePath, id), session);
@@ -18,9 +18,11 @@
18
18
 
19
19
  import fs from "node:fs";
20
20
  import path from "node:path";
21
- import { GLOBAL_MESSAGES_DIR } from "./config.js";
21
+ import { GLOBAL_MESSAGES_DIR } from "../config/index.js";
22
+ import { CHANNELS } from "../constants/channels.js";
23
+ import { SUPERAGENT_ACTOR_ID } from "../constants/actors.js";
22
24
 
23
- const nowIso = () => new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
25
+ import { nowIso } from "../util/time.js";
24
26
 
25
27
  function dayPathJsonl(projectRoot, ts) {
26
28
  const day = (ts || nowIso()).slice(0, 10);
@@ -63,7 +65,7 @@ function inferActorKind({ actor_kind, type, actor_id, meta = {} } = {}) {
63
65
  if (explicit) return explicit;
64
66
  if (type === "compact") return "compact";
65
67
  if (type === "user" || type === "tool" || type === "system") return type;
66
- if (type === "agent") return actor_id === "super_agent" ? "superagent" : "agent";
68
+ if (type === "agent") return actor_id === SUPERAGENT_ACTOR_ID ? "superagent" : "agent";
67
69
  return null;
68
70
  }
69
71
 
@@ -531,7 +533,7 @@ export function getRecentChannelTurnsFromFs({
531
533
 
532
534
  // Telegram-specific wrapper kept for back-compat with existing call sites.
533
535
  export function getRecentTelegramTurnsFromFs(opts = {}) {
534
- return getRecentChannelTurnsFromFs({ ...opts, channel: "telegram" });
536
+ return getRecentChannelTurnsFromFs({ ...opts, channel: CHANNELS.TELEGRAM });
535
537
  }
536
538
 
537
539
  // ---------------------------------------------------------------------------
@@ -0,0 +1,71 @@
1
+ // Per-routine memory.md — durable notes scoped to a single routine.
2
+ //
3
+ // Path: <projectStoragePath>/routines/<routineId>/memory.md
4
+ //
5
+ // The routine handler (host/daemon/routines.js) creates the file on first read
6
+ // and injects a bounded slice into the super-agent prompt via
7
+ // channelMeta.routineMemory. The routine can write back with future tooling;
8
+ // today we only read.
9
+ //
10
+ // Distinct from:
11
+ // - Agent memory (~/.apx/projects/<id>/agents/<slug>/memory.md) — per-agent.
12
+ // - Super-agent self-memory (~/.apx/memory.md) — global to the super-agent.
13
+ import fs from "node:fs";
14
+ import path from "node:path";
15
+
16
+ const PROMPT_LIMIT = 1500;
17
+
18
+ export function routineMemoryDir(storagePath, routineId) {
19
+ return path.join(storagePath, "routines", String(routineId || "_unknown"));
20
+ }
21
+
22
+ export function routineMemoryPath(storagePath, routineId) {
23
+ return path.join(routineMemoryDir(storagePath, routineId), "memory.md");
24
+ }
25
+
26
+ /** Read the memory body. Returns "" when the file doesn't exist. Never throws. */
27
+ export function readRoutineMemory(storagePath, routineId) {
28
+ try {
29
+ return fs.readFileSync(routineMemoryPath(storagePath, routineId), "utf8");
30
+ } catch {
31
+ return "";
32
+ }
33
+ }
34
+
35
+ /** Bounded slice for the system prompt. Returns "" when empty. */
36
+ export function readRoutineMemoryForPrompt(storagePath, routineId, limit = PROMPT_LIMIT) {
37
+ const body = readRoutineMemory(storagePath, routineId).trim();
38
+ if (!body) return "";
39
+ if (body.length <= limit) return body;
40
+ return body.slice(0, limit).trimEnd() + "\n… (truncated)";
41
+ }
42
+
43
+ /** Ensure the routine memory file exists. Returns true if it was created. */
44
+ export function ensureRoutineMemory(storagePath, routineId, routineName = "") {
45
+ const file = routineMemoryPath(storagePath, routineId);
46
+ if (fs.existsSync(file)) return false;
47
+ fs.mkdirSync(path.dirname(file), { recursive: true });
48
+ const header = routineName
49
+ ? `# Routine memory — ${routineName}\n`
50
+ : "# Routine memory\n";
51
+ fs.writeFileSync(file, header);
52
+ return true;
53
+ }
54
+
55
+ /** Append a dated note to the routine memory. Creates the file on first write. */
56
+ export function appendRoutineMemory(storagePath, routineId, note, { routineName = "" } = {}) {
57
+ const text = String(note || "").trim();
58
+ if (!text) throw new Error("nothing to remember (empty note)");
59
+ ensureRoutineMemory(storagePath, routineId, routineName);
60
+ const file = routineMemoryPath(storagePath, routineId);
61
+ const today = new Date().toISOString().slice(0, 10);
62
+ const heading = `## ${today}`;
63
+ const oneLine = text.replace(/\n+/g, " ").trim();
64
+ const bullet = `- ${oneLine}`;
65
+ const existing = fs.readFileSync(file, "utf8");
66
+ const next = existing.includes(heading)
67
+ ? existing.trimEnd() + `\n${bullet}\n`
68
+ : existing.trimEnd() + `\n\n${heading}\n${bullet}\n`;
69
+ fs.writeFileSync(file, next);
70
+ return { path: file, note: text };
71
+ }
@@ -3,9 +3,7 @@
3
3
  import fs from "node:fs";
4
4
  import path from "node:path";
5
5
  import cronParser from "cron-parser";
6
-
7
- const nowIso = () => new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
8
- const isoToMs = (iso) => (iso ? Date.parse(iso) : 0);
6
+ import { nowIso, isoToMs } from "../util/time.js";
9
7
 
10
8
  function routinesPath(storagePath) {
11
9
  // storagePath is always ~/.apx/projects/{apxId}/ — flat, no .apc subdir needed.
@@ -0,0 +1,99 @@
1
+ // On-disk session state for external runtimes (Claude Code, Codex, OpenCode,
2
+ // Aider, Cursor Agent, Gemini CLI, Qwen Code). One markdown file per session
3
+ // under <storageRoot>/agents/<slug>/sessions/<id>.md with YAML frontmatter
4
+ // (id, agent, title, task_ref, status, started, completed, result, runtime,
5
+ // external_session_path).
6
+ //
7
+ // The "bridge" prompt-text builder that explains this layout to the external
8
+ // runtime lives in core/agent/runtime-bridge.js. Both used to live together
9
+ // in host/daemon/apc-runtime-context.js; they were split because they have
10
+ // different homes (text → core/agent, state → core/stores).
11
+ import fs from "node:fs";
12
+ import path from "node:path";
13
+ import { generateSessionId } from "./sessions.js";
14
+ import { nowIso } from "#core/util/time.js";
15
+
16
+ /** Create the APX runtime session file. Returns { id, filename, path }. */
17
+ export function createRuntimeSession({
18
+ projectRoot,
19
+ storageRoot = projectRoot,
20
+ agentSlug,
21
+ runtime,
22
+ taskRef = "",
23
+ title,
24
+ }) {
25
+ const dir = path.join(storageRoot, "agents", agentSlug, "sessions");
26
+ fs.mkdirSync(dir, { recursive: true });
27
+ const id = generateSessionId(storageRoot, agentSlug);
28
+ const file = path.join(dir, `${id}.md`);
29
+ const started = nowIso();
30
+ const sessionTitle = title || `Runtime: ${runtime}`;
31
+ const body =
32
+ `---\n` +
33
+ `id: ${id}\n` +
34
+ `agent: ${agentSlug}\n` +
35
+ `title: ${sessionTitle}\n` +
36
+ `task_ref: ${taskRef}\n` +
37
+ `status: 🔄 In progress\n` +
38
+ `started: ${started}\n` +
39
+ `completed: \n` +
40
+ `result: \n` +
41
+ `runtime: ${runtime}\n` +
42
+ `external_session_path: \n` +
43
+ `---\n\n` +
44
+ `# ${sessionTitle}\n\n`;
45
+ fs.writeFileSync(file, body);
46
+ return { id, filename: `${id}.md`, path: file };
47
+ }
48
+
49
+ /** Update session frontmatter with the external transcript path + final state. */
50
+ export function closeRuntimeSession({ filePath, externalSessionPath, exitCode, result }) {
51
+ let text = fs.readFileSync(filePath, "utf8");
52
+ text = setField(text, "completed", nowIso());
53
+ if (externalSessionPath) {
54
+ text = setField(text, "external_session_path", externalSessionPath);
55
+ }
56
+ if (typeof exitCode === "number") {
57
+ text = setField(
58
+ text,
59
+ "result",
60
+ `${exitCode === 0 ? "✅" : "⚠️"} exit ${exitCode}: ${(result || "").slice(0, 200)}`
61
+ );
62
+ } else if (result) {
63
+ text = setField(text, "result", result.slice(0, 300));
64
+ }
65
+ text = setField(text, "status", exitCode === 0 ? "✅ Completed" : "⚠️ Closed with error");
66
+ fs.writeFileSync(filePath, text);
67
+ }
68
+
69
+ function setField(text, field, value) {
70
+ if (!text.startsWith("---\n")) return text;
71
+ const end = text.indexOf("\n---", 4);
72
+ if (end === -1) return text;
73
+ const fmText = text.slice(4, end);
74
+ const lines = fmText.split("\n");
75
+ let found = false;
76
+ const out = lines.map((line) => {
77
+ if (line.startsWith(`${field}:`)) {
78
+ found = true;
79
+ return `${field}: ${value}`;
80
+ }
81
+ return line;
82
+ });
83
+ if (!found) out.push(`${field}: ${value}`);
84
+ return `---\n${out.join("\n")}\n---${text.slice(end + 4)}`;
85
+ }
86
+
87
+ /**
88
+ * Extract a self-reported "APC_RESULT: ..." line from the runtime's stdout
89
+ * (the convention printed in the bridge hint). Returns the captured string
90
+ * or null. Fallback for runtimes that can't shell out to `apx session close`.
91
+ */
92
+ export function extractRuntimeResult(stdout) {
93
+ if (!stdout || typeof stdout !== "string") return null;
94
+ const m = stdout.match(/^APC_RESULT:\s*(.+?)\s*$/m);
95
+ return m ? m[1].trim() : null;
96
+ }
97
+
98
+ // Back-compat alias.
99
+ export const extractApfResult = extractRuntimeResult;
@@ -16,7 +16,8 @@
16
16
  // caller explicitly re-opens with op="reopen".
17
17
  import fs from "node:fs";
18
18
  import path from "node:path";
19
- import { randomUUID } from "node:crypto";
19
+ import { nowIso } from "../util/time.js";
20
+ import { shortId as makeShortId } from "../util/ids.js";
20
21
 
21
22
  function tasksDir(storagePath) {
22
23
  return path.join(storagePath, "tasks");
@@ -27,14 +28,8 @@ function monthlyFile(storagePath, date = new Date()) {
27
28
  return path.join(tasksDir(storagePath), `${ym}.jsonl`);
28
29
  }
29
30
 
30
- function nowIso() {
31
- return new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
32
- }
33
-
34
31
  function shortId() {
35
- // 6 base36 chars from randomUUID's first 8 hex chars → 32 bits → ~4B keyspace.
36
- const hex = randomUUID().replace(/-/g, "").slice(0, 8);
37
- return "t_" + parseInt(hex, 16).toString(36).padStart(6, "0").slice(-6);
32
+ return makeShortId("t");
38
33
  }
39
34
 
40
35
  function appendEvent(storagePath, event) {
@@ -6,7 +6,7 @@
6
6
  import fs from "node:fs";
7
7
  import path from "node:path";
8
8
  import https from "node:https";
9
- import { APX_HOME } from "./config.js";
9
+ import { APX_HOME } from "./config/index.js";
10
10
 
11
11
  const PACKAGE_NAME = "@agentprojectcontext/apx";
12
12
  const CACHE_PATH = path.join(APX_HOME, "update-check.json");
@@ -0,0 +1,14 @@
1
+ // Short, prefix-able ids derived from a UUID. Stores use these for primary
2
+ // keys when an autoincrement isn't a fit (no SQL row counter, JSON files).
3
+ import { randomUUID } from "node:crypto";
4
+
5
+ /**
6
+ * Six-character base36 id derived from a UUID. Random enough for in-process
7
+ * uniqueness inside a single file/store; combine with a prefix when collisions
8
+ * across stores would be ambiguous.
9
+ */
10
+ export function shortId(prefix = "") {
11
+ const hex = randomUUID().replace(/-/g, "").slice(0, 8);
12
+ const id = parseInt(hex, 16).toString(36).padStart(6, "0").slice(-6);
13
+ return prefix ? `${prefix}_${id}` : id;
14
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./time.js";
2
+ export * from "./ids.js";
@@ -0,0 +1,9 @@
1
+ // Time helpers. Single source for ISO timestamps so every store agrees on
2
+ // resolution (seconds) and shape ("YYYY-MM-DDTHH:MM:SSZ").
3
+ export function nowIso() {
4
+ return new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
5
+ }
6
+
7
+ export function isoToMs(iso) {
8
+ return iso ? Date.parse(iso) : 0;
9
+ }
@@ -10,7 +10,7 @@
10
10
  import fs from "node:fs";
11
11
  import path from "node:path";
12
12
  import os from "node:os";
13
- import { readConfig } from "../config.js";
13
+ import { readConfig } from "../config/index.js";
14
14
  import {
15
15
  selectTtsEngine,
16
16
  listAvailableTtsEngines,
@@ -5,9 +5,10 @@
5
5
  // The PATCH variant is intentional: PUT would force the caller to send the
6
6
  // whole credentials block, and a UI that forgot one field would wipe secrets.
7
7
  // Dotted keys make every edit narrowly-scoped.
8
- import { readConfig, writeConfig } from "../../../core/config.js";
9
- import { resolveAgentName } from "../../../core/identity.js";
8
+ import { readConfig, writeConfig } from "#core/config/index.js";
9
+ import { resolveAgentName } from "#core/identity/index.js";
10
10
  import { setDottedKey, unsetDottedKey } from "../project-config.js";
11
+ import { PERMISSION_MODES, DEFAULT_PERMISSION_MODE } from "#core/constants/permissions.js";
11
12
 
12
13
  const SECRET_PATHS = [
13
14
  "engines.anthropic.api_key",
@@ -148,7 +149,7 @@ export function register(app, { config, scheduler, plugins }) {
148
149
  name: resolveAgentName(fresh),
149
150
  model: sa.model || "",
150
151
  system: sa.system || "",
151
- permission_mode: sa.permission_mode || "permiso",
152
+ permission_mode: sa.permission_mode || PERMISSION_MODES.PERMISO,
152
153
  allowed_tools: sa.allowed_tools || [],
153
154
  model_fallback: sa.model_fallback || { enabled: false, models: [], order: [] },
154
155
  });
@@ -3,7 +3,7 @@
3
3
  // POST /admin/shutdown — clean exit (50 ms grace so the response flushes).
4
4
  //
5
5
  // Both are auth-gated (the global middleware applies).
6
- import { readConfig } from "../../../core/config.js";
6
+ import { readConfig } from "#core/config/index.js";
7
7
  import fs from "node:fs";
8
8
  import os from "node:os";
9
9
  import path from "node:path";
@@ -6,44 +6,23 @@
6
6
  // PUT /projects/:pid/agents/:slug/memory
7
7
  import fs from "node:fs";
8
8
  import path from "node:path";
9
- import { readAgents, readVaultAgents, readVaultAgent } from "../../../core/parser.js";
9
+ import { readAgents, readVaultAgents, readVaultAgent } from "#core/apc/parser.js";
10
10
  import {
11
11
  writeAgentFile,
12
12
  writeVaultAgentFile,
13
13
  removeVaultAgent,
14
14
  restoreVaultAgent,
15
15
  ensureAgentDir,
16
- } from "../../../core/scaffold.js";
16
+ } from "#core/apc/scaffold.js";
17
17
  import {
18
18
  ensureAgentRuntimeDir,
19
19
  readAgentMemory,
20
20
  writeAgentMemory,
21
21
  agentMemoryPath,
22
22
  legacyAgentMemoryPath,
23
- } from "../../../core/agent-memory.js";
23
+ } from "#core/agent/memory.js";
24
24
  import { agentToResponse } from "./shared.js";
25
-
26
- // Lowercase the patch keys we accept on the vault and turn skills/tools into
27
- // arrays. The writer takes either case but normalizes; passing this through
28
- // it keeps the on-disk format consistent.
29
- const VAULT_PATCH_FIELDS = ["role", "model", "language", "description", "skills", "tools", "is_master"];
30
- function normalizeVaultPatch(input = {}) {
31
- const out = {};
32
- for (const k of VAULT_PATCH_FIELDS) {
33
- const lower = k;
34
- const title = k.charAt(0).toUpperCase() + k.slice(1);
35
- const v = input[lower] ?? input[title];
36
- if (v === undefined || v === null) continue;
37
- if (k === "skills" || k === "tools") {
38
- out[title] = Array.isArray(v)
39
- ? v.map(String).map((s) => s.trim()).filter(Boolean)
40
- : String(v).split(",").map((s) => s.trim()).filter(Boolean);
41
- } else {
42
- out[title] = v;
43
- }
44
- }
45
- return out;
46
- }
25
+ import { normalizeVaultPatch } from "#core/apc/agents-vault.js";
47
26
 
48
27
  export function register(app, { projects, project }) {
49
28
  // Vault = global agent templates. Two-layer: bundled defaults shipped with
@@ -13,7 +13,7 @@ import {
13
13
  listArtifacts,
14
14
  readArtifact,
15
15
  removeArtifact,
16
- } from "../../../core/artifacts-store.js";
16
+ } from "#core/stores/artifacts.js";
17
17
 
18
18
  // Same heuristic as `apx artifact run` (cli/commands/artifact.js): exec bit
19
19
  // OR shebang counts as runnable. We auto-chmod when shebang-only so the
@@ -119,6 +119,31 @@ export function register(app, { project }) {
119
119
  }
120
120
  });
121
121
 
122
+ app.patch("/projects/:pid/artifacts/:name", (req, res) => {
123
+ const p = project(req, res);
124
+ if (!p) return;
125
+ const name = decodeURIComponent(req.params.name);
126
+ const { content, newName } = req.body || {};
127
+ try {
128
+ const absPath = artifactPath(p.storagePath, name);
129
+ if (!fs.existsSync(absPath)) {
130
+ return res.status(404).json({ error: `artifact "${name}" not found` });
131
+ }
132
+ if (typeof content === "string") {
133
+ fs.writeFileSync(absPath, content, "utf8");
134
+ }
135
+ let finalName = name;
136
+ if (newName && newName !== name) {
137
+ const newAbsPath = artifactPath(p.storagePath, newName);
138
+ fs.renameSync(absPath, newAbsPath);
139
+ finalName = newName;
140
+ }
141
+ res.json({ ok: true, name: finalName });
142
+ } catch (e) {
143
+ res.status(400).json({ error: e.message });
144
+ }
145
+ });
146
+
122
147
  app.delete("/projects/:pid/artifacts/:name", (req, res) => {
123
148
  const p = project(req, res);
124
149
  if (!p) return;