@agentprojectcontext/apx 1.33.0 → 1.34.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 (172) hide show
  1. package/package.json +1 -1
  2. package/skills/apc-context/SKILL.md +2 -5
  3. package/skills/apx/SKILL.md +49 -61
  4. package/src/core/agent/a2a/reply.js +48 -0
  5. package/src/core/agent/build-agent-system.js +4 -3
  6. package/src/core/agent/channels/voice-context.js +98 -0
  7. package/src/core/agent/memory.js +2 -1
  8. package/src/core/agent/prompt-builder.js +2 -1
  9. package/src/core/agent/prompts/modes/code-build.md +1 -0
  10. package/src/core/agent/prompts/modes/code-plan.md +1 -0
  11. package/src/core/agent/prompts/modes/index.js +28 -0
  12. package/src/core/agent/skills/loader.js +22 -18
  13. package/src/core/agent/stream/turn-accumulator.js +73 -0
  14. package/src/core/agent/suggestions.js +37 -0
  15. package/src/core/agent/tools/handlers/add-project.js +5 -2
  16. package/src/core/agent/tools/handlers/call-runtime.js +3 -2
  17. package/src/core/agent/tools/handlers/transcribe-audio.js +1 -1
  18. package/src/core/agent/tools/helpers.js +2 -2
  19. package/src/core/agent/tools/names.js +138 -0
  20. package/src/core/agent/tools/registry-bridge.js +6 -14
  21. package/src/core/agent/tools/registry.js +68 -65
  22. package/src/core/apc/context-copy.js +27 -0
  23. package/src/core/apc/notes.js +19 -0
  24. package/src/core/apc/parser.js +13 -6
  25. package/src/core/apc/paths.js +87 -0
  26. package/src/core/apc/scaffold.js +82 -74
  27. package/src/core/apc/skill-sync.js +13 -1
  28. package/src/core/channels/telegram/dispatch.js +595 -0
  29. package/src/core/channels/telegram/helpers.js +130 -0
  30. package/src/core/config/index.js +3 -2
  31. package/src/core/config/redact.js +95 -0
  32. package/src/core/constants/channels.js +2 -0
  33. package/src/core/constants/code-modes.js +10 -0
  34. package/src/core/constants/index.js +1 -0
  35. package/src/core/deck/manifest.js +186 -0
  36. package/src/core/engines/catalog.js +83 -0
  37. package/src/core/engines/gemini.js +28 -11
  38. package/src/core/engines/index.js +11 -1
  39. package/src/core/{tools → http-tools}/browser.js +0 -1
  40. package/src/core/{tools → http-tools}/fetch.js +0 -1
  41. package/src/core/{tools → http-tools}/glob.js +0 -1
  42. package/src/core/{tools → http-tools}/grep.js +0 -1
  43. package/src/core/{tools → http-tools}/registry.js +0 -1
  44. package/src/core/{tools → http-tools}/search.js +0 -1
  45. package/src/core/i18n/en.js +9 -0
  46. package/src/core/i18n/es.js +12 -0
  47. package/src/core/i18n/index.js +54 -0
  48. package/src/core/i18n/pt.js +9 -0
  49. package/src/core/identity/telegram.js +2 -1
  50. package/src/core/mcp/runner.js +272 -14
  51. package/src/core/mcp/sources.js +3 -2
  52. package/src/core/routines/index.js +16 -0
  53. package/src/{host/daemon/routines.js → core/routines/runner.js} +36 -103
  54. package/src/core/runtime-skills/apc-context/SKILL.md +159 -0
  55. package/src/core/runtime-skills/apx/SKILL.md +95 -0
  56. package/src/core/runtime-skills/apx-mcp/SKILL.md +116 -0
  57. package/src/core/runtime-skills/{claude-code.md → claude-code/SKILL.md} +1 -0
  58. package/src/core/runtime-skills/{codex-cli.md → codex-cli/SKILL.md} +1 -0
  59. package/src/core/runtime-skills/{opencode-cli.md → opencode-cli/SKILL.md} +1 -0
  60. package/src/core/runtime-skills/{openrouter.md → openrouter/SKILL.md} +1 -0
  61. package/src/{host/daemon/env-detect.js → core/runtimes/detect.js} +1 -1
  62. package/src/core/stores/code-sessions.js +50 -2
  63. package/src/core/stores/routine-memory.js +1 -1
  64. package/src/core/stores/sessions-search.js +121 -0
  65. package/src/core/stores/sessions.js +38 -0
  66. package/src/core/vars/index.js +14 -0
  67. package/src/core/vars/interpolate.js +86 -0
  68. package/src/core/vars/sources.js +151 -0
  69. package/src/core/voice/audio-decode.js +38 -0
  70. package/src/core/voice/transcription.js +225 -0
  71. package/src/host/daemon/api/admin-config.js +5 -82
  72. package/src/host/daemon/api/agents.js +5 -5
  73. package/src/host/daemon/api/code.js +17 -169
  74. package/src/host/daemon/api/config.js +3 -4
  75. package/src/host/daemon/api/conversations.js +8 -29
  76. package/src/host/daemon/api/deck.js +37 -404
  77. package/src/host/daemon/api/engines.js +1 -50
  78. package/src/host/daemon/api/exec.js +1 -1
  79. package/src/host/daemon/api/mcps.js +32 -0
  80. package/src/host/daemon/api/routines.js +1 -1
  81. package/src/host/daemon/api/runtimes.js +4 -3
  82. package/src/host/daemon/api/sessions-search.js +24 -140
  83. package/src/host/daemon/api/sessions.js +12 -30
  84. package/src/host/daemon/api/shared.js +2 -1
  85. package/src/host/daemon/api/telegram.js +1 -11
  86. package/src/host/daemon/api/tools.js +6 -6
  87. package/src/host/daemon/api/transcribe.js +2 -2
  88. package/src/host/daemon/api/vars.js +137 -0
  89. package/src/host/daemon/api/voice.js +13 -290
  90. package/src/host/daemon/api.js +2 -0
  91. package/src/host/daemon/db.js +6 -6
  92. package/src/host/daemon/deck-exec.js +148 -0
  93. package/src/host/daemon/index.js +3 -3
  94. package/src/host/daemon/plugins/telegram/index.js +24 -687
  95. package/src/host/daemon/routines-scheduler.js +64 -0
  96. package/src/host/daemon/smoke.js +3 -2
  97. package/src/host/daemon/whisper-server.js +225 -0
  98. package/src/interfaces/cli/commands/agent.js +3 -2
  99. package/src/interfaces/cli/commands/command.js +2 -3
  100. package/src/interfaces/cli/commands/messages.js +6 -2
  101. package/src/interfaces/cli/commands/pair.js +5 -4
  102. package/src/interfaces/cli/commands/search.js +1 -1
  103. package/src/interfaces/cli/commands/sessions.js +3 -2
  104. package/src/interfaces/cli/commands/skills.js +36 -55
  105. package/src/interfaces/web/dist/assets/index-DdmSRtsz.css +1 -0
  106. package/src/interfaces/web/dist/assets/index-M4FspaCH.js +613 -0
  107. package/src/interfaces/web/dist/assets/index-M4FspaCH.js.map +1 -0
  108. package/src/interfaces/web/dist/index.html +2 -2
  109. package/src/interfaces/web/package-lock.json +182 -182
  110. package/src/interfaces/web/src/components/ModelCombobox.tsx +44 -8
  111. package/src/interfaces/web/src/components/TelegramChannelDialog.tsx +1 -1
  112. package/src/interfaces/web/src/components/chat/AskAnswersCard.tsx +76 -0
  113. package/src/interfaces/web/src/components/chat/MessageBubble.tsx +16 -3
  114. package/src/interfaces/web/src/components/chat/MessageList.tsx +23 -1
  115. package/src/interfaces/web/src/components/chat/ModelPicker.tsx +3 -1
  116. package/src/interfaces/web/src/components/code/CodeArtifactsTab.tsx +4 -4
  117. package/src/interfaces/web/src/components/code/CodeChangesTab.tsx +1 -1
  118. package/src/interfaces/web/src/components/code/CodeFileTree.tsx +3 -2
  119. package/src/interfaces/web/src/components/code/CodeFileViewer.tsx +3 -2
  120. package/src/interfaces/web/src/components/code/CodeTerminal.tsx +3 -2
  121. package/src/interfaces/web/src/components/config/GlobalConfigEditor.tsx +2 -1
  122. package/src/interfaces/web/src/components/deck/WidgetRow.tsx +2 -1
  123. package/src/interfaces/web/src/components/inputs/KeyValueList.tsx +93 -0
  124. package/src/interfaces/web/src/components/inputs/VarTokenInput.tsx +449 -0
  125. package/src/interfaces/web/src/components/settings/DefaultRouterCard.tsx +2 -1
  126. package/src/interfaces/web/src/components/settings/EnginesPanel.tsx +2 -2
  127. package/src/interfaces/web/src/components/settings/MemoryPanel.tsx +5 -4
  128. package/src/interfaces/web/src/components/settings/providers/ProviderCard.tsx +3 -2
  129. package/src/interfaces/web/src/components/settings/providers/ProviderModal.tsx +3 -2
  130. package/src/interfaces/web/src/components/ui/chat-input.tsx +5 -4
  131. package/src/interfaces/web/src/components/ui/sidebar.tsx +3 -2
  132. package/src/interfaces/web/src/components/voice/VoiceProviderModal.tsx +2 -1
  133. package/src/interfaces/web/src/constants/index.ts +1 -1
  134. package/src/interfaces/web/src/i18n/en.ts +174 -7
  135. package/src/interfaces/web/src/i18n/es.ts +179 -15
  136. package/src/interfaces/web/src/lib/api/mcps.ts +25 -0
  137. package/src/interfaces/web/src/lib/api/vars.ts +38 -0
  138. package/src/interfaces/web/src/lib/api.ts +1 -0
  139. package/src/interfaces/web/src/screens/ProjectScreen.tsx +8 -31
  140. package/src/interfaces/web/src/screens/modules/CodeScreen.tsx +1 -1
  141. package/src/interfaces/web/src/screens/modules/DeckScreen.tsx +4 -3
  142. package/src/interfaces/web/src/screens/modules/DesktopScreen.tsx +7 -6
  143. package/src/interfaces/web/src/screens/modules/VoiceScreen.tsx +4 -3
  144. package/src/interfaces/web/src/screens/project/AgentDetailScreen.tsx +1 -1
  145. package/src/interfaces/web/src/screens/project/ConfigTab.tsx +132 -1
  146. package/src/interfaces/web/src/screens/project/McpsTab.tsx +549 -104
  147. package/src/interfaces/web/src/screens/project/RoutinesTab.tsx +1 -1
  148. package/src/interfaces/web/src/screens/project/VarsTab.tsx +300 -0
  149. package/src/interfaces/web/src/types/daemon.ts +5 -0
  150. package/src/host/daemon/transcription.js +0 -538
  151. package/src/host/daemon/whisper-transcribe.py +0 -73
  152. package/src/interfaces/web/dist/assets/index-7dVT2O1S.css +0 -1
  153. package/src/interfaces/web/dist/assets/index-DWsE_8Nz.js +0 -602
  154. package/src/interfaces/web/dist/assets/index-DWsE_8Nz.js.map +0 -1
  155. /package/src/{host/daemon → core/apc}/projects-helpers.js +0 -0
  156. /package/src/{host/daemon/plugins → core/channels}/telegram/ask.js +0 -0
  157. /package/src/{host/daemon/plugins → core/channels}/telegram/media.js +0 -0
  158. /package/src/core/{tools → http-tools}/index.js +0 -0
  159. /package/{skills → src/core/runtime-skills}/apx-agency-agents/SKILL.md +0 -0
  160. /package/{skills → src/core/runtime-skills}/apx-agent/SKILL.md +0 -0
  161. /package/{skills → src/core/runtime-skills}/apx-mcp-builder/SKILL.md +0 -0
  162. /package/{skills → src/core/runtime-skills}/apx-project/SKILL.md +0 -0
  163. /package/{skills → src/core/runtime-skills}/apx-routine/SKILL.md +0 -0
  164. /package/{skills → src/core/runtime-skills}/apx-runtime/SKILL.md +0 -0
  165. /package/{skills → src/core/runtime-skills}/apx-sessions/SKILL.md +0 -0
  166. /package/{skills → src/core/runtime-skills}/apx-skill-builder/SKILL.md +0 -0
  167. /package/{skills → src/core/runtime-skills}/apx-task/SKILL.md +0 -0
  168. /package/{skills → src/core/runtime-skills}/apx-telegram/SKILL.md +0 -0
  169. /package/{skills → src/core/runtime-skills}/apx-voice/SKILL.md +0 -0
  170. /package/src/{host/daemon/compact.js → core/stores/conversations-compactor.js} +0 -0
  171. /package/src/{host/daemon → core/stores}/conversations.js +0 -0
  172. /package/src/{host/daemon → core/util}/thinking.js +0 -0
@@ -2,12 +2,15 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { readConfig, addProject as addProjectInConfig } from "#core/config/index.js";
4
4
  import { initApf } from "#core/apc/scaffold.js";
5
+ import { agentsMdFile, apcProjectFile } from "#core/apc/paths.js";
5
6
  import { projectMeta } from "../helpers.js";
6
7
 
8
+ // Stricter than core/apc/paths.js::isApcProject — `add_project` also requires
9
+ // AGENTS.md at the root, matching the gate that config.addProject() enforces.
7
10
  function isApcProject(absPath) {
8
11
  return (
9
- fs.existsSync(path.join(absPath, "AGENTS.md")) &&
10
- fs.existsSync(path.join(absPath, ".apc", "project.json"))
12
+ fs.existsSync(agentsMdFile(absPath)) &&
13
+ fs.existsSync(apcProjectFile(absPath))
11
14
  );
12
15
  }
13
16
 
@@ -2,13 +2,14 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { loggerFor } from "#core/logging.js";
4
4
  import { readAgents } from "#core/apc/parser.js";
5
+ import { apcProjectFile } from "#core/apc/paths.js";
5
6
  import {
6
7
  closeRuntimeSession,
7
8
  createRuntimeSession,
8
9
  extractRuntimeResult as extractApfResult,
9
10
  } from "#core/stores/runtime-sessions.js";
10
11
  import { buildRuntimeBridgeHint as buildApfHint } from "#core/agent/runtime-bridge.js";
11
- import { detectAll } from "#host/daemon/env-detect.js";
12
+ import { detectAll } from "#core/runtimes/detect.js";
12
13
  import {
13
14
  findEngineSessionById,
14
15
  readEngineSessionContext,
@@ -88,7 +89,7 @@ function resolveProjectForAgent(projects, project, slug) {
88
89
 
89
90
  function projectName(project) {
90
91
  try {
91
- const meta = JSON.parse(fs.readFileSync(path.join(project.path, ".apc", "project.json"), "utf8"));
92
+ const meta = JSON.parse(fs.readFileSync(apcProjectFile(project.path), "utf8"));
92
93
  if (meta.name) return meta.name;
93
94
  } catch {}
94
95
  return path.basename(project.path);
@@ -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 "#host/daemon/transcription.js";
5
+ import { transcribe } from "#core/voice/transcription.js";
6
6
 
7
7
  export default {
8
8
  name: "transcribe_audio",
@@ -1,12 +1,12 @@
1
1
  // Pure / config-only helpers used by tool handlers. Anything that needs the
2
2
  // running daemon's projects registry (projectMeta, resolveProject) lives in
3
- // host/daemon/projects-helpers.js and is re-exported here for back-compat.
3
+ // core/apc/projects-helpers.js and is re-exported here for back-compat.
4
4
  import path from "node:path";
5
5
  import { agentSkills, buildAgentSystem as buildCoreAgentSystem } from "#core/agent/build-agent-system.js";
6
6
  import { buildConfirmDescription } from "#core/confirmation/index.js";
7
7
  import { PERMISSION_MODES, DEFAULT_PERMISSION_MODE } from "#core/constants/permissions.js";
8
8
 
9
- export { projectMeta, resolveProject } from "#host/daemon/projects-helpers.js";
9
+ export { projectMeta, resolveProject } from "#core/apc/projects-helpers.js";
10
10
 
11
11
  export function safePathJoin(root, sub = ".") {
12
12
  const target = path.resolve(root, sub || ".");
@@ -0,0 +1,138 @@
1
+ // Canonical tool names. Every place that mentions a tool by name — handler
2
+ // dispatch, allow-lists, prompt rules, the registry bridge skip-set — imports
3
+ // the constant from here. Refactor-safe: rename once, the rest follows.
4
+ //
5
+ // Keep the keys SCREAMING_SNAKE_CASE and the values snake_case (the on-wire
6
+ // tool name the LLM sees). The two halves stay aligned so a typo on either
7
+ // side is obvious.
8
+
9
+ export const TOOLS = Object.freeze({
10
+ // Discovery / projects / agents
11
+ LIST_PROJECTS: "list_projects",
12
+ ADD_PROJECT: "add_project",
13
+ LIST_AGENTS: "list_agents",
14
+ LIST_VAULT_AGENTS: "list_vault_agents",
15
+ IMPORT_AGENT: "import_agent",
16
+ LIST_MCPS: "list_mcps",
17
+
18
+ // Memory
19
+ READ_AGENT_MEMORY: "read_agent_memory",
20
+ READ_SELF_MEMORY: "read_self_memory",
21
+ REMEMBER: "remember",
22
+
23
+ // Filesystem / shell
24
+ LIST_FILES: "list_files",
25
+ READ_FILE: "read_file",
26
+ WRITE_FILE: "write_file",
27
+ EDIT_FILE: "edit_file",
28
+ SEARCH_FILES: "search_files",
29
+ RUN_SHELL: "run_shell",
30
+
31
+ // History / messages / sessions
32
+ TAIL_MESSAGES: "tail_messages",
33
+ SEARCH_MESSAGES: "search_messages",
34
+ SEARCH_SESSIONS: "search_sessions",
35
+
36
+ // Skills + dynamic tool surface
37
+ LIST_SKILLS: "list_skills",
38
+ LOAD_SKILL: "load_skill",
39
+ DISCOVER_TOOLS: "discover_tools",
40
+
41
+ // Tasks
42
+ LIST_TASKS: "list_tasks",
43
+ CREATE_TASK: "create_task",
44
+
45
+ // Interaction
46
+ ASK_QUESTIONS: "ask_questions",
47
+
48
+ // Delegation / external
49
+ CALL_AGENT: "call_agent",
50
+ CALL_MCP: "call_mcp",
51
+ CALL_RUNTIME: "call_runtime",
52
+
53
+ // Side-effects
54
+ SEND_TELEGRAM: "send_telegram",
55
+ SET_IDENTITY: "set_identity",
56
+ SET_PERMISSION_MODE: "set_permission_mode",
57
+ TRANSCRIBE_AUDIO: "transcribe_audio",
58
+
59
+ // HTTP-bridged registry tools (not native handlers; served via
60
+ // core/tools/registry.js so the regular generic tools work the same way).
61
+ GREP: "grep",
62
+ GLOB: "glob",
63
+ FETCH: "fetch",
64
+ SEARCH: "search",
65
+ });
66
+
67
+ /**
68
+ * Native handlers in src/core/agent/tools/handlers/ that own these names.
69
+ * The registry bridge MUST skip these — otherwise the HTTP roundtrip would
70
+ * shadow the in-process handler with possibly different semantics.
71
+ */
72
+ export const NATIVE_TOOL_NAMES = new Set([
73
+ TOOLS.LIST_PROJECTS,
74
+ TOOLS.LIST_AGENTS,
75
+ TOOLS.LIST_VAULT_AGENTS,
76
+ TOOLS.IMPORT_AGENT,
77
+ TOOLS.ADD_PROJECT,
78
+ TOOLS.LIST_MCPS,
79
+ TOOLS.READ_AGENT_MEMORY,
80
+ TOOLS.LIST_FILES,
81
+ TOOLS.READ_FILE,
82
+ TOOLS.WRITE_FILE,
83
+ TOOLS.EDIT_FILE,
84
+ TOOLS.SEARCH_FILES,
85
+ TOOLS.RUN_SHELL,
86
+ TOOLS.TAIL_MESSAGES,
87
+ TOOLS.SEARCH_MESSAGES,
88
+ TOOLS.CALL_AGENT,
89
+ TOOLS.CALL_MCP,
90
+ TOOLS.CALL_RUNTIME,
91
+ TOOLS.SEND_TELEGRAM,
92
+ TOOLS.SET_IDENTITY,
93
+ TOOLS.SET_PERMISSION_MODE,
94
+ TOOLS.READ_SELF_MEMORY,
95
+ TOOLS.REMEMBER,
96
+ TOOLS.LIST_SKILLS,
97
+ TOOLS.LOAD_SKILL,
98
+ TOOLS.LIST_TASKS,
99
+ TOOLS.CREATE_TASK,
100
+ TOOLS.ASK_QUESTIONS,
101
+ TOOLS.SEARCH_SESSIONS,
102
+ TOOLS.TRANSCRIBE_AUDIO,
103
+ TOOLS.DISCOVER_TOOLS,
104
+ ]);
105
+
106
+ /**
107
+ * Read-only allow-list for the Code module's PLAN mode: the agent explores
108
+ * the repo and proposes changes without mutating anything. Build mode uses
109
+ * the full registry — see CODE_BUILD_TOOLS below.
110
+ */
111
+ export const CODE_PLAN_TOOLS = Object.freeze([
112
+ TOOLS.READ_FILE,
113
+ TOOLS.LIST_FILES,
114
+ TOOLS.SEARCH_FILES,
115
+ TOOLS.GREP,
116
+ TOOLS.GLOB,
117
+ TOOLS.LIST_PROJECTS,
118
+ TOOLS.LIST_AGENTS,
119
+ TOOLS.LIST_MCPS,
120
+ TOOLS.READ_AGENT_MEMORY,
121
+ TOOLS.READ_SELF_MEMORY,
122
+ TOOLS.SEARCH_SESSIONS,
123
+ TOOLS.SEARCH_MESSAGES,
124
+ TOOLS.TAIL_MESSAGES,
125
+ TOOLS.LIST_SKILLS,
126
+ TOOLS.LOAD_SKILL,
127
+ TOOLS.LIST_TASKS,
128
+ TOOLS.ASK_QUESTIONS,
129
+ TOOLS.FETCH,
130
+ TOOLS.SEARCH,
131
+ ]);
132
+
133
+ /**
134
+ * BUILD mode = unrestricted. Kept as a sentinel value so callers compare
135
+ * against the constant instead of the magic "*" string. The registry treats
136
+ * "*" as "expose every tool the channel is otherwise allowed to see".
137
+ */
138
+ export const CODE_BUILD_TOOLS = "*";
@@ -1,10 +1,9 @@
1
- // daemon/super-agent-tools/registry-bridge.js
2
1
  //
3
2
  // Generic bridge that exposes registry-backed HTTP tools (browser, fetch,
4
3
  // search, glob, grep, etc.) to the super-agent — no per-tool import boilerplate.
5
4
  //
6
5
  // How it works:
7
- // 1. Read TOOL_DEFINITIONS from daemon/tools/registry.js
6
+ // 1. Read TOOL_DEFINITIONS from core/http-tools/registry.js
8
7
  // 2. Drop entries whose names collide with native super-agent tools (those
9
8
  // win — they touch in-process state directly).
10
9
  // 3. For each remaining entry, produce { name, schema, makeHandler } in the
@@ -19,8 +18,9 @@
19
18
  // super-agent-tools/tools/, no import in index.js.
20
19
 
21
20
  import fs from "node:fs";
22
- import { TOOL_DEFINITIONS } from "#core/tools/registry.js";
21
+ import { TOOL_DEFINITIONS } from "#core/http-tools/registry.js";
23
22
  import { TOKEN_PATH } from "#core/config/index.js";
23
+ import { NATIVE_TOOL_NAMES } from "./names.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
@@ -42,17 +42,9 @@ function daemonToken() {
42
42
  return cachedToken;
43
43
  }
44
44
 
45
- // Native handlers in super-agent-tools/tools/ that own these names. The bridge
46
- // MUST skip them or the registry version (HTTP roundtrip) would shadow the
47
- // native one with possibly different semantics.
48
- const NATIVE_NAMES = new Set([
49
- "list_projects", "list_agents", "list_vault_agents", "import_agent",
50
- "add_project", "list_mcps", "read_agent_memory",
51
- "list_files", "read_file", "write_file", "edit_file", "search_files",
52
- "run_shell", "tail_messages", "search_messages",
53
- "call_agent", "call_mcp", "call_runtime",
54
- "send_telegram", "set_identity", "set_permission_mode",
55
- ]);
45
+ // Native handler list lives in ./names.js so the bridge skip-set and the
46
+ // allow-lists in api/code.js share one source of truth.
47
+ const NATIVE_NAMES = NATIVE_TOOL_NAMES;
56
48
 
57
49
  // Default allow-list of categories the bridge will expose. The NATIVE_NAMES
58
50
  // filter handles duplicates inside these categories (e.g. "file" contains
@@ -31,6 +31,8 @@ import listTasks from "./handlers/list-tasks.js";
31
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
+ import { TOOLS } from "./names.js";
35
+ import { CHANNELS } from "#core/constants/channels.js";
34
36
 
35
37
  const NATIVE_TOOLS = [
36
38
  listProjects,
@@ -76,9 +78,9 @@ function resolveBridgeCategories() {
76
78
  }
77
79
 
78
80
  const BRIDGED_TOOLS = buildBridgedTools({ categories: resolveBridgeCategories() });
79
- const TOOLS = [...NATIVE_TOOLS, ...BRIDGED_TOOLS];
81
+ const ALL_TOOLS = [...NATIVE_TOOLS, ...BRIDGED_TOOLS];
80
82
 
81
- export const TOOL_SCHEMAS = TOOLS.map((tool) => tool.schema);
83
+ export const TOOL_SCHEMAS = ALL_TOOLS.map((tool) => tool.schema);
82
84
 
83
85
  // ---------------------------------------------------------------------------
84
86
  // Lazy tools: base set (always loaded) + on-demand set (revealed via
@@ -93,78 +95,83 @@ export const TOOL_SCHEMAS = TOOLS.map((tool) => tool.schema);
93
95
  // sessions, projects/inventory, basic shell, tasks, skills, and discovery.
94
96
  export const BASE_TOOL_NAMES = new Set([
95
97
  // Discovery — the entry point to everything not loaded here.
96
- "discover_tools",
98
+ TOOLS.DISCOVER_TOOLS,
97
99
  // Inventory — the model needs these to know what exists.
98
- "list_projects",
99
- "list_agents",
100
- "list_mcps",
101
- "list_skills",
102
- "load_skill",
100
+ TOOLS.LIST_PROJECTS,
101
+ TOOLS.LIST_AGENTS,
102
+ TOOLS.LIST_MCPS,
103
+ TOOLS.LIST_SKILLS,
104
+ TOOLS.LOAD_SKILL,
103
105
  // Memory + identity.
104
- "read_agent_memory",
105
- "read_self_memory",
106
- "remember",
107
- "set_identity",
106
+ TOOLS.READ_AGENT_MEMORY,
107
+ TOOLS.READ_SELF_MEMORY,
108
+ TOOLS.REMEMBER,
109
+ TOOLS.SET_IDENTITY,
108
110
  // Sessions + messages (self-recall + channel history).
109
- "search_sessions",
110
- "search_messages",
111
- "tail_messages",
111
+ TOOLS.SEARCH_SESSIONS,
112
+ TOOLS.SEARCH_MESSAGES,
113
+ TOOLS.TAIL_MESSAGES,
112
114
  // Channels + conversation control + lightweight delegation.
113
- "send_telegram",
114
- "ask_questions",
115
- "call_agent",
115
+ TOOLS.SEND_TELEGRAM,
116
+ TOOLS.ASK_QUESTIONS,
117
+ TOOLS.CALL_AGENT,
116
118
  // Tasks (very common ask via chat).
117
- "create_task",
118
- "list_tasks",
119
+ TOOLS.CREATE_TASK,
120
+ TOOLS.LIST_TASKS,
119
121
  // Files + basic shell — frequent enough on chat to keep hot.
120
- "read_file",
121
- "write_file",
122
- "edit_file",
123
- "list_files",
124
- "search_files",
125
- "run_shell",
122
+ TOOLS.READ_FILE,
123
+ TOOLS.WRITE_FILE,
124
+ TOOLS.EDIT_FILE,
125
+ TOOLS.LIST_FILES,
126
+ TOOLS.SEARCH_FILES,
127
+ TOOLS.RUN_SHELL,
126
128
  ]);
127
129
 
128
130
  // Channels that get the FULL registry up front (deliberate, user-picked model,
129
131
  // no cheap-tier TPM cap). Everything else is a "lightweight" channel and starts
130
132
  // on BASE_TOOL_NAMES with discover_tools to expand.
131
- const FULL_CHANNELS = new Set(["routine", "api", "web", "code", "terminal"]);
133
+ const FULL_CHANNELS = new Set([
134
+ CHANNELS.ROUTINE,
135
+ CHANNELS.API,
136
+ CHANNELS.WEB,
137
+ CHANNELS.CODE,
138
+ ]);
132
139
 
133
140
  // Category labels for grouping the discover_tools catalog. Native tools have no
134
141
  // registry category, so we assign one here; bridged tools carry their own
135
142
  // (browser/fetch/search/file) from registry-bridge.js.
136
143
  const NATIVE_CATEGORY = {
137
- discover_tools: "system",
138
- set_permission_mode: "system",
139
- list_projects: "inventory",
140
- list_agents: "inventory",
141
- list_vault_agents: "inventory",
142
- list_mcps: "inventory",
143
- list_skills: "inventory",
144
- load_skill: "skills",
145
- import_agent: "agents",
146
- add_project: "projects",
147
- call_agent: "agents",
148
- call_runtime: "runtime",
149
- call_mcp: "mcp",
150
- read_agent_memory: "memory",
151
- read_self_memory: "memory",
152
- remember: "memory",
153
- set_identity: "identity",
154
- search_sessions: "sessions",
155
- search_messages: "messages",
156
- tail_messages: "messages",
157
- send_telegram: "messages",
158
- ask_questions: "conversation",
159
- create_task: "tasks",
160
- list_tasks: "tasks",
161
- transcribe_audio: "voice",
162
- read_file: "files",
163
- write_file: "files",
164
- edit_file: "files",
165
- list_files: "files",
166
- search_files: "files",
167
- run_shell: "shell",
144
+ [TOOLS.DISCOVER_TOOLS]: "system",
145
+ [TOOLS.SET_PERMISSION_MODE]: "system",
146
+ [TOOLS.LIST_PROJECTS]: "inventory",
147
+ [TOOLS.LIST_AGENTS]: "inventory",
148
+ [TOOLS.LIST_VAULT_AGENTS]: "inventory",
149
+ [TOOLS.LIST_MCPS]: "inventory",
150
+ [TOOLS.LIST_SKILLS]: "inventory",
151
+ [TOOLS.LOAD_SKILL]: "skills",
152
+ [TOOLS.IMPORT_AGENT]: "agents",
153
+ [TOOLS.ADD_PROJECT]: "projects",
154
+ [TOOLS.CALL_AGENT]: "agents",
155
+ [TOOLS.CALL_RUNTIME]: "runtime",
156
+ [TOOLS.CALL_MCP]: "mcp",
157
+ [TOOLS.READ_AGENT_MEMORY]: "memory",
158
+ [TOOLS.READ_SELF_MEMORY]: "memory",
159
+ [TOOLS.REMEMBER]: "memory",
160
+ [TOOLS.SET_IDENTITY]: "identity",
161
+ [TOOLS.SEARCH_SESSIONS]: "sessions",
162
+ [TOOLS.SEARCH_MESSAGES]: "messages",
163
+ [TOOLS.TAIL_MESSAGES]: "messages",
164
+ [TOOLS.SEND_TELEGRAM]: "messages",
165
+ [TOOLS.ASK_QUESTIONS]: "conversation",
166
+ [TOOLS.CREATE_TASK]: "tasks",
167
+ [TOOLS.LIST_TASKS]: "tasks",
168
+ [TOOLS.TRANSCRIBE_AUDIO]: "voice",
169
+ [TOOLS.READ_FILE]: "files",
170
+ [TOOLS.WRITE_FILE]: "files",
171
+ [TOOLS.EDIT_FILE]: "files",
172
+ [TOOLS.LIST_FILES]: "files",
173
+ [TOOLS.SEARCH_FILES]: "files",
174
+ [TOOLS.RUN_SHELL]: "shell",
168
175
  };
169
176
 
170
177
  function categoryOf(tool) {
@@ -179,7 +186,7 @@ function oneLine(desc = "") {
179
186
 
180
187
  // Static metadata index for every tool — name, schema, category, short blurb.
181
188
  // Used by the per-turn tool session for the catalog and activation lookups.
182
- const TOOL_META = TOOLS.map((t) => ({
189
+ const TOOL_META = ALL_TOOLS.map((t) => ({
183
190
  name: t.name,
184
191
  schema: t.schema,
185
192
  category: categoryOf(t),
@@ -187,14 +194,10 @@ const TOOL_META = TOOLS.map((t) => ({
187
194
  }));
188
195
  const META_BY_NAME = new Map(TOOL_META.map((m) => [m.name, m]));
189
196
 
190
- export const BASE_TOOL_SCHEMAS = TOOLS
197
+ export const BASE_TOOL_SCHEMAS = ALL_TOOLS
191
198
  .filter((t) => BASE_TOOL_NAMES.has(t.name))
192
199
  .map((t) => t.schema);
193
200
 
194
- // Back-compat alias: a few callers/tests historically referenced the "core"
195
- // subset. The base set supersedes it.
196
- export const CORE_TOOL_SCHEMAS = BASE_TOOL_SCHEMAS;
197
-
198
201
  const schemaName = (s) => s?.function?.name || s?.name;
199
202
 
200
203
  /**
@@ -337,7 +340,7 @@ export function makeToolHandlers(ctx) {
337
340
  requestConfirmation: ctx.requestConfirmation || null,
338
341
  }),
339
342
  };
340
- return Object.fromEntries(TOOLS.map((tool) => [tool.name, tool.makeHandler(toolCtx)]));
343
+ return Object.fromEntries(ALL_TOOLS.map((tool) => [tool.name, tool.makeHandler(toolCtx)]));
341
344
  }
342
345
 
343
346
  // Diagnostic helper — useful for `apx daemon status` or debug logging.
@@ -0,0 +1,27 @@
1
+ // Snapshot a project's read-aloud context — AGENTS.md + .apc/memory.md —
2
+ // into a single labelled markdown string. Used by /deck/context/copy and
3
+ // (potentially) any other "give me the context" surface.
4
+ import fs from "node:fs/promises";
5
+ import { AGENTS_MD, apcMemoryFile } from "./paths.js";
6
+ import path from "node:path";
7
+
8
+ const CONTEXT_FILES = [
9
+ { rel: AGENTS_MD, label: AGENTS_MD },
10
+ { rel: `.apc/${"memory.md"}`, label: ".apc/memory.md", abs: (root) => apcMemoryFile(root) },
11
+ ];
12
+
13
+ export async function readProjectContext(projectPath) {
14
+ const chunks = [];
15
+ for (const entry of CONTEXT_FILES) {
16
+ const abs = entry.abs ? entry.abs(projectPath) : path.join(projectPath, entry.rel);
17
+ try {
18
+ const content = await fs.readFile(abs, "utf8");
19
+ if (content.trim()) {
20
+ chunks.push(`# ${entry.label}\n\n${content.trim()}\n`);
21
+ }
22
+ } catch {
23
+ // missing file is fine; we just skip it.
24
+ }
25
+ }
26
+ return chunks.join("\n---\n\n");
27
+ }
@@ -0,0 +1,19 @@
1
+ // Append-only project notes under .apc/notes/YYYY-MM-DD.md. Each note is a
2
+ // timestamped markdown block; the file is append-only so no UID management
3
+ // is required.
4
+ import fs from "node:fs/promises";
5
+ import path from "node:path";
6
+ import { apcNotesDir } from "./paths.js";
7
+
8
+ export async function appendProjectNote(projectPath, { title, body }) {
9
+ const notesDir = apcNotesDir(projectPath);
10
+ await fs.mkdir(notesDir, { recursive: true });
11
+ const today = new Date().toISOString().slice(0, 10);
12
+ const file = path.join(notesDir, `${today}.md`);
13
+ const ts = new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
14
+ const block = title
15
+ ? `\n## ${title}\n_${ts}_\n\n${body}\n`
16
+ : `\n### ${ts}\n\n${body}\n`;
17
+ await fs.appendFile(file, block, "utf8");
18
+ return file;
19
+ }
@@ -1,6 +1,13 @@
1
1
  // Core parsers for APC — pure ESM, no deps.
2
2
  import fs from "node:fs";
3
3
  import path from "node:path";
4
+ import {
5
+ apcAgentsDir,
6
+ apcAgentFile,
7
+ apcProjectFile,
8
+ agentsMdFile,
9
+ isApcProject,
10
+ } from "./paths.js";
4
11
 
5
12
  export const SLUG_RE = /^[a-z][a-z0-9_-]*$/;
6
13
  const H1_RE = /^#\s+Agents\s*$/i;
@@ -91,7 +98,7 @@ export function parseAgentFile(slug, text) {
91
98
 
92
99
  // Read all .apc/agents/<slug>.md files. Returns [] if none exist.
93
100
  export function readAgentsFromDir(root) {
94
- const dir = path.join(root, ".apc", "agents");
101
+ const dir = apcAgentsDir(root);
95
102
  if (!fs.existsSync(dir)) return [];
96
103
  return fs
97
104
  .readdirSync(dir)
@@ -123,7 +130,7 @@ import { fileURLToPath } from "node:url";
123
130
  const __parserDir = path.dirname(fileURLToPath(import.meta.url));
124
131
 
125
132
  export const VAULT_DIR = path.join(os.homedir(), ".apx", "agents");
126
- export const BUNDLED_VAULT_DIR = path.resolve(__parserDir, "../../assets/agent-vault-defaults");
133
+ export const BUNDLED_VAULT_DIR = path.resolve(__parserDir, "../../../assets/agent-vault-defaults");
127
134
  export const VAULT_TOMBSTONE_PATH = path.join(VAULT_DIR, ".removed.json");
128
135
 
129
136
  function readVaultDirRaw(dir) {
@@ -189,7 +196,7 @@ function readVaultAgent(slug, { includeRemoved = false } = {}) {
189
196
 
190
197
  // Resolve a single agent for a project: local file → vault (layered) → null.
191
198
  export function resolveAgent(root, slug) {
192
- const localPath = path.join(root, ".apc", "agents", `${slug}.md`);
199
+ const localPath = apcAgentFile(root, slug);
193
200
  if (fs.existsSync(localPath)) {
194
201
  const agent = parseAgentFile(slug, fs.readFileSync(localPath, "utf8"));
195
202
  return { ...agent, source: "local" };
@@ -203,7 +210,7 @@ export { readVaultAgent };
203
210
 
204
211
  // Return slugs imported from vault in this project (from project.json)
205
212
  export function importedVaultSlugs(root) {
206
- const p = path.join(root, ".apc", "project.json");
213
+ const p = apcProjectFile(root);
207
214
  if (!fs.existsSync(p)) return [];
208
215
  try {
209
216
  const cfg = JSON.parse(fs.readFileSync(p, "utf8"));
@@ -235,7 +242,7 @@ export function readAgents(root) {
235
242
  const all = [...fromFiles, ...vaultAgents];
236
243
  const allSlugs = new Set(all.map((a) => a.slug));
237
244
 
238
- const agentsMdPath = path.join(root, "AGENTS.md");
245
+ const agentsMdPath = agentsMdFile(root);
239
246
  if (!fs.existsSync(agentsMdPath)) return all;
240
247
 
241
248
  const mdText = fs.readFileSync(agentsMdPath, "utf8");
@@ -255,7 +262,7 @@ export function readAgents(root) {
255
262
  export function findApfRoot(start = process.cwd()) {
256
263
  let cur = path.resolve(start);
257
264
  while (true) {
258
- if (fs.existsSync(path.join(cur, ".apc", "project.json"))) return cur;
265
+ if (isApcProject(cur)) return cur;
259
266
  const parent = path.dirname(cur);
260
267
  if (parent === cur) return null;
261
268
  cur = parent;
@@ -0,0 +1,87 @@
1
+ // Filesystem layout of an APC project. The `.apc/` folder is APX's footprint
2
+ // inside someone else's repo — everything that the daemon writes to a
3
+ // user-checked-out project lives here, plus the top-level AGENTS.md spec.
4
+ //
5
+ // Use the helpers below instead of joining the literal names yourself. A typo
6
+ // in `".apc"` or `"project.json"` will silently create an orphan tree instead
7
+ // of failing loud, which is exactly the bug class these constants prevent.
8
+ import fs from "node:fs";
9
+ import path from "node:path";
10
+
11
+ // Raw names — exported for the rare caller that needs to glob/match by name.
12
+ export const APC_DIR = ".apc";
13
+ export const APC_PROJECT_FILE = "project.json";
14
+ export const APC_PROJECT_CONFIG_FILE = "config.json";
15
+ export const APC_PROJECT_MEMORY_FILE = "memory.md";
16
+ export const APC_AGENTS_DIR = "agents";
17
+ export const APC_SKILLS_DIR = "skills";
18
+ export const APC_COMMANDS_DIR = "commands";
19
+ export const APC_NOTES_DIR = "notes";
20
+ export const APC_MCPS_FILE = "mcps.json";
21
+ export const APC_REMOVED_FILE = ".removed.json";
22
+ export const AGENTS_MD = "AGENTS.md";
23
+
24
+ // Path builders. `root` is the project root (the directory that contains
25
+ // `.apc/` and `AGENTS.md`).
26
+ export function apcDir(root) {
27
+ return path.join(root, APC_DIR);
28
+ }
29
+
30
+ export function apcProjectFile(root) {
31
+ return path.join(root, APC_DIR, APC_PROJECT_FILE);
32
+ }
33
+
34
+ export function apcProjectConfigFile(root) {
35
+ return path.join(root, APC_DIR, APC_PROJECT_CONFIG_FILE);
36
+ }
37
+
38
+ export function apcAgentsDir(root) {
39
+ return path.join(root, APC_DIR, APC_AGENTS_DIR);
40
+ }
41
+
42
+ export function apcAgentFile(root, slug) {
43
+ return path.join(root, APC_DIR, APC_AGENTS_DIR, `${slug}.md`);
44
+ }
45
+
46
+ export function apcAgentMemoryFile(root, slug) {
47
+ return path.join(root, APC_DIR, APC_AGENTS_DIR, slug, "memory.md");
48
+ }
49
+
50
+ export function apcRemovedFile(root) {
51
+ return path.join(root, APC_DIR, APC_AGENTS_DIR, APC_REMOVED_FILE);
52
+ }
53
+
54
+ export function apcSkillsDir(root) {
55
+ return path.join(root, APC_DIR, APC_SKILLS_DIR);
56
+ }
57
+
58
+ export function apcSkillFile(root, slug) {
59
+ return path.join(root, APC_DIR, APC_SKILLS_DIR, `${slug}.md`);
60
+ }
61
+
62
+ export function apcCommandsDir(root) {
63
+ return path.join(root, APC_DIR, APC_COMMANDS_DIR);
64
+ }
65
+
66
+ export function apcNotesDir(root) {
67
+ return path.join(root, APC_DIR, APC_NOTES_DIR);
68
+ }
69
+
70
+ // Project-level memory (super-agent / project-wide), distinct from the
71
+ // per-agent memory.md that lives under .apc/agents/<slug>/.
72
+ export function apcMemoryFile(root) {
73
+ return path.join(root, APC_DIR, APC_PROJECT_MEMORY_FILE);
74
+ }
75
+
76
+ export function apcMcpsFile(root) {
77
+ return path.join(root, APC_DIR, APC_MCPS_FILE);
78
+ }
79
+
80
+ export function agentsMdFile(root) {
81
+ return path.join(root, AGENTS_MD);
82
+ }
83
+
84
+ // True when `root` looks like an initialized APC project (has the marker file).
85
+ export function isApcProject(root) {
86
+ return fs.existsSync(apcProjectFile(root));
87
+ }