@agentprojectcontext/apx 1.33.1 → 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 (169) hide show
  1. package/package.json +1 -1
  2. package/skills/apx/SKILL.md +49 -61
  3. package/src/core/agent/a2a/reply.js +48 -0
  4. package/src/core/agent/build-agent-system.js +4 -3
  5. package/src/core/agent/channels/voice-context.js +98 -0
  6. package/src/core/agent/memory.js +2 -1
  7. package/src/core/agent/prompt-builder.js +2 -1
  8. package/src/core/agent/prompts/modes/code-build.md +1 -0
  9. package/src/core/agent/prompts/modes/code-plan.md +1 -0
  10. package/src/core/agent/prompts/modes/index.js +28 -0
  11. package/src/core/agent/skills/loader.js +22 -18
  12. package/src/core/agent/stream/turn-accumulator.js +73 -0
  13. package/src/core/agent/suggestions.js +37 -0
  14. package/src/core/agent/tools/handlers/add-project.js +5 -2
  15. package/src/core/agent/tools/handlers/call-runtime.js +3 -2
  16. package/src/core/agent/tools/handlers/transcribe-audio.js +1 -1
  17. package/src/core/agent/tools/helpers.js +2 -2
  18. package/src/core/agent/tools/names.js +138 -0
  19. package/src/core/agent/tools/registry-bridge.js +6 -14
  20. package/src/core/agent/tools/registry.js +68 -65
  21. package/src/core/apc/context-copy.js +27 -0
  22. package/src/core/apc/notes.js +19 -0
  23. package/src/core/apc/parser.js +12 -5
  24. package/src/core/apc/paths.js +87 -0
  25. package/src/core/apc/scaffold.js +82 -76
  26. package/src/core/apc/skill-sync.js +10 -0
  27. package/src/{host/daemon/plugins → core/channels}/telegram/dispatch.js +38 -16
  28. package/src/core/config/index.js +3 -2
  29. package/src/core/config/redact.js +95 -0
  30. package/src/core/constants/channels.js +2 -0
  31. package/src/core/constants/code-modes.js +10 -0
  32. package/src/core/constants/index.js +1 -0
  33. package/src/core/deck/manifest.js +186 -0
  34. package/src/core/engines/catalog.js +83 -0
  35. package/src/core/{tools → http-tools}/browser.js +0 -1
  36. package/src/core/{tools → http-tools}/fetch.js +0 -1
  37. package/src/core/{tools → http-tools}/glob.js +0 -1
  38. package/src/core/{tools → http-tools}/grep.js +0 -1
  39. package/src/core/{tools → http-tools}/registry.js +0 -1
  40. package/src/core/{tools → http-tools}/search.js +0 -1
  41. package/src/core/i18n/en.js +9 -0
  42. package/src/core/i18n/es.js +12 -0
  43. package/src/core/i18n/index.js +54 -0
  44. package/src/core/i18n/pt.js +9 -0
  45. package/src/core/identity/telegram.js +2 -1
  46. package/src/core/mcp/runner.js +272 -14
  47. package/src/core/mcp/sources.js +3 -2
  48. package/src/core/routines/index.js +16 -0
  49. package/src/{host/daemon/routines.js → core/routines/runner.js} +36 -103
  50. package/src/core/runtime-skills/apc-context/SKILL.md +159 -0
  51. package/src/core/runtime-skills/apx/SKILL.md +95 -0
  52. package/src/core/runtime-skills/apx-mcp/SKILL.md +116 -0
  53. package/src/core/runtime-skills/{claude-code.md → claude-code/SKILL.md} +1 -0
  54. package/src/core/runtime-skills/{codex-cli.md → codex-cli/SKILL.md} +1 -0
  55. package/src/core/runtime-skills/{opencode-cli.md → opencode-cli/SKILL.md} +1 -0
  56. package/src/core/runtime-skills/{openrouter.md → openrouter/SKILL.md} +1 -0
  57. package/src/{host/daemon/env-detect.js → core/runtimes/detect.js} +1 -1
  58. package/src/core/stores/code-sessions.js +50 -2
  59. package/src/core/stores/routine-memory.js +1 -1
  60. package/src/core/stores/sessions-search.js +121 -0
  61. package/src/core/stores/sessions.js +38 -0
  62. package/src/core/vars/index.js +14 -0
  63. package/src/core/vars/interpolate.js +86 -0
  64. package/src/core/vars/sources.js +151 -0
  65. package/src/core/voice/audio-decode.js +38 -0
  66. package/src/core/voice/transcription.js +225 -0
  67. package/src/host/daemon/api/admin-config.js +5 -82
  68. package/src/host/daemon/api/agents.js +5 -5
  69. package/src/host/daemon/api/code.js +17 -169
  70. package/src/host/daemon/api/config.js +3 -4
  71. package/src/host/daemon/api/conversations.js +8 -29
  72. package/src/host/daemon/api/deck.js +37 -404
  73. package/src/host/daemon/api/engines.js +1 -80
  74. package/src/host/daemon/api/exec.js +1 -1
  75. package/src/host/daemon/api/mcps.js +32 -0
  76. package/src/host/daemon/api/routines.js +1 -1
  77. package/src/host/daemon/api/runtimes.js +4 -3
  78. package/src/host/daemon/api/sessions-search.js +24 -140
  79. package/src/host/daemon/api/sessions.js +12 -30
  80. package/src/host/daemon/api/shared.js +2 -1
  81. package/src/host/daemon/api/telegram.js +1 -11
  82. package/src/host/daemon/api/tools.js +6 -6
  83. package/src/host/daemon/api/transcribe.js +2 -2
  84. package/src/host/daemon/api/vars.js +137 -0
  85. package/src/host/daemon/api/voice.js +13 -290
  86. package/src/host/daemon/api.js +2 -0
  87. package/src/host/daemon/db.js +6 -6
  88. package/src/host/daemon/deck-exec.js +148 -0
  89. package/src/host/daemon/index.js +3 -3
  90. package/src/host/daemon/plugins/telegram/index.js +9 -9
  91. package/src/host/daemon/routines-scheduler.js +64 -0
  92. package/src/host/daemon/smoke.js +3 -2
  93. package/src/host/daemon/whisper-server.js +225 -0
  94. package/src/interfaces/cli/commands/agent.js +3 -2
  95. package/src/interfaces/cli/commands/command.js +2 -3
  96. package/src/interfaces/cli/commands/messages.js +6 -2
  97. package/src/interfaces/cli/commands/pair.js +5 -4
  98. package/src/interfaces/cli/commands/search.js +1 -1
  99. package/src/interfaces/cli/commands/sessions.js +3 -2
  100. package/src/interfaces/cli/commands/skills.js +36 -55
  101. package/src/interfaces/web/dist/assets/index-DdmSRtsz.css +1 -0
  102. package/src/interfaces/web/dist/assets/index-M4FspaCH.js +613 -0
  103. package/src/interfaces/web/dist/assets/index-M4FspaCH.js.map +1 -0
  104. package/src/interfaces/web/dist/index.html +2 -2
  105. package/src/interfaces/web/package-lock.json +182 -182
  106. package/src/interfaces/web/src/components/ModelCombobox.tsx +2 -1
  107. package/src/interfaces/web/src/components/TelegramChannelDialog.tsx +1 -1
  108. package/src/interfaces/web/src/components/chat/AskAnswersCard.tsx +76 -0
  109. package/src/interfaces/web/src/components/chat/MessageBubble.tsx +16 -3
  110. package/src/interfaces/web/src/components/chat/MessageList.tsx +23 -1
  111. package/src/interfaces/web/src/components/chat/ModelPicker.tsx +3 -1
  112. package/src/interfaces/web/src/components/code/CodeArtifactsTab.tsx +4 -4
  113. package/src/interfaces/web/src/components/code/CodeChangesTab.tsx +1 -1
  114. package/src/interfaces/web/src/components/code/CodeFileTree.tsx +3 -2
  115. package/src/interfaces/web/src/components/code/CodeFileViewer.tsx +3 -2
  116. package/src/interfaces/web/src/components/code/CodeTerminal.tsx +3 -2
  117. package/src/interfaces/web/src/components/config/GlobalConfigEditor.tsx +2 -1
  118. package/src/interfaces/web/src/components/deck/WidgetRow.tsx +2 -1
  119. package/src/interfaces/web/src/components/inputs/KeyValueList.tsx +93 -0
  120. package/src/interfaces/web/src/components/inputs/VarTokenInput.tsx +449 -0
  121. package/src/interfaces/web/src/components/settings/DefaultRouterCard.tsx +2 -1
  122. package/src/interfaces/web/src/components/settings/EnginesPanel.tsx +2 -2
  123. package/src/interfaces/web/src/components/settings/MemoryPanel.tsx +5 -4
  124. package/src/interfaces/web/src/components/settings/providers/ProviderCard.tsx +3 -2
  125. package/src/interfaces/web/src/components/settings/providers/ProviderModal.tsx +3 -2
  126. package/src/interfaces/web/src/components/ui/chat-input.tsx +5 -4
  127. package/src/interfaces/web/src/components/ui/sidebar.tsx +3 -2
  128. package/src/interfaces/web/src/components/voice/VoiceProviderModal.tsx +2 -1
  129. package/src/interfaces/web/src/constants/index.ts +1 -1
  130. package/src/interfaces/web/src/i18n/en.ts +174 -7
  131. package/src/interfaces/web/src/i18n/es.ts +179 -15
  132. package/src/interfaces/web/src/lib/api/mcps.ts +25 -0
  133. package/src/interfaces/web/src/lib/api/vars.ts +38 -0
  134. package/src/interfaces/web/src/lib/api.ts +1 -0
  135. package/src/interfaces/web/src/screens/ProjectScreen.tsx +8 -31
  136. package/src/interfaces/web/src/screens/modules/CodeScreen.tsx +1 -1
  137. package/src/interfaces/web/src/screens/modules/DeckScreen.tsx +4 -3
  138. package/src/interfaces/web/src/screens/modules/DesktopScreen.tsx +7 -6
  139. package/src/interfaces/web/src/screens/modules/VoiceScreen.tsx +4 -3
  140. package/src/interfaces/web/src/screens/project/AgentDetailScreen.tsx +1 -1
  141. package/src/interfaces/web/src/screens/project/ConfigTab.tsx +132 -1
  142. package/src/interfaces/web/src/screens/project/McpsTab.tsx +549 -104
  143. package/src/interfaces/web/src/screens/project/RoutinesTab.tsx +1 -1
  144. package/src/interfaces/web/src/screens/project/VarsTab.tsx +300 -0
  145. package/src/interfaces/web/src/types/daemon.ts +5 -0
  146. package/src/host/daemon/transcription.js +0 -538
  147. package/src/host/daemon/whisper-transcribe.py +0 -73
  148. package/src/interfaces/web/dist/assets/index-Aaiw8BZN.css +0 -1
  149. package/src/interfaces/web/dist/assets/index-DPqtjDjh.js +0 -602
  150. package/src/interfaces/web/dist/assets/index-DPqtjDjh.js.map +0 -1
  151. /package/src/{host/daemon → core/apc}/projects-helpers.js +0 -0
  152. /package/src/{host/daemon/plugins → core/channels}/telegram/ask.js +0 -0
  153. /package/src/{host/daemon/plugins → core/channels}/telegram/helpers.js +0 -0
  154. /package/src/{host/daemon/plugins → core/channels}/telegram/media.js +0 -0
  155. /package/src/core/{tools → http-tools}/index.js +0 -0
  156. /package/{skills → src/core/runtime-skills}/apx-agency-agents/SKILL.md +0 -0
  157. /package/{skills → src/core/runtime-skills}/apx-agent/SKILL.md +0 -0
  158. /package/{skills → src/core/runtime-skills}/apx-mcp-builder/SKILL.md +0 -0
  159. /package/{skills → src/core/runtime-skills}/apx-project/SKILL.md +0 -0
  160. /package/{skills → src/core/runtime-skills}/apx-routine/SKILL.md +0 -0
  161. /package/{skills → src/core/runtime-skills}/apx-runtime/SKILL.md +0 -0
  162. /package/{skills → src/core/runtime-skills}/apx-sessions/SKILL.md +0 -0
  163. /package/{skills → src/core/runtime-skills}/apx-skill-builder/SKILL.md +0 -0
  164. /package/{skills → src/core/runtime-skills}/apx-task/SKILL.md +0 -0
  165. /package/{skills → src/core/runtime-skills}/apx-telegram/SKILL.md +0 -0
  166. /package/{skills → src/core/runtime-skills}/apx-voice/SKILL.md +0 -0
  167. /package/src/{host/daemon/compact.js → core/stores/conversations-compactor.js} +0 -0
  168. /package/src/{host/daemon → core/stores}/conversations.js +0 -0
  169. /package/src/{host/daemon → core/util}/thinking.js +0 -0
@@ -16,6 +16,7 @@ import { runSuperAgent } from "#core/agent/super-agent.js";
16
16
  import { appendSuperAgentErrorTrace } from "./shared.js";
17
17
  import { createWebConfirmAdapter } from "#core/confirmation/adapters/web.js";
18
18
  import { CHANNELS } from "#core/constants/channels.js";
19
+ import { CODE_MODES, DEFAULT_CODE_MODE } from "#core/constants/code-modes.js";
19
20
  import {
20
21
  listCodeSessions,
21
22
  getCodeSession,
@@ -23,181 +24,28 @@ import {
23
24
  updateCodeSession,
24
25
  removeCodeSession,
25
26
  appendTurn,
27
+ codeSessionHistory,
26
28
  } from "#core/stores/code-sessions.js";
29
+ import { makeTurnAccumulator } from "#core/agent/stream/turn-accumulator.js";
27
30
  import { captureBaseline, diffAgainstBaseline, initGitRepo } from "#core/git-baseline.js";
28
31
  import { loggerFor } from "#core/logging.js";
29
32
  import { readAgents } from "#core/apc/parser.js";
33
+ import { CODE_PLAN_TOOLS, CODE_BUILD_TOOLS } from "#core/agent/tools/names.js";
34
+ import { codeModeGuidance } from "#core/agent/prompts/modes/index.js";
30
35
 
31
36
  const log = loggerFor("code");
32
37
 
33
- // Read-only tool allowlist for PLAN mode: the agent can explore the repo but
34
- // not mutate it (no write/edit/shell/delegation/side-effects). Build mode gets
35
- // the unrestricted set ("*"). Names must match super-agent-tools/index.js.
36
- const PLAN_TOOLS = [
37
- "read_file",
38
- "list_files",
39
- "search_files",
40
- "grep",
41
- "glob",
42
- "list_projects",
43
- "list_agents",
44
- "list_mcps",
45
- "read_agent_memory",
46
- "read_self_memory",
47
- "search_sessions",
48
- "search_messages",
49
- "tail_messages",
50
- "list_skills",
51
- "load_skill",
52
- "list_tasks",
53
- "ask_questions",
54
- "fetch",
55
- "search",
56
- ];
57
-
38
+ // Mode-specific tool allow-lists and prompt fragments are owned by core/:
39
+ // - tool names + plan/build lists → #core/agent/tools/names.js
40
+ // - per-mode guidance text → #core/agent/prompts/modes/*.md
41
+ // This file just picks the right pair for the request.
58
42
  function modeGuidanceFor(mode) {
59
- if (mode === "plan") {
60
- return [
61
- "MODE: plan. Investigate the codebase (read/list/search/grep) and propose",
62
- "an approach with the EXACT changes you would make (files + diffs/snippets).",
63
- "Do NOT write or edit files and do NOT run mutating shell commands — your",
64
- "editing tools are disabled in this mode. End with a concise, ordered plan.",
65
- ].join(" ");
66
- }
67
- return [
68
- "MODE: build. Make the changes directly using your file and shell tools",
69
- "(read_file, write_file, edit_file, run_shell, …). Do not ask for",
70
- "confirmation and do not stop after one step — keep calling tools until the",
71
- "entire task is done, then briefly summarize what you changed and why.",
72
- "Prefer surgical edits over rewrites.",
73
- "When the user asks for a reusable script, snippet, or 'artifact' (something",
74
- "they want to keep and run later), put it under `artifacts/<name>` inside",
75
- "the project — it then shows up in the Artifacts tab. Don't drop reusable",
76
- "scripts at the project root.",
77
- "If a parameter you need is missing (API key, app id, target URL, …), call",
78
- "`ask_questions` ONCE with all your questions and stop — control returns",
79
- "to the user. Do not call ask_questions again in the same turn; you'll just",
80
- "get the same blank state back. Each question can be a string (free-text",
81
- "answer) OR an object {question, options:[{label, description}], multiSelect}",
82
- "for choices. Prefer 2–4 mutually-exclusive options when a question has a",
83
- "natural shortlist (yes/no, which-of-these, …); leave options empty for",
84
- "open-ended answers (API keys, names, free-form ideas).",
85
- "If the previous assistant turn already asked these same questions and the",
86
- "current user message is the compiled answers, DO NOT call ask_questions",
87
- "again — process the answers and proceed with the task.",
88
- ].join(" ");
89
- }
90
-
91
- // Build the [{role, content}] history the super-agent expects from the stored
92
- // rich transcript: flatten each turn's text parts. Tool parts are normally
93
- // internal, but ask_questions is special — without surfacing it the model
94
- // loses track that it ALREADY asked, sees the user's compiled-answer reply
95
- // as a fresh request, and asks again forever. We render a one-line synthetic
96
- // summary of each ask_questions call so the next turn's context shows
97
- // "I asked X, the user replied Y" naturally.
98
- function summarizeAskQuestionsPart(part) {
99
- const raw = part?.args?.questions;
100
- if (!Array.isArray(raw) || raw.length === 0) return null;
101
- const lines = raw
102
- .map((q) => {
103
- if (typeof q === "string") return `- ${q}`;
104
- if (!q || typeof q !== "object" || typeof q.question !== "string") return null;
105
- const opts = Array.isArray(q.options) ? q.options : [];
106
- const optStr = opts
107
- .map((o) => (typeof o === "string" ? o : (o && typeof o.label === "string" ? o.label : "")))
108
- .filter(Boolean)
109
- .join(", ");
110
- return optStr ? `- ${q.question} (opciones: ${optStr})` : `- ${q.question}`;
111
- })
112
- .filter(Boolean);
113
- if (lines.length === 0) return null;
114
- return `[ask_questions]\n${lines.join("\n")}`;
115
- }
116
-
117
- function historyFrom(session) {
118
- return (session.messages || []).map((m) => {
119
- const chunks = [];
120
- for (const p of m.parts || []) {
121
- if (!p) continue;
122
- if (p.kind === "text" && p.text) chunks.push(p.text);
123
- else if (p.kind === "tool" && p.tool === "ask_questions") {
124
- const summary = summarizeAskQuestionsPart(p);
125
- if (summary) chunks.push(summary);
126
- }
127
- }
128
- return { role: m.role, content: chunks.join("\n\n").trim() };
129
- });
43
+ return codeModeGuidance(mode);
130
44
  }
131
45
 
132
- // Accumulate stream events into the rich ChatPart shape so the persisted
133
- // assistant turn matches exactly what the UI rendered live. Mirrors the
134
- // front-end reducer in hooks/useChat.ts (applyStreamEvent).
135
- function makeTurnAccumulator() {
136
- const parts = [];
137
- const notes = [];
138
- let model = null;
139
- let usage = null;
140
- const findTool = (id) => parts.find((p) => p.kind === "tool" && p.id === id);
141
- return {
142
- apply(ev) {
143
- switch (ev?.type) {
144
- case "model_start":
145
- if (ev.model) model = ev.model;
146
- break;
147
- case "model_routed":
148
- if (ev.model) model = ev.model;
149
- if (ev.from_fallback) notes.push(`routing fell back → ${ev.model}`);
150
- break;
151
- case "engine_failed":
152
- notes.push(`engine ${ev.model || "?"} failed → ${ev.retry_with || "retry"}`);
153
- break;
154
- case "model_retry":
155
- notes.push(`retry (${ev.reason || "?"})`);
156
- break;
157
- case "tools_suppressed":
158
- notes.push(`tools suppressed: ${(ev.tools || []).join(", ")}`);
159
- break;
160
- case "assistant_text":
161
- if (ev.text) parts.push({ kind: "text", text: ev.text });
162
- break;
163
- case "tool_start":
164
- if (ev.trace)
165
- parts.push({
166
- kind: "tool",
167
- id: ev.trace.id,
168
- tool: ev.trace.tool,
169
- args: ev.trace.args,
170
- status: "running",
171
- });
172
- break;
173
- case "tool_deduped": {
174
- const t = ev.trace && findTool(ev.trace.id);
175
- if (t) t.status = "deduped";
176
- break;
177
- }
178
- case "tool_result": {
179
- const t = ev.trace && findTool(ev.trace.id);
180
- if (t) {
181
- t.result = ev.trace.result;
182
- const errored =
183
- ev.trace.result && typeof ev.trace.result === "object" && ev.trace.result.error;
184
- t.status = errored ? "error" : t.status === "deduped" ? "deduped" : "done";
185
- }
186
- break;
187
- }
188
- case "final":
189
- usage = ev.result?.usage ?? usage;
190
- if (!model) model = ev.result?.name || null;
191
- break;
192
- default:
193
- break;
194
- }
195
- },
196
- build() {
197
- return { parts, notes, model, usage };
198
- },
199
- };
200
- }
46
+ // History flattening + stream-event accumulator now live in core/ see
47
+ // codeSessionHistory() (transcript engine messages) and makeTurnAccumulator()
48
+ // (stream events persistable ChatParts) imported above.
201
49
 
202
50
  export function register(app, { projects, project, config, registries, plugins }) {
203
51
  const findProject = (req, res) => project(req, res);
@@ -290,8 +138,8 @@ export function register(app, { projects, project, config, registries, plugins }
290
138
  const { prompt } = req.body || {};
291
139
  if (!prompt) return res.status(400).json({ error: "prompt required" });
292
140
 
293
- const mode = session.mode === "plan" ? "plan" : "build";
294
- const previousMessages = historyFrom(session);
141
+ const mode = session.mode === CODE_MODES.PLAN ? CODE_MODES.PLAN : DEFAULT_CODE_MODE;
142
+ const previousMessages = codeSessionHistory(session);
295
143
 
296
144
  // If a project agent is selected, inject its system prompt as a suffix so
297
145
  // the super-agent's tool loop runs with the agent's personality/context.
@@ -340,7 +188,7 @@ export function register(app, { projects, project, config, registries, plugins }
340
188
  previousMessages,
341
189
  systemSuffix: agentSystemSuffix,
342
190
  overrideModel: session.model || undefined,
343
- allowedTools: mode === "plan" ? PLAN_TOOLS : "*",
191
+ allowedTools: mode === CODE_MODES.PLAN ? CODE_PLAN_TOOLS : CODE_BUILD_TOOLS,
344
192
  // Coding tasks are multi-step: give the loop a high safety ceiling so it
345
193
  // can chain 20-30+ tools (read → edit → run → verify …) and a real
346
194
  // output budget for substantial code / explanations per turn. The
@@ -351,7 +199,7 @@ export function register(app, { projects, project, config, registries, plugins }
351
199
  // Build mode = the model must keep calling tools until it calls `finish`.
352
200
  // Plan mode is read-only investigation that ends with a written plan, so
353
201
  // it keeps the normal "text ends the turn" behavior.
354
- completionContract: mode === "build",
202
+ completionContract: mode === CODE_MODES.BUILD,
355
203
  onEvent,
356
204
  requestConfirmation: createWebConfirmAdapter({ onEvent }),
357
205
  });
@@ -10,10 +10,9 @@ import {
10
10
  setDottedKey,
11
11
  unsetDottedKey,
12
12
  } from "../project-config.js";
13
+ import { apcProjectFile, apcProjectConfigFile } from "#core/apc/paths.js";
13
14
 
14
- function projectJsonPath(root) {
15
- return path.join(root, ".apc", "project.json");
16
- }
15
+ const projectJsonPath = apcProjectFile;
17
16
 
18
17
  function readProjectJson(root) {
19
18
  const p = projectJsonPath(root);
@@ -34,7 +33,7 @@ export function register(app, { projects, project }) {
34
33
  res.json({
35
34
  effective: p.config || {},
36
35
  project_only: readProjectConfig(p.path),
37
- project_config_path: path.join(p.path, ".apc", "config.json"),
36
+ project_config_path: apcProjectConfigFile(p.path),
38
37
  apc_project: readProjectJson(p.path),
39
38
  project_json_path: projectJsonPath(p.path),
40
39
  });
@@ -4,12 +4,10 @@
4
4
  // POST /projects/:pid/agents/:slug/compact
5
5
  // POST /projects/:pid/agents/:slug/conversations/:id/compact
6
6
  // POST /projects/:pid/send (agent-to-agent)
7
- import fs from "node:fs";
8
- import path from "node:path";
9
7
  import { readAgents } from "#core/apc/parser.js";
10
- import { callEngine } from "#core/engines/index.js";
11
- import { listConversations, readConversation } from "../conversations.js";
12
- import { compactConversation } from "../compact.js";
8
+ import { listConversations, readConversation } from "#core/stores/conversations.js";
9
+ import { compactConversation } from "#core/stores/conversations-compactor.js";
10
+ import { replyAsAgent } from "#core/agent/a2a/reply.js";
13
11
  import { nowIso } from "./shared.js";
14
12
 
15
13
  export function register(app, { project, config }) {
@@ -102,30 +100,11 @@ export function register(app, { project, config }) {
102
100
  let reply = null;
103
101
  if (deliver && toAgent.fields.Model) {
104
102
  try {
105
- const tf = toAgent.fields;
106
- const parts = [];
107
- if (tf.Description) parts.push(tf.Description);
108
- if (tf.Role) parts.push(`Role: ${tf.Role}`);
109
- if (tf.Language) parts.push(`Default language: ${tf.Language}`);
110
- parts.push(
111
- `You are ${toAgent.slug}. You just received a message from ${fromAgent.slug}. Reply concisely.`
112
- );
113
- const memPath = path.join(
114
- p.path,
115
- ".apc",
116
- "agents",
117
- toAgent.slug,
118
- "memory.md"
119
- );
120
- if (fs.existsSync(memPath))
121
- parts.push("## Memory\n" + fs.readFileSync(memPath, "utf8"));
122
-
123
- const result = await callEngine({
124
- modelId: toAgent.fields.Model,
125
- system: parts.join("\n\n"),
126
- messages: [
127
- { role: "user", content: `From ${fromAgent.slug}:\n\n${body}` },
128
- ],
103
+ const result = await replyAsAgent({
104
+ projectPath: p.path,
105
+ toAgent,
106
+ fromAgent,
107
+ body,
129
108
  config: p.config || config,
130
109
  });
131
110