@agentprojectcontext/apx 1.7.0 → 1.8.1
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/README.md +4 -0
- package/package.json +1 -1
- package/src/cli/commands/config.js +23 -0
- package/src/cli/commands/identity.js +2 -2
- package/src/cli/commands/messages.js +45 -0
- package/src/cli/commands/routine.js +27 -2
- package/src/cli/commands/session.js +7 -7
- package/src/cli/commands/setup.js +9 -9
- package/src/cli/index.js +969 -3
- package/src/core/agent-system.js +95 -0
- package/src/core/apc-context-skill.md +3 -0
- package/src/core/apx-skill.md +30 -0
- package/src/core/messages-store.js +97 -23
- package/src/core/routines-store.js +3 -1
- package/src/daemon/apc-runtime-context.js +2 -2
- package/src/daemon/api.js +12 -32
- package/src/daemon/index.js +1 -0
- package/src/daemon/plugins/telegram.js +39 -25
- package/src/daemon/project-config.js +1 -1
- package/src/daemon/routines.js +70 -13
- package/src/daemon/super-agent-tools/helpers.js +8 -24
- package/src/daemon/super-agent-tools/index.js +5 -1
- package/src/daemon/super-agent-tools/tools/call-agent.js +4 -1
- package/src/daemon/super-agent-tools/tools/call-runtime.js +5 -1
- package/src/daemon/super-agent-tools/tools/run-shell.js +23 -1
- package/src/daemon/super-agent-tools/tools/search-messages.js +2 -0
- package/src/daemon/super-agent-tools/tools/set-permission-mode.js +32 -0
- package/src/daemon/super-agent-tools/tools/tail-messages.js +2 -0
- package/src/daemon/super-agent.js +28 -5
- package/src/daemon/wakeup.js +12 -12
package/README.md
CHANGED
|
@@ -88,6 +88,7 @@ apx exec <slug> "<prompt>" # quick LLM call
|
|
|
88
88
|
|
|
89
89
|
apx session list <slug> # list past sessions
|
|
90
90
|
apx messages tail # last 50 messages, all channels
|
|
91
|
+
apx messages chat --channel telegram # chat view with user/agent/system type
|
|
91
92
|
apx messages tail --channel runtime # only agent invocations
|
|
92
93
|
```
|
|
93
94
|
|
|
@@ -96,6 +97,9 @@ apx messages tail --channel runtime # only agent invocations
|
|
|
96
97
|
Activity belongs to APX runtime state, not `.apc/`. Message storage is local to APX, under
|
|
97
98
|
`~/.apx/`:
|
|
98
99
|
|
|
100
|
+
JSONL messages include `type` (`user`, `agent`, `tool`, or `system`) plus `actor_id`, so chat views
|
|
101
|
+
can distinguish Telegram users from APX agents and future subagents.
|
|
102
|
+
|
|
99
103
|
| Channel | What it captures |
|
|
100
104
|
|---------|-----------------|
|
|
101
105
|
| `runtime` | `apx run` invocations (prompt in, response out) |
|
package/package.json
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { http } from "../http.js";
|
|
2
2
|
import { resolveProjectId } from "./project.js";
|
|
3
|
+
import { readConfig, writeConfig } from "../../core/config.js";
|
|
3
4
|
|
|
4
5
|
function parseValue(raw) {
|
|
5
6
|
// best-effort: try JSON first (covers numbers, bools, objects, arrays, null,
|
|
@@ -54,3 +55,25 @@ export async function cmdConfigUnset(args) {
|
|
|
54
55
|
await http.patch(`/projects/${pid}/config`, { unset: [key] });
|
|
55
56
|
console.log(`unset ${key}`);
|
|
56
57
|
}
|
|
58
|
+
|
|
59
|
+
export function cmdPermission(args = {}) {
|
|
60
|
+
const sub = args._[0] || "show";
|
|
61
|
+
const cfg = readConfig();
|
|
62
|
+
cfg.super_agent = cfg.super_agent || {};
|
|
63
|
+
if (sub === "show" || sub === "get" || sub === "ls") {
|
|
64
|
+
console.log(`permission_mode=${cfg.super_agent.permission_mode || "automatico"}`);
|
|
65
|
+
console.log(`allowed_tools=${(cfg.super_agent.allowed_tools || []).join(",") || "(none)"}`);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (sub === "set") {
|
|
69
|
+
const mode = args._[1];
|
|
70
|
+
if (!["total", "automatico", "permiso"].includes(mode)) {
|
|
71
|
+
throw new Error("apx permissions set: mode must be total, automatico, or permiso");
|
|
72
|
+
}
|
|
73
|
+
cfg.super_agent.permission_mode = mode;
|
|
74
|
+
writeConfig(cfg);
|
|
75
|
+
console.log(`permission_mode=${mode}`);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
throw new Error(`unknown permissions subcommand: ${sub}`);
|
|
79
|
+
}
|
|
@@ -19,7 +19,7 @@ function askYN(rl, question, defaultYes = false) {
|
|
|
19
19
|
rl.question(`${question} [${hint}]: `, (ans) => {
|
|
20
20
|
const a = ans.trim().toLowerCase();
|
|
21
21
|
if (!a) return resolve(defaultYes);
|
|
22
|
-
resolve(a === "y" || a === "yes"
|
|
22
|
+
resolve(a === "y" || a === "yes");
|
|
23
23
|
});
|
|
24
24
|
});
|
|
25
25
|
}
|
|
@@ -120,7 +120,7 @@ export async function runWizard() {
|
|
|
120
120
|
const personality = await ask(rl, " Personality (comma-separated traits)", existing.personality || "direct, curious, helpful");
|
|
121
121
|
const owner_name = await ask(rl, " Your name", existing.owner_name || "");
|
|
122
122
|
const owner_context = await ask(rl, " What are you building / working on", existing.owner_context || "");
|
|
123
|
-
const language = await ask(rl, " Language for agent messages (e.g. Spanish, English)", existing.language || "
|
|
123
|
+
const language = await ask(rl, " Language for agent messages (e.g. Spanish, English)", existing.language || "English");
|
|
124
124
|
|
|
125
125
|
console.log("\n Claude Code permissions");
|
|
126
126
|
console.log(" APX can configure Claude Code to allow terminal commands without prompts.");
|
|
@@ -46,6 +46,51 @@ export async function cmdMessagesTail(args) {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
function chatTimestamp(ts) {
|
|
50
|
+
if (!ts) return "????-??-?? ??:??:??";
|
|
51
|
+
return ts.replace("T", " ").replace(/Z$/, "");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function chatActor(row) {
|
|
55
|
+
const type = row.type || (row.direction === "in" ? "user" : "agent");
|
|
56
|
+
const who = row.author || row.actor_id || "";
|
|
57
|
+
return who ? `${type} ${who}` : type;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function printChatRows(rows) {
|
|
61
|
+
if (rows.length === 0) {
|
|
62
|
+
console.log("(no messages)");
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
for (const r of rows) {
|
|
66
|
+
const body = String(r.body || "");
|
|
67
|
+
const lines = body.split("\n");
|
|
68
|
+
console.log(`[${chatTimestamp(r.ts)}] ${chatActor(r)}: ${lines[0] || ""}`);
|
|
69
|
+
for (const line of lines.slice(1)) console.log(` ${line}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function cmdMessagesChat(args) {
|
|
74
|
+
const channel = args.flags.channel && args.flags.channel !== true ? args.flags.channel : "telegram";
|
|
75
|
+
const n = args.flags.n || args.flags.last || "50";
|
|
76
|
+
const isGlobal = args.flags.global || isGlobalChannel(channel);
|
|
77
|
+
|
|
78
|
+
if (isGlobal) {
|
|
79
|
+
const params = new URLSearchParams({ limit: String(n) });
|
|
80
|
+
if (channel) params.set("channel", channel);
|
|
81
|
+
const rows = await http.get(`/messages/global?${params}`);
|
|
82
|
+
printChatRows(rows);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const pid = await resolveProjectId(args?.flags?.project);
|
|
87
|
+
const params = new URLSearchParams({ limit: String(n) });
|
|
88
|
+
if (args.flags.agent && args.flags.agent !== true) params.set("agent", args.flags.agent);
|
|
89
|
+
if (channel) params.set("channel", channel);
|
|
90
|
+
const rows = await http.get(`/projects/${pid}/messages?${params}`);
|
|
91
|
+
printChatRows(rows.reverse());
|
|
92
|
+
}
|
|
93
|
+
|
|
49
94
|
export async function cmdMessagesSearch(args) {
|
|
50
95
|
const q = args._[0];
|
|
51
96
|
if (!q) throw new Error("apx messages search: missing <query>");
|
|
@@ -12,7 +12,7 @@ function parseSpec(args) {
|
|
|
12
12
|
}
|
|
13
13
|
const spec = {};
|
|
14
14
|
for (const [k, v] of Object.entries(args.flags)) {
|
|
15
|
-
if (["name", "kind", "schedule", "spec", "verbose"].includes(k)) continue;
|
|
15
|
+
if (["name", "kind", "schedule", "spec", "verbose", "permission-mode", "allowed-tools"].includes(k)) continue;
|
|
16
16
|
if (v === true) continue;
|
|
17
17
|
// try to JSON-parse the value (numbers, bools), else keep as string
|
|
18
18
|
let val = v;
|
|
@@ -59,8 +59,14 @@ export async function cmdRoutineAdd(args) {
|
|
|
59
59
|
if (!kind || !schedule)
|
|
60
60
|
throw new Error("apx routine add: --kind and --schedule are required");
|
|
61
61
|
const spec = parseSpec(args);
|
|
62
|
+
const permission_mode = args.flags["permission-mode"] && args.flags["permission-mode"] !== true
|
|
63
|
+
? args.flags["permission-mode"]
|
|
64
|
+
: undefined;
|
|
65
|
+
const allowed_tools = args.flags["allowed-tools"] && args.flags["allowed-tools"] !== true
|
|
66
|
+
? String(args.flags["allowed-tools"]).split(",").map((s) => s.trim()).filter(Boolean)
|
|
67
|
+
: undefined;
|
|
62
68
|
const pid = await resolveProjectId(args?.flags?.project);
|
|
63
|
-
const r = await http.post(`/projects/${pid}/routines`, { name, kind, schedule, spec });
|
|
69
|
+
const r = await http.post(`/projects/${pid}/routines`, { name, kind, schedule, spec, permission_mode, allowed_tools });
|
|
64
70
|
console.log(`added routine "${r.name}" (${r.kind}, ${r.schedule}) → next ${r.next_run_at}`);
|
|
65
71
|
}
|
|
66
72
|
|
|
@@ -97,3 +103,22 @@ export async function cmdRoutineRun(args) {
|
|
|
97
103
|
const r = await http.post(`/projects/${pid}/routines/${name}/run`);
|
|
98
104
|
console.log(JSON.stringify(r, null, 2));
|
|
99
105
|
}
|
|
106
|
+
|
|
107
|
+
export async function cmdRoutineHistory(args) {
|
|
108
|
+
const name = args._[0];
|
|
109
|
+
if (!name) throw new Error("apx routine history: missing <name>");
|
|
110
|
+
const pid = await resolveProjectId(args?.flags?.project);
|
|
111
|
+
const limit = args.flags.n || args.flags.last || "50";
|
|
112
|
+
const rows = await http.get(`/projects/${pid}/messages?channel=routine&limit=${encodeURIComponent(limit)}`);
|
|
113
|
+
const filtered = rows
|
|
114
|
+
.filter((r) => r.meta?.routine === name)
|
|
115
|
+
.reverse();
|
|
116
|
+
if (filtered.length === 0) {
|
|
117
|
+
console.log("(no routine history)");
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
for (const r of filtered) {
|
|
121
|
+
const status = r.meta?.status ? ` ${r.meta.status}` : "";
|
|
122
|
+
console.log(`${r.ts}${status} ${r.author || ""}: ${r.body}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -104,9 +104,9 @@ function findSessionById(root, id) {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
function statusEmoji(status) {
|
|
107
|
-
if (/
|
|
108
|
-
if (/
|
|
109
|
-
if (/stale|
|
|
107
|
+
if (/complete/i.test(status)) return "✅";
|
|
108
|
+
if (/in.progress/i.test(status)) return "🔄";
|
|
109
|
+
if (/stale|closed/i.test(status)) return "⚠️";
|
|
110
110
|
return "❓";
|
|
111
111
|
}
|
|
112
112
|
|
|
@@ -242,7 +242,7 @@ export function cmdSessionClose(args) {
|
|
|
242
242
|
if (!s) throw new Error(`session "${id}" not found`);
|
|
243
243
|
|
|
244
244
|
let text = fs.readFileSync(s.path, "utf8");
|
|
245
|
-
text = setFrontmatterField(text, "status", "✅
|
|
245
|
+
text = setFrontmatterField(text, "status", "✅ Completed");
|
|
246
246
|
text = setFrontmatterField(text, "completed", nowIso());
|
|
247
247
|
if (args.flags.result && args.flags.result !== true) {
|
|
248
248
|
text = setFrontmatterField(text, "result", String(args.flags.result));
|
|
@@ -254,7 +254,7 @@ export function cmdSessionClose(args) {
|
|
|
254
254
|
export function cmdSessionCheck() {
|
|
255
255
|
const root = requireRoot();
|
|
256
256
|
const sessions = listAllSessions(root).filter((s) =>
|
|
257
|
-
/
|
|
257
|
+
/in.progress/i.test(s.status)
|
|
258
258
|
);
|
|
259
259
|
|
|
260
260
|
if (sessions.length === 0) {
|
|
@@ -338,7 +338,7 @@ export async function cmdSessionResume(args) {
|
|
|
338
338
|
export function cmdSessionCloseStale() {
|
|
339
339
|
const root = requireRoot();
|
|
340
340
|
const sessions = listAllSessions(root).filter((s) =>
|
|
341
|
-
/
|
|
341
|
+
/in.progress/i.test(s.status)
|
|
342
342
|
);
|
|
343
343
|
let closed = 0;
|
|
344
344
|
for (const s of sessions) {
|
|
@@ -348,7 +348,7 @@ export function cmdSessionCloseStale() {
|
|
|
348
348
|
text = setFrontmatterField(
|
|
349
349
|
text,
|
|
350
350
|
"status",
|
|
351
|
-
`⚠️
|
|
351
|
+
`⚠️ Automatically closed (stale >${STALE_HOURS}h)`
|
|
352
352
|
);
|
|
353
353
|
text = setFrontmatterField(text, "completed", nowIso());
|
|
354
354
|
text = setFrontmatterField(
|
|
@@ -194,7 +194,7 @@ export async function cmdSetup() {
|
|
|
194
194
|
console.log(b(" Language:"));
|
|
195
195
|
console.log(di(" The super-agent will always respond in your language."));
|
|
196
196
|
console.log();
|
|
197
|
-
const language = await ask(" Your language (e.g. English,
|
|
197
|
+
const language = await ask(" Your language (e.g. English, Spanish, Portuguese): ") || "English";
|
|
198
198
|
|
|
199
199
|
// ── Summary ─────────────────────────────────────────────────────────────────
|
|
200
200
|
console.log();
|
|
@@ -221,8 +221,8 @@ export async function cmdSetup() {
|
|
|
221
221
|
|
|
222
222
|
cfg.super_agent.enabled = true;
|
|
223
223
|
cfg.super_agent.model = chosenModel;
|
|
224
|
-
|
|
225
|
-
cfg.super_agent.
|
|
224
|
+
cfg.super_agent.system = "";
|
|
225
|
+
cfg.super_agent.permission_mode = cfg.super_agent.permission_mode || "automatico";
|
|
226
226
|
|
|
227
227
|
if (provider.id === "ollama") {
|
|
228
228
|
cfg.engines.ollama.base_url = ollamaUrl;
|
|
@@ -324,15 +324,15 @@ async function sendTelegramWakeup({ botToken, chatId, language, model }) {
|
|
|
324
324
|
// Minimal fallback messages per common language (used only if daemon can't respond)
|
|
325
325
|
function languageFallback(lang) {
|
|
326
326
|
const l = lang.toLowerCase();
|
|
327
|
-
if (/
|
|
328
|
-
return "⚡
|
|
327
|
+
if (/spanish|arg|lat/i.test(l))
|
|
328
|
+
return "⚡ APX is online and ready to work.\nI do not have a name yet. What would you like to call me?\nAnd what should I call you?";
|
|
329
329
|
if (/portugu|brasil/i.test(l))
|
|
330
|
-
return "⚡
|
|
330
|
+
return "⚡ APX is online and ready to work.\nI do not have a name yet. What would you like to call me?\nAnd what should I call you?";
|
|
331
331
|
if (/franc|french/i.test(l))
|
|
332
|
-
return "⚡
|
|
332
|
+
return "⚡ APX is online and ready to work.\nI do not have a name yet. What would you like to call me?\nAnd what should I call you?";
|
|
333
333
|
if (/deutsch|german/i.test(l))
|
|
334
|
-
return "⚡
|
|
334
|
+
return "⚡ APX is online and ready to work.\nI do not have a name yet. What would you like to call me?\nAnd what should I call you?";
|
|
335
335
|
if (/ital/i.test(l))
|
|
336
|
-
return "⚡
|
|
336
|
+
return "⚡ APX is online and ready to work.\nI do not have a name yet. What would you like to call me?\nAnd what should I call you?";
|
|
337
337
|
return "⚡ I'm awake and ready to go! APX is online.\nI don't have a name yet — what would you like to call me?\nAnd you, what's your name or what should I call you?";
|
|
338
338
|
}
|