@agentprojectcontext/apx 1.14.1 → 1.15.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 +2 -1
- package/skills/apc-context/SKILL.md +68 -18
- package/skills/apx/SKILL.md +89 -33
- package/src/cli/commands/sys.js +249 -21
- package/src/cli/commands/telegram.js +8 -2
- package/src/cli/http.js +24 -7
- package/src/cli/index.js +10 -3
- package/src/cli/postinstall.js +54 -4
- package/src/cli/terminal-chat/renderer.js +60 -3
- package/src/core/logging.js +37 -0
- package/src/core/scaffold.js +70 -56
- package/src/daemon/api.js +29 -2
- package/src/daemon/engines/anthropic.js +2 -1
- package/src/daemon/engines/gemini.js +2 -1
- package/src/daemon/engines/index.js +3 -3
- package/src/daemon/engines/ollama.js +2 -1
- package/src/daemon/engines/openai.js +2 -1
- package/src/daemon/plugins/telegram.js +20 -1
- package/src/daemon/skills-loader.js +31 -66
- package/src/daemon/smoke.js +9 -1
- package/src/daemon/super-agent-tools/index.js +2 -0
- package/src/daemon/super-agent-tools/tools/ask-questions.js +28 -0
- package/src/daemon/super-agent.js +97 -9
- package/src/core/apc-context-skill.md +0 -105
- package/src/core/apx-skill.md +0 -135
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
name: "ask_questions",
|
|
3
|
+
schema: {
|
|
4
|
+
function: {
|
|
5
|
+
name: "ask_questions",
|
|
6
|
+
description: "Ask the user one or more specific questions to clarify the task or gather requirements.",
|
|
7
|
+
parameters: {
|
|
8
|
+
type: "object",
|
|
9
|
+
properties: {
|
|
10
|
+
questions: {
|
|
11
|
+
type: "array",
|
|
12
|
+
items: { type: "string" },
|
|
13
|
+
description: "A list of questions for the user."
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
required: ["questions"]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
makeHandler: () => async ({ questions }) => {
|
|
21
|
+
// This tool is used by the agent to explicitly signal that it is waiting for
|
|
22
|
+
// answers to specific questions. The UI can then highlight these.
|
|
23
|
+
return {
|
|
24
|
+
status: "Questions presented to user. Waiting for input.",
|
|
25
|
+
count: questions.length
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
};
|
|
@@ -68,6 +68,47 @@ HARD RULES (do not deviate):
|
|
|
68
68
|
21. **SKILLS — ON DEMAND**: The "# Available skills" section below lists every skill available to you (slug + description, NO body). When the user asks about specific APX/APC commands, project structure, agent runtimes, or anything where exact syntax or detailed behavior matches a skill description (in ANY language — match semantically, not by keyword), call load_skill({slug}) to fetch the full markdown body. If a CWD is in the contextNote, pass it as project_path so project-scoped skills resolve. If the user explicitly asks "what skills do you have?", you can either read the catalog below directly OR call list_skills to get a fresh enumeration. Do NOT load skills for trivial / unrelated questions — that wastes tokens. Don't guess CLI syntax when a skill can tell you; load it.
|
|
69
69
|
22. **NEVER PASTE BASE64 OR DATA URIs IN MESSAGE TEXT**: When you need to send an image, audio, or file via Telegram (or any channel), you MUST pass it via the dedicated parameter — NEVER embed it in the text field. Concretely: after browser_screenshot returns its base64 field, call send_telegram({text: "<short caption>", photo_base64: "<that base64>"}). Do NOT write text like 'Aquí está: ' — Telegram (and most chat clients) do NOT render data URIs or markdown images; the user sees thousands of garbage characters. Same for files: use document_path / document_base64 / document_url, NOT the text field. The text field is exclusively for human-readable prose (and becomes the caption when media is attached). If unsure, save the image to /tmp/screenshot-<ts>.png first (browser_screenshot supports save_to_tmp=true and returns a path field) and pass that path to send_telegram via photo_path — never inline the bytes in text.`;
|
|
70
70
|
|
|
71
|
+
function compactToolSchema(schema) {
|
|
72
|
+
const fn = schema?.function || {};
|
|
73
|
+
const params = fn.parameters || {};
|
|
74
|
+
const properties = params.properties || {};
|
|
75
|
+
return {
|
|
76
|
+
name: fn.name,
|
|
77
|
+
description: fn.description,
|
|
78
|
+
required: params.required || [],
|
|
79
|
+
properties: Object.fromEntries(
|
|
80
|
+
Object.entries(properties).map(([name, spec]) => [
|
|
81
|
+
name,
|
|
82
|
+
{
|
|
83
|
+
type: spec?.type || "string",
|
|
84
|
+
enum: spec?.enum,
|
|
85
|
+
description: spec?.description,
|
|
86
|
+
},
|
|
87
|
+
])
|
|
88
|
+
),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function pseudoToolSystem(system) {
|
|
93
|
+
const catalog = TOOL_SCHEMAS.map(compactToolSchema);
|
|
94
|
+
return [
|
|
95
|
+
system,
|
|
96
|
+
"# Structured tool fallback",
|
|
97
|
+
"The engine rejected native structured tools. You can still call tools by emitting plain JSON.",
|
|
98
|
+
"When you need a tool, respond ONLY with one JSON object per line:",
|
|
99
|
+
"{\"name\":\"tool_name\",\"arguments\":{\"arg\":\"value\"}}",
|
|
100
|
+
"After tool results arrive, continue the task or give the final answer normally.",
|
|
101
|
+
"Available tools:",
|
|
102
|
+
JSON.stringify(catalog),
|
|
103
|
+
].join("\n\n");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function shouldRetryWithPseudoTools(modelId, error, alreadyPseudo) {
|
|
107
|
+
if (alreadyPseudo) return false;
|
|
108
|
+
const message = String(error?.message || "");
|
|
109
|
+
return /^ollama:/i.test(String(modelId || "")) && /ollama\s+500/i.test(message);
|
|
110
|
+
}
|
|
111
|
+
|
|
71
112
|
function isShortConfirmation(text) {
|
|
72
113
|
return /^(yes|y|si|si dale|dale|ok|okay|confirm|confirmed|go|proceed|do it)\b/i
|
|
73
114
|
.test(String(text || "").trim());
|
|
@@ -115,6 +156,7 @@ export async function runSuperAgent({
|
|
|
115
156
|
previousMessages = [],
|
|
116
157
|
overrideModel = null,
|
|
117
158
|
onEvent = null,
|
|
159
|
+
signal,
|
|
118
160
|
}) {
|
|
119
161
|
if (!isSuperAgentEnabled(globalConfig)) {
|
|
120
162
|
throw new Error("super-agent not enabled (set super_agent.enabled and .model in ~/.apx/config.json)");
|
|
@@ -187,6 +229,7 @@ export async function runSuperAgent({
|
|
|
187
229
|
const trace = [];
|
|
188
230
|
let totalUsage = { input_tokens: 0, output_tokens: 0 };
|
|
189
231
|
let lastText = "";
|
|
232
|
+
let usePseudoTools = false;
|
|
190
233
|
|
|
191
234
|
for (let iter = 0; iter < MAX_TOOL_ITERS; iter++) {
|
|
192
235
|
await emitProgress(onEvent, { type: "model_start", iteration: iter + 1 });
|
|
@@ -195,15 +238,38 @@ export async function runSuperAgent({
|
|
|
195
238
|
// acting on an action request. On later iterations (after tool results
|
|
196
239
|
// have been fed back) tool_choice is "auto" so the model can produce its
|
|
197
240
|
// final text summary.
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
241
|
+
let result;
|
|
242
|
+
try {
|
|
243
|
+
result = await callEngine({
|
|
244
|
+
modelId: activeModel,
|
|
245
|
+
system: usePseudoTools ? pseudoToolSystem(system) : system,
|
|
246
|
+
messages: conversation,
|
|
247
|
+
config: globalConfig,
|
|
248
|
+
tools: usePseudoTools ? null : TOOL_SCHEMAS,
|
|
249
|
+
toolChoice: usePseudoTools ? null : (iter === 0 ? "required" : "auto"),
|
|
250
|
+
maxTokens: 1024,
|
|
251
|
+
signal,
|
|
252
|
+
});
|
|
253
|
+
} catch (e) {
|
|
254
|
+
if (usePseudoTools && /^ollama:/i.test(String(activeModel || "")) && /ollama\s+500/i.test(String(e?.message || "")) && trace.length > 0) {
|
|
255
|
+
await emitProgress(onEvent, { type: "model_retry", reason: "ollama_final_response_500", iteration: iter + 1 });
|
|
256
|
+
lastText = fallbackFinalText(trace, e);
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
if (!shouldRetryWithPseudoTools(activeModel, e, usePseudoTools)) throw e;
|
|
260
|
+
usePseudoTools = true;
|
|
261
|
+
await emitProgress(onEvent, { type: "model_retry", reason: "ollama_structured_tools_500", iteration: iter + 1 });
|
|
262
|
+
result = await callEngine({
|
|
263
|
+
modelId: activeModel,
|
|
264
|
+
system: pseudoToolSystem(system),
|
|
265
|
+
messages: conversation,
|
|
266
|
+
config: globalConfig,
|
|
267
|
+
tools: null,
|
|
268
|
+
toolChoice: null,
|
|
269
|
+
maxTokens: 1024,
|
|
270
|
+
signal,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
207
273
|
totalUsage.input_tokens += result.usage?.input_tokens || 0;
|
|
208
274
|
totalUsage.output_tokens += result.usage?.output_tokens || 0;
|
|
209
275
|
lastText = result.text || "";
|
|
@@ -317,3 +383,25 @@ function summarizeForTrace(r) {
|
|
|
317
383
|
if (s.length <= 400) return r;
|
|
318
384
|
return s.slice(0, 380) + "…(truncated)";
|
|
319
385
|
}
|
|
386
|
+
|
|
387
|
+
function fallbackFinalText(trace, error) {
|
|
388
|
+
const lines = [
|
|
389
|
+
"Tool execution completed, but the model failed while composing the final answer.",
|
|
390
|
+
`Engine error: ${String(error?.message || error).slice(0, 220)}`,
|
|
391
|
+
"Trace:",
|
|
392
|
+
];
|
|
393
|
+
for (const item of trace.slice(-8)) {
|
|
394
|
+
lines.push(`- ${item.tool}: ${previewTraceResult(item.result)}`);
|
|
395
|
+
}
|
|
396
|
+
return lines.join("\n");
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function previewTraceResult(result) {
|
|
400
|
+
if (result === null || result === undefined) return "ok";
|
|
401
|
+
if (typeof result === "string") return result.slice(0, 180);
|
|
402
|
+
if (result.error) return `error: ${String(result.error).slice(0, 180)}`;
|
|
403
|
+
if (result.path) return String(result.path).slice(0, 180);
|
|
404
|
+
if (result.content) return String(result.content).slice(0, 180);
|
|
405
|
+
if (result.results) return JSON.stringify(result.results).slice(0, 180);
|
|
406
|
+
return JSON.stringify(result).slice(0, 180);
|
|
407
|
+
}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
# Agent Project Context
|
|
3
|
-
|
|
4
|
-
This project uses APC. APC stores portable project context in `.apc/` and `AGENTS.md`.
|
|
5
|
-
|
|
6
|
-
APC does not store raw runtime sessions. Sessions, conversations, messages, caches, provider
|
|
7
|
-
threads, and private runtime memory stay in the IDE, CLI, daemon, or user-level store that created
|
|
8
|
-
them.
|
|
9
|
-
|
|
10
|
-
## FIRST: check for pending migration
|
|
11
|
-
|
|
12
|
-
Before doing anything else, check if `.apc/migrate.md` exists:
|
|
13
|
-
|
|
14
|
-
```bash
|
|
15
|
-
cat .apc/migrate.md 2>/dev/null
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
If it exists, offer to migrate before answering anything else. Read detected files, separate durable
|
|
19
|
-
project context from runtime/private state, and migrate only what belongs in APC.
|
|
20
|
-
|
|
21
|
-
If the user says no or later, delete `.apc/migrate.md` so the offer is not repeated.
|
|
22
|
-
|
|
23
|
-
## Migration rule: think, do not copy
|
|
24
|
-
|
|
25
|
-
Classify content:
|
|
26
|
-
|
|
27
|
-
| Content | Action |
|
|
28
|
-
|---|---|
|
|
29
|
-
| Agent definitions: role, model, skills, description | Put in `.apc/agents/<slug>.md` and/or `AGENTS.md` |
|
|
30
|
-
| Shared project rules, stack notes, commands, testing policy | Keep in `AGENTS.md` |
|
|
31
|
-
| Reusable instruction blocks | Move to `.apc/skills/<name>.md` |
|
|
32
|
-
| Durable safe facts useful to all contributors | Add to `.apc/agents/<slug>/memory.md` only after curation |
|
|
33
|
-
| MCP expectations without secrets | Add to `.apc/mcps.json` |
|
|
34
|
-
| Raw sessions, transcripts, conversations, messages, tool logs | Do not move into `.apc/`; leave with source runtime |
|
|
35
|
-
| Secrets, tokens, credentials, private headers | Do not store in repository |
|
|
36
|
-
| IDE UI settings or personal aliases | Leave in IDE/user config |
|
|
37
|
-
| Instructions to store sessions under `.apc/` | Drop as obsolete |
|
|
38
|
-
|
|
39
|
-
## APC structure
|
|
40
|
-
|
|
41
|
-
```text
|
|
42
|
-
AGENTS.md ← root project contract
|
|
43
|
-
.apc/
|
|
44
|
-
project.json ← project metadata
|
|
45
|
-
.gitignore ← safety guard
|
|
46
|
-
agents/<slug>.md ← agent definition
|
|
47
|
-
agents/<slug>/memory.md ← optional curated project memory
|
|
48
|
-
skills/<name>.md ← reusable project instructions
|
|
49
|
-
mcps.json ← MCP hints without secrets
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
Do not store:
|
|
53
|
-
|
|
54
|
-
```text
|
|
55
|
-
.apc/agents/<slug>/sessions/
|
|
56
|
-
.apc/sessions/
|
|
57
|
-
.apc/conversations/
|
|
58
|
-
.apc/messages/
|
|
59
|
-
.apc/project.db
|
|
60
|
-
.apc/cache/
|
|
61
|
-
.apc/tmp/
|
|
62
|
-
.apc/private/
|
|
63
|
-
.apc/secrets/
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
## Operating rules
|
|
67
|
-
|
|
68
|
-
1. Read `AGENTS.md` and relevant `.apc/` files before assuming project context.
|
|
69
|
-
2. Read agent definitions from `.apc/agents/<slug>.md` when present.
|
|
70
|
-
3. Read curated project memory from `.apc/agents/<slug>/memory.md` when present.
|
|
71
|
-
4. Write only durable, safe, curated facts to APC memory.
|
|
72
|
-
5. Never write raw sessions, transcripts, messages, conversations, or tool logs into `.apc/`.
|
|
73
|
-
6. Keep secrets out of APC and out of git.
|
|
74
|
-
7. Treat `.apc/mcps.json` as MCP configuration hints, not as an MCP implementation.
|
|
75
|
-
|
|
76
|
-
## Sessions
|
|
77
|
-
|
|
78
|
-
Sessions belong to the runtime that created them.
|
|
79
|
-
|
|
80
|
-
Examples:
|
|
81
|
-
|
|
82
|
-
```text
|
|
83
|
-
Codex runtime storage
|
|
84
|
-
Claude Code runtime storage
|
|
85
|
-
OpenCode runtime storage
|
|
86
|
-
~/.apx/projects/<project-id>/agents/<slug>/sessions/
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
At task end, provide the user a concise result. If project memory should be updated, write a short
|
|
90
|
-
sanitized fact to `.apc/agents/<slug>/memory.md` only when useful and safe.
|
|
91
|
-
|
|
92
|
-
## APX
|
|
93
|
-
|
|
94
|
-
APX can provide a local daemon, MCP management, Telegram bridge, routines, and runtime dispatch
|
|
95
|
-
across Codex, Claude Code, OpenCode, Aider, Cursor Agent, Gemini CLI, Qwen Code, or direct LLM
|
|
96
|
-
engines. Those are APX runtime features, not APC portable-core requirements.
|
|
97
|
-
|
|
98
|
-
The APX super-agent uses `~/.apx/projects/default` for system-level work when no project is named.
|
|
99
|
-
APX routines can run heartbeat, shell, Telegram, project agent, or super-agent tasks on a schedule.
|
|
100
|
-
|
|
101
|
-
APX runtime state belongs outside the repository:
|
|
102
|
-
|
|
103
|
-
```text
|
|
104
|
-
~/.apx/projects/<project-id>/
|
|
105
|
-
```
|
package/src/core/apx-skill.md
DELETED
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
# APX — Agent Project Context Runtime
|
|
2
|
-
|
|
3
|
-
The daemon runs on `127.0.0.1:7430` and auto-starts on first `apx` call.
|
|
4
|
-
|
|
5
|
-
APX reads APC project context from `.apc/`, but APX runtime state belongs outside the repository
|
|
6
|
-
under `~/.apx/projects/<project-id>/`.
|
|
7
|
-
|
|
8
|
-
The APX super-agent has an always-available default workspace at `~/.apx/projects/default`.
|
|
9
|
-
When no project is named, system-level work belongs there.
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## Coordinate with other agents
|
|
14
|
-
|
|
15
|
-
**First: can you spawn a subagent natively in this IDE?**
|
|
16
|
-
|
|
17
|
-
If yes — do that. No APX needed. Claude Code, Cursor, and other IDEs can spawn subagents directly using your current context.
|
|
18
|
-
|
|
19
|
-
Use `apx run` only when:
|
|
20
|
-
- The user explicitly asks to run the agent in a specific external runtime ("run this in Codex", "run the QA agent outside this session")
|
|
21
|
-
- You need to run an agent in a runtime different from the one you're in
|
|
22
|
-
- You're orchestrating from outside any IDE (e.g. a script, Telegram bot, CI)
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
# Run agent in an external runtime — full isolated session
|
|
26
|
-
apx run <slug> --runtime claude-code "<prompt>"
|
|
27
|
-
apx run <slug> --runtime codex "<prompt>"
|
|
28
|
-
apx run <slug> --runtime opencode "<prompt>"
|
|
29
|
-
apx run <slug> --runtime aider "<prompt>"
|
|
30
|
-
apx run <slug> --runtime cursor-agent "<prompt>"
|
|
31
|
-
apx run <slug> --runtime gemini-cli "<prompt>"
|
|
32
|
-
apx run <slug> --runtime qwen-code "<prompt>"
|
|
33
|
-
|
|
34
|
-
# Example: run the qa agent in codex with a specific task
|
|
35
|
-
apx run qa --runtime codex "run the full test suite and report failures"
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
The output is the agent's full stdout. If it printed `APC_RESULT: <value>`, that value is captured as structured output.
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
# Quick one-shot LLM call (no external CLI needed, uses ~/.apx/config.json engine key)
|
|
42
|
-
apx exec <slug> "<prompt>"
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
## Command accuracy
|
|
46
|
-
|
|
47
|
-
Do not invent APX subcommands. Before telling another runtime to call APX, verify the exact CLI
|
|
48
|
-
form with `apx --help` or `apx <command> --help`.
|
|
49
|
-
|
|
50
|
-
Known Telegram form:
|
|
51
|
-
|
|
52
|
-
```bash
|
|
53
|
-
apx telegram status
|
|
54
|
-
apx telegram send "message"
|
|
55
|
-
apx telegram send "message" --chat 123456
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
Do not use guessed aliases such as `apx send-telegram` or `apx telegram "message"` unless current
|
|
59
|
-
`apx --help` shows that exact form.
|
|
60
|
-
|
|
61
|
-
## MCP tools
|
|
62
|
-
|
|
63
|
-
MCPs declared in `.apc/mcps.json` are proxied through the APX daemon. Use `apx mcp` only for MCPs registered there — not for MCPs that are already running locally in your IDE session.
|
|
64
|
-
|
|
65
|
-
```bash
|
|
66
|
-
apx mcp list # MCPs registered in .apc/mcps.json
|
|
67
|
-
apx mcp tools <server> # tools a server exposes
|
|
68
|
-
apx mcp run <server> <tool> '<json>' # call a tool
|
|
69
|
-
|
|
70
|
-
# Example:
|
|
71
|
-
apx mcp tools filesystem
|
|
72
|
-
apx mcp run filesystem read_file '{"path": "README.md"}'
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
## Memory
|
|
76
|
-
|
|
77
|
-
Write memory only for durable, safe project facts. Do not store raw transcripts or secrets.
|
|
78
|
-
|
|
79
|
-
```bash
|
|
80
|
-
apx memory <slug> # read agent's memory.md
|
|
81
|
-
apx memory <slug> --append "<fact>" # append a durable note
|
|
82
|
-
apx memory <slug> --replace < file.md # replace entire memory from stdin
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
## Sessions
|
|
86
|
-
|
|
87
|
-
Sessions are APX runtime state. They do not belong in `.apc/`.
|
|
88
|
-
|
|
89
|
-
```bash
|
|
90
|
-
apx session new <slug> --title "What you did" # create APX local session file
|
|
91
|
-
apx session list <slug> # list sessions
|
|
92
|
-
apx session check # exits 1 if session already active
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
## Observe activity
|
|
96
|
-
|
|
97
|
-
```bash
|
|
98
|
-
apx messages tail # last 50 messages, all channels
|
|
99
|
-
apx messages chat --channel telegram -n 20 # chat view with user/agent/system type
|
|
100
|
-
apx messages tail --channel runtime # only agent invocations
|
|
101
|
-
apx messages tail --agent <slug> -n 20
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
Message rows expose `type` (`user`, `agent`, `tool`, `system`) and `actor_id`; use `messages chat`
|
|
105
|
-
when you need a readable transcript.
|
|
106
|
-
|
|
107
|
-
## APX tool permissions
|
|
108
|
-
|
|
109
|
-
```bash
|
|
110
|
-
apx permission show
|
|
111
|
-
apx permission set automatico # total | automatico | permiso
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
`automatico` runs read/list/safe shell checks directly and asks before destructive shell, MCP,
|
|
115
|
-
runtime, outbound, config, or filesystem mutation actions.
|
|
116
|
-
|
|
117
|
-
## Routines
|
|
118
|
-
|
|
119
|
-
```bash
|
|
120
|
-
apx routine list
|
|
121
|
-
apx routine get <name>
|
|
122
|
-
apx routine history <name>
|
|
123
|
-
apx routine add clima --kind super_agent --schedule every:5m \
|
|
124
|
-
--permission-mode total \
|
|
125
|
-
--spec '{"prompt":"Check weather and send Telegram update."}'
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
Routine kinds: `heartbeat`, `exec_agent`, `super_agent`, `telegram`, `shell`.
|
|
129
|
-
|
|
130
|
-
## APC_RESULT
|
|
131
|
-
|
|
132
|
-
Print on the last meaningful line of your output so the invoker captures it:
|
|
133
|
-
```
|
|
134
|
-
APC_RESULT: <one-line summary or value>
|
|
135
|
-
```
|