@agentprojectcontext/apx 1.31.0 → 1.31.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/package.json +1 -1
- package/skills/apx-agency-agents/SKILL.md +1 -1
- package/skills/apx-agent/SKILL.md +6 -6
- package/skills/apx-project/SKILL.md +1 -2
- package/src/core/agent/self-memory.js +1 -1
- package/src/core/agent-memory.js +64 -0
- package/src/core/agent-system.js +3 -2
- package/src/core/scaffold.js +43 -18
- package/src/core/tools/registry.js +7 -7
- package/src/host/daemon/api/agents.js +19 -21
- package/src/host/daemon/api/sessions-search.js +1 -1
- package/src/host/daemon/api/shared.js +5 -8
- package/src/host/daemon/super-agent-tools/tools/import-agent.js +2 -0
- package/src/host/daemon/super-agent-tools/tools/read-agent-memory.js +5 -4
- package/src/interfaces/cli/commands/agent.js +4 -1
- package/src/interfaces/cli/commands/memory.js +9 -10
- package/src/interfaces/web/dist/assets/{index-CfWyjPBa.js → index-BV615I9p.js} +5 -5
- package/src/interfaces/web/dist/assets/{index-CfWyjPBa.js.map → index-BV615I9p.js.map} +1 -1
- package/src/interfaces/web/dist/index.html +1 -1
- package/src/interfaces/web/src/i18n/en.ts +6 -6
- package/src/interfaces/web/src/i18n/es.ts +6 -6
- package/src/interfaces/web/src/screens/project/AgentDetailScreen.tsx +1 -1
package/package.json
CHANGED
|
@@ -59,7 +59,7 @@ apx agent vault list --all
|
|
|
59
59
|
# Create a new template (writes ~/.apx/agents/<slug>.md)
|
|
60
60
|
apx agent vault add reviewer \
|
|
61
61
|
--role "Code reviewer" \
|
|
62
|
-
--model
|
|
62
|
+
--model ollama:llama3.2:3b \
|
|
63
63
|
--language es \
|
|
64
64
|
--skills code-review,git \
|
|
65
65
|
--description "Reviews PRs and pushes back on hand-wavy diffs."
|
|
@@ -5,7 +5,7 @@ description: How to create, configure, and use project agents in APX. Load when
|
|
|
5
5
|
|
|
6
6
|
# apx-agent
|
|
7
7
|
|
|
8
|
-
A project agent is a named persona inside an APC project. Canonical definition: `.apc/agents/<slug>.md` (flat file). `AGENTS.md` is auto-regenerated for discovery. Per-agent runtime data
|
|
8
|
+
A project agent is a named persona inside an APC project. Canonical definition: `.apc/agents/<slug>.md` (flat file). `AGENTS.md` is auto-regenerated for discovery. Per-agent runtime data (memory, conversations, sessions) lives under `~/.apx/projects/<apx_id>/agents/<slug>/` and is never committed. APX still reads legacy `.apc/agents/<slug>/memory.md` as a migration fallback only.
|
|
9
9
|
|
|
10
10
|
## Concrete CLI calls
|
|
11
11
|
|
|
@@ -13,10 +13,10 @@ A project agent is a named persona inside an APC project. Canonical definition:
|
|
|
13
13
|
# List agents in a project
|
|
14
14
|
apx agent list --project iacrmar
|
|
15
15
|
|
|
16
|
-
# Create a new agent (writes .apc/agents/<slug>.md
|
|
16
|
+
# Create a new agent (writes .apc/agents/<slug>.md, creates runtime dir, regenerates AGENTS.md)
|
|
17
17
|
apx agent add reviewer \
|
|
18
18
|
--role "Code reviewer" \
|
|
19
|
-
--model
|
|
19
|
+
--model ollama:llama3.2:3b \
|
|
20
20
|
--language es \
|
|
21
21
|
--description "Reviews PRs and pushes back on hand-wavy diffs." \
|
|
22
22
|
--tools read,write,run \
|
|
@@ -45,7 +45,7 @@ apx memory <slug> --project iacrmar --replace < file.md # full replace fro
|
|
|
45
45
|
2. Description (from AGENTS.md).
|
|
46
46
|
3. Role + Language fields.
|
|
47
47
|
4. Invocation context: `engine | telegram | routine | runtime` — the channel calling the agent.
|
|
48
|
-
5. Memory:
|
|
48
|
+
5. Memory: `~/.apx/projects/<apx_id>/agents/<slug>/memory.md` if it exists, with legacy `.apc/agents/<slug>/memory.md` as a migration fallback.
|
|
49
49
|
6. Skills declared in the agent's `Skills:` field, each loaded from `.apc/skills/<slug>.md` or the bundled set.
|
|
50
50
|
7. The `apx` meta-skill (so the agent knows how to operate APX).
|
|
51
51
|
8. ACTION_DISCIPLINE_RULES (fixed footer — anti-ghost, anti-disclaimer, action-first).
|
|
@@ -54,13 +54,13 @@ That's the prompt the engine sees on every `apx exec <agent>` or `apx chat <agen
|
|
|
54
54
|
|
|
55
55
|
## Models per agent
|
|
56
56
|
|
|
57
|
-
Each agent can set `Model:` in its `AGENT.md` to override the global super-agent model.
|
|
57
|
+
Each agent can set `Model:` in its `AGENT.md` to override the global super-agent model. Leave it empty when the agent should follow the project/global default.
|
|
58
58
|
|
|
59
59
|
```markdown
|
|
60
60
|
# .apc/agents/reviewer.md
|
|
61
61
|
---
|
|
62
62
|
Role: Code reviewer
|
|
63
|
-
Model:
|
|
63
|
+
Model: ollama:llama3.2:3b ← this agent uses this model, independent of super_agent.model
|
|
64
64
|
Language: es
|
|
65
65
|
---
|
|
66
66
|
```
|
|
@@ -57,7 +57,6 @@ The CLI calls `resolveProjectId()` which does fuzzy id-or-name-or-path matching.
|
|
|
57
57
|
└── .apc/
|
|
58
58
|
├── project.json ← { apxId, name, ... }
|
|
59
59
|
├── agents/<slug>.md
|
|
60
|
-
├── agents/<slug>/memory.md
|
|
61
60
|
├── skills/<slug>.md or <slug>/SKILL.md
|
|
62
61
|
├── mcps.json ← shared MCPs (committed)
|
|
63
62
|
├── commands/ ← custom slash-commands
|
|
@@ -65,7 +64,7 @@ The CLI calls `resolveProjectId()` which does fuzzy id-or-name-or-path matching.
|
|
|
65
64
|
|
|
66
65
|
~/.apx/projects/<apxId>/ ← runtime state (never committed)
|
|
67
66
|
├── messages/YYYY-MM-DD.jsonl
|
|
68
|
-
├── agents/<slug>/{sessions/, conversations/}
|
|
67
|
+
├── agents/<slug>/{memory.md, sessions/, conversations/}
|
|
69
68
|
├── routines.json
|
|
70
69
|
├── tasks/YYYY-MM.jsonl
|
|
71
70
|
├── artifacts/
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
//
|
|
3
3
|
// This is distinct from:
|
|
4
4
|
// - identity.json → who Roby is (name, personality, owner)
|
|
5
|
-
// - project agents'
|
|
5
|
+
// - project agents' ~/.apx/projects/<apx_id>/agents/<slug>/memory.md → per-agent, per-project
|
|
6
6
|
// - sessions → raw transcripts of past work (search_sessions)
|
|
7
7
|
//
|
|
8
8
|
// It is a single free-form markdown file at ~/.apx/memory.md that Roby keeps
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { projectStorageRoot } from "./config.js";
|
|
4
|
+
import { getOrCreateApxId } from "./scaffold.js";
|
|
5
|
+
|
|
6
|
+
const EMPTY_MEMORY = (slug) =>
|
|
7
|
+
`# Memory — ${slug}\n\n` +
|
|
8
|
+
`## Identity\n- \n\n` +
|
|
9
|
+
`## Long-term facts\n- \n\n` +
|
|
10
|
+
`## Recent context\n- \n`;
|
|
11
|
+
|
|
12
|
+
export function agentRuntimeDir(projectOrRoot, slug) {
|
|
13
|
+
const storagePath =
|
|
14
|
+
typeof projectOrRoot === "object" && projectOrRoot?.storagePath
|
|
15
|
+
? projectOrRoot.storagePath
|
|
16
|
+
: null;
|
|
17
|
+
const root =
|
|
18
|
+
typeof projectOrRoot === "string"
|
|
19
|
+
? projectOrRoot
|
|
20
|
+
: projectOrRoot?.path;
|
|
21
|
+
const base = storagePath || projectStorageRoot(getOrCreateApxId(root));
|
|
22
|
+
return path.join(base, "agents", slug);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function agentMemoryPath(projectOrRoot, slug) {
|
|
26
|
+
return path.join(agentRuntimeDir(projectOrRoot, slug), "memory.md");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function legacyAgentMemoryPath(projectRoot, slug) {
|
|
30
|
+
return path.join(projectRoot, ".apc", "agents", slug, "memory.md");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function ensureAgentRuntimeDir(projectOrRoot, slug, { createMemory = false } = {}) {
|
|
34
|
+
const dir = agentRuntimeDir(projectOrRoot, slug);
|
|
35
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
36
|
+
if (createMemory) {
|
|
37
|
+
const memory = path.join(dir, "memory.md");
|
|
38
|
+
if (!fs.existsSync(memory)) fs.writeFileSync(memory, EMPTY_MEMORY(slug));
|
|
39
|
+
}
|
|
40
|
+
return dir;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function readAgentMemory(projectOrRoot, slug) {
|
|
44
|
+
const primary = agentMemoryPath(projectOrRoot, slug);
|
|
45
|
+
if (fs.existsSync(primary)) return fs.readFileSync(primary, "utf8");
|
|
46
|
+
|
|
47
|
+
const root =
|
|
48
|
+
typeof projectOrRoot === "string"
|
|
49
|
+
? projectOrRoot
|
|
50
|
+
: projectOrRoot?.path;
|
|
51
|
+
if (root) {
|
|
52
|
+
const legacy = legacyAgentMemoryPath(root, slug);
|
|
53
|
+
if (fs.existsSync(legacy)) return fs.readFileSync(legacy, "utf8");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return "";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function writeAgentMemory(projectOrRoot, slug, body) {
|
|
60
|
+
ensureAgentRuntimeDir(projectOrRoot, slug);
|
|
61
|
+
const memory = agentMemoryPath(projectOrRoot, slug);
|
|
62
|
+
fs.writeFileSync(memory, body);
|
|
63
|
+
return memory;
|
|
64
|
+
}
|
package/src/core/agent-system.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { readAgentMemory } from "./agent-memory.js";
|
|
3
4
|
|
|
4
5
|
// ---------------------------------------------------------------------------
|
|
5
6
|
// Anti-ghost-response rules injected into every agent system prompt.
|
|
@@ -62,8 +63,8 @@ export function buildAgentSystem(project, agent, {
|
|
|
62
63
|
|
|
63
64
|
parts.push(buildInvocationContext({ invocation, runtime, channel, caller, routine }));
|
|
64
65
|
|
|
65
|
-
const
|
|
66
|
-
if (
|
|
66
|
+
const memory = readAgentMemory(project, agent.slug);
|
|
67
|
+
if (memory) parts.push("## Memory\n" + memory);
|
|
67
68
|
|
|
68
69
|
const apxSkill = path.join(project.path, ".apc", "skills", "apx.md");
|
|
69
70
|
if (fs.existsSync(apxSkill)) parts.push("## APX\n" + fs.readFileSync(apxSkill, "utf8"));
|
package/src/core/scaffold.js
CHANGED
|
@@ -325,24 +325,60 @@ const AGENTS_MD_TEMPLATE = `# AGENTS.md
|
|
|
325
325
|
<!-- Hard constraints: what agents must always / never do here. -->
|
|
326
326
|
`;
|
|
327
327
|
|
|
328
|
-
const APC_GITIGNORE = `# APC
|
|
329
|
-
#
|
|
330
|
-
|
|
331
|
-
agents
|
|
328
|
+
const APC_GITIGNORE = `# APC repository-safe context only.
|
|
329
|
+
# Runtime state belongs in ~/.apx/projects/<id>/, not in .apc/.
|
|
330
|
+
|
|
331
|
+
# Legacy per-agent runtime dirs (agent definitions are flat: agents/<slug>.md)
|
|
332
|
+
agents/*/
|
|
333
|
+
|
|
334
|
+
# Runtime sessions / conversations / messages
|
|
332
335
|
sessions/
|
|
333
336
|
conversations/
|
|
334
337
|
messages/
|
|
335
338
|
chats/
|
|
339
|
+
threads/
|
|
340
|
+
transcripts/
|
|
341
|
+
runs/
|
|
342
|
+
|
|
343
|
+
# Runtime memory, indexes, databases, and caches
|
|
344
|
+
memory.local.md
|
|
345
|
+
auto-memory.md
|
|
336
346
|
cache/
|
|
337
347
|
tmp/
|
|
338
348
|
private/
|
|
339
349
|
secrets/
|
|
350
|
+
*.db
|
|
351
|
+
*.db-*
|
|
352
|
+
*.sqlite
|
|
353
|
+
*.sqlite3
|
|
354
|
+
project.db
|
|
355
|
+
memory.db
|
|
356
|
+
memory-index.jsonl
|
|
357
|
+
memory-cursor.json
|
|
358
|
+
|
|
359
|
+
# Local config and secrets
|
|
360
|
+
.env
|
|
340
361
|
*.local.json
|
|
341
362
|
*.secret.json
|
|
342
363
|
*.env
|
|
343
364
|
*.env.*
|
|
344
|
-
|
|
365
|
+
*.key
|
|
366
|
+
*.pem
|
|
367
|
+
*.p12
|
|
368
|
+
*.crt
|
|
369
|
+
credentials.json
|
|
370
|
+
service-account*.json
|
|
371
|
+
token*.json
|
|
372
|
+
mcps.local.json
|
|
373
|
+
config.local.json
|
|
374
|
+
|
|
375
|
+
# Scratch planning state
|
|
376
|
+
plans/scratch/
|
|
377
|
+
plans/*.local.md
|
|
378
|
+
|
|
379
|
+
# Migration marker and OS noise
|
|
345
380
|
migrate.md
|
|
381
|
+
.DS_Store
|
|
346
382
|
`;
|
|
347
383
|
|
|
348
384
|
function nowIso() {
|
|
@@ -448,19 +484,8 @@ export function initApf(directory, { name } = {}) {
|
|
|
448
484
|
}
|
|
449
485
|
|
|
450
486
|
export function ensureAgentDir(root, slug) {
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
const memory = path.join(dir, "memory.md");
|
|
454
|
-
if (!fs.existsSync(memory)) {
|
|
455
|
-
fs.writeFileSync(
|
|
456
|
-
memory,
|
|
457
|
-
`# Memory — ${slug}\n\n` +
|
|
458
|
-
`## Identity\n- \n\n` +
|
|
459
|
-
`## Long-term facts\n- \n\n` +
|
|
460
|
-
`## Recent context\n- \n`
|
|
461
|
-
);
|
|
462
|
-
}
|
|
463
|
-
return dir;
|
|
487
|
+
fs.mkdirSync(path.join(root, ".apc", "agents"), { recursive: true });
|
|
488
|
+
return path.join(root, ".apc", "agents");
|
|
464
489
|
}
|
|
465
490
|
|
|
466
491
|
// Write .apc/agents/<slug>.md — the canonical agent definition file.
|
|
@@ -619,6 +619,8 @@ function makeInlineHandlers({ projects, registries }) {
|
|
|
619
619
|
memory_list: async (body) => {
|
|
620
620
|
const { default: fs } = await import("node:fs");
|
|
621
621
|
const { default: path } = await import("node:path");
|
|
622
|
+
const { readAgents } = await import("../parser.js");
|
|
623
|
+
const { agentMemoryPath } = await import("../agent-memory.js");
|
|
622
624
|
// Find the project
|
|
623
625
|
const all = projects.list();
|
|
624
626
|
let p = null;
|
|
@@ -629,15 +631,13 @@ function makeInlineHandlers({ projects, registries }) {
|
|
|
629
631
|
}
|
|
630
632
|
if (!p) p = projects.get(all.filter((x) => x.id !== 0)[0]?.id) || projects.get(0);
|
|
631
633
|
if (!p) throw new Error("no project registered");
|
|
632
|
-
const
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
}).map((slug) => {
|
|
637
|
-
const memPath = path.join(agentsDir, slug, "memory.md");
|
|
634
|
+
const result = readAgents(p.path).map((agent) => {
|
|
635
|
+
const slug = agent.slug;
|
|
636
|
+
const memPath = agentMemoryPath(p, slug);
|
|
637
|
+
if (!fs.existsSync(memPath)) return null;
|
|
638
638
|
const stat = fs.statSync(memPath);
|
|
639
639
|
return { agent: slug, path: memPath, size: stat.size, mtime: stat.mtime };
|
|
640
|
-
});
|
|
640
|
+
}).filter(Boolean);
|
|
641
641
|
return { project: p.path, agents_with_memory: result };
|
|
642
642
|
},
|
|
643
643
|
};
|
|
@@ -14,6 +14,13 @@ import {
|
|
|
14
14
|
restoreVaultAgent,
|
|
15
15
|
ensureAgentDir,
|
|
16
16
|
} from "../../../core/scaffold.js";
|
|
17
|
+
import {
|
|
18
|
+
ensureAgentRuntimeDir,
|
|
19
|
+
readAgentMemory,
|
|
20
|
+
writeAgentMemory,
|
|
21
|
+
agentMemoryPath,
|
|
22
|
+
legacyAgentMemoryPath,
|
|
23
|
+
} from "../../../core/agent-memory.js";
|
|
17
24
|
import { agentToResponse } from "./shared.js";
|
|
18
25
|
|
|
19
26
|
// Lowercase the patch keys we accept on the vault and turn skills/tools into
|
|
@@ -114,6 +121,7 @@ export function register(app, { projects, project }) {
|
|
|
114
121
|
try {
|
|
115
122
|
writeAgentFile(p.path, slug, vault.fields || {}, vault.body || "");
|
|
116
123
|
ensureAgentDir(p.path, slug);
|
|
124
|
+
ensureAgentRuntimeDir(p, slug);
|
|
117
125
|
projects.rebuild(p.id);
|
|
118
126
|
res.status(201).json(agentToResponse(readAgents(p.path).find((a) => a.slug === slug)));
|
|
119
127
|
} catch (e) {
|
|
@@ -133,10 +141,7 @@ export function register(app, { projects, project }) {
|
|
|
133
141
|
const agents = readAgents(p.path);
|
|
134
142
|
const a = agents.find((x) => x.slug === req.params.slug);
|
|
135
143
|
if (!a) return res.status(404).json({ error: "agent not found" });
|
|
136
|
-
const
|
|
137
|
-
const memory = fs.existsSync(memPath)
|
|
138
|
-
? fs.readFileSync(memPath, "utf8")
|
|
139
|
-
: "";
|
|
144
|
+
const memory = readAgentMemory(p, a.slug);
|
|
140
145
|
res.json({ ...agentToResponse(a), memory, system: a.body || "" });
|
|
141
146
|
});
|
|
142
147
|
|
|
@@ -163,6 +168,7 @@ export function register(app, { projects, project }) {
|
|
|
163
168
|
Parent: parent || null,
|
|
164
169
|
});
|
|
165
170
|
ensureAgentDir(p.path, slug);
|
|
171
|
+
ensureAgentRuntimeDir(p, slug);
|
|
166
172
|
projects.rebuild(p.id);
|
|
167
173
|
const created = readAgents(p.path).find((a) => a.slug === slug);
|
|
168
174
|
res.status(201).json(agentToResponse(created));
|
|
@@ -203,6 +209,7 @@ export function register(app, { projects, project }) {
|
|
|
203
209
|
try {
|
|
204
210
|
writeAgentFile(p.path, slug, fields, body);
|
|
205
211
|
ensureAgentDir(p.path, slug);
|
|
212
|
+
ensureAgentRuntimeDir(p, slug);
|
|
206
213
|
projects.rebuild(p.id);
|
|
207
214
|
const updated = readAgents(p.path).find((a) => a.slug === slug);
|
|
208
215
|
res.json(agentToResponse(updated));
|
|
@@ -211,18 +218,20 @@ export function register(app, { projects, project }) {
|
|
|
211
218
|
}
|
|
212
219
|
});
|
|
213
220
|
|
|
214
|
-
// Delete an agent: removes .apc/agents/<slug>.md and
|
|
221
|
+
// Delete an agent: removes .apc/agents/<slug>.md and runtime data dir.
|
|
215
222
|
app.delete("/projects/:pid/agents/:slug", (req, res) => {
|
|
216
223
|
const p = project(req, res);
|
|
217
224
|
if (!p) return;
|
|
218
225
|
const slug = req.params.slug;
|
|
219
226
|
const file = path.join(p.path, ".apc", "agents", `${slug}.md`);
|
|
220
|
-
const
|
|
221
|
-
|
|
227
|
+
const runtimeDir = path.dirname(agentMemoryPath(p, slug));
|
|
228
|
+
const legacyDir = path.dirname(legacyAgentMemoryPath(p.path, slug));
|
|
229
|
+
if (!fs.existsSync(file) && !fs.existsSync(runtimeDir) && !fs.existsSync(legacyDir))
|
|
222
230
|
return res.status(404).json({ error: "agent not found" });
|
|
223
231
|
try {
|
|
224
232
|
if (fs.existsSync(file)) fs.rmSync(file);
|
|
225
|
-
if (fs.existsSync(
|
|
233
|
+
if (fs.existsSync(runtimeDir)) fs.rmSync(runtimeDir, { recursive: true, force: true });
|
|
234
|
+
if (fs.existsSync(legacyDir)) fs.rmSync(legacyDir, { recursive: true, force: true });
|
|
226
235
|
projects.rebuild(p.id);
|
|
227
236
|
res.json({ ok: true });
|
|
228
237
|
} catch (e) {
|
|
@@ -257,15 +266,7 @@ export function register(app, { projects, project }) {
|
|
|
257
266
|
app.get("/projects/:pid/agents/:slug/memory", (req, res) => {
|
|
258
267
|
const p = project(req, res);
|
|
259
268
|
if (!p) return;
|
|
260
|
-
|
|
261
|
-
p.path,
|
|
262
|
-
".apc",
|
|
263
|
-
"agents",
|
|
264
|
-
req.params.slug,
|
|
265
|
-
"memory.md"
|
|
266
|
-
);
|
|
267
|
-
if (!fs.existsSync(memPath)) return res.json({ body: "" });
|
|
268
|
-
res.json({ body: fs.readFileSync(memPath, "utf8") });
|
|
269
|
+
res.json({ body: readAgentMemory(p, req.params.slug) });
|
|
269
270
|
});
|
|
270
271
|
|
|
271
272
|
app.put("/projects/:pid/agents/:slug/memory", (req, res) => {
|
|
@@ -274,10 +275,7 @@ export function register(app, { projects, project }) {
|
|
|
274
275
|
const { body } = req.body || {};
|
|
275
276
|
if (typeof body !== "string")
|
|
276
277
|
return res.status(400).json({ error: "body must be string" });
|
|
277
|
-
|
|
278
|
-
fs.mkdirSync(path.join(dir, "sessions"), { recursive: true });
|
|
279
|
-
const memPath = path.join(dir, "memory.md");
|
|
280
|
-
fs.writeFileSync(memPath, body);
|
|
278
|
+
writeAgentMemory(p, req.params.slug, body);
|
|
281
279
|
projects.rebuild(p.id);
|
|
282
280
|
res.json({ ok: true, bytes: Buffer.byteLength(body, "utf8") });
|
|
283
281
|
});
|
|
@@ -31,7 +31,7 @@ export function register(app, { projects, config }) {
|
|
|
31
31
|
for (const p of targetProjects) {
|
|
32
32
|
if (!p) continue;
|
|
33
33
|
|
|
34
|
-
// 1)
|
|
34
|
+
// 1) Legacy session files in the repo (.apc/agents/<slug>/sessions/)
|
|
35
35
|
const sessionAgentsDir = path.join(p.path, ".apc", "agents");
|
|
36
36
|
if (fs.existsSync(sessionAgentsDir)) {
|
|
37
37
|
for (const slug of fs.readdirSync(sessionAgentsDir)) {
|
|
@@ -6,6 +6,8 @@ import fs from "node:fs";
|
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { randomUUID } from "node:crypto";
|
|
8
8
|
import { appendErrorTrace, previewText } from "../../../core/logging.js";
|
|
9
|
+
import { readAgents } from "../../../core/parser.js";
|
|
10
|
+
import { agentMemoryPath } from "../../../core/agent-memory.js";
|
|
9
11
|
|
|
10
12
|
export const nowIso = () =>
|
|
11
13
|
new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
@@ -110,15 +112,10 @@ export function makeTopProjectResolver(projects) {
|
|
|
110
112
|
}
|
|
111
113
|
|
|
112
114
|
// Pick the memory.md to use when /memory is called without an agent ref.
|
|
113
|
-
// Prefer the first
|
|
115
|
+
// Prefer the first agent's runtime-local memory; else project-level .apc/memory.md.
|
|
114
116
|
export function resolveMemoryPath(p) {
|
|
115
|
-
const
|
|
116
|
-
if (
|
|
117
|
-
const slugs = fs.readdirSync(agentsDir).filter((s) =>
|
|
118
|
-
fs.statSync(path.join(agentsDir, s)).isDirectory()
|
|
119
|
-
);
|
|
120
|
-
if (slugs.length) return path.join(agentsDir, slugs[0], "memory.md");
|
|
121
|
-
}
|
|
117
|
+
const firstAgent = readAgents(p.path)[0];
|
|
118
|
+
if (firstAgent) return agentMemoryPath(p, firstAgent.slug);
|
|
122
119
|
return path.join(p.path, ".apc", "memory.md");
|
|
123
120
|
}
|
|
124
121
|
|
|
@@ -2,6 +2,7 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { readVaultAgents, VAULT_DIR } from "../../../../core/parser.js";
|
|
4
4
|
import { addImportedAgent, ensureAgentDir } from "../../../../core/scaffold.js";
|
|
5
|
+
import { ensureAgentRuntimeDir } from "../../../../core/agent-memory.js";
|
|
5
6
|
import { confirmedProperty, projectMeta, resolveProject } from "../helpers.js";
|
|
6
7
|
|
|
7
8
|
export default {
|
|
@@ -35,6 +36,7 @@ export default {
|
|
|
35
36
|
const p = resolveProject(projects, project || "default");
|
|
36
37
|
addImportedAgent(p.path, slug);
|
|
37
38
|
ensureAgentDir(p.path, slug);
|
|
39
|
+
ensureAgentRuntimeDir(p, slug);
|
|
38
40
|
projects.rebuild(p.id);
|
|
39
41
|
|
|
40
42
|
return {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
2
|
import { resolveProject } from "../helpers.js";
|
|
3
|
+
import { agentMemoryPath, readAgentMemory } from "../../../../core/agent-memory.js";
|
|
4
4
|
|
|
5
5
|
export default {
|
|
6
6
|
name: "read_agent_memory",
|
|
@@ -21,8 +21,9 @@ export default {
|
|
|
21
21
|
},
|
|
22
22
|
makeHandler: ({ projects }) => ({ project, agent }) => {
|
|
23
23
|
const p = resolveProject(projects, project);
|
|
24
|
-
const file =
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
const file = agentMemoryPath(p, agent);
|
|
25
|
+
const body = readAgentMemory(p, agent);
|
|
26
|
+
if (!body && !fs.existsSync(file)) return { error: `no memory.md for agent ${agent}` };
|
|
27
|
+
return { body, path: file };
|
|
27
28
|
},
|
|
28
29
|
};
|
|
@@ -2,6 +2,7 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { findApfRoot, readAgents, readVaultAgents, readVaultAgent, VAULT_DIR, SLUG_RE } from "../../../core/parser.js";
|
|
4
4
|
import { writeAgentFile, writeVaultAgentFile, removeVaultAgent, restoreVaultAgent, addImportedAgent, ensureAgentDir } from "../../../core/scaffold.js";
|
|
5
|
+
import { ensureAgentRuntimeDir, agentMemoryPath } from "../../../core/agent-memory.js";
|
|
5
6
|
import { http } from "../http.js";
|
|
6
7
|
|
|
7
8
|
// ── ANSI ──────────────────────────────────────────────────────────────────────
|
|
@@ -49,6 +50,7 @@ export async function cmdAgentAdd(args) {
|
|
|
49
50
|
|
|
50
51
|
writeAgentFile(root, slug, fields);
|
|
51
52
|
ensureAgentDir(root, slug);
|
|
53
|
+
ensureAgentRuntimeDir(root, slug);
|
|
52
54
|
await nudgeDaemon(root);
|
|
53
55
|
|
|
54
56
|
console.log(`Added agent ${slug}`);
|
|
@@ -207,10 +209,11 @@ export async function cmdAgentImport(args) {
|
|
|
207
209
|
addImportedAgent(root, slug);
|
|
208
210
|
console.log(`\n ${bold(slug)} imported from vault ${tag("↑ vault")}\n`);
|
|
209
211
|
console.log(gray(` definition: ${vaultPath}`));
|
|
210
|
-
console.log(gray(` memory: ${
|
|
212
|
+
console.log(gray(` memory: ${agentMemoryPath(root, slug)} (runtime-local)`));
|
|
211
213
|
console.log();
|
|
212
214
|
}
|
|
213
215
|
|
|
214
216
|
ensureAgentDir(root, slug);
|
|
217
|
+
ensureAgentRuntimeDir(root, slug);
|
|
215
218
|
await nudgeDaemon(root);
|
|
216
219
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
2
|
import { findApfRoot } from "../../../core/parser.js";
|
|
4
|
-
import {
|
|
3
|
+
import { agentMemoryPath, readAgentMemory, writeAgentMemory, ensureAgentRuntimeDir } from "../../../core/agent-memory.js";
|
|
5
4
|
import { http } from "../http.js";
|
|
6
5
|
|
|
7
6
|
function requireRoot() {
|
|
@@ -23,12 +22,11 @@ export async function cmdMemory(args) {
|
|
|
23
22
|
const slug = args._[0];
|
|
24
23
|
if (!slug) throw new Error("apx memory: missing <agent-slug>");
|
|
25
24
|
const root = requireRoot();
|
|
26
|
-
const memPath =
|
|
25
|
+
const memPath = agentMemoryPath(root, slug);
|
|
27
26
|
|
|
28
27
|
if (args.flags.replace) {
|
|
29
28
|
const newBody = readStdinSync();
|
|
30
|
-
|
|
31
|
-
fs.writeFileSync(memPath, newBody);
|
|
29
|
+
writeAgentMemory(root, slug, newBody);
|
|
32
30
|
await nudgeDaemon(root);
|
|
33
31
|
console.log(`replaced memory for ${slug} (${Buffer.byteLength(newBody)} bytes)`);
|
|
34
32
|
return;
|
|
@@ -36,23 +34,24 @@ export async function cmdMemory(args) {
|
|
|
36
34
|
|
|
37
35
|
if (args.flags.append && args.flags.append !== true) {
|
|
38
36
|
const note = String(args.flags.append);
|
|
39
|
-
|
|
40
|
-
let body =
|
|
37
|
+
ensureAgentRuntimeDir(root, slug);
|
|
38
|
+
let body = readAgentMemory(root, slug);
|
|
41
39
|
if (!/##\s+Recent context/i.test(body)) {
|
|
42
40
|
body += body.endsWith("\n") ? "\n## Recent context\n" : "\n\n## Recent context\n";
|
|
43
41
|
}
|
|
44
42
|
const today = new Date().toISOString().slice(0, 10);
|
|
45
43
|
body = body.replace(/(##\s+Recent context\s*\n)/i, `$1- ${today}: ${note}\n`);
|
|
46
|
-
|
|
44
|
+
writeAgentMemory(root, slug, body);
|
|
47
45
|
await nudgeDaemon(root);
|
|
48
46
|
console.log(`appended to ${slug} memory: ${note}`);
|
|
49
47
|
return;
|
|
50
48
|
}
|
|
51
49
|
|
|
52
|
-
|
|
50
|
+
const body = readAgentMemory(root, slug);
|
|
51
|
+
if (!body && !fs.existsSync(memPath)) {
|
|
53
52
|
throw new Error(`no memory for "${slug}" — agent dir not yet created`);
|
|
54
53
|
}
|
|
55
|
-
process.stdout.write(
|
|
54
|
+
process.stdout.write(body);
|
|
56
55
|
}
|
|
57
56
|
|
|
58
57
|
function readStdinSync() {
|