@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/src/daemon/routines.js
CHANGED
|
@@ -6,14 +6,15 @@
|
|
|
6
6
|
// Kinds:
|
|
7
7
|
// heartbeat — log a heartbeat message. spec: { channel?, message? }
|
|
8
8
|
// exec_agent — call an agent engine. spec: { agent: slug, prompt }
|
|
9
|
+
// super_agent — call the APX super-agent. spec: { prompt }
|
|
9
10
|
// telegram — send a Telegram message. spec: { channel?, chat_id?, text }
|
|
10
11
|
// shell — run a shell command. spec: { command, timeout_ms? }
|
|
11
12
|
|
|
12
13
|
import { spawn } from "node:child_process";
|
|
13
|
-
import path from "node:path";
|
|
14
|
-
import fs from "node:fs";
|
|
15
14
|
import { callEngine } from "./engines/index.js";
|
|
15
|
+
import { runSuperAgent } from "./super-agent.js";
|
|
16
16
|
import { readAgents } from "../core/parser.js";
|
|
17
|
+
import { buildAgentSystem } from "../core/agent-system.js";
|
|
17
18
|
import {
|
|
18
19
|
listRoutines,
|
|
19
20
|
getRoutine,
|
|
@@ -48,6 +49,8 @@ async function handleHeartbeat(ctx, routine) {
|
|
|
48
49
|
project.logMessage({
|
|
49
50
|
channel,
|
|
50
51
|
direction: "out",
|
|
52
|
+
type: "system",
|
|
53
|
+
actor_id: "apx:routine",
|
|
51
54
|
author: "apx",
|
|
52
55
|
body: message,
|
|
53
56
|
meta: { routine: routine.name },
|
|
@@ -66,17 +69,13 @@ async function handleExecAgent(ctx, routine) {
|
|
|
66
69
|
const model = agent.fields.Model;
|
|
67
70
|
if (!model) throw new Error(`agent ${slug} has no model`);
|
|
68
71
|
|
|
69
|
-
const f = agent.fields;
|
|
70
|
-
const parts = [];
|
|
71
|
-
if (f.Description) parts.push(f.Description);
|
|
72
|
-
if (f.Role) parts.push(`Role: ${f.Role}`);
|
|
73
|
-
const memPath = path.join(project.path, ".apc", "agents", slug, "memory.md");
|
|
74
|
-
if (fs.existsSync(memPath)) parts.push("## Memory\n" + fs.readFileSync(memPath, "utf8"));
|
|
75
|
-
parts.push(`You were invoked by routine "${routine.name}". Reply briefly, max 4 sentences.`);
|
|
76
|
-
|
|
77
72
|
const result = await callEngine({
|
|
78
73
|
modelId: model,
|
|
79
|
-
system:
|
|
74
|
+
system: buildAgentSystem(project, agent, {
|
|
75
|
+
invocation: "routine",
|
|
76
|
+
routine: routine.name,
|
|
77
|
+
extraParts: [`Reply briefly, max 4 sentences.`],
|
|
78
|
+
}),
|
|
80
79
|
messages: [{ role: "user", content: prompt }],
|
|
81
80
|
config: project.config || globalConfig,
|
|
82
81
|
});
|
|
@@ -85,6 +84,8 @@ async function handleExecAgent(ctx, routine) {
|
|
|
85
84
|
agent_slug: slug,
|
|
86
85
|
channel: "routine",
|
|
87
86
|
direction: "out",
|
|
87
|
+
type: "agent",
|
|
88
|
+
actor_id: slug,
|
|
88
89
|
author: slug,
|
|
89
90
|
body: result.text,
|
|
90
91
|
meta: { routine: routine.name, usage: result.usage },
|
|
@@ -92,6 +93,39 @@ async function handleExecAgent(ctx, routine) {
|
|
|
92
93
|
return { status: "ok", reply: result.text };
|
|
93
94
|
}
|
|
94
95
|
|
|
96
|
+
async function handleSuperAgent(ctx, routine) {
|
|
97
|
+
const { project, globalConfig, projects, plugins, registries } = ctx;
|
|
98
|
+
const { prompt } = routine.spec;
|
|
99
|
+
if (!prompt) throw new Error("super_agent: spec needs { prompt }");
|
|
100
|
+
|
|
101
|
+
const cfg = structuredClone(globalConfig || {});
|
|
102
|
+
cfg.super_agent = {
|
|
103
|
+
...(globalConfig?.super_agent || {}),
|
|
104
|
+
...(routine.permission_mode ? { permission_mode: routine.permission_mode } : {}),
|
|
105
|
+
...(Array.isArray(routine.allowed_tools) ? { allowed_tools: routine.allowed_tools } : {}),
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const result = await runSuperAgent({
|
|
109
|
+
globalConfig: cfg,
|
|
110
|
+
projects,
|
|
111
|
+
plugins,
|
|
112
|
+
registries,
|
|
113
|
+
prompt,
|
|
114
|
+
contextNote: `You were invoked by APX routine "${routine.name}" in project ${project.path}. This is an autonomous scheduled run, not an interactive Telegram reply.`,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
project.logMessage({
|
|
118
|
+
channel: "routine",
|
|
119
|
+
direction: "out",
|
|
120
|
+
type: "agent",
|
|
121
|
+
actor_id: result.name || "super_agent",
|
|
122
|
+
author: result.name || "super_agent",
|
|
123
|
+
body: result.text || "",
|
|
124
|
+
meta: { routine: routine.name, tool_trace: result.trace, usage: result.usage },
|
|
125
|
+
});
|
|
126
|
+
return { status: "ok", reply: result.text, trace: result.trace };
|
|
127
|
+
}
|
|
128
|
+
|
|
95
129
|
async function handleTelegram(ctx, routine) {
|
|
96
130
|
const { plugins } = ctx;
|
|
97
131
|
const tg = plugins?.get("telegram");
|
|
@@ -130,6 +164,7 @@ function handleShell(ctx, routine) {
|
|
|
130
164
|
const HANDLERS = {
|
|
131
165
|
heartbeat: handleHeartbeat,
|
|
132
166
|
exec_agent: handleExecAgent,
|
|
167
|
+
super_agent: handleSuperAgent,
|
|
133
168
|
telegram: handleTelegram,
|
|
134
169
|
shell: handleShell,
|
|
135
170
|
};
|
|
@@ -144,6 +179,10 @@ export async function runRoutineNow(ctx, routine) {
|
|
|
144
179
|
let errMsg = null;
|
|
145
180
|
try {
|
|
146
181
|
result = await handler(ctx, routine);
|
|
182
|
+
if (result?.status === "error") {
|
|
183
|
+
status = "error";
|
|
184
|
+
errMsg = result.error || result.stderr || `routine ${routine.name} returned error status`;
|
|
185
|
+
}
|
|
147
186
|
} catch (e) {
|
|
148
187
|
status = "error";
|
|
149
188
|
errMsg = e.message;
|
|
@@ -159,13 +198,25 @@ export async function runRoutineNow(ctx, routine) {
|
|
|
159
198
|
next_run_at: next,
|
|
160
199
|
disable: isOnce,
|
|
161
200
|
});
|
|
201
|
+
ctx.project.logMessage?.({
|
|
202
|
+
channel: "routine",
|
|
203
|
+
direction: "out",
|
|
204
|
+
type: "system",
|
|
205
|
+
actor_id: "apx:routine",
|
|
206
|
+
author: "apx",
|
|
207
|
+
body: status === "ok"
|
|
208
|
+
? `routine ${routine.name} ok`
|
|
209
|
+
: `routine ${routine.name} error: ${errMsg}`,
|
|
210
|
+
meta: { routine: routine.name, status, result },
|
|
211
|
+
});
|
|
162
212
|
return { ...result, last_run_at: lastRun, next_run_at: next };
|
|
163
213
|
}
|
|
164
214
|
|
|
165
215
|
export class RoutineScheduler {
|
|
166
|
-
constructor({ projects, plugins, globalConfig, log }) {
|
|
216
|
+
constructor({ projects, plugins, registries, globalConfig, log }) {
|
|
167
217
|
this.projects = projects;
|
|
168
218
|
this.plugins = plugins;
|
|
219
|
+
this.registries = registries;
|
|
169
220
|
this.globalConfig = globalConfig;
|
|
170
221
|
this.log = log || (() => {});
|
|
171
222
|
this._timer = null;
|
|
@@ -199,7 +250,13 @@ export class RoutineScheduler {
|
|
|
199
250
|
for (const r of due) {
|
|
200
251
|
this.log(`routine ${r.name} (${r.kind}) firing in project #${proj.id}`);
|
|
201
252
|
await runRoutineNow(
|
|
202
|
-
{
|
|
253
|
+
{
|
|
254
|
+
project: proj,
|
|
255
|
+
projects: this.projects,
|
|
256
|
+
plugins: this.plugins,
|
|
257
|
+
registries: this.registries,
|
|
258
|
+
globalConfig: this.globalConfig,
|
|
259
|
+
},
|
|
203
260
|
r
|
|
204
261
|
);
|
|
205
262
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
1
|
import path from "node:path";
|
|
2
|
+
import { agentSkills, buildAgentSystem as buildCoreAgentSystem } from "../../core/agent-system.js";
|
|
3
3
|
|
|
4
4
|
export function projectMeta(projects, entry) {
|
|
5
5
|
const meta = projects.list().find((p) => p.id === entry.id);
|
|
@@ -61,8 +61,7 @@ export function safePathJoin(root, sub = ".") {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
export function skillsFromFields(fields = {}) {
|
|
64
|
-
|
|
65
|
-
return (fields.Skills || "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
64
|
+
return agentSkills({ fields });
|
|
66
65
|
}
|
|
67
66
|
|
|
68
67
|
export function agentRow(agent) {
|
|
@@ -76,36 +75,21 @@ export function agentRow(agent) {
|
|
|
76
75
|
};
|
|
77
76
|
}
|
|
78
77
|
|
|
79
|
-
export function buildAgentSystem(project, agent) {
|
|
80
|
-
|
|
81
|
-
if (agent.fields.Description) parts.push(agent.fields.Description);
|
|
82
|
-
if (agent.fields.Role) parts.push(`Role: ${agent.fields.Role}`);
|
|
83
|
-
if (agent.fields.Language) parts.push(`Default language: ${agent.fields.Language}`);
|
|
84
|
-
|
|
85
|
-
const memPath = path.join(project.path, ".apc", "agents", agent.slug, "memory.md");
|
|
86
|
-
if (fs.existsSync(memPath)) parts.push("## Memory\n" + fs.readFileSync(memPath, "utf8"));
|
|
87
|
-
|
|
88
|
-
const apxSkill = path.join(project.path, ".apc", "skills", "apx.md");
|
|
89
|
-
if (fs.existsSync(apxSkill)) parts.push("## APX\n" + fs.readFileSync(apxSkill, "utf8"));
|
|
90
|
-
|
|
91
|
-
for (const skill of skillsFromFields(agent.fields)) {
|
|
92
|
-
const skillPath = path.join(project.path, ".apc", "skills", `${skill}.md`);
|
|
93
|
-
if (fs.existsSync(skillPath)) parts.push(`## Skill: ${skill}\n` + fs.readFileSync(skillPath, "utf8"));
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return parts.join("\n\n");
|
|
78
|
+
export function buildAgentSystem(project, agent, opts = {}) {
|
|
79
|
+
return buildCoreAgentSystem(project, agent, opts);
|
|
97
80
|
}
|
|
98
81
|
|
|
99
|
-
export function createPermissionGuard(globalConfig = {}) {
|
|
82
|
+
export function createPermissionGuard(globalConfig = {}, { implicitConfirmation = false } = {}) {
|
|
100
83
|
const permissionMode = globalConfig.super_agent?.permission_mode || "automatico";
|
|
101
84
|
const allowedTools = new Set(globalConfig.super_agent?.allowed_tools || []);
|
|
102
85
|
|
|
103
86
|
return function requirePermission(tool, { dangerous = false, confirmed = false } = {}) {
|
|
87
|
+
const ok = confirmed || implicitConfirmation;
|
|
104
88
|
if (permissionMode === "total") return;
|
|
105
|
-
if (permissionMode === "permiso" && !allowedTools.has(tool) && !
|
|
89
|
+
if (permissionMode === "permiso" && !allowedTools.has(tool) && !ok) {
|
|
106
90
|
throw new Error(`requires_confirmation: permission_mode=permiso blocks ${tool}`);
|
|
107
91
|
}
|
|
108
|
-
if (permissionMode === "automatico" && dangerous && !
|
|
92
|
+
if (permissionMode === "automatico" && dangerous && !ok) {
|
|
109
93
|
throw new Error(`requires_confirmation: permission_mode=automatico requires confirmation for ${tool}`);
|
|
110
94
|
}
|
|
111
95
|
};
|
|
@@ -17,6 +17,7 @@ import callMcp from "./tools/call-mcp.js";
|
|
|
17
17
|
import callRuntime from "./tools/call-runtime.js";
|
|
18
18
|
import sendTelegram from "./tools/send-telegram.js";
|
|
19
19
|
import setIdentity from "./tools/set-identity.js";
|
|
20
|
+
import setPermissionMode from "./tools/set-permission-mode.js";
|
|
20
21
|
import { createPermissionGuard } from "./helpers.js";
|
|
21
22
|
|
|
22
23
|
const TOOLS = [
|
|
@@ -39,6 +40,7 @@ const TOOLS = [
|
|
|
39
40
|
callRuntime,
|
|
40
41
|
sendTelegram,
|
|
41
42
|
setIdentity,
|
|
43
|
+
setPermissionMode,
|
|
42
44
|
];
|
|
43
45
|
|
|
44
46
|
export const TOOL_SCHEMAS = TOOLS.map((tool) => tool.schema);
|
|
@@ -46,7 +48,9 @@ export const TOOL_SCHEMAS = TOOLS.map((tool) => tool.schema);
|
|
|
46
48
|
export function makeToolHandlers(ctx) {
|
|
47
49
|
const toolCtx = {
|
|
48
50
|
...ctx,
|
|
49
|
-
requirePermission: createPermissionGuard(ctx.globalConfig || {}
|
|
51
|
+
requirePermission: createPermissionGuard(ctx.globalConfig || {}, {
|
|
52
|
+
implicitConfirmation: !!ctx.implicitConfirmation,
|
|
53
|
+
}),
|
|
50
54
|
};
|
|
51
55
|
return Object.fromEntries(TOOLS.map((tool) => [tool.name, tool.makeHandler(toolCtx)]));
|
|
52
56
|
}
|
|
@@ -28,7 +28,10 @@ export default {
|
|
|
28
28
|
|
|
29
29
|
const result = await callEngine({
|
|
30
30
|
modelId: agent.fields.Model,
|
|
31
|
-
system: buildAgentSystem(p, agent
|
|
31
|
+
system: buildAgentSystem(p, agent, {
|
|
32
|
+
invocation: "engine",
|
|
33
|
+
caller: "super_agent_tool",
|
|
34
|
+
}),
|
|
32
35
|
messages: [{ role: "user", content: prompt }],
|
|
33
36
|
config: p.config || globalConfig,
|
|
34
37
|
});
|
|
@@ -68,7 +68,11 @@ export default {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
const r = await rt.run({
|
|
71
|
-
system: buildAgentSystem(p, agent
|
|
71
|
+
system: buildAgentSystem(p, agent, {
|
|
72
|
+
invocation: "runtime",
|
|
73
|
+
runtime,
|
|
74
|
+
caller: "super_agent_tool",
|
|
75
|
+
}),
|
|
72
76
|
prompt,
|
|
73
77
|
cwd: p.path,
|
|
74
78
|
timeoutMs: timeout_s * 1000,
|
|
@@ -21,6 +21,28 @@ function run(command, { cwd, timeoutMs }) {
|
|
|
21
21
|
});
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
function isSafeShellCommand(command) {
|
|
25
|
+
const text = String(command || "").trim();
|
|
26
|
+
if (!text) return false;
|
|
27
|
+
if (/[`$<>]/.test(text)) return false;
|
|
28
|
+
if (/\b(rm|mv|cp|chmod|chown|mkdir|touch|tee|kill|pkill|npm\s+install|curl\s+-X|apx\s+routine\s+(add|remove|rm|enable|disable|run)|apx\s+config\s+set)\b/i.test(text)) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const segments = text.split(/\s*(?:\||&&|\|\|)\s*/).filter(Boolean);
|
|
33
|
+
return segments.every((segment) => {
|
|
34
|
+
const cmd = segment.trim().split(/\s+/)[0];
|
|
35
|
+
if (["pwd", "ls", "find", "rg", "grep", "cat", "head", "tail", "sed", "wc", "date", "stat", "file", "du", "df", "whoami", "id", "uname", "echo"].includes(cmd)) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
if (cmd === "docker") return /^docker\s+ps\b/.test(segment.trim());
|
|
39
|
+
if (cmd === "apx") {
|
|
40
|
+
return /^apx\s+(--help|-h|help|status|daemon\s+status|routine\s+(list|ls|get|show)\b|project\s+(list|ls)\b|agent\s+(list|ls)\b|config\s+(show|ls)\b)/.test(segment.trim());
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
24
46
|
export default {
|
|
25
47
|
name: "run_shell",
|
|
26
48
|
schema: {
|
|
@@ -42,7 +64,7 @@ export default {
|
|
|
42
64
|
},
|
|
43
65
|
},
|
|
44
66
|
makeHandler: ({ projects, requirePermission }) => async ({ project, cwd = ".", command, timeout_s = 60, confirmed = false }) => {
|
|
45
|
-
requirePermission("run_shell", { dangerous:
|
|
67
|
+
requirePermission("run_shell", { dangerous: !isSafeShellCommand(command), confirmed });
|
|
46
68
|
if (!command) throw new Error("run_shell: command required");
|
|
47
69
|
|
|
48
70
|
const p = resolveProject(projects, project);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { readConfig, writeConfig } from "../../../core/config.js";
|
|
2
|
+
import { confirmedProperty } from "../helpers.js";
|
|
3
|
+
|
|
4
|
+
const MODES = new Set(["total", "automatico", "permiso"]);
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
name: "set_permission_mode",
|
|
8
|
+
schema: {
|
|
9
|
+
type: "function",
|
|
10
|
+
function: {
|
|
11
|
+
name: "set_permission_mode",
|
|
12
|
+
description: "Set the super-agent permission mode in ~/.apx/config.json. Modes: total, automatico, permiso.",
|
|
13
|
+
parameters: {
|
|
14
|
+
type: "object",
|
|
15
|
+
properties: {
|
|
16
|
+
mode: { type: "string", enum: ["total", "automatico", "permiso"] },
|
|
17
|
+
confirmed: confirmedProperty("true only after explicit user confirmation for this exact permission-mode change"),
|
|
18
|
+
},
|
|
19
|
+
required: ["mode"],
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
makeHandler: ({ requirePermission }) => ({ mode, confirmed = false }) => {
|
|
24
|
+
requirePermission("set_permission_mode", { dangerous: true, confirmed });
|
|
25
|
+
if (!MODES.has(mode)) throw new Error("mode must be total, automatico, or permiso");
|
|
26
|
+
const cfg = readConfig();
|
|
27
|
+
cfg.super_agent = cfg.super_agent || {};
|
|
28
|
+
cfg.super_agent.permission_mode = mode;
|
|
29
|
+
writeConfig(cfg);
|
|
30
|
+
return { ok: true, mode };
|
|
31
|
+
},
|
|
32
|
+
};
|
|
@@ -30,6 +30,12 @@ APX is a local daemon + CLI for APC projects. User-level runtime state lives und
|
|
|
30
30
|
|
|
31
31
|
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.
|
|
32
32
|
|
|
33
|
+
Useful CLI facts:
|
|
34
|
+
- Permission mode: apx permission show; apx permission set total|automatico|permiso.
|
|
35
|
+
- Routines: apx routine list|get|history|run|add. Autonomous super-agent routines use kind super_agent.
|
|
36
|
+
- 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?"
|
|
37
|
+
- Safe read-only shell checks such as apx --help, apx routine list, docker ps, find, ls, rg, grep can run in automatico without asking.
|
|
38
|
+
|
|
33
39
|
Channel context:
|
|
34
40
|
- 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.
|
|
35
41
|
- If not Telegram, answer normally for the caller, still concise.
|
|
@@ -48,22 +54,37 @@ Available tools:
|
|
|
48
54
|
- call_runtime — spawn a separate claude-code/codex/opencode/aider session when the user wants an external runtime/chat
|
|
49
55
|
- send_telegram — send a message
|
|
50
56
|
- set_identity — update agent name, personality, owner, language (persists to disk)
|
|
57
|
+
- set_permission_mode — set total/automatico/permiso in ~/.apx/config.json
|
|
51
58
|
|
|
52
59
|
HARD RULES (do not deviate):
|
|
53
60
|
1. NEVER invent project names, agent slugs, model ids, MCP names or paths. ALWAYS look them up via list_* first.
|
|
54
|
-
2. If the user
|
|
61
|
+
2. If the user asks for agents, lists, inventory, or "what exists" without specifying a project, that means **all of them** — call the tool WITHOUT a project argument and the result will include every project.
|
|
55
62
|
3. NEVER answer "specify a project" — instead, just call the tool with no argument and you'll get the full picture.
|
|
56
63
|
4. If a tool result has an error, retry with different arguments before falling back to asking the user.
|
|
57
|
-
5. Respect permission mode. total = execute requested actions without confirmation. automatico = read/list/safe actions run directly; destructive, external,
|
|
58
|
-
6.
|
|
64
|
+
5. Respect permission mode. total = execute requested actions without confirmation. automatico = read/list/safe shell actions run directly; destructive, external, runtime, MCP calls, outbound messages, config, and filesystem mutations need explicit user confirmation. permiso = only allowed tools run directly; everything else needs confirmation.
|
|
65
|
+
6. Write in the user's language unless they request another language. The system prompt stays English. Plain text, no markdown formatting for Telegram.
|
|
59
66
|
7. Stay brief: under 6 sentences unless asked for detail.
|
|
60
67
|
8. You DO see recent prior turns of this chat as previous messages when applicable. **Use them ONLY to disambiguate references** (e.g. "el primero" → first project mentioned earlier). For ANY factual data — agent details, MCP details, file contents, memory — RE-CALL the tool. Past turns are context, not a cache. Models change, agents change, files change.
|
|
61
68
|
9. /reset or /new from the user means "forget previous turns and answer this one fresh" — if you see those prefixes the operator already cleared the context for you.
|
|
62
69
|
10. ACTION RULE: use direct tools for direct work. run_shell executes commands; write_file/edit_file modify files. call_runtime is only for spawning a separate external runtime/chat. call_mcp is only for an MCP server/tool.
|
|
63
|
-
11. DISPATCH RULE: when the user
|
|
70
|
+
11. DISPATCH RULE: when the user asks a named agent to work inside Claude, Codex, OpenCode, or Aider, that is a call_runtime request. Look up the agent slug with list_agents if needed, then call call_runtime({agent: <slug>, runtime: 'claude-code'|'codex'|'opencode'|'aider', prompt: <user's request>}). The agent's declared model (in AGENTS.md) is IGNORED in this case; the runtime supplies the model. Memory + skills of the agent become the system prompt of the runtime.
|
|
64
71
|
12. PROJECT RULE: when the user gives no project, use project "default". Do not infer a non-default project from old chat history unless the user references it. If they mention a path or project name, look it up or add it with add_project.
|
|
65
72
|
13. VAULT RULE: when the user wants a new existing agent/template, call list_vault_agents first. If a suitable vault agent exists, import_agent into the chosen project. If none fits, say briefly what is missing.
|
|
66
|
-
14.
|
|
73
|
+
14. NO-PENDING RULE: never say "give me a second", "I will do it", or "I will try later" as a final answer. Either call the tool in this same turn or say what blocks you.
|
|
74
|
+
15. IDENTITY RULE: when the user asks you to change your name, call yourself something, or update your personality/language, call set_identity and persist the change. Then confirm with your new name.`;
|
|
75
|
+
|
|
76
|
+
function isShortConfirmation(text) {
|
|
77
|
+
return /^(yes|y|si|si dale|dale|ok|okay|confirm|confirmed|go|proceed|do it)\b/i
|
|
78
|
+
.test(String(text || "").trim());
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function lastAssistantAskedForConfirmation(messages) {
|
|
82
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
83
|
+
if (messages[i]?.role !== "assistant") continue;
|
|
84
|
+
return /\b(confirm|confirmation|ok|okay|permission|allowed|proceed|do it|dale)\b/i.test(messages[i].content || "");
|
|
85
|
+
}
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
67
88
|
|
|
68
89
|
export function isSuperAgentEnabled(cfg) {
|
|
69
90
|
return !!(cfg && cfg.super_agent && cfg.super_agent.enabled && cfg.super_agent.model);
|
|
@@ -117,6 +138,8 @@ export async function runSuperAgent({
|
|
|
117
138
|
plugins,
|
|
118
139
|
registries,
|
|
119
140
|
globalConfig,
|
|
141
|
+
implicitConfirmation:
|
|
142
|
+
isShortConfirmation(prompt) && lastAssistantAskedForConfirmation(previousMessages),
|
|
120
143
|
});
|
|
121
144
|
|
|
122
145
|
// Agent loop: call model → if tool_calls, execute and feed back; repeat.
|
package/src/daemon/wakeup.js
CHANGED
|
@@ -11,20 +11,20 @@ function detectLanguage(identity) {
|
|
|
11
11
|
const lang = process.env.LANG || process.env.LC_MESSAGES || process.env.LC_ALL || "";
|
|
12
12
|
const code = lang.split(/[_\.]/)[0].toLowerCase();
|
|
13
13
|
const map = {
|
|
14
|
-
es: "Spanish
|
|
14
|
+
es: "Spanish",
|
|
15
15
|
en: "English",
|
|
16
|
-
fr: "French
|
|
17
|
-
pt: "Portuguese
|
|
18
|
-
de: "German
|
|
19
|
-
it: "Italian
|
|
20
|
-
nl: "Dutch
|
|
21
|
-
ru: "Russian
|
|
22
|
-
ja: "Japanese
|
|
23
|
-
zh: "Chinese
|
|
24
|
-
ko: "Korean
|
|
25
|
-
ar: "Arabic
|
|
16
|
+
fr: "French",
|
|
17
|
+
pt: "Portuguese",
|
|
18
|
+
de: "German",
|
|
19
|
+
it: "Italian",
|
|
20
|
+
nl: "Dutch",
|
|
21
|
+
ru: "Russian",
|
|
22
|
+
ja: "Japanese",
|
|
23
|
+
zh: "Chinese",
|
|
24
|
+
ko: "Korean",
|
|
25
|
+
ar: "Arabic",
|
|
26
26
|
};
|
|
27
|
-
return map[code] || "
|
|
27
|
+
return map[code] || "English";
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
async function generateMessage(identity, engineConfig) {
|