@agentprojectcontext/apx 1.18.0 → 1.19.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentprojectcontext/apx",
3
- "version": "1.18.0",
3
+ "version": "1.19.0",
4
4
  "description": "APX — unified CLI + daemon for the Agent Project Context (APC) standard.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -79,17 +79,43 @@ export async function cmdStatus() {
79
79
  }
80
80
 
81
81
  // ── Telegram ───────────────────────────────────────────────────────────────
82
+ // Prefer the daemon's live view (real polling state). Fall back to the
83
+ // config file only when the daemon is unreachable. The config check must
84
+ // honour BOTH the legacy top-level bot_token AND per-channel tokens.
82
85
  console.log(sec("Telegram"));
83
86
  const tg = cfg.telegram || {};
84
- if (tg.enabled && tg.bot_token) {
85
- console.log(` ${ok("enabled")}`);
86
- console.log(`${key("chat_id")}${val(tg.chat_id || "(not set)")}`);
87
- console.log(`${key("bot_token")}${DI}${maskToken(tg.bot_token)}${R}`);
88
- if (tg.channels?.length) {
89
- console.log(`${key("channels")}${val(tg.channels.length + " configured")}`);
87
+ let tgLive = null;
88
+ if (daemonOk) {
89
+ try { tgLive = await http.get("/telegram/status"); } catch {}
90
+ }
91
+
92
+ if (tgLive) {
93
+ const channels = tgLive.channels || [];
94
+ const polling = channels.filter((c) => c.polling).length;
95
+ if (!tgLive.enabled) {
96
+ console.log(` ${off("disabled in config")} run: ${CY}apx setup${R}`);
97
+ } else if (channels.length === 0) {
98
+ console.log(` ${off("enabled, no channels")} run: ${CY}apx setup${R}`);
99
+ } else if (polling === 0) {
100
+ console.log(` ${err("enabled, not polling")} run: ${CY}apx telegram start${R}`);
101
+ } else {
102
+ console.log(` ${ok("polling")} ${val(`${polling}/${channels.length} channel${channels.length !== 1 ? "s" : ""}`)}`);
103
+ }
104
+ for (const c of channels) {
105
+ const mark = c.polling ? `${GR}●${R}` : `${RE}○${R}`;
106
+ const tok = c.bot_token_present ? "" : ` ${DI}(no token)${R}`;
107
+ const lastErr = c.last_error ? ` ${RE}${c.last_error}${R}` : "";
108
+ console.log(`${key(c.name)}${mark} ${DI}chat ${c.chat_id || "(unset)"}${R}${tok}${lastErr}`);
90
109
  }
91
110
  } else {
92
- console.log(` ${off("disabled")} run: ${CY}apx setup${R}`);
111
+ // Daemon down — best-effort from config. Token may be top-level or per-channel.
112
+ const hasToken = !!tg.bot_token || (tg.channels || []).some((c) => c.bot_token);
113
+ if (tg.enabled && hasToken) {
114
+ console.log(` ${off("configured")} ${DI}daemon down — start it to see live status${R}`);
115
+ console.log(`${key("channels")}${val((tg.channels?.length || 1) + " configured")}`);
116
+ } else {
117
+ console.log(` ${off("disabled")} run: ${CY}apx setup${R}`);
118
+ }
93
119
  }
94
120
 
95
121
  // ── Projects ───────────────────────────────────────────────────────────────
@@ -114,11 +140,6 @@ export async function cmdStatus() {
114
140
  console.log();
115
141
  }
116
142
 
117
- function maskToken(token) {
118
- if (!token || token.length < 12) return "***";
119
- return token.slice(0, 6) + "…" + token.slice(-4);
120
- }
121
-
122
143
  function formatUptime(s) {
123
144
  if (!s) return "?";
124
145
  if (s < 60) return `${s}s`;
@@ -36,6 +36,29 @@ export async function cmdTelegramStatus() {
36
36
  }
37
37
  }
38
38
 
39
+ export async function cmdTelegramStart() {
40
+ const r = await http.post("/telegram/start", {});
41
+ const channels = r.status?.channels || [];
42
+ const polling = channels.filter((c) => c.polling).length;
43
+ if (channels.length === 0) {
44
+ console.log("⚠️ no telegram channels configured — run: apx telegram setup");
45
+ return;
46
+ }
47
+ if (polling === 0) {
48
+ console.log("⚠️ polling did not start — check telegram.enabled in ~/.apx/config.json and that a bot_token is set");
49
+ for (const c of channels) {
50
+ if (c.last_error) console.log(` ${c.name}: ${c.last_error}`);
51
+ }
52
+ return;
53
+ }
54
+ console.log(`✅ telegram polling (${polling}/${channels.length} channel${channels.length !== 1 ? "s" : ""})`);
55
+ }
56
+
57
+ export async function cmdTelegramStop() {
58
+ await http.post("/telegram/stop", {});
59
+ console.log("⏹ telegram polling stopped (config unchanged — apx telegram start to resume)");
60
+ }
61
+
39
62
  export function cmdTelegramSetup() {
40
63
  console.log(`Edit ~/.apx/config.json — telegram section:
41
64
 
package/src/cli/index.js CHANGED
@@ -52,6 +52,8 @@ import {
52
52
  cmdTelegramSend,
53
53
  cmdTelegramStatus,
54
54
  cmdTelegramSetup,
55
+ cmdTelegramStart,
56
+ cmdTelegramStop,
55
57
  } from "./commands/telegram.js";
56
58
  import { cmdMessagesTail, cmdMessagesSearch, cmdMessagesChat } from "./commands/messages.js";
57
59
  import { cmdLog } from "./commands/log.js";
@@ -598,13 +600,15 @@ const HELP_TOPICS = new Map(Object.entries({
598
600
  telegram: topic({
599
601
  title: "apx telegram",
600
602
  summary: "Configure, inspect, and send through the Telegram bridge.",
601
- usage: ["apx telegram <send|status|setup> [args] [--flags]"],
603
+ usage: ["apx telegram <send|status|start|stop|setup> [args] [--flags]"],
602
604
  commands: [
603
605
  ["send \"text\"", "Send a Telegram message."],
604
606
  ["status", "Show Telegram plugin status."],
607
+ ["start", "Start polling on every configured channel."],
608
+ ["stop", "Stop polling (config stays intact)."],
605
609
  ["setup", "Print setup guidance."],
606
610
  ],
607
- examples: ["apx telegram status", "apx telegram send \"hello\" --chat 123456"],
611
+ examples: ["apx telegram status", "apx telegram start", "apx telegram send \"hello\" --chat 123456"],
608
612
  }),
609
613
  "telegram send": topic({
610
614
  title: "apx telegram send",
@@ -626,6 +630,18 @@ const HELP_TOPICS = new Map(Object.entries({
626
630
  usage: ["apx telegram status"],
627
631
  examples: ["apx telegram status"],
628
632
  }),
633
+ "telegram start": topic({
634
+ title: "apx telegram start",
635
+ summary: "Start Telegram polling on every configured channel.",
636
+ usage: ["apx telegram start"],
637
+ examples: ["apx telegram start"],
638
+ }),
639
+ "telegram stop": topic({
640
+ title: "apx telegram stop",
641
+ summary: "Stop Telegram polling (config stays intact; resume with apx telegram start).",
642
+ usage: ["apx telegram stop"],
643
+ examples: ["apx telegram stop"],
644
+ }),
629
645
  "telegram setup": topic({
630
646
  title: "apx telegram setup",
631
647
  summary: "Print Telegram setup guidance.",
@@ -1441,6 +1457,8 @@ async function dispatch(cmd, rest) {
1441
1457
  const a = parseArgs(rest.slice(1));
1442
1458
  if (sub === "send") await cmdTelegramSend(a);
1443
1459
  else if (sub === "status") await cmdTelegramStatus();
1460
+ else if (sub === "start") await cmdTelegramStart();
1461
+ else if (sub === "stop") await cmdTelegramStop();
1444
1462
  else if (sub === "setup") cmdTelegramSetup();
1445
1463
  else die(`unknown telegram subcommand: ${sub || "(none)"}`);
1446
1464
  break;
@@ -50,7 +50,7 @@ const DEFAULT_CONFIG = {
50
50
  name: "apx",
51
51
  model: "", // e.g. "ollama:llama3.2:3b"
52
52
  system: "", // optional override; defaults baked into super-agent.js
53
- permission_mode: "automatico", // total | automatico | permiso
53
+ permission_mode: "total", // total | automatico | permiso
54
54
  allowed_tools: [], // used by permission_mode="permiso"
55
55
  },
56
56
  engines: {
package/src/daemon/api.js CHANGED
@@ -442,6 +442,28 @@ export function buildApi({ projects, registries, plugins, scheduler, version, st
442
442
  res.json(telegram.status());
443
443
  });
444
444
 
445
+ // POST /telegram/start — (re)start polling for every configured channel.
446
+ app.post("/telegram/start", (_req, res) => {
447
+ if (!telegram) return res.status(503).json({ error: "telegram plugin not loaded" });
448
+ try {
449
+ telegram.start();
450
+ res.json({ ok: true, status: telegram.status() });
451
+ } catch (e) {
452
+ res.status(502).json({ error: e.message });
453
+ }
454
+ });
455
+
456
+ // POST /telegram/stop — stop polling on every channel (config stays intact).
457
+ app.post("/telegram/stop", (_req, res) => {
458
+ if (!telegram) return res.status(503).json({ error: "telegram plugin not loaded" });
459
+ try {
460
+ telegram.stop();
461
+ res.json({ ok: true, status: telegram.status() });
462
+ } catch (e) {
463
+ res.status(502).json({ error: e.message });
464
+ }
465
+ });
466
+
445
467
  app.post("/telegram/send", async (req, res) => {
446
468
  const { chat_id, text, channel } = req.body || {};
447
469
  if (!text) return res.status(400).json({ error: "text required" });
@@ -344,6 +344,8 @@ class ChannelPoller {
344
344
  while (this.polling) {
345
345
  try {
346
346
  const updates = await this._getUpdates();
347
+ // A successful poll clears any stale error so status reflects recovery.
348
+ this.lastError = null;
347
349
  for (const u of updates) {
348
350
  await this._handleUpdate(u);
349
351
  this.offset = u.update_id + 1;
@@ -42,6 +42,11 @@ You are **APX** — Manuel's personal assistant running on his Mac.
42
42
  You are NOT a code analyzer, NOT a generic chatbot, NOT a tutor.
43
43
  You are an **action agent**: you USE TOOLS to do real things on Manuel's system.
44
44
 
45
+ # Sobre Manuel (el usuario)
46
+ - Se llama **Manuel**, es un desarrollador argentino.
47
+ - Está en **Argentina**, timezone **UTC-3**. Cuando hables de horarios, asumí UTC-3 salvo que diga otra cosa.
48
+ - Habla **español rioplatense** (voseo). Hablale así.
49
+
45
50
  # Language — non-negotiable
46
51
  ALWAYS reply in **Spanish (rioplatense, voseo when natural)** unless Manuel
47
52
  explicitly writes to you in another language for that turn. The user is an
@@ -49,6 +54,17 @@ Argentinian developer; English replies feel broken to him. If you find
49
54
  yourself writing English, stop and rewrite in Spanish before sending.
50
55
  This rule beats every other formatting hint below.
51
56
 
57
+ # Cómo se reciben los mensajes de audio
58
+ Cuando el usuario manda un audio por Telegram, el sistema lo transcribe
59
+ automáticamente y te lo entrega en este formato:
60
+ [audio] <texto transcripto del audio>
61
+
62
+ Cuando veas "[audio]" al inicio del mensaje, significa que el usuario HABLÓ ese
63
+ mensaje — lo que viene después es la transcripción exacta de lo que dijo.
64
+ Tratalo exactamente igual que si el usuario lo hubiera escrito, pero sabiendo
65
+ que fue hablado. Nunca le digas al usuario que "no escuchaste nada" o que "no
66
+ hay ningún audio" — el audio YA fue procesado y lo tenés en texto delante tuyo.
67
+
52
68
  # What you must NOT do
53
69
  - Do NOT explain code or write essays about "the provided snippet".
54
70
  - Do NOT describe what a tool *would* do — call it and report the result.
@@ -57,15 +73,39 @@ This rule beats every other formatting hint below.
57
73
  - If a user message is short or ambiguous, ASK one short clarifying question
58
74
  in Spanish — do not invent a topic.
59
75
 
60
- # How you operate
61
- You are the **APX dispatcher** the daemon-level agent that runs above all APC projects.
62
-
63
- APX is a local daemon + CLI for APC projects. User-level runtime state lives under ~/.apx/:
64
- - ~/.apx/config.json: daemon config, engines, Telegram, super-agent settings
65
- - ~/.apx/projects/default: your default APX workspace; use it for system-level work when the user does not name a project
66
- - ~/.apx/agents: vault of reusable agent templates
67
- - ~/.apx/messages: global channel logs such as Telegram
76
+ # Qué es APX y qué sos vos
77
+ **Vos SOS el superagente de APX.** No sos un modelo genérico sos el agente
78
+ dispatcher que corre dentro del daemon de APX, y el usuario te habla por Telegram.
79
+
80
+ APX es un daemon + CLI local para proyectos APC (Agent Project Context):
81
+ - El daemon corre en localhost:7430 y mantiene estado en ~/.apx/
82
+ - ~/.apx/config.json: config del daemon, engines, Telegram, ajustes del superagente
83
+ - ~/.apx/projects/default: tu workspace por defecto; usalo para trabajo de sistema cuando el usuario no nombra un proyecto
84
+ - ~/.apx/agents: vault de templates de agentes reutilizables
85
+ - ~/.apx/messages: logs de canales globales como Telegram
86
+ - Los **proyectos** son carpetas en disco con AGENTS.md y .apc/project.json (agentes, memorias, skills, hints de MCP, comandos, routines). Por ahora el único proyecto del usuario se llama \`default\`.
87
+
88
+ Comandos de la CLI de APX (por si el usuario pregunta cómo hacer algo):
89
+ - \`apx daemon start|stop|status|logs\` — controlar el daemon
90
+ - \`apx status\` — estado completo de un vistazo (daemon, superagente, engines, Telegram, proyectos)
91
+ - \`apx code\` — asistente de coding en terminal (TUI)
92
+ - \`apx log\` / \`apx log -f\` — ver/seguir el log unificado en ~/.apx/logs/apx.log
93
+ - \`apx update\` — actualizar APX a la última versión de npm
94
+ - \`apx search <query>\` — buscar en mensajes/proyectos
95
+ - \`apx project add <path>\` — registrar un proyecto
96
+ - \`apx telegram status|start|stop|send\` — controlar el canal de Telegram
97
+ - \`apx routine list|add|run\` — routines programadas
98
+ - \`apx permission show|set\` — modo de permisos
99
+ - \`apx setup\` — wizard de configuración inicial
100
+
101
+ Tus tools (resumen — usalas, no las describas): list_projects / list_agents /
102
+ list_mcps / list_skills para inventario; read_file / list_files / read_agent_memory
103
+ para leer; write_file / add_project / import_agent para mutar; run_shell para
104
+ comandos; call_agent / call_runtime para delegar; send_telegram para mandar
105
+ mensajes/fotos/audio; load_skill para traer docs; web_search / browser_screenshot
106
+ para la web; set_identity para cambiar tu nombre/personalidad.
68
107
 
108
+ # How you operate
69
109
  APC projects are filesystem projects anywhere on disk with AGENTS.md and .apc/project.json. They contain agents, memories, skills, MCP hints, commands, and routines. The default workspace is not a user project; it is your APX home workspace. Registered projects are listed below as a tiny index; call tools for details.
70
110
 
71
111
  Useful CLI facts:
@@ -74,6 +114,7 @@ Useful CLI facts:
74
114
  - Routine design: if the user asks for an agent to think, decide, write, or reply, create an exec_agent routine with spec.agent and spec.prompt. If the user asks APX itself to orchestrate tools or Telegram, create a super_agent routine. If the request is only a deterministic command, create a shell routine. If unclear, ask one short question: "agent routine or simple command routine?"
75
115
  - Routine schedules: APX supports standard cron expressions (e.g. '*/5 * * * *'), OR 'every:<number><s|m|h|d>' (e.g. 'every:60s'), OR 'once:<iso-8601>'.
76
116
  - Safe read-only shell checks such as apx --help, apx routine list, docker ps, find, ls, rg, grep can run in automatico without asking.
117
+ - Búsquedas en el filesystem: usá herramientas específicas y eficientes — \`find <dir> -name <patrón>\`, \`fd <patrón>\`, \`rg <texto>\` / \`grep -rn <texto>\`, o glob patterns concretos. NUNCA uses \`ls -R\` ni \`ls\` recursivo sobre directorios grandes (volúmenes, home, raíz) — es lento, primitivo y trae basura. Acotá siempre el directorio de búsqueda y el patrón.
77
118
 
78
119
  Channel context:
79
120
  - If the context note says Telegram, you are replying through Telegram. Use plain text, brief replies, no markdown tables, no code fences unless needed, no long dumps.
@@ -235,7 +276,7 @@ export async function runSuperAgent({
235
276
  .map((p) => ` ${p.id}: ${p.id === 0 ? "[default]" : "[project]"} "${p.name}" (${p.path})`)
236
277
  .join("\n");
237
278
 
238
- const permissionMode = sa.permission_mode || "automatico";
279
+ const permissionMode = sa.permission_mode || "total";
239
280
  const allowedTools = Array.isArray(sa.allowed_tools) ? sa.allowed_tools : [];
240
281
  const permissionNote = [
241
282
  "# Permission mode",