@agentprojectcontext/apx 1.33.1 → 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.
- package/package.json +1 -1
- package/skills/apx/SKILL.md +49 -61
- package/src/core/agent/a2a/reply.js +48 -0
- package/src/core/agent/build-agent-system.js +136 -59
- package/src/core/agent/channels/voice-context.js +98 -0
- package/src/core/agent/memory.js +2 -1
- package/src/core/agent/prompt-builder.js +178 -124
- 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/prompts/modes/code-build.md +1 -0
- package/src/core/agent/prompts/modes/code-plan.md +1 -0
- package/src/core/agent/prompts/modes/index.js +28 -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/skills/loader.js +22 -18
- package/src/core/agent/stream/turn-accumulator.js +73 -0
- package/src/core/agent/suggestions.js +37 -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/add-project.js +5 -2
- package/src/core/agent/tools/handlers/call-runtime.js +3 -2
- 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/handlers/transcribe-audio.js +1 -1
- package/src/core/agent/tools/helpers.js +2 -2
- package/src/core/agent/tools/names.js +169 -0
- package/src/core/agent/tools/registry-bridge.js +6 -14
- package/src/core/agent/tools/registry.js +103 -69
- package/src/core/apc/context-copy.js +27 -0
- package/src/core/apc/notes.js +19 -0
- package/src/core/apc/parser.js +12 -5
- package/src/core/apc/paths.js +87 -0
- package/src/core/apc/scaffold.js +82 -76
- package/src/core/apc/skill-sync.js +10 -0
- package/src/{host/daemon/plugins → core/channels}/telegram/dispatch.js +38 -16
- package/src/core/config/index.js +24 -2
- package/src/core/config/redact.js +95 -0
- package/src/core/constants/channels.js +2 -0
- package/src/core/constants/code-modes.js +10 -0
- package/src/core/constants/index.js +1 -0
- package/src/core/deck/manifest.js +186 -0
- package/src/core/engines/catalog.js +83 -0
- package/src/core/{tools → http-tools}/browser.js +0 -1
- package/src/core/{tools → http-tools}/fetch.js +0 -1
- package/src/core/{tools → http-tools}/glob.js +0 -1
- package/src/core/{tools → http-tools}/grep.js +0 -1
- package/src/core/{tools → http-tools}/registry.js +0 -1
- package/src/core/{tools → http-tools}/search.js +0 -1
- package/src/core/i18n/en.js +9 -0
- package/src/core/i18n/es.js +12 -0
- package/src/core/i18n/index.js +54 -0
- package/src/core/i18n/pt.js +9 -0
- package/src/core/identity/telegram.js +2 -1
- package/src/core/mcp/runner.js +272 -14
- package/src/core/mcp/sources.js +3 -2
- package/src/core/routines/index.js +16 -0
- package/src/{host/daemon/routines.js → core/routines/runner.js} +36 -103
- package/src/core/runtime-skills/apc-context/SKILL.md +159 -0
- package/src/core/runtime-skills/apx/SKILL.md +83 -0
- package/src/core/runtime-skills/apx-agency-agents/SKILL.md +125 -0
- package/src/core/runtime-skills/apx-agent/SKILL.md +97 -0
- package/src/core/runtime-skills/apx-mcp/SKILL.md +111 -0
- package/src/core/runtime-skills/apx-mcp-builder/SKILL.md +169 -0
- package/{skills → src/core/runtime-skills}/apx-project/SKILL.md +20 -29
- package/src/core/runtime-skills/apx-routine/SKILL.md +127 -0
- package/src/core/runtime-skills/apx-runtime/SKILL.md +99 -0
- package/src/core/runtime-skills/apx-sessions/SKILL.md +232 -0
- package/src/core/runtime-skills/apx-skill-builder/SKILL.md +129 -0
- package/{skills → src/core/runtime-skills}/apx-task/SKILL.md +18 -21
- package/src/core/runtime-skills/apx-telegram/SKILL.md +120 -0
- package/src/core/runtime-skills/apx-voice/SKILL.md +117 -0
- package/src/core/runtime-skills/{claude-code.md → claude-code/SKILL.md} +1 -0
- package/src/core/runtime-skills/{codex-cli.md → codex-cli/SKILL.md} +1 -0
- package/src/core/runtime-skills/{opencode-cli.md → opencode-cli/SKILL.md} +1 -0
- package/src/core/runtime-skills/{openrouter.md → openrouter/SKILL.md} +1 -0
- package/src/{host/daemon/env-detect.js → core/runtimes/detect.js} +1 -1
- package/src/core/stores/code-sessions.js +50 -2
- package/src/core/stores/routine-memory.js +1 -1
- package/src/core/stores/sessions-search.js +121 -0
- package/src/core/stores/sessions.js +38 -0
- package/src/core/vars/index.js +14 -0
- package/src/core/vars/interpolate.js +86 -0
- package/src/core/vars/sources.js +151 -0
- package/src/core/voice/audio-decode.js +38 -0
- package/src/core/voice/transcription.js +225 -0
- package/src/host/daemon/api/admin-config.js +5 -82
- package/src/host/daemon/api/agents.js +5 -5
- package/src/host/daemon/api/code.js +17 -169
- package/src/host/daemon/api/config.js +3 -4
- package/src/host/daemon/api/conversations.js +8 -29
- package/src/host/daemon/api/deck.js +37 -404
- package/src/host/daemon/api/engines.js +1 -80
- package/src/host/daemon/api/exec.js +1 -1
- package/src/host/daemon/api/mcps.js +32 -0
- package/src/host/daemon/api/routines.js +1 -1
- package/src/host/daemon/api/runtimes.js +4 -3
- package/src/host/daemon/api/sessions-search.js +24 -140
- package/src/host/daemon/api/sessions.js +12 -30
- package/src/host/daemon/api/shared.js +2 -1
- package/src/host/daemon/api/skills.js +140 -6
- package/src/host/daemon/api/super-agent.js +56 -1
- package/src/host/daemon/api/telegram.js +1 -11
- package/src/host/daemon/api/tools.js +6 -6
- package/src/host/daemon/api/transcribe.js +2 -2
- package/src/host/daemon/api/vars.js +137 -0
- package/src/host/daemon/api/voice.js +13 -290
- package/src/host/daemon/api.js +2 -0
- package/src/host/daemon/db.js +6 -6
- package/src/host/daemon/deck-exec.js +148 -0
- package/src/host/daemon/index.js +20 -3
- package/src/host/daemon/plugins/telegram/index.js +9 -9
- package/src/host/daemon/routines-scheduler.js +64 -0
- package/src/host/daemon/smoke.js +3 -2
- package/src/host/daemon/whisper-server.js +225 -0
- package/src/interfaces/cli/branding.js +53 -0
- package/src/interfaces/cli/commands/agent.js +3 -2
- package/src/interfaces/cli/commands/command.js +2 -3
- package/src/interfaces/cli/commands/messages.js +6 -2
- package/src/interfaces/cli/commands/pair.js +5 -4
- package/src/interfaces/cli/commands/search.js +1 -1
- package/src/interfaces/cli/commands/sessions.js +3 -2
- package/src/interfaces/cli/commands/skills.js +290 -55
- package/src/interfaces/cli/index.js +84 -2
- package/src/interfaces/web/dist/assets/index-C0fm31dY.js +618 -0
- package/src/interfaces/web/dist/assets/index-C0fm31dY.js.map +1 -0
- package/src/interfaces/web/dist/assets/index-UcAqlBO6.css +1 -0
- package/src/interfaces/web/dist/index.html +2 -2
- package/src/interfaces/web/package-lock.json +182 -182
- package/src/interfaces/web/src/components/ModelCombobox.tsx +2 -1
- package/src/interfaces/web/src/components/TelegramChannelDialog.tsx +1 -1
- package/src/interfaces/web/src/components/chat/AskAnswersCard.tsx +76 -0
- package/src/interfaces/web/src/components/chat/MessageBubble.tsx +37 -4
- package/src/interfaces/web/src/components/chat/MessageList.tsx +23 -1
- package/src/interfaces/web/src/components/chat/ModelPicker.tsx +3 -1
- package/src/interfaces/web/src/components/code/CodeArtifactsTab.tsx +4 -4
- package/src/interfaces/web/src/components/code/CodeChangesTab.tsx +1 -1
- package/src/interfaces/web/src/components/code/CodeFileTree.tsx +3 -2
- package/src/interfaces/web/src/components/code/CodeFileViewer.tsx +3 -2
- package/src/interfaces/web/src/components/code/CodeTerminal.tsx +3 -2
- package/src/interfaces/web/src/components/config/GlobalConfigEditor.tsx +2 -1
- package/src/interfaces/web/src/components/deck/WidgetRow.tsx +2 -1
- package/src/interfaces/web/src/components/inputs/KeyValueList.tsx +93 -0
- package/src/interfaces/web/src/components/inputs/VarTokenInput.tsx +449 -0
- package/src/interfaces/web/src/components/settings/DefaultRouterCard.tsx +2 -1
- package/src/interfaces/web/src/components/settings/EnginesPanel.tsx +2 -2
- package/src/interfaces/web/src/components/settings/MemoryPanel.tsx +73 -4
- package/src/interfaces/web/src/components/settings/SkillsInspectorPanel.tsx +222 -0
- package/src/interfaces/web/src/components/settings/providers/ProviderCard.tsx +3 -2
- package/src/interfaces/web/src/components/settings/providers/ProviderModal.tsx +3 -2
- package/src/interfaces/web/src/components/ui/chat-input.tsx +5 -4
- package/src/interfaces/web/src/components/ui/sidebar.tsx +3 -2
- package/src/interfaces/web/src/components/voice/VoiceProviderModal.tsx +2 -1
- package/src/interfaces/web/src/constants/index.ts +1 -1
- package/src/interfaces/web/src/hooks/useChat.ts +19 -0
- package/src/interfaces/web/src/i18n/en.ts +175 -7
- package/src/interfaces/web/src/i18n/es.ts +180 -15
- package/src/interfaces/web/src/lib/api/mcps.ts +25 -0
- package/src/interfaces/web/src/lib/api/skills.ts +70 -0
- package/src/interfaces/web/src/lib/api/vars.ts +38 -0
- package/src/interfaces/web/src/lib/api.ts +1 -0
- package/src/interfaces/web/src/screens/ProjectScreen.tsx +8 -31
- package/src/interfaces/web/src/screens/SettingsScreen.tsx +6 -2
- package/src/interfaces/web/src/screens/modules/CodeScreen.tsx +1 -1
- package/src/interfaces/web/src/screens/modules/DeckScreen.tsx +4 -3
- package/src/interfaces/web/src/screens/modules/DesktopScreen.tsx +7 -6
- package/src/interfaces/web/src/screens/modules/VoiceScreen.tsx +4 -3
- package/src/interfaces/web/src/screens/project/AgentDetailScreen.tsx +1 -1
- package/src/interfaces/web/src/screens/project/ConfigTab.tsx +132 -1
- package/src/interfaces/web/src/screens/project/McpsTab.tsx +549 -104
- package/src/interfaces/web/src/screens/project/RoutinesTab.tsx +1 -1
- package/src/interfaces/web/src/screens/project/VarsTab.tsx +300 -0
- package/src/interfaces/web/src/types/daemon.ts +15 -0
- package/skills/apx-agency-agents/SKILL.md +0 -141
- package/skills/apx-agent/SKILL.md +0 -100
- package/skills/apx-mcp-builder/SKILL.md +0 -183
- package/skills/apx-routine/SKILL.md +0 -140
- package/skills/apx-runtime/SKILL.md +0 -117
- package/skills/apx-sessions/SKILL.md +0 -281
- package/skills/apx-skill-builder/SKILL.md +0 -153
- package/skills/apx-telegram/SKILL.md +0 -131
- package/skills/apx-voice/SKILL.md +0 -137
- package/src/core/agent/prompts/action-discipline.md +0 -24
- package/src/core/agent/prompts/super-agent-base.md +0 -42
- package/src/host/daemon/transcription.js +0 -538
- package/src/host/daemon/whisper-transcribe.py +0 -73
- package/src/interfaces/web/dist/assets/index-Aaiw8BZN.css +0 -1
- package/src/interfaces/web/dist/assets/index-DPqtjDjh.js +0 -602
- package/src/interfaces/web/dist/assets/index-DPqtjDjh.js.map +0 -1
- /package/src/{host/daemon → core/apc}/projects-helpers.js +0 -0
- /package/src/{host/daemon/plugins → core/channels}/telegram/ask.js +0 -0
- /package/src/{host/daemon/plugins → core/channels}/telegram/helpers.js +0 -0
- /package/src/{host/daemon/plugins → core/channels}/telegram/media.js +0 -0
- /package/src/core/{tools → http-tools}/index.js +0 -0
- /package/src/{host/daemon/compact.js → core/stores/conversations-compactor.js} +0 -0
- /package/src/{host/daemon → core/stores}/conversations.js +0 -0
- /package/src/{host/daemon → core/util}/thinking.js +0 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Snapshot a project's read-aloud context — AGENTS.md + .apc/memory.md —
|
|
2
|
+
// into a single labelled markdown string. Used by /deck/context/copy and
|
|
3
|
+
// (potentially) any other "give me the context" surface.
|
|
4
|
+
import fs from "node:fs/promises";
|
|
5
|
+
import { AGENTS_MD, apcMemoryFile } from "./paths.js";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
|
|
8
|
+
const CONTEXT_FILES = [
|
|
9
|
+
{ rel: AGENTS_MD, label: AGENTS_MD },
|
|
10
|
+
{ rel: `.apc/${"memory.md"}`, label: ".apc/memory.md", abs: (root) => apcMemoryFile(root) },
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
export async function readProjectContext(projectPath) {
|
|
14
|
+
const chunks = [];
|
|
15
|
+
for (const entry of CONTEXT_FILES) {
|
|
16
|
+
const abs = entry.abs ? entry.abs(projectPath) : path.join(projectPath, entry.rel);
|
|
17
|
+
try {
|
|
18
|
+
const content = await fs.readFile(abs, "utf8");
|
|
19
|
+
if (content.trim()) {
|
|
20
|
+
chunks.push(`# ${entry.label}\n\n${content.trim()}\n`);
|
|
21
|
+
}
|
|
22
|
+
} catch {
|
|
23
|
+
// missing file is fine; we just skip it.
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return chunks.join("\n---\n\n");
|
|
27
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// Append-only project notes under .apc/notes/YYYY-MM-DD.md. Each note is a
|
|
2
|
+
// timestamped markdown block; the file is append-only so no UID management
|
|
3
|
+
// is required.
|
|
4
|
+
import fs from "node:fs/promises";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { apcNotesDir } from "./paths.js";
|
|
7
|
+
|
|
8
|
+
export async function appendProjectNote(projectPath, { title, body }) {
|
|
9
|
+
const notesDir = apcNotesDir(projectPath);
|
|
10
|
+
await fs.mkdir(notesDir, { recursive: true });
|
|
11
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
12
|
+
const file = path.join(notesDir, `${today}.md`);
|
|
13
|
+
const ts = new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
14
|
+
const block = title
|
|
15
|
+
? `\n## ${title}\n_${ts}_\n\n${body}\n`
|
|
16
|
+
: `\n### ${ts}\n\n${body}\n`;
|
|
17
|
+
await fs.appendFile(file, block, "utf8");
|
|
18
|
+
return file;
|
|
19
|
+
}
|
package/src/core/apc/parser.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
// Core parsers for APC — pure ESM, no deps.
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import {
|
|
5
|
+
apcAgentsDir,
|
|
6
|
+
apcAgentFile,
|
|
7
|
+
apcProjectFile,
|
|
8
|
+
agentsMdFile,
|
|
9
|
+
isApcProject,
|
|
10
|
+
} from "./paths.js";
|
|
4
11
|
|
|
5
12
|
export const SLUG_RE = /^[a-z][a-z0-9_-]*$/;
|
|
6
13
|
const H1_RE = /^#\s+Agents\s*$/i;
|
|
@@ -91,7 +98,7 @@ export function parseAgentFile(slug, text) {
|
|
|
91
98
|
|
|
92
99
|
// Read all .apc/agents/<slug>.md files. Returns [] if none exist.
|
|
93
100
|
export function readAgentsFromDir(root) {
|
|
94
|
-
const dir =
|
|
101
|
+
const dir = apcAgentsDir(root);
|
|
95
102
|
if (!fs.existsSync(dir)) return [];
|
|
96
103
|
return fs
|
|
97
104
|
.readdirSync(dir)
|
|
@@ -189,7 +196,7 @@ function readVaultAgent(slug, { includeRemoved = false } = {}) {
|
|
|
189
196
|
|
|
190
197
|
// Resolve a single agent for a project: local file → vault (layered) → null.
|
|
191
198
|
export function resolveAgent(root, slug) {
|
|
192
|
-
const localPath =
|
|
199
|
+
const localPath = apcAgentFile(root, slug);
|
|
193
200
|
if (fs.existsSync(localPath)) {
|
|
194
201
|
const agent = parseAgentFile(slug, fs.readFileSync(localPath, "utf8"));
|
|
195
202
|
return { ...agent, source: "local" };
|
|
@@ -203,7 +210,7 @@ export { readVaultAgent };
|
|
|
203
210
|
|
|
204
211
|
// Return slugs imported from vault in this project (from project.json)
|
|
205
212
|
export function importedVaultSlugs(root) {
|
|
206
|
-
const p =
|
|
213
|
+
const p = apcProjectFile(root);
|
|
207
214
|
if (!fs.existsSync(p)) return [];
|
|
208
215
|
try {
|
|
209
216
|
const cfg = JSON.parse(fs.readFileSync(p, "utf8"));
|
|
@@ -235,7 +242,7 @@ export function readAgents(root) {
|
|
|
235
242
|
const all = [...fromFiles, ...vaultAgents];
|
|
236
243
|
const allSlugs = new Set(all.map((a) => a.slug));
|
|
237
244
|
|
|
238
|
-
const agentsMdPath =
|
|
245
|
+
const agentsMdPath = agentsMdFile(root);
|
|
239
246
|
if (!fs.existsSync(agentsMdPath)) return all;
|
|
240
247
|
|
|
241
248
|
const mdText = fs.readFileSync(agentsMdPath, "utf8");
|
|
@@ -255,7 +262,7 @@ export function readAgents(root) {
|
|
|
255
262
|
export function findApfRoot(start = process.cwd()) {
|
|
256
263
|
let cur = path.resolve(start);
|
|
257
264
|
while (true) {
|
|
258
|
-
if (
|
|
265
|
+
if (isApcProject(cur)) return cur;
|
|
259
266
|
const parent = path.dirname(cur);
|
|
260
267
|
if (parent === cur) return null;
|
|
261
268
|
cur = parent;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// Filesystem layout of an APC project. The `.apc/` folder is APX's footprint
|
|
2
|
+
// inside someone else's repo — everything that the daemon writes to a
|
|
3
|
+
// user-checked-out project lives here, plus the top-level AGENTS.md spec.
|
|
4
|
+
//
|
|
5
|
+
// Use the helpers below instead of joining the literal names yourself. A typo
|
|
6
|
+
// in `".apc"` or `"project.json"` will silently create an orphan tree instead
|
|
7
|
+
// of failing loud, which is exactly the bug class these constants prevent.
|
|
8
|
+
import fs from "node:fs";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
|
|
11
|
+
// Raw names — exported for the rare caller that needs to glob/match by name.
|
|
12
|
+
export const APC_DIR = ".apc";
|
|
13
|
+
export const APC_PROJECT_FILE = "project.json";
|
|
14
|
+
export const APC_PROJECT_CONFIG_FILE = "config.json";
|
|
15
|
+
export const APC_PROJECT_MEMORY_FILE = "memory.md";
|
|
16
|
+
export const APC_AGENTS_DIR = "agents";
|
|
17
|
+
export const APC_SKILLS_DIR = "skills";
|
|
18
|
+
export const APC_COMMANDS_DIR = "commands";
|
|
19
|
+
export const APC_NOTES_DIR = "notes";
|
|
20
|
+
export const APC_MCPS_FILE = "mcps.json";
|
|
21
|
+
export const APC_REMOVED_FILE = ".removed.json";
|
|
22
|
+
export const AGENTS_MD = "AGENTS.md";
|
|
23
|
+
|
|
24
|
+
// Path builders. `root` is the project root (the directory that contains
|
|
25
|
+
// `.apc/` and `AGENTS.md`).
|
|
26
|
+
export function apcDir(root) {
|
|
27
|
+
return path.join(root, APC_DIR);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function apcProjectFile(root) {
|
|
31
|
+
return path.join(root, APC_DIR, APC_PROJECT_FILE);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function apcProjectConfigFile(root) {
|
|
35
|
+
return path.join(root, APC_DIR, APC_PROJECT_CONFIG_FILE);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function apcAgentsDir(root) {
|
|
39
|
+
return path.join(root, APC_DIR, APC_AGENTS_DIR);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function apcAgentFile(root, slug) {
|
|
43
|
+
return path.join(root, APC_DIR, APC_AGENTS_DIR, `${slug}.md`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function apcAgentMemoryFile(root, slug) {
|
|
47
|
+
return path.join(root, APC_DIR, APC_AGENTS_DIR, slug, "memory.md");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function apcRemovedFile(root) {
|
|
51
|
+
return path.join(root, APC_DIR, APC_AGENTS_DIR, APC_REMOVED_FILE);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function apcSkillsDir(root) {
|
|
55
|
+
return path.join(root, APC_DIR, APC_SKILLS_DIR);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function apcSkillFile(root, slug) {
|
|
59
|
+
return path.join(root, APC_DIR, APC_SKILLS_DIR, `${slug}.md`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function apcCommandsDir(root) {
|
|
63
|
+
return path.join(root, APC_DIR, APC_COMMANDS_DIR);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function apcNotesDir(root) {
|
|
67
|
+
return path.join(root, APC_DIR, APC_NOTES_DIR);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Project-level memory (super-agent / project-wide), distinct from the
|
|
71
|
+
// per-agent memory.md that lives under .apc/agents/<slug>/.
|
|
72
|
+
export function apcMemoryFile(root) {
|
|
73
|
+
return path.join(root, APC_DIR, APC_PROJECT_MEMORY_FILE);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function apcMcpsFile(root) {
|
|
77
|
+
return path.join(root, APC_DIR, APC_MCPS_FILE);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function agentsMdFile(root) {
|
|
81
|
+
return path.join(root, AGENTS_MD);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// True when `root` looks like an initialized APC project (has the marker file).
|
|
85
|
+
export function isApcProject(root) {
|
|
86
|
+
return fs.existsSync(apcProjectFile(root));
|
|
87
|
+
}
|
package/src/core/apc/scaffold.js
CHANGED
|
@@ -11,14 +11,22 @@ import {
|
|
|
11
11
|
} from "./parser.js";
|
|
12
12
|
import { readApcContextSkill } from "./skill-sync.js";
|
|
13
13
|
import { nowIso } from "../util/time.js";
|
|
14
|
+
import {
|
|
15
|
+
apcDir,
|
|
16
|
+
apcProjectFile,
|
|
17
|
+
apcAgentsDir,
|
|
18
|
+
apcAgentFile,
|
|
19
|
+
agentsMdFile,
|
|
20
|
+
} from "./paths.js";
|
|
14
21
|
|
|
15
22
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
16
23
|
// Now under src/core/apc/ — one more "../" to escape than before.
|
|
17
24
|
const PACKAGE_ROOT = path.resolve(__dirname, "..", "..", "..");
|
|
25
|
+
// <packageRoot>/skills/<slug>/SKILL.md — the SLIM engine-side set: every skill
|
|
26
|
+
// here is replicated verbatim into project IDE rule files (`apx skills add`) and
|
|
27
|
+
// into ~/.<host>/skills/ (`apx skills sync`). The rich super-agent catalog lives
|
|
28
|
+
// at src/core/runtime-skills/ and is intentionally NOT copied out.
|
|
18
29
|
const BUNDLED_SKILLS_DIR = path.join(PACKAGE_ROOT, "skills");
|
|
19
|
-
// runtime-skills lives at src/core/runtime-skills/, one level up from this
|
|
20
|
-
// file's new home in src/core/apc/ (was a sibling before the Phase 3 move).
|
|
21
|
-
const RUNTIME_SKILLS_DIR = path.join(__dirname, "..", "runtime-skills");
|
|
22
30
|
|
|
23
31
|
export const SPEC_VERSION = "0.1.0";
|
|
24
32
|
|
|
@@ -28,12 +36,12 @@ export const SPEC_VERSION = "0.1.0";
|
|
|
28
36
|
// install/update from the canonical APC repo (see src/interfaces/cli/postinstall.js).
|
|
29
37
|
// ---------------------------------------------------------------------------
|
|
30
38
|
|
|
31
|
-
//
|
|
32
|
-
//
|
|
39
|
+
// Read one slim skill from <packageRoot>/skills/<slug>/SKILL.md. `apc-context`
|
|
40
|
+
// is special-cased to refresh from the canonical APC repo if available.
|
|
33
41
|
function readBundledSkill(slug) {
|
|
34
42
|
if (slug === "apc-context") {
|
|
35
43
|
const synced = readApcContextSkill();
|
|
36
|
-
|
|
44
|
+
if (synced?.text) return synced.text;
|
|
37
45
|
}
|
|
38
46
|
const file = path.join(BUNDLED_SKILLS_DIR, slug, "SKILL.md");
|
|
39
47
|
if (!fs.existsSync(file)) return null;
|
|
@@ -134,19 +142,11 @@ const GLOBAL_SKILL_DIRS = [
|
|
|
134
142
|
path.join(os.homedir(), ".agents", "skills"), // Antigravity/other skills.sh adopters
|
|
135
143
|
];
|
|
136
144
|
|
|
137
|
-
function readRuntimeSkillFiles() {
|
|
138
|
-
if (!fs.existsSync(RUNTIME_SKILLS_DIR)) return [];
|
|
139
|
-
return fs.readdirSync(RUNTIME_SKILLS_DIR)
|
|
140
|
-
.filter((name) => name.endsWith(".md"))
|
|
141
|
-
.sort()
|
|
142
|
-
.map((name) => ({
|
|
143
|
-
slug: path.basename(name, ".md"),
|
|
144
|
-
md: fs.readFileSync(path.join(RUNTIME_SKILLS_DIR, name), "utf8").trim(),
|
|
145
|
-
}));
|
|
146
|
-
}
|
|
147
|
-
|
|
148
145
|
// Install APX + APC context skills into IDE rule files. Returns an array of result objects.
|
|
149
146
|
// targetIds: array of target ids to install; null = all project targets.
|
|
147
|
+
// Writes the slim engine-side skill from <packageRoot>/skills/. The rich
|
|
148
|
+
// super-agent set in src/core/runtime-skills/ is intentionally never written
|
|
149
|
+
// into project IDE files.
|
|
150
150
|
export function installIdeSkills(root, targetIds = null) {
|
|
151
151
|
const apxRaw = readBundledSkill("apx");
|
|
152
152
|
const apcRaw = readBundledSkill("apc-context");
|
|
@@ -197,36 +197,27 @@ export function installIdeSkills(root, targetIds = null) {
|
|
|
197
197
|
// on the user's machine after `npm install -g .` (or `npm update -g apx`)
|
|
198
198
|
// without anyone having to touch this file.
|
|
199
199
|
//
|
|
200
|
-
// Excluded: directory names starting with "." (e.g. .DS_Store)
|
|
201
|
-
//
|
|
202
|
-
//
|
|
203
|
-
//
|
|
204
|
-
// public → pushed to every global skill dir on install / sync (default).
|
|
205
|
-
// optional → not pushed by default; user opts in with --include-optional
|
|
206
|
-
// or `apx skills add <slug> --global` for one-off install.
|
|
207
|
-
// internal → APX-developer skills (mcp-builder, skill-builder, etc.); never
|
|
208
|
-
// pushed globally, only available to APX itself via the bundled
|
|
209
|
-
// copy. Avoids cluttering other IDEs with stuff their users won't
|
|
210
|
-
// run.
|
|
200
|
+
// Excluded: directory names starting with "." (e.g. .DS_Store).
|
|
201
|
+
// Every slug under <packageRoot>/skills/ is part of the slim engine set and
|
|
202
|
+
// gets pushed to global dirs on `apx skills sync`. No scope filtering — the
|
|
203
|
+
// dir IS the contract.
|
|
211
204
|
export function listBundledSkillSlugs() {
|
|
212
205
|
return discoverBundledSkills().map((s) => s.slug);
|
|
213
206
|
}
|
|
214
207
|
|
|
215
208
|
export function listBundledSkills() {
|
|
216
|
-
return discoverBundledSkills().map(({ slug
|
|
209
|
+
return discoverBundledSkills().map(({ slug }) => ({ slug }));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Backwards-compat alias — every bundled slug here IS an engine slug now.
|
|
213
|
+
export function listEngineSkills() {
|
|
214
|
+
return listBundledSkills();
|
|
217
215
|
}
|
|
218
216
|
|
|
219
|
-
//
|
|
220
|
-
//
|
|
221
|
-
function
|
|
222
|
-
|
|
223
|
-
const end = md.indexOf("\n---", 4);
|
|
224
|
-
if (end === -1) return "public";
|
|
225
|
-
const m = md.slice(4, end).match(/^scope:\s*(\w+)/m);
|
|
226
|
-
if (!m) return "public";
|
|
227
|
-
const s = m[1].toLowerCase();
|
|
228
|
-
if (s === "internal" || s === "optional" || s === "public") return s;
|
|
229
|
-
return "public";
|
|
217
|
+
// Legacy slugs APX used to ship to global dirs but no longer does — exposed so
|
|
218
|
+
// the CLI can report what `installGlobalSkills` will prune.
|
|
219
|
+
export function listLegacyPruneSlugs() {
|
|
220
|
+
return [...PRUNE_LEGACY_SLUGS];
|
|
230
221
|
}
|
|
231
222
|
|
|
232
223
|
function discoverBundledSkills() {
|
|
@@ -239,65 +230,80 @@ function discoverBundledSkills() {
|
|
|
239
230
|
const skillFile = path.join(root, entry.name, "SKILL.md");
|
|
240
231
|
if (!fs.existsSync(skillFile)) continue;
|
|
241
232
|
const md = fs.readFileSync(skillFile, "utf8");
|
|
242
|
-
out.push({ slug: entry.name, md
|
|
233
|
+
out.push({ slug: entry.name, md });
|
|
243
234
|
}
|
|
244
235
|
return out.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
245
236
|
}
|
|
246
237
|
|
|
247
|
-
// Install
|
|
248
|
-
// Cursor, Codex,
|
|
238
|
+
// Install the slim engine skill set to every global ~/.../skills/ dir
|
|
239
|
+
// (Claude Code, Cursor, Codex, Antigravity/skills.sh). External engines only
|
|
240
|
+
// need to know how to talk TO apx — not the full APX sub-skill catalog. The
|
|
241
|
+
// rich bundled set in skills/<slug>/ stays in-repo for the APX super-agent.
|
|
242
|
+
//
|
|
243
|
+
// The set lives at skills/engines/<slug>/SKILL.md and is currently:
|
|
244
|
+
// apx, apx-mcp, apc-context.
|
|
249
245
|
//
|
|
250
|
-
//
|
|
251
|
-
//
|
|
252
|
-
// `apx skills add <slug> --global` for a single one).
|
|
246
|
+
// `includeOptional` / `includeInternal` are kept as no-op flags for backward
|
|
247
|
+
// compatibility with `apx skills sync --include-…`; the slim set has no tiers.
|
|
253
248
|
//
|
|
254
|
-
// Pruning:
|
|
255
|
-
//
|
|
256
|
-
// the
|
|
249
|
+
// Pruning: removes stale APX-shipped slugs that are no longer in the engine
|
|
250
|
+
// set (the catalog of slugs APX has ever published, see PRUNE_LEGACY_SLUGS).
|
|
251
|
+
// Skills the user installed themselves are NOT touched.
|
|
257
252
|
//
|
|
258
253
|
// Returns an array of { dir, skill, file, status, scope }.
|
|
259
|
-
// status ∈ {created, updated, unchanged, pruned
|
|
254
|
+
// status ∈ {created, updated, unchanged, pruned}
|
|
255
|
+
const PRUNE_LEGACY_SLUGS = [
|
|
256
|
+
"apx-agency-agents",
|
|
257
|
+
"apx-agent",
|
|
258
|
+
"apx-mcp-builder",
|
|
259
|
+
"apx-project",
|
|
260
|
+
"apx-routine",
|
|
261
|
+
"apx-runtime",
|
|
262
|
+
"apx-sessions",
|
|
263
|
+
"apx-skill-builder",
|
|
264
|
+
"apx-task",
|
|
265
|
+
"apx-telegram",
|
|
266
|
+
"apx-voice",
|
|
267
|
+
// Runtime CLI docs that previously leaked into global dirs — these are
|
|
268
|
+
// loaded in-process by the daemon and should NOT live on disk in IDE skill
|
|
269
|
+
// dirs.
|
|
270
|
+
"claude-code",
|
|
271
|
+
"codex-cli",
|
|
272
|
+
"opencode-cli",
|
|
273
|
+
"openrouter",
|
|
274
|
+
];
|
|
275
|
+
|
|
260
276
|
export function installGlobalSkills({
|
|
261
|
-
includeOptional = false,
|
|
262
|
-
includeInternal = false,
|
|
263
277
|
prune = true,
|
|
278
|
+
// No-ops kept for CLI backward compatibility (the slim engine set has no tiers).
|
|
279
|
+
includeOptional: _includeOptional = false,
|
|
280
|
+
includeInternal: _includeInternal = false,
|
|
264
281
|
} = {}) {
|
|
265
|
-
const
|
|
266
|
-
const wanted = all.filter((s) => {
|
|
267
|
-
if (s.scope === "internal") return includeInternal;
|
|
268
|
-
if (s.scope === "optional") return includeOptional;
|
|
269
|
-
return true; // public
|
|
270
|
-
});
|
|
282
|
+
const wanted = discoverBundledSkills();
|
|
271
283
|
const wantedSlugs = new Set(wanted.map((s) => s.slug));
|
|
272
|
-
const knownSlugs = new Set(all.map((s) => s.slug));
|
|
273
284
|
|
|
274
285
|
const results = [];
|
|
275
286
|
for (const base of GLOBAL_SKILL_DIRS) {
|
|
276
|
-
|
|
277
|
-
for (const { slug, md, scope } of wanted) {
|
|
287
|
+
for (const { slug, md } of wanted) {
|
|
278
288
|
const dest = path.join(base, slug, "SKILL.md");
|
|
279
289
|
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
280
290
|
const existed = fs.existsSync(dest);
|
|
281
291
|
const previous = existed ? fs.readFileSync(dest, "utf8") : null;
|
|
282
292
|
if (previous === md) {
|
|
283
|
-
results.push({ dir: base, skill: slug, file: dest, status: "unchanged", scope });
|
|
293
|
+
results.push({ dir: base, skill: slug, file: dest, status: "unchanged", scope: "engine" });
|
|
284
294
|
continue;
|
|
285
295
|
}
|
|
286
296
|
fs.writeFileSync(dest, md, "utf8");
|
|
287
|
-
results.push({ dir: base, skill: slug, file: dest, status: existed ? "updated" : "created", scope });
|
|
297
|
+
results.push({ dir: base, skill: slug, file: dest, status: existed ? "updated" : "created", scope: "engine" });
|
|
288
298
|
}
|
|
289
|
-
// Prune anything WE shipped previously but should no longer be there
|
|
290
|
-
// (slug exists in the bundle but isn't `wanted` this run).
|
|
291
299
|
if (prune) {
|
|
292
|
-
for (const
|
|
300
|
+
for (const slug of PRUNE_LEGACY_SLUGS) {
|
|
293
301
|
if (wantedSlugs.has(slug)) continue;
|
|
294
|
-
if (!knownSlugs.has(slug)) continue;
|
|
295
302
|
const dest = path.join(base, slug, "SKILL.md");
|
|
296
303
|
if (!fs.existsSync(dest)) continue;
|
|
297
304
|
fs.unlinkSync(dest);
|
|
298
|
-
// Best-effort: drop the now-empty <slug>/ dir too.
|
|
299
305
|
try { fs.rmdirSync(path.dirname(dest)); } catch {}
|
|
300
|
-
results.push({ dir: base, skill: slug, file: dest, status: "pruned", scope });
|
|
306
|
+
results.push({ dir: base, skill: slug, file: dest, status: "pruned", scope: "legacy" });
|
|
301
307
|
}
|
|
302
308
|
}
|
|
303
309
|
}
|
|
@@ -423,7 +429,7 @@ function writeMigrateMd(apfDir, found) {
|
|
|
423
429
|
// Get the stable APX storage ID for a project, generating one if it doesn't exist.
|
|
424
430
|
// Called by the daemon when registering a project.
|
|
425
431
|
export function getOrCreateApxId(root) {
|
|
426
|
-
const p =
|
|
432
|
+
const p = apcProjectFile(root);
|
|
427
433
|
if (!fs.existsSync(p)) return null;
|
|
428
434
|
let cfg;
|
|
429
435
|
try { cfg = JSON.parse(fs.readFileSync(p, "utf8")); } catch { return null; }
|
|
@@ -439,7 +445,7 @@ export function initApf(directory, { name } = {}) {
|
|
|
439
445
|
const root = path.resolve(directory);
|
|
440
446
|
fs.mkdirSync(root, { recursive: true });
|
|
441
447
|
|
|
442
|
-
const apfDir =
|
|
448
|
+
const apfDir = apcDir(root);
|
|
443
449
|
fs.mkdirSync(path.join(apfDir, "agents"), { recursive: true });
|
|
444
450
|
fs.mkdirSync(path.join(apfDir, "skills"), { recursive: true });
|
|
445
451
|
fs.mkdirSync(path.join(apfDir, "commands"), { recursive: true });
|
|
@@ -469,7 +475,7 @@ export function initApf(directory, { name } = {}) {
|
|
|
469
475
|
fs.writeFileSync(gitignore, APC_GITIGNORE);
|
|
470
476
|
}
|
|
471
477
|
|
|
472
|
-
const agentsMd =
|
|
478
|
+
const agentsMd = agentsMdFile(root);
|
|
473
479
|
if (!fs.existsSync(agentsMd)) {
|
|
474
480
|
fs.writeFileSync(agentsMd, AGENTS_MD_TEMPLATE);
|
|
475
481
|
}
|
|
@@ -485,13 +491,13 @@ export function initApf(directory, { name } = {}) {
|
|
|
485
491
|
}
|
|
486
492
|
|
|
487
493
|
export function ensureAgentDir(root, slug) {
|
|
488
|
-
fs.mkdirSync(
|
|
489
|
-
return
|
|
494
|
+
fs.mkdirSync(apcAgentsDir(root), { recursive: true });
|
|
495
|
+
return apcAgentsDir(root);
|
|
490
496
|
}
|
|
491
497
|
|
|
492
498
|
// Write .apc/agents/<slug>.md — the canonical agent definition file.
|
|
493
499
|
export function writeAgentFile(root, slug, fields, body = "") {
|
|
494
|
-
const dest =
|
|
500
|
+
const dest = apcAgentFile(root, slug);
|
|
495
501
|
const lines = ["---"];
|
|
496
502
|
const order = ["role", "model", "language", "description", "skills", "tools"];
|
|
497
503
|
const written = new Set();
|
|
@@ -579,7 +585,7 @@ export function restoreVaultAgent(slug) {
|
|
|
579
585
|
|
|
580
586
|
// Add a slug to the project's agents.imported list in project.json
|
|
581
587
|
export function addImportedAgent(root, slug) {
|
|
582
|
-
const p =
|
|
588
|
+
const p = apcProjectFile(root);
|
|
583
589
|
let cfg = {};
|
|
584
590
|
try { cfg = JSON.parse(fs.readFileSync(p, "utf8")); } catch {}
|
|
585
591
|
if (!cfg.agents) cfg.agents = {};
|
|
@@ -8,7 +8,10 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
8
8
|
// Repo root is three levels up, not two.
|
|
9
9
|
export const PACKAGE_ROOT = path.resolve(__dirname, "..", "..", "..");
|
|
10
10
|
|
|
11
|
+
// Engine-side slim copy (replicated to ~/.<host>/skills/ by apx skills sync).
|
|
11
12
|
export const APC_SKILL_REL = path.join("skills", "apc-context", "SKILL.md");
|
|
13
|
+
// Runtime-internal copy (loaded by the super-agent — never published outside).
|
|
14
|
+
export const APC_BUILTIN_SKILL_REL = path.join("src", "core", "runtime-skills", "apc-context", "SKILL.md");
|
|
12
15
|
export const APC_SKILL_REMOTE =
|
|
13
16
|
"https://raw.githubusercontent.com/agentprojectcontext/agentprojectcontext/main/skills/apc-context/SKILL.md";
|
|
14
17
|
|
|
@@ -95,5 +98,12 @@ export async function refreshApcContextSkill({ packageRoot = PACKAGE_ROOT, timeo
|
|
|
95
98
|
|
|
96
99
|
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
97
100
|
fs.writeFileSync(dest, text, "utf8");
|
|
101
|
+
|
|
102
|
+
// Keep the runtime-internal copy (src/core/runtime-skills/apc-context/) in
|
|
103
|
+
// sync — that's where the super-agent loads it from.
|
|
104
|
+
const builtinDest = path.join(packageRoot, APC_BUILTIN_SKILL_REL);
|
|
105
|
+
fs.mkdirSync(path.dirname(builtinDest), { recursive: true });
|
|
106
|
+
fs.writeFileSync(builtinDest, text, "utf8");
|
|
107
|
+
|
|
98
108
|
return { ok: true, source, refreshed: true };
|
|
99
109
|
}
|
|
@@ -1,10 +1,38 @@
|
|
|
1
1
|
// Inbound Telegram update dispatcher.
|
|
2
2
|
//
|
|
3
|
-
// Extracted from the ChannelPoller class so
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
3
|
+
// Extracted from the ChannelPoller class so index.js stays under ~800 lines
|
|
4
|
+
// and the routing logic for text/photo/voice/document updates lives on its
|
|
5
|
+
// own. Takes the poller instance as `self`; every `this.X` in the original
|
|
6
|
+
// method becomes `self.X` here. The poller exposes _handleUpdate as a thin
|
|
7
|
+
// facade that delegates to handleUpdate(this, u).
|
|
8
|
+
//
|
|
9
|
+
// IMPORTANT: this module needs the same imports the original index.js had
|
|
10
|
+
// in module scope, because the extracted body references identifiers like
|
|
11
|
+
// `appendGlobalMessage`, `CHANNELS`, `nowIso`, etc. Top-level imports here
|
|
12
|
+
// keep that scope intact — earlier splits forgot them and the bug only
|
|
13
|
+
// surfaced when a real telegram update arrived (ReferenceError at runtime).
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import { callEngine } from "#core/engines/index.js";
|
|
16
|
+
import { runSuperAgent, isSuperAgentEnabled } from "#core/agent/super-agent.js";
|
|
17
|
+
import { stripThinking } from "#core/util/thinking.js";
|
|
18
|
+
import { getRecentTelegramTurnsFromFs, appendGlobalMessage } from "#core/stores/messages.js";
|
|
19
|
+
import { compactChannelIfNeeded } from "#core/memory/index.js";
|
|
20
|
+
import { readAgents } from "#core/apc/parser.js";
|
|
21
|
+
import { buildAgentSystem } from "#core/agent/build-agent-system.js";
|
|
22
|
+
import { transcribe as transcribeAudioFile } from "#core/voice/transcription.js";
|
|
23
|
+
import { resolveAgentName, SUPERAGENT_ACTOR_ID } from "#core/identity/index.js";
|
|
24
|
+
import { registerSender, resolveAllowedTools } from "#core/identity/telegram.js";
|
|
25
|
+
import { buildRelationshipBlock } from "#core/agent/index.js";
|
|
26
|
+
import { getConfirmationStore as getConfirmStore } from "#core/confirmation/pending-store.js";
|
|
27
|
+
import { CHANNELS } from "#core/constants/channels.js";
|
|
28
|
+
import { tryResolveSkillCommand } from "#core/agent/skills/trigger.js";
|
|
29
|
+
import { createTelegramConfirmAdapter } from "#core/confirmation/adapters/telegram.js";
|
|
30
|
+
import * as askFlow from "./ask.js";
|
|
31
|
+
import { buildTelegramMeta, resolveBotToken, sleep } from "./helpers.js";
|
|
32
|
+
import { sendPhoto, sendVoice, sendDocument, sendAudio, downloadTelegramFile, API_BASE } from "./media.js";
|
|
33
|
+
import { t, resolveLang } from "#core/i18n/index.js";
|
|
34
|
+
|
|
35
|
+
const nowIso = () => new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
8
36
|
|
|
9
37
|
export async function handleUpdate(self, u) {
|
|
10
38
|
self.lastUpdateAt = nowIso();
|
|
@@ -259,7 +287,7 @@ export async function handleUpdate(self, u) {
|
|
|
259
287
|
// honor it for future messages.
|
|
260
288
|
if (isReset) {
|
|
261
289
|
try {
|
|
262
|
-
const ack = "
|
|
290
|
+
const ack = t("telegram.reset_ack", { lang: resolveLang(self.globalConfig) });
|
|
263
291
|
await self._send({ chat_id, text: ack });
|
|
264
292
|
appendGlobalMessage({
|
|
265
293
|
channel: CHANNELS.TELEGRAM,
|
|
@@ -343,15 +371,7 @@ export async function handleUpdate(self, u) {
|
|
|
343
371
|
// short localized heads-up the moment real work starts (first tool_start),
|
|
344
372
|
// but only if the agent didn't already write its own "on it" line.
|
|
345
373
|
let sentHeadsUp = false;
|
|
346
|
-
const headsUpPhrase = () => {
|
|
347
|
-
const lang = (self.globalConfig?.user?.language || "es").slice(0, 2);
|
|
348
|
-
const byLang = {
|
|
349
|
-
es: "Dale, estoy con eso… 🛠️",
|
|
350
|
-
en: "On it — working on that… 🛠️",
|
|
351
|
-
pt: "Já estou nisso… 🛠️",
|
|
352
|
-
};
|
|
353
|
-
return byLang[lang] || byLang.es;
|
|
354
|
-
};
|
|
374
|
+
const headsUpPhrase = () => t("telegram.heads_up", { lang: resolveLang(self.globalConfig) });
|
|
355
375
|
if (!replyText && isSuperAgentEnabled(self.globalConfig)) {
|
|
356
376
|
const onEvent = async (ev) => {
|
|
357
377
|
try {
|
|
@@ -523,7 +543,9 @@ export async function handleUpdate(self, u) {
|
|
|
523
543
|
const finalClean = replyText ? stripThinking(replyText).trim() : "";
|
|
524
544
|
let toSend = "";
|
|
525
545
|
if (finalClean && finalClean !== lastStreamedText) toSend = finalClean;
|
|
526
|
-
else if (!finalClean && streamedCount === 0)
|
|
546
|
+
else if (!finalClean && streamedCount === 0) {
|
|
547
|
+
toSend = t("telegram.fallback_listo", { lang: resolveLang(self.globalConfig) });
|
|
548
|
+
}
|
|
527
549
|
|
|
528
550
|
stopTyping();
|
|
529
551
|
if (!toSend) return; // everything was already streamed — nothing left to send
|
package/src/core/config/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import fs from "node:fs";
|
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { APX_HOME, CONFIG_PATH } from "./paths.js";
|
|
8
8
|
import { PERMISSION_MODES, DEFAULT_PERMISSION_MODE } from "../constants/permissions.js";
|
|
9
|
+
import { agentsMdFile, apcProjectFile } from "../apc/paths.js";
|
|
9
10
|
|
|
10
11
|
export {
|
|
11
12
|
APX_HOME,
|
|
@@ -116,6 +117,27 @@ const DEFAULT_CONFIG = {
|
|
|
116
117
|
compact_model: "ollama:gemma4:31b-cloud", // light LLM for compaction (Ollama, local endpoint)
|
|
117
118
|
compact_fallback_model: "", // "" → falls back to super_agent.model (APX default)
|
|
118
119
|
},
|
|
120
|
+
skills: {
|
|
121
|
+
// Skill Inspector — opt-in test feature. When enabled, the static
|
|
122
|
+
// "Available skills" hint block (slug dump) is removed from the system
|
|
123
|
+
// prompt; instead a local RAG inspects each turn's user prompt and
|
|
124
|
+
// injects either the matching skill's body (high confidence) or a hint
|
|
125
|
+
// to call load_skill (mid confidence). Below threshold → nothing.
|
|
126
|
+
// Re-evaluated every turn → natural decay (a skill that stopped matching
|
|
127
|
+
// disappears next turn).
|
|
128
|
+
// Embedding provider is whatever memory.embeddings resolves to (defaults
|
|
129
|
+
// to local: ollama → gemini → openai → offline TF). No paid keys needed.
|
|
130
|
+
inspector: {
|
|
131
|
+
enabled: false,
|
|
132
|
+
load_threshold: 0.55,
|
|
133
|
+
hint_threshold: 0.40,
|
|
134
|
+
margin: 0.04,
|
|
135
|
+
max_loaded: 1,
|
|
136
|
+
max_hints: 2,
|
|
137
|
+
prompt_floor: 8,
|
|
138
|
+
body_char_cap: 6000,
|
|
139
|
+
},
|
|
140
|
+
},
|
|
119
141
|
voice: {
|
|
120
142
|
// Text-to-speech configuration. Selector reads voice.tts.provider; "auto"
|
|
121
143
|
// probes engines in preference order (piper → elevenlabs → openai →
|
|
@@ -395,10 +417,10 @@ export function effectiveHost(cfg) {
|
|
|
395
417
|
|
|
396
418
|
export function addProject(cfg, projectPath) {
|
|
397
419
|
const abs = path.resolve(projectPath);
|
|
398
|
-
if (!fs.existsSync(
|
|
420
|
+
if (!fs.existsSync(agentsMdFile(abs))) {
|
|
399
421
|
throw new Error(`not an APC project: ${abs} (no AGENTS.md)`);
|
|
400
422
|
}
|
|
401
|
-
if (!fs.existsSync(
|
|
423
|
+
if (!fs.existsSync(apcProjectFile(abs))) {
|
|
402
424
|
throw new Error(`not an APC project: ${abs} (no .apc/project.json)`);
|
|
403
425
|
}
|
|
404
426
|
const exists = cfg.projects.find((p) => path.resolve(p.path) === abs);
|