@agentprojectcontext/apx 1.34.0 → 1.36.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.
- package/package.json +1 -1
- package/skills/apx/SKILL.md +1 -1
- package/src/core/agent/build-agent-system.js +134 -58
- package/src/core/agent/channels/voice-context.js +4 -4
- package/src/core/agent/prompt-builder.js +176 -123
- package/src/core/agent/prompts/channels/code.md +12 -10
- package/src/core/agent/prompts/channels/desktop.md +5 -32
- package/src/core/agent/prompts/channels/telegram.md +4 -15
- package/src/core/agent/prompts/channels/web_code.md +11 -11
- package/src/core/agent/prompts/core/agent-base.md +24 -0
- package/src/core/agent/prompts/core/project-agent.md +11 -0
- package/src/core/agent/prompts/core/super-agent.md +21 -0
- package/src/core/agent/prompts/discipline/action.md +10 -0
- package/src/core/agent/prompts/discipline/single-segment.md +6 -0
- package/src/core/agent/prompts/discipline/two-segment.md +11 -0
- package/src/core/agent/self-memory.js +43 -1
- package/src/core/agent/skills/index-store.js +307 -0
- package/src/core/agent/skills/index.js +15 -1
- package/src/core/agent/skills/inspector.js +317 -0
- package/src/core/agent/super-agent.js +7 -1
- package/src/core/agent/tools/handlers/_git.js +50 -0
- package/src/core/agent/tools/handlers/git-diff.js +44 -0
- package/src/core/agent/tools/handlers/git-log.js +38 -0
- package/src/core/agent/tools/handlers/git-show.js +34 -0
- package/src/core/agent/tools/handlers/git-status.js +61 -0
- package/src/core/agent/tools/names.js +31 -0
- package/src/core/agent/tools/registry.js +36 -5
- package/src/core/config/index.js +21 -0
- package/src/core/runtime-skills/apx/SKILL.md +27 -39
- package/src/core/runtime-skills/apx-agency-agents/SKILL.md +40 -56
- package/src/core/runtime-skills/apx-agent/SKILL.md +27 -30
- package/src/core/runtime-skills/apx-mcp/SKILL.md +31 -36
- package/src/core/runtime-skills/apx-mcp-builder/SKILL.md +37 -51
- package/src/core/runtime-skills/apx-project/SKILL.md +20 -29
- package/src/core/runtime-skills/apx-routine/SKILL.md +34 -47
- package/src/core/runtime-skills/apx-runtime/SKILL.md +32 -50
- package/src/core/runtime-skills/apx-sessions/SKILL.md +96 -145
- package/src/core/runtime-skills/apx-skill-builder/SKILL.md +53 -77
- package/src/core/runtime-skills/apx-task/SKILL.md +18 -21
- package/src/core/runtime-skills/apx-telegram/SKILL.md +43 -54
- package/src/core/runtime-skills/apx-voice/SKILL.md +36 -56
- package/src/core/stores/conversations.js +27 -2
- package/src/host/daemon/api/exec.js +2 -0
- package/src/host/daemon/api/skills.js +140 -6
- package/src/host/daemon/api/super-agent.js +56 -1
- package/src/host/daemon/index.js +17 -0
- package/src/interfaces/cli/branding.js +53 -0
- package/src/interfaces/cli/commands/skills.js +254 -0
- package/src/interfaces/cli/index.js +84 -2
- package/src/interfaces/web/dist/assets/index-Cm0KyPoZ.css +1 -0
- package/src/interfaces/web/dist/assets/index-DJKA763h.js +628 -0
- package/src/interfaces/web/dist/assets/index-DJKA763h.js.map +1 -0
- package/src/interfaces/web/dist/index.html +2 -2
- package/src/interfaces/web/src/App.tsx +0 -1
- package/src/interfaces/web/src/components/chat/ChatList.tsx +412 -0
- package/src/interfaces/web/src/components/chat/MessageBubble.tsx +21 -1
- package/src/interfaces/web/src/components/settings/AppearancePanel.tsx +1 -1
- package/src/interfaces/web/src/components/settings/MemoryPanel.tsx +69 -1
- package/src/interfaces/web/src/components/settings/SkillsInspectorPanel.tsx +222 -0
- package/src/interfaces/web/src/hooks/useChat.ts +54 -2
- package/src/interfaces/web/src/i18n/en.ts +12 -1
- package/src/interfaces/web/src/i18n/es.ts +12 -1
- package/src/interfaces/web/src/lib/api/agents.ts +1 -1
- package/src/interfaces/web/src/lib/api/skills.ts +70 -0
- package/src/interfaces/web/src/screens/ProjectScreen.tsx +3 -5
- package/src/interfaces/web/src/screens/SettingsScreen.tsx +12 -6
- package/src/interfaces/web/src/screens/modules/VoiceScreen.tsx +1 -1
- package/src/interfaces/web/src/screens/project/ChatTab.tsx +120 -87
- package/src/interfaces/web/src/types/daemon.ts +10 -0
- package/src/core/agent/prompts/action-discipline.md +0 -24
- package/src/core/agent/prompts/super-agent-base.md +0 -42
- package/src/interfaces/web/dist/assets/index-DdmSRtsz.css +0 -1
- package/src/interfaces/web/dist/assets/index-M4FspaCH.js +0 -613
- package/src/interfaces/web/dist/assets/index-M4FspaCH.js.map +0 -1
- package/src/interfaces/web/src/screens/project/ThreadsTab.tsx +0 -100
|
@@ -1,9 +1,18 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
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
|
-
//
|
|
20
|
-
// (
|
|
21
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
//
|
|
47
|
-
//
|
|
48
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
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 = ["#
|
|
149
|
+
const lines = ["# Agent profile"];
|
|
118
150
|
|
|
119
|
-
const
|
|
120
|
-
if (
|
|
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
|
|
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
|
|
146
|
-
// sender (see core/identity/telegram.js)
|
|
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
|
|
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
|
-
//
|
|
185
|
-
//
|
|
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
|
-
"#
|
|
191
|
-
"Durable
|
|
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
|
|
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
|
|
216
|
-
//
|
|
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
|
|
222
|
-
//
|
|
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
|
|
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 "
|
|
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
|
-
|
|
252
|
-
|
|
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
|
-
|
|
294
|
+
roleBlock,
|
|
258
295
|
buildUserContextBlock(identity, globalConfig),
|
|
296
|
+
customInstructions,
|
|
259
297
|
memoryBlock || buildSelfMemoryBlock(),
|
|
260
298
|
activeThreadsBlock,
|
|
261
299
|
relationshipBlock,
|
|
262
300
|
extraContext,
|
|
263
|
-
|
|
264
|
-
projectIndex || "(no projects registered)",
|
|
301
|
+
buildProjectIndex(projects),
|
|
265
302
|
buildProjectAgentsBlock(channelMeta?.projectPath),
|
|
266
|
-
buildSkillsHintBlock(listSkills),
|
|
303
|
+
skipSkillsHint ? "" : buildSkillsHintBlock(listSkills),
|
|
267
304
|
lazyToolsBlock,
|
|
268
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5
|
-
|
|
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 —
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
- After each tool result, decide the next concrete step and take it.
|
|
12
|
-
- If something fails, read the error, fix it,
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
16
|
-
|
|
17
|
-
-
|
|
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
|
-
|
|
2
|
+
**Telegram** bot `{{channelName}}` · author: {{author}} · chat_id: {{chatId}}
|
|
3
3
|
{{projectBlock}}{{routeBlock}}
|
|
4
4
|
Formatting:
|
|
5
|
-
- Plain text only
|
|
6
|
-
-
|
|
7
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
6
|
+
File and shell tools resolve relative to that project path unless told otherwise.
|
|
7
7
|
|
|
8
8
|
{{modeGuidance}}
|
|
9
9
|
|
|
10
|
-
Working style —
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
- After each tool result, decide the next
|
|
15
|
-
- If something fails, read the error, fix it,
|
|
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
|
|
19
|
-
- Lead with the result.
|
|
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.
|