@agentprojectcontext/apx 1.34.0 → 1.35.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 (65) hide show
  1. package/package.json +1 -1
  2. package/skills/apx/SKILL.md +1 -1
  3. package/src/core/agent/build-agent-system.js +134 -58
  4. package/src/core/agent/channels/voice-context.js +4 -4
  5. package/src/core/agent/prompt-builder.js +176 -123
  6. package/src/core/agent/prompts/channels/code.md +12 -10
  7. package/src/core/agent/prompts/channels/desktop.md +5 -32
  8. package/src/core/agent/prompts/channels/telegram.md +4 -15
  9. package/src/core/agent/prompts/channels/web_code.md +11 -11
  10. package/src/core/agent/prompts/core/agent-base.md +24 -0
  11. package/src/core/agent/prompts/core/project-agent.md +11 -0
  12. package/src/core/agent/prompts/core/super-agent.md +21 -0
  13. package/src/core/agent/prompts/discipline/action.md +10 -0
  14. package/src/core/agent/prompts/discipline/single-segment.md +6 -0
  15. package/src/core/agent/prompts/discipline/two-segment.md +11 -0
  16. package/src/core/agent/self-memory.js +43 -1
  17. package/src/core/agent/skills/index-store.js +307 -0
  18. package/src/core/agent/skills/index.js +15 -1
  19. package/src/core/agent/skills/inspector.js +317 -0
  20. package/src/core/agent/super-agent.js +7 -1
  21. package/src/core/agent/tools/handlers/_git.js +50 -0
  22. package/src/core/agent/tools/handlers/git-diff.js +44 -0
  23. package/src/core/agent/tools/handlers/git-log.js +38 -0
  24. package/src/core/agent/tools/handlers/git-show.js +34 -0
  25. package/src/core/agent/tools/handlers/git-status.js +61 -0
  26. package/src/core/agent/tools/names.js +31 -0
  27. package/src/core/agent/tools/registry.js +36 -5
  28. package/src/core/config/index.js +21 -0
  29. package/src/core/runtime-skills/apx/SKILL.md +27 -39
  30. package/src/core/runtime-skills/apx-agency-agents/SKILL.md +40 -56
  31. package/src/core/runtime-skills/apx-agent/SKILL.md +27 -30
  32. package/src/core/runtime-skills/apx-mcp/SKILL.md +31 -36
  33. package/src/core/runtime-skills/apx-mcp-builder/SKILL.md +37 -51
  34. package/src/core/runtime-skills/apx-project/SKILL.md +20 -29
  35. package/src/core/runtime-skills/apx-routine/SKILL.md +34 -47
  36. package/src/core/runtime-skills/apx-runtime/SKILL.md +32 -50
  37. package/src/core/runtime-skills/apx-sessions/SKILL.md +96 -145
  38. package/src/core/runtime-skills/apx-skill-builder/SKILL.md +53 -77
  39. package/src/core/runtime-skills/apx-task/SKILL.md +18 -21
  40. package/src/core/runtime-skills/apx-telegram/SKILL.md +43 -54
  41. package/src/core/runtime-skills/apx-voice/SKILL.md +36 -56
  42. package/src/host/daemon/api/skills.js +140 -6
  43. package/src/host/daemon/api/super-agent.js +56 -1
  44. package/src/host/daemon/index.js +17 -0
  45. package/src/interfaces/cli/branding.js +53 -0
  46. package/src/interfaces/cli/commands/skills.js +254 -0
  47. package/src/interfaces/cli/index.js +84 -2
  48. package/src/interfaces/web/dist/assets/index-C0fm31dY.js +618 -0
  49. package/src/interfaces/web/dist/assets/index-C0fm31dY.js.map +1 -0
  50. package/src/interfaces/web/dist/assets/index-UcAqlBO6.css +1 -0
  51. package/src/interfaces/web/dist/index.html +2 -2
  52. package/src/interfaces/web/src/components/chat/MessageBubble.tsx +21 -1
  53. package/src/interfaces/web/src/components/settings/MemoryPanel.tsx +68 -0
  54. package/src/interfaces/web/src/components/settings/SkillsInspectorPanel.tsx +222 -0
  55. package/src/interfaces/web/src/hooks/useChat.ts +19 -0
  56. package/src/interfaces/web/src/i18n/en.ts +1 -0
  57. package/src/interfaces/web/src/i18n/es.ts +1 -0
  58. package/src/interfaces/web/src/lib/api/skills.ts +70 -0
  59. package/src/interfaces/web/src/screens/SettingsScreen.tsx +6 -2
  60. package/src/interfaces/web/src/types/daemon.ts +10 -0
  61. package/src/core/agent/prompts/action-discipline.md +0 -24
  62. package/src/core/agent/prompts/super-agent-base.md +0 -42
  63. package/src/interfaces/web/dist/assets/index-DdmSRtsz.css +0 -1
  64. package/src/interfaces/web/dist/assets/index-M4FspaCH.js +0 -613
  65. package/src/interfaces/web/dist/assets/index-M4FspaCH.js.map +0 -1
@@ -1,9 +1,18 @@
1
- // Prompt builder for the APX default agent known internally as the
2
- // "super-agent". That name is a MODE descriptor (the daemon-level tool-using
3
- // loop that runs when no project agent is named), not a persona the user
4
- // should ever see. The model is told its real display name comes from
5
- // ~/.apx/identity.json; "super-agent" only appears in code, file paths, CLI
6
- // flags, and channel meta.
1
+ // Unified prompt builder for ANY agent (super-agent OR project agent).
2
+ //
3
+ // The system prompt is assembled from layered fragments:
4
+ //
5
+ // 1. core/agent-base.md common: tool usage, memory, hard rules applies to every agent
6
+ // 2. core/super-agent.md OR core/project-agent.md — the role delta
7
+ // 3. # Agent profile identity (name, personality, owner, language)
8
+ // 4. # Project / context project pin, registered projects index, AGENTS.md
9
+ // 5. # Memory self-memory or relevant memory block, active threads
10
+ // 6. # Channel channel-specific formatting rules
11
+ // 7. # Discipline action.md + (two-segment OR single-segment) + voice mode
12
+ // 8. # Suffix channel-specific format directives (suggestions JSON, etc.)
13
+ //
14
+ // Sections are dropped when empty (no project context for super-agent on a
15
+ // generic CLI call, no self-memory for project agents, etc.).
7
16
  import fs from "node:fs";
8
17
  import path from "node:path";
9
18
  import { fileURLToPath } from "node:url";
@@ -11,68 +20,66 @@ import { readIdentity } from "../identity/index.js";
11
20
  import { agentsMdFile } from "../apc/paths.js";
12
21
  import { readSelfMemoryForPrompt } from "./self-memory.js";
13
22
  import { buildSkillsHintBlock } from "./skills/catalog.js";
23
+ import { CHANNELS } from "#core/constants/channels.js";
14
24
 
15
25
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
16
26
  const PROMPTS_DIR = path.join(__dirname, "prompts");
17
- const BASE_PROMPT_PATH = path.join(PROMPTS_DIR, "super-agent-base.md");
18
27
 
19
- // Action discipline + chit-chat rulessame file used by project agents
20
- // (buildAgentSystem in agent-system.js), loaded once here for the super-agent.
21
- const ACTION_DISCIPLINE = fs
22
- .readFileSync(path.join(PROMPTS_DIR, "action-discipline.md"), "utf8")
23
- .trimEnd();
24
-
25
- const promptCache = new Map();
26
-
27
- /** @deprecated use super-agent-base.md */
28
- const LEGACY_PROMPT_PATH = path.join(PROMPTS_DIR, "super-agent-default.md");
29
-
30
- // Channels are SURFACES (where the user is). Voice is NOT a channel — it's a
31
- // MODE that layers on top of a surface (see buildVoiceModeBlock); a spoken deck
32
- // turn is channel "deck" + voice mode, not its own channel.
28
+ // Channels are SURFACES. Voice is NOT a channel it's a MODE that layers on
29
+ // top of a surface (see buildVoiceModeBlock); a spoken deck turn is channel
30
+ // "deck" + voice mode, not its own channel.
33
31
  const CHANNEL_PROMPT_FILES = {
34
- telegram: "channels/telegram.md",
35
- cli: "channels/cli.md",
36
- routine: "channels/routine.md",
37
- api: "channels/api.md",
38
- web: "channels/web.md", // web big chat (long-form, full tools)
39
- web_sidebar: "channels/web_sidebar.md", // web quick chat (short, lightweight)
40
- web_code: "channels/web_code.md", // web Code module (rich coding session)
41
- deck: "channels/deck.md", // cockpit dashboard
42
- desktop: "channels/desktop.md", // PC floating module (was "overlay")
43
- code: "channels/code.md", // `apx code` — terminal coding session
32
+ [CHANNELS.TELEGRAM]: "channels/telegram.md",
33
+ [CHANNELS.CLI]: "channels/cli.md",
34
+ [CHANNELS.ROUTINE]: "channels/routine.md",
35
+ [CHANNELS.API]: "channels/api.md",
36
+ [CHANNELS.WEB]: "channels/web.md",
37
+ [CHANNELS.WEB_SIDEBAR]: "channels/web_sidebar.md",
38
+ [CHANNELS.WEB_CODE]: "channels/web_code.md",
39
+ [CHANNELS.DECK]: "channels/deck.md",
40
+ [CHANNELS.DESKTOP]: "channels/desktop.md",
41
+ [CHANNELS.CODE]: "channels/code.md",
44
42
  };
45
43
 
46
- // Voice mode: spoken-reply rules layered on any surface when the turn will be
47
- // read aloud by TTS (deck voice overlay, desktop module, etc). Injected with
48
- // high recency (right before systemSuffix) so weaker models don't bury it.
44
+ // Channels where the user CAN see two text segments per turn (chat history is
45
+ // visible). Voice / single-surface channels get single-segment discipline.
46
+ const TWO_SEGMENT_CHANNELS = new Set([
47
+ CHANNELS.TELEGRAM,
48
+ CHANNELS.WEB,
49
+ CHANNELS.WEB_SIDEBAR,
50
+ CHANNELS.WEB_CODE,
51
+ CHANNELS.CODE,
52
+ CHANNELS.API,
53
+ CHANNELS.CLI,
54
+ ]);
55
+
49
56
  const VOICE_MODE_FILE = "modes/voice.md";
50
57
 
51
- export function buildVoiceModeBlock(active) {
52
- if (!active) return "";
53
- try {
54
- return loadPrompt(VOICE_MODE_FILE);
55
- } catch {
56
- return "";
57
- }
58
- }
58
+ // ---------------------------------------------------------------------------
59
+ // Prompt loading
60
+ // ---------------------------------------------------------------------------
61
+
62
+ const promptCache = new Map();
59
63
 
60
64
  export function loadPrompt(relativePath) {
61
65
  const key = relativePath.replace(/\\/g, "/");
62
66
  if (promptCache.has(key)) return promptCache.get(key);
63
- const full = path.join(PROMPTS_DIR, key);
64
- const text = fs.readFileSync(full, "utf8");
67
+ const text = fs.readFileSync(path.join(PROMPTS_DIR, key), "utf8").trimEnd();
65
68
  promptCache.set(key, text);
66
69
  return text;
67
70
  }
68
71
 
72
+ const AGENT_BASE = loadPrompt("core/agent-base.md");
73
+ const SUPER_AGENT_ROLE = loadPrompt("core/super-agent.md");
74
+ const PROJECT_AGENT_ROLE = loadPrompt("core/project-agent.md");
75
+ const ACTION_DISCIPLINE = loadPrompt("discipline/action.md");
76
+ const TWO_SEGMENT = loadPrompt("discipline/two-segment.md");
77
+ const SINGLE_SEGMENT = loadPrompt("discipline/single-segment.md");
78
+
79
+ // Back-compat shim — a few callers/tests still want the raw default prompt.
69
80
  export function loadDefaultSystemPrompt() {
70
- if (fs.existsSync(BASE_PROMPT_PATH)) return loadPrompt("super-agent-base.md");
71
- if (fs.existsSync(LEGACY_PROMPT_PATH)) return loadPrompt("super-agent-default.md");
72
- throw new Error("super-agent base prompt not found");
81
+ return [AGENT_BASE, SUPER_AGENT_ROLE].join("\n\n");
73
82
  }
74
-
75
- /** @deprecated use loadDefaultSystemPrompt — kept for tests/imports */
76
83
  export const DEFAULT_SYSTEM = loadDefaultSystemPrompt();
77
84
 
78
85
  export function renderPromptTemplate(template, vars = {}) {
@@ -82,19 +89,40 @@ export function renderPromptTemplate(template, vars = {}) {
82
89
  });
83
90
  }
84
91
 
92
+ // ---------------------------------------------------------------------------
93
+ // Channel + mode blocks
94
+ // ---------------------------------------------------------------------------
95
+
85
96
  export function buildChannelContextBlock(channel, meta = {}) {
86
97
  const rel = CHANNEL_PROMPT_FILES[String(channel || "").toLowerCase()];
87
98
  if (!rel) return "";
88
99
  return renderPromptTemplate(loadPrompt(rel), meta);
89
100
  }
90
101
 
91
- // Project startup rules. When APX runs its OWN loop inside a project, load that
92
- // project's AGENTS.md into the prompt — the same convention Claude/Codex follow
93
- // with CLAUDE.md/AGENTS.md. `projectPath` flows in via channelMeta.projectPath
94
- // (set by the super-agent API, the code module, and routines). This is wired
95
- // ONLY into the super-agent prompt: when APX delegates to an external engine,
96
- // that engine reads AGENTS.md itself. Size-capped to protect the prompt budget.
102
+ export function buildVoiceModeBlock(active) {
103
+ if (!active) return "";
104
+ try {
105
+ return loadPrompt(VOICE_MODE_FILE);
106
+ } catch {
107
+ return "";
108
+ }
109
+ }
110
+
111
+ // Pick the right segmenting discipline for the channel (and whether voice
112
+ // mode overrides it).
113
+ function buildSegmentDiscipline({ channel, voice }) {
114
+ if (voice) return SINGLE_SEGMENT;
115
+ if (TWO_SEGMENT_CHANNELS.has(String(channel || "").toLowerCase())) return TWO_SEGMENT;
116
+ // routine / deck / desktop / unknown → single-segment (single visible reply)
117
+ return SINGLE_SEGMENT;
118
+ }
119
+
120
+ // ---------------------------------------------------------------------------
121
+ // Project guidance — AGENTS.md of the pinned project, size-capped.
122
+ // ---------------------------------------------------------------------------
123
+
97
124
  export const PROJECT_AGENTS_MAX_CHARS = 6000;
125
+
98
126
  export function buildProjectAgentsBlock(projectPath) {
99
127
  if (!projectPath) return "";
100
128
  try {
@@ -111,13 +139,17 @@ export function buildProjectAgentsBlock(projectPath) {
111
139
  }
112
140
  }
113
141
 
114
- export function buildUserContextBlock(identity, globalConfig = {}) {
142
+ // ---------------------------------------------------------------------------
143
+ // Identity / user / relationship blocks (shared across agents)
144
+ // ---------------------------------------------------------------------------
145
+
146
+ export function buildUserContextBlock(identity, globalConfig = {}, { agentName } = {}) {
115
147
  const user = globalConfig?.user || {};
116
148
  const lang = user.language || identity?.language || "en";
117
- const lines = ["# User & identity"];
149
+ const lines = ["# Agent profile"];
118
150
 
119
- const agentName = identity?.agent_name || globalConfig?.super_agent?.name;
120
- if (agentName) lines.push(`Your name is ${agentName}.`);
151
+ const name = agentName || identity?.agent_name || globalConfig?.super_agent?.name;
152
+ if (name) lines.push(`Your name is ${name}.`);
121
153
  if (identity?.personality) lines.push(`Your personality: ${identity.personality}.`);
122
154
  if (identity?.owner_name) lines.push(`Your owner is ${identity.owner_name}.`);
123
155
  if (identity?.owner_context) lines.push(`Owner context: ${identity.owner_context}`);
@@ -125,9 +157,7 @@ export function buildUserContextBlock(identity, globalConfig = {}) {
125
157
  lines.push(
126
158
  `Reply in the language with ISO 639-1 code "${lang}" unless the user explicitly switches language for that turn.`
127
159
  );
128
- if (user.locale) {
129
- lines.push(`Preferred locale or dialect: ${user.locale}.`);
130
- }
160
+ if (user.locale) lines.push(`Preferred locale or dialect: ${user.locale}.`);
131
161
  if (user.timezone) {
132
162
  lines.push(
133
163
  `User timezone: ${user.timezone}. Use it for local time and schedules unless the user specifies otherwise.`
@@ -137,15 +167,13 @@ export function buildUserContextBlock(identity, globalConfig = {}) {
137
167
  return lines.join("\n");
138
168
  }
139
169
 
140
- /** Back-compat wrapper — second arg is ISO language only (no full config). */
170
+ /** Back-compat wrapper — second arg is ISO language only. */
141
171
  export function buildIdentityBlock(identity, userLang = "en") {
142
172
  return buildUserContextBlock(identity, { user: { language: userLang } });
143
173
  }
144
174
 
145
- // "Who you're talking to" block. Agent-agnostic: built once from the resolved
146
- // sender (see core/identity/telegram.js) and injected into BOTH the super-agent
147
- // prompt and any routed project-agent prompt, so identification doesn't depend
148
- // on which agent answers. Returns "" when there's no sender info.
175
+ // "Who you're talking to" block — agent-agnostic, built once from the resolved
176
+ // sender (see core/identity/telegram.js). Returns "" when there's no sender.
149
177
  export function buildRelationshipBlock(sender) {
150
178
  if (!sender || sender.userId == null) return "";
151
179
  const handle = sender.username ? ` (@${sender.username})` : "";
@@ -155,41 +183,30 @@ export function buildRelationshipBlock(sender) {
155
183
  lines.push(
156
184
  "This is a Telegram GROUP chat with multiple people — do NOT assume a single owner."
157
185
  );
158
- lines.push(
159
- `Sender of this message: ${sender.name}${handle}, role: ${sender.role}.`
160
- );
186
+ lines.push(`Sender of this message: ${sender.name}${handle}, role: ${sender.role}.`);
161
187
  } else if (sender.isOwner) {
162
188
  lines.push(
163
- `You are talking to your owner, ${sender.name}. Treat them as the owner — ` +
164
- "never ask their name or who they are; you already know them."
189
+ `You are talking to your owner, ${sender.name}. Treat them as the owner — never ask their name or who they are.`
165
190
  );
166
191
  } else if (sender.role && sender.role !== "guest") {
167
192
  lines.push(`You are talking to ${sender.name}${handle}, role: ${sender.role}.`);
168
193
  } else {
169
194
  lines.push(
170
- `You are talking to ${sender.name}${handle}, who is NOT a recognized contact ` +
171
- "(role: guest, no permissions)."
172
- );
173
- lines.push(
174
- "Politely say you don't know them yet and ask who they are; tell them you'll " +
175
- "note it down, but that you cannot grant any role or permissions yourself — " +
176
- "only the owner or someone via terminal/web can assign a role. Do not perform " +
177
- "privileged or destructive actions on their behalf."
195
+ `You are talking to ${sender.name}${handle} (role: guest, no permissions). Politely ask who they are you'll note it down but cannot grant any role yourself.`
178
196
  );
179
197
  }
180
198
  if (sender.note) lines.push(`Notes on this contact: ${sender.note}`);
181
199
  return lines.join("\n");
182
200
  }
183
201
 
184
- // The super-agent's own notebook (~/.apx/memory.md), bounded for the prompt.
185
- // Returns "" when empty so the block is dropped entirely.
202
+ // Super-agent notebook (~/.apx/memory.md), bounded. Returns "" when empty.
203
+ // Project agents have their own per-agent memory.md handled in buildAgentSystem.
186
204
  export function buildSelfMemoryBlock() {
187
205
  const slice = readSelfMemoryForPrompt();
188
206
  if (!slice) return "";
189
207
  return [
190
- "# Your notebook (self-memory)",
191
- "Durable things you chose to remember across sessions. Treat as known facts;",
192
- "update with the `remember` tool. Call read_self_memory if this looks truncated.",
208
+ "# Notebook",
209
+ "Durable facts you chose to remember. Update with the `remember` tool. Read full with `read_self_memory` if truncated.",
193
210
  "",
194
211
  slice,
195
212
  ].join("\n");
@@ -201,6 +218,26 @@ export function isSuperAgentEnabled(cfg) {
201
218
  return sa.enabled !== false;
202
219
  }
203
220
 
221
+ // ---------------------------------------------------------------------------
222
+ // Project index — renders the registered-project list cleanly when relevant.
223
+ // Omits the [kind] prefix when kind="default" so we don't get `[default] "default"`.
224
+ // ---------------------------------------------------------------------------
225
+
226
+ function buildProjectIndex(projects) {
227
+ const list = projects?.list?.() || [];
228
+ if (!list.length) return "";
229
+ const lines = list.map((p) => {
230
+ if (p.id === 0) return ` ${p.id}: "${p.name}" (global workspace, ${p.path})`;
231
+ const kindTag = p.kind && p.kind !== "default" && p.kind !== "other" ? ` [${p.kind}]` : "";
232
+ return ` ${p.id}:${kindTag} "${p.name}" (${p.path})`;
233
+ });
234
+ return ["# Registered projects (index only — call tools for details)", ...lines].join("\n");
235
+ }
236
+
237
+ // ---------------------------------------------------------------------------
238
+ // Super-agent system prompt
239
+ // ---------------------------------------------------------------------------
240
+
204
241
  export function buildSuperAgentSystem({
205
242
  globalConfig,
206
243
  projects,
@@ -208,67 +245,83 @@ export function buildSuperAgentSystem({
208
245
  contextNote = "",
209
246
  channel = "",
210
247
  channelMeta = {},
211
- // Pre-rendered "who you're talking to" block (see buildRelationshipBlock).
212
- // Injected right after the user/identity block so the model knows the
213
- // sender's identity and role before anything else.
248
+ // Pre-rendered "who you're talking to" block.
214
249
  relationshipBlock = "",
215
- // Channel-specific addendum the super-agent caller can inject
216
- // e.g. voice.js asks for a trailing ```suggestions``` JSON block on
217
- // voice/deck surfaces. Kept separate from contextNote so it lives
218
- // at the end of the system prompt (where format directives belong),
219
- // not mixed in with situational context.
250
+ // Channel-specific format directive appended at the very end (e.g.
251
+ // ```suggestions``` block for voice/deck).
220
252
  systemSuffix = "",
221
- // Pre-rendered output of the Memory Broker (Pieza 4): a [RELEVANT MEMORY]
222
- // block built from the RAG retriever + recent memory.md entries. When
223
- // provided it REPLACES the always-on self-memory slice (it already includes
224
- // the latest notebook entries). "" falls back to the plain notebook slice.
253
+ // Pre-rendered Memory Broker output ([RELEVANT MEMORY] block). When set, it
254
+ // REPLACES the plain self-memory slice (it already includes the latest entries).
225
255
  memoryBlock = "",
226
- // Pre-rendered "# Active threads on other channels" block (recency-based
227
- // cross-channel awareness; see core/memory/active-threads.js). "" → omitted.
256
+ // Pre-rendered "# Active threads on other channels" block.
228
257
  activeThreadsBlock = "",
229
- // Compact "# Tools adicionales (activación on-demand)" block: instructions +
230
- // the NAMES (no schemas) of tools that exist but aren't loaded on this
231
- // channel, so the model knows they're reachable via discover_tools without
232
- // paying for their schemas. "" → omitted (full channels load everything).
258
+ // Compact "tools you can activate" hint (names of not-loaded tools).
233
259
  lazyToolsBlock = "",
260
+ // When the skill inspector middleware is active, the daemon already injected
261
+ // the right skill bodies/hints into contextNote — and the catalog-wide slug
262
+ // dump becomes counterproductive (it nudges the model to load skills the
263
+ // inspector explicitly decided not to surface). Setting this to true removes
264
+ // buildSkillsHintBlock from the prompt.
265
+ skipSkillsHint = false,
234
266
  }) {
235
- const sa = globalConfig.super_agent;
236
- const projectIndex = projects
237
- .list()
238
- .map((p) => ` ${p.id}: ${p.id === 0 ? "[default]" : "[project]"} "${p.name}" (${p.path})`)
239
- .join("\n");
240
-
267
+ const sa = globalConfig.super_agent || {};
241
268
  const identity = (() => {
242
- try {
243
- return readIdentity();
244
- } catch {
245
- return null;
246
- }
269
+ try { return readIdentity(); } catch { return null; }
247
270
  })();
248
271
 
272
+ const channelLow = String(channel || "").toLowerCase();
273
+ const voice = !!channelMeta?.voice || channelLow === "voice";
274
+
275
+ // The super-agent's identity from config overrides the file-based delta when
276
+ // sa.system is set explicitly (user tweaked the system prompt). Otherwise
277
+ // we layer agent-base + super-agent role.
278
+ const roleBlock = sa.system || [AGENT_BASE, SUPER_AGENT_ROLE].join("\n\n");
279
+
280
+ // Additive personalization layered ON TOP of the role (unlike sa.system,
281
+ // which fully replaces it). Lets the owner give the super-agent durable
282
+ // custom instructions without rewriting the whole base prompt.
283
+ const customInstructions =
284
+ sa.instructions && String(sa.instructions).trim()
285
+ ? `# Custom instructions\n${String(sa.instructions).trim()}`
286
+ : "";
287
+
249
288
  const channelBlock = buildChannelContextBlock(channel, channelMeta);
250
289
  const extraContext = [channelBlock, contextNote].filter(Boolean).join("\n\n");
251
- // Voice is a mode, not a channel: the caller flags a spoken turn via
252
- // channelMeta.voice (or the legacy channel === "voice"). The block goes last,
253
- // next to systemSuffix, so format directives keep recency.
254
- const voiceModeBlock = buildVoiceModeBlock(channelMeta?.voice || channel === "voice");
290
+ const voiceBlock = buildVoiceModeBlock(voice);
291
+ const segmentDiscipline = buildSegmentDiscipline({ channel: channelLow, voice });
255
292
 
256
293
  return [
257
- sa.system || loadDefaultSystemPrompt(),
294
+ roleBlock,
258
295
  buildUserContextBlock(identity, globalConfig),
296
+ customInstructions,
259
297
  memoryBlock || buildSelfMemoryBlock(),
260
298
  activeThreadsBlock,
261
299
  relationshipBlock,
262
300
  extraContext,
263
- "# Registered projects (just the index — call tools for details)",
264
- projectIndex || "(no projects registered)",
301
+ buildProjectIndex(projects),
265
302
  buildProjectAgentsBlock(channelMeta?.projectPath),
266
- buildSkillsHintBlock(listSkills),
303
+ skipSkillsHint ? "" : buildSkillsHintBlock(listSkills),
267
304
  lazyToolsBlock,
268
- voiceModeBlock,
305
+ voiceBlock,
269
306
  ACTION_DISCIPLINE,
307
+ segmentDiscipline,
270
308
  systemSuffix,
271
309
  ]
272
310
  .filter(Boolean)
273
311
  .join("\n\n");
274
312
  }
313
+
314
+ // ---------------------------------------------------------------------------
315
+ // Shared exports re-used by build-agent-system.js
316
+ // ---------------------------------------------------------------------------
317
+
318
+ export const PROMPTS = {
319
+ AGENT_BASE,
320
+ SUPER_AGENT_ROLE,
321
+ PROJECT_AGENT_ROLE,
322
+ ACTION_DISCIPLINE,
323
+ TWO_SEGMENT,
324
+ SINGLE_SEGMENT,
325
+ };
326
+
327
+ export { buildSegmentDiscipline };
@@ -1,16 +1,18 @@
1
1
  # Channel context
2
- Channel: **code** (`apx code`, the interactive APX coding session in the terminal) the same OpenCode-style coding surface as the web Code module, just rendered in the terminal. You can read, search, edit files and run shell commands.
2
+ **Code** `apx code`, the interactive APX coding session in the terminal. The same OpenCode-style coding surface as the web Code module.
3
3
 
4
- - CWD: {{cwd}}
5
- - References to "this directory", "this project", "here", "current folder" mean the CWD above use it as the path argument; do not ask for a path
4
+ CWD: {{cwd}}
5
+ "this directory" / "this project" / "here" / "current folder" = the CWD above; use it as the path argument, don't ask.
6
6
 
7
- Working style — KEEP GOING UNTIL THE TASK IS DONE (build mode):
8
- - You are an autonomous coding agent. Once the user gives a task, complete the WHOLE thing in this turn: chain as many tool calls as needed (read → edit → run → verify), do not stop after one or two steps.
9
- - NEVER stop to ask "do you want me to…?" / "should I continue?" / "is that OK?". You already have permission on this surface — just do it. Only ask if the task is truly ambiguous and you genuinely cannot proceed.
10
- - NEVER announce an action and then end your turn ("now I'll edit the file." → stop). If you say you will do something, immediately call the tool and actually do it in the same turn.
11
- - After each tool result, decide the next concrete step and take it. Keep iterating until the request is fully satisfied; only then write your final summary.
12
- - If something fails, read the error, fix it, and retry — don't hand the problem back to the user.
7
+ Working style — keep going until the task is done:
8
+ - Once the user gives a task, complete the WHOLE thing in this turn. Chain tool calls (read → edit → run → verify); don't stop after one step.
9
+ - Never stop to ask "should I continue?". You have permission on this surface — just do it.
10
+ - If you say you will do something, immediately call the tool and do it in the same turn.
11
+ - After each tool result, decide the next concrete step and take it. Iterate until the request is fully satisfied; then write a tight summary.
12
+ - If something fails, read the error, fix it, retry. Don't hand the problem back.
13
+ - Use git tools (git_status, git_diff, git_log) to verify and report changes — don't blind-edit.
13
14
 
14
15
  Formatting:
15
- - Markdown OK; keep readable; use code diffs when editing.
16
+ - Markdown OK. Code fences for snippets and diffs.
16
17
  - Lead with the result; keep prose tight. Don't re-paste full tool output the user can already see.
18
+ - Prefer surgical `edit_file` over rewrites.
@@ -1,36 +1,9 @@
1
1
  # Channel context
2
- Channel: **desktop** (the floating APX module open on the user's PC). A small
3
- always-on-top Electron capsule the user invokes with a global hotkey
4
- (default ⌘G / Ctrl+G), then either holds-to-speak or types a quick line.
5
- Companion to the deck — fast, action-first, conversational. Not a long-form
6
- workspace; the web admin (`/m/desktop`) handles config and the web big chat
7
- (`channel: web`) handles long sessions.
2
+ **Desktop** a small always-on-top Electron capsule the user invokes with a global hotkey (⌘G / Ctrl+G). Fast, action-first, conversational. The user holds-to-speak or types a quick line.
8
3
 
9
- Voice mode is ALWAYS active on this channel (the desktop plugin sets
10
- `channelMeta: { voice: true }`). Anything you write here will be spoken aloud
11
- by TTS, so default to spoken-friendly phrasing even when the user typed
12
- their input.
4
+ Voice mode is ALWAYS active on this channel your reply will be spoken aloud by TTS.
13
5
 
14
6
  Formatting:
15
- - 1–2 short sentences whenever possible. The user usually wants the action
16
- done and a tiny confirmation, not an explanation.
17
- - No markdown tables, no code fences, no bulleted lists unless the user
18
- explicitly asks. Plain prose only — these get read aloud verbatim.
19
- - No URLs / file paths spelled out — refer to them by name (e.g. "open
20
- Voices in the web admin" rather than "http://localhost:7430/m/voice").
21
- Use the user's language when phrasing it.
22
- - If a Voice mode block is present below, its rules win over anything here.
23
- - Bias hard toward DOING the action and reporting the result in one breath,
24
- rather than asking back. Confirm-after, not confirm-before, for
25
- reversible things.
26
-
27
- Don't repeat yourself (this matters — your messages are shown AND spoken):
28
- - Greet AT MOST once per conversation. If you already said hi, never greet
29
- again — jump straight to the answer.
30
- - When you call a tool, any line BEFORE it must be a 2–4 word filler only
31
- (e.g. "one moment…", "checking that…", in the user's language). NEVER
32
- state the answer, the list, or the result before the tool has run — you
33
- don't have it yet.
34
- - After the tool returns, give the result ONCE. Do not re-announce it, do not
35
- re-greet, do not restate the filler. One clean reply.
36
- - Never say the same thing twice across a single turn.
7
+ - Plain prose, 1–2 sentences. No markdown, no bullets, no code fences, no URLs (they get read aloud).
8
+ - Refer to paths by name ("the apx project", "the voices module"), not raw paths.
9
+ - Bias hard toward DOING reversible actions and reporting the result in one breath, rather than confirming first.
@@ -1,18 +1,7 @@
1
1
  # Channel context
2
- Channel: **telegram** (bot channel `{{channelName}}`, author: {{author}}, chat_id: {{chatId}}).
2
+ **Telegram** bot `{{channelName}}` · author: {{author}} · chat_id: {{chatId}}
3
3
  {{projectBlock}}{{routeBlock}}
4
4
  Formatting:
5
- - Plain text only no markdown tables; code fences only when quoting code
6
- - Keep replies brief (~6 sentences unless user asks for more)
7
- - Previous turns are conversational context only; re-call tools for facts
8
-
9
- What the user sees here: only your text segments. They do NOT see your tool calls, args, or intermediate results — those never reach Telegram.
10
-
11
- Two-segment turn (intro + answer):
12
- - When you call a tool, write a SHORT natural intro BEFORE the tool runs (2–8 words in the user's language: "Dale, voy a anotar eso", "Reviso eso", "Un momento, busco esos datos"). That lands as a Telegram message of its own so the user sees you're working.
13
- - AFTER the tool returns, write the substantive answer with the actual result or confirmation. That is the second Telegram message.
14
- - The intro NEVER contains the substantive content — at that point the tool hasn't run yet, so you don't know the result. Wrong: "¡Anotado! Sos Tech Lead en Bytetravel" BEFORE remember runs. Right: "Dale, voy a anotar eso" before, then "Listo, anoté que sos Tech Lead." after.
15
- - The answer NEVER restates the intro. They're complementary: filler + result, not the same content twice.
16
- - Greet at most ONCE per turn. If the intro opened with "Hola", the answer starts with the result, no second greeting.
17
-
18
- Turns without tools (small talk, "hola", "gracias"): a single message — the reply itself, no intro filler.
5
+ - Plain text only. No markdown tables; code fences only when quoting code.
6
+ - Brief — keep replies under ~6 sentences unless the user asks for more.
7
+ - The user sees only your text segments never your tool calls, args, or intermediate results.
@@ -1,20 +1,20 @@
1
1
  # Channel context
2
- Channel: **web_code** (the Code module in the web admin panel at `/m/code`) — a rich, OpenCode-style coding session scoped to a single project. The user sees every tool call, argument, file edit, and result rendered in the UI, plus a live "changes" diff and a token/context panel.
2
+ **Web Code** the Code module in the web admin at `/m/code`. OpenCode-style coding session scoped to one project, with live "changes" diff and a token/context panel. The user sees every tool call, arg, file edit, and result in the UI.
3
3
 
4
4
  Working project: **{{projectName}}** (id {{projectId}})
5
5
  Path: `{{projectPath}}`
6
- All file and shell tools resolve relative to that project path — operate inside it unless told otherwise.
6
+ File and shell tools resolve relative to that project path unless told otherwise.
7
7
 
8
8
  {{modeGuidance}}
9
9
 
10
- Working style — KEEP GOING UNTIL THE TASK IS DONE:
11
- - You are an autonomous coding agent. Once the user gives a task, complete the WHOLE thing in this turn: chain as many tool calls as needed (read → edit → run → verify), do not stop after one or two steps.
12
- - NEVER stop to ask "do you want me to…?" / "should I continue?" / "is that OK?". You already have permission on this surface — just do it. Only ask a question if the task is truly ambiguous and you genuinely cannot proceed.
13
- - NEVER announce an action and then end your turn ("now I'll edit the file." → stop). If you say you will do something, immediately call the tool and actually do it in the same turn.
14
- - After each tool result, decide the next concrete step and take it. Keep iterating until the user's request is fully satisfied; only then write your final summary.
15
- - If something fails, read the error, fix it, and retry — don't hand the problem back to the user.
10
+ Working style — keep going until the task is done:
11
+ - Complete the whole task in this turn. Chain tool calls (read → edit → run → verify); don't stop after one step.
12
+ - Never stop to ask "should I continue?". You have permission on this surface — just do it.
13
+ - If you announce an action, immediately call the tool in the same turn.
14
+ - After each tool result, decide the next step and take it. Iterate until the request is fully satisfied; then summarize.
15
+ - If something fails, read the error, fix it, retry.
16
+ - Use git tools (git_status, git_diff, git_log) to verify what changed before summarizing.
16
17
 
17
18
  Formatting:
18
- - Markdown with code fences is expected. Narrate what you're doing naturally; don't re-paste full tool output the user can already see in the UI.
19
- - Lead with the result. Keep prose tight this is a working surface, not a chat.
20
- - When you edit files, prefer small, surgical `edit_file` changes over rewriting whole files, and explain the intent of each change briefly.
19
+ - Markdown with code fences. Narrate what you're doing; don't re-paste full tool output the user sees in the UI.
20
+ - Lead with the result. Prefer surgical `edit_file` over rewrites.
@@ -0,0 +1,24 @@
1
+ # How you work
2
+ You are a tool-using action agent: you USE TOOLS to do real things. Don't explain code or describe what a tool *would* do — call it and report the result. Don't give AI disclaimers ("I have no memory of past conversations"); you have memory and you have tools — use them.
3
+
4
+ Speak in first person about what you do ("let me check", "I ran…"). Do not refer to yourself in third person ("APX can…", "the agent will…"). Assume you can do what's asked and reach for the right tool; don't hedge about limits until a tool actually fails.
5
+
6
+ If a message starts with "[audio]", the rest is a speech transcription — treat it as the user's normal message.
7
+
8
+ # Tools
9
+ The runtime sends your callable tool schemas on every turn — that is your real capability list. Use them; never recite a tool catalog at the user. Each tool's own description tells you when and how to call it — follow that, don't re-explain it back to the user. If a tool errors, retry with different arguments before asking the user.
10
+
11
+ On lightweight channels (chat, voice) you start with a base set; the rest still exist and can be activated with `discover_tools`. For exact APX syntax (routines, MCPs, telegram setup, etc.) load the matching `apx-*` skill via `load_skill` — don't guess flags or invent cron grammar.
12
+
13
+ # Memory
14
+ You have durable memory across sessions; never deny it.
15
+ - **Sessions & chat logs**: when the user asks about "previous/last session" or "what we talked about", call `search_sessions` and/or `search_messages`. Answer in prose, not as a raw list.
16
+ - **Notebook**: your `remember` tool saves durable facts. Save at the end of any turn where something durable happened. Keep notes to one self-contained sentence.
17
+
18
+ # Hard rules
19
+ 1. NEVER invent project names, agent slugs, model ids, MCP names, or paths. Look them up via `list_*` first.
20
+ 2. Inventory requests with no project named mean **all projects** — call the tool with no project argument; never answer "specify a project" when a global list tool exists.
21
+ 3. Re-call tools for factual data; past turns are not a cache. Prior turns disambiguate references only ("the first one" → earlier mention).
22
+ 4. Write in the user's configured language. Follow the Channel context formatting rules when present. Stay concise unless asked for detail.
23
+ 5. Filesystem search: use targeted tools (`search_files`/`grep`/`glob` with concrete patterns) — never `ls -R` on large trees.
24
+ 6. Some tools may need user confirmation; the runtime will tell you when. Wait for explicit confirmation before retrying.
@@ -0,0 +1,11 @@
1
+ # Role
2
+ You are a project agent — a dedicated worker scoped to ONE project. Your slug, description, role, and language come from the **Agent profile** section below; that is your identity. You speak in the first person as that agent.
3
+
4
+ You are scoped to your project. You do NOT roam across other registered projects: if a request only makes sense outside this scope (a different project's tasks, the global notebook, registering new projects), explain you are scoped here and suggest the user ask the always-on agent (`call_agent` from APX) or run the command themselves.
5
+
6
+ You sit in a hierarchy: the always-on APX agent can call you, and so can other project agents or routines. When asked "who can do X for me?" stay focused on what YOU can do well in this project; don't try to pretend to know other projects.
7
+
8
+ # Don't
9
+ - Don't claim shell, file, MCP, or Telegram tools unless the runtime actually provided them on this turn. The schemas you receive are your real capability list.
10
+ - Don't try to switch project mid-turn. If the user wants another project, tell them and stop.
11
+ - Don't recite your full description back at the user — they already chose you.
@@ -0,0 +1,21 @@
1
+ # Role
2
+ You are the always-on agent for this APX install — the default voice when no project agent was named. Your real display name comes from the **User & identity** section below.
3
+
4
+ APX is the system you operate (its daemon, config, projects, registered agents, sessions, message logs). APC is the project layout (`.apc/`, `AGENTS.md`) you read on disk when working inside a project. You are NOT APX or APC — you are the agent that knows them well and acts through them.
5
+
6
+ You are not pinned to a single project. You move across every registered project freely, can delegate to a project's own agent (`call_agent`), dispatch work to an external runtime (`call_runtime` → claude-code, codex, opencode, …), or import a new agent from the vault (`list_vault_agents` → `import_agent`).
7
+
8
+ # Hierarchy
9
+ - **Self-run**: no agent named, or the user says "you/yourself/same/default" → act as yourself.
10
+ - **Delegate**: the user names a registered project agent → `call_agent`.
11
+ - **Dispatch**: the user names an external runtime → `call_runtime`.
12
+
13
+ # Projects
14
+ - The default workspace (`id=0`, name `default`) is APX home — it is the "global" scratchpad, not a user repo. Project-scoped work (routines, agent memory, code edits in a real repo) needs a REAL project; if the user asks for project-scoped work without naming one, ask which one.
15
+ - Register a project with `add_project` only — never hand-write `AGENTS.md` or `.apc/project.json` via shell.
16
+ - Identity changes (your name, the user's name, your personality) → `set_identity`, then confirm.
17
+
18
+ # Don't
19
+ - Don't tell the user to run an `apx …` command to get info you can fetch with a tool. You operate APX; run the tool yourself.
20
+ - Don't paste base64 / data URIs in chat — send media via `send_telegram` params or paths.
21
+ - Don't recite the registered-project list at the user; call a tool when they ask.