agentopology 0.2.5 → 0.3.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/README.md +69 -3
- package/dist/bindings/claude-code.d.ts.map +1 -1
- package/dist/bindings/claude-code.js +100 -2
- package/dist/bindings/claude-code.js.map +1 -1
- package/dist/cli/index.js +195 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/parser/__tests__/parser.test.js +170 -0
- package/dist/parser/__tests__/parser.test.js.map +1 -1
- package/dist/parser/ast.d.ts +39 -0
- package/dist/parser/ast.d.ts.map +1 -1
- package/dist/parser/index.d.ts.map +1 -1
- package/dist/parser/index.js +58 -2
- package/dist/parser/index.js.map +1 -1
- package/dist/parser/validator.d.ts.map +1 -1
- package/dist/parser/validator.js +66 -3
- package/dist/parser/validator.js.map +1 -1
- package/dist/visualizer/__tests__/brain.test.d.ts +2 -0
- package/dist/visualizer/__tests__/brain.test.d.ts.map +1 -0
- package/dist/visualizer/__tests__/brain.test.js +138 -0
- package/dist/visualizer/__tests__/brain.test.js.map +1 -0
- package/dist/visualizer/__tests__/topology-brain-link.test.d.ts +2 -0
- package/dist/visualizer/__tests__/topology-brain-link.test.d.ts.map +1 -0
- package/dist/visualizer/__tests__/topology-brain-link.test.js +41 -0
- package/dist/visualizer/__tests__/topology-brain-link.test.js.map +1 -0
- package/dist/visualizer/brain.d.ts +97 -0
- package/dist/visualizer/brain.d.ts.map +1 -0
- package/dist/visualizer/brain.js +473 -0
- package/dist/visualizer/brain.js.map +1 -0
- package/dist/visualizer/index.d.ts +19 -1
- package/dist/visualizer/index.d.ts.map +1 -1
- package/dist/visualizer/index.js +26 -11
- package/dist/visualizer/index.js.map +1 -1
- package/examples/company-brain-team.at +241 -0
- package/examples/company-brain.at +104 -0
- package/examples/logos/gmail.svg +1 -0
- package/examples/logos/slack.svg +1 -0
- package/package.json +1 -1
- package/spec/grammar.md +30 -2
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<h1 align="center">AgenTopology</h1>
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
|
-
<strong>The Terraform for AI agents
|
|
5
|
-
Define your agent team once. Deploy to any platform.
|
|
4
|
+
<strong>Harness as code.</strong> The Terraform for AI agents.<br/>
|
|
5
|
+
Define your agent team — and its memory — once. Deploy to any platform.
|
|
6
6
|
</p>
|
|
7
7
|
|
|
8
8
|
<p align="center">
|
|
@@ -364,6 +364,36 @@ mcp-servers {
|
|
|
364
364
|
}
|
|
365
365
|
```
|
|
366
366
|
|
|
367
|
+
</td>
|
|
368
|
+
</tr>
|
|
369
|
+
<tr>
|
|
370
|
+
<td>
|
|
371
|
+
|
|
372
|
+
**Company Brain** — agent-maintained, Obsidian-portable
|
|
373
|
+
```
|
|
374
|
+
store brain {
|
|
375
|
+
type: brain # markdown, no DB
|
|
376
|
+
path: "brain/"
|
|
377
|
+
format: obsidian # ports both ways
|
|
378
|
+
}
|
|
379
|
+
agent librarian {
|
|
380
|
+
custodian-of: [brain] # owns its upkeep
|
|
381
|
+
}
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
</td>
|
|
385
|
+
<td>
|
|
386
|
+
|
|
387
|
+
**Provenance styling** — color the graph by source
|
|
388
|
+
```
|
|
389
|
+
store brain {
|
|
390
|
+
sources {
|
|
391
|
+
gmail { color: "#EA4335" icon: "./gmail.svg" }
|
|
392
|
+
slack { color: "#4A154B" }
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
367
397
|
</td>
|
|
368
398
|
</tr>
|
|
369
399
|
</table>
|
|
@@ -389,6 +419,39 @@ In Claude Code, this compiles to a **file-based protocol** — a shared transcri
|
|
|
389
419
|
|
|
390
420
|
---
|
|
391
421
|
|
|
422
|
+
## Company Brain — Knowledge That Builds Itself
|
|
423
|
+
|
|
424
|
+
A **brain** is a folder of linked markdown that agents maintain, not you. It's the [Obsidian](https://obsidian.md) graph model — notes connected by `[[wikilinks]]` and `#tags` — but **agent-maintained instead of hand-curated**.
|
|
425
|
+
|
|
426
|
+
```
|
|
427
|
+
store brain {
|
|
428
|
+
type: brain # file-native: markdown, no database, no embeddings
|
|
429
|
+
path: "brain/"
|
|
430
|
+
format: obsidian # ports both ways — open it in Obsidian, the graph just works
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
agent librarian {
|
|
434
|
+
custodian-of: [brain] { # OWNS the brain's upkeep, not just read access
|
|
435
|
+
does: [link, tag, index, dedupe]
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
- **Custodian agents** own a memory layer. Drop a note in, the librarian wires it into the graph — resolving `[[links]]`, assigning `#tags`, updating hub notes. Humans drop; agents wire.
|
|
441
|
+
- **Ingester agents** feed it from anywhere. One agent per source (Gmail, Slack, calendar) writes raw notes; the librarian links them. See [`examples/company-brain-team.at`](examples/company-brain-team.at).
|
|
442
|
+
- **Pure markdown, zero infrastructure.** No vector DB, no MCP server. Coding agents retrieve with `grep`. The whole brain is a folder you can `git clone`.
|
|
443
|
+
- **100% Obsidian-compatible.** The vault *is* an Obsidian vault — agents build it, you open it in Obsidian for the graph view. Or use our built-in graph viewer (below) and skip Obsidian entirely.
|
|
444
|
+
|
|
445
|
+
**See it without Obsidian** — render any brain vault as an interactive graph in a single self-contained HTML file:
|
|
446
|
+
|
|
447
|
+
```bash
|
|
448
|
+
agentopology visualize-brain brain/
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
Nodes are colored by what they *are* (person / org / topic) and by where they *came from* (declare `sources { gmail { color, icon } }` and notes from Gmail render in Gmail red with the logo). When you `visualize` a topology that owns brains, the two graphs **cross-link** — jump from the team diagram to its brain and back.
|
|
452
|
+
|
|
453
|
+
This is **brain as code**: you declare the brain's architecture — its layers, its custodians, its sources — the same way you declare the agent team. See [`docs/company-brain.md`](docs/company-brain.md) for the full walkthrough.
|
|
454
|
+
|
|
392
455
|
---
|
|
393
456
|
|
|
394
457
|
## Programmatic API
|
|
@@ -462,6 +525,8 @@ You can `agentopology visualize` it into an interactive graph. You can hand it t
|
|
|
462
525
|
- [`data-processing.at`](examples/data-processing.at) — ETL pipeline with batch processing and metering
|
|
463
526
|
- [`scheduled-monitor.at`](examples/scheduled-monitor.at) — Monitoring system with scheduled health checks
|
|
464
527
|
- [`openclaw-assistant.at`](examples/openclaw-assistant.at) — Customer support with routing and scheduling
|
|
528
|
+
- [`company-brain.at`](examples/company-brain.at) — A plug-and-play company brain (librarian + researcher)
|
|
529
|
+
- [`company-brain-team.at`](examples/company-brain-team.at) — A team that ingests (Gmail/Slack/calendar), wires, and audits a brain
|
|
465
530
|
|
|
466
531
|
---
|
|
467
532
|
|
|
@@ -472,7 +537,8 @@ agentopology validate <file> Validate an .at file (82 rules)
|
|
|
472
537
|
agentopology scaffold <file> --target <t> Generate platform configs
|
|
473
538
|
agentopology sync <file> --target <t> --dir <d> Sync platform files back to .at
|
|
474
539
|
agentopology import --target <t> --dir <d> Reverse-engineer platform files to .at
|
|
475
|
-
agentopology visualize <file> Interactive topology graph
|
|
540
|
+
agentopology visualize <file> Interactive topology graph (+ cross-links any brains)
|
|
541
|
+
agentopology visualize-brain <folder> Render a brain vault as an Obsidian-style graph (no Obsidian)
|
|
476
542
|
agentopology export <file> --format <fmt> Export as markdown, mermaid, or json
|
|
477
543
|
agentopology info <file> Topology analysis and suggestions
|
|
478
544
|
agentopology targets List supported platforms
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../src/bindings/claude-code.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAuBH,OAAO,KAAK,EAAE,aAAa,EAAiB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../src/bindings/claude-code.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAuBH,OAAO,KAAK,EAAE,aAAa,EAAiB,MAAM,YAAY,CAAC;AAu4F/D,kEAAkE;AAClE,eAAO,MAAM,iBAAiB,EAAE,aAkG/B,CAAC"}
|
|
@@ -132,6 +132,58 @@ function gitkeep(dirPath) {
|
|
|
132
132
|
function agentPhase(agent) {
|
|
133
133
|
return agent.phase ?? Number.MAX_SAFE_INTEGER;
|
|
134
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* The standard meaning of each custody maintenance verb. The verb NAMES are
|
|
137
|
+
* language vocabulary (public); these one-line charters tell the agent what each
|
|
138
|
+
* verb means operationally. The deeper "how to do it brilliantly" stays in the
|
|
139
|
+
* skill repo (MOAT) — these are intentionally the safe, obvious definitions.
|
|
140
|
+
*/
|
|
141
|
+
const CUSTODY_VERBS = {
|
|
142
|
+
link: "find mentions of existing notes and turn them into `[[wikilinks]]` (add-only — never delete a human's links)",
|
|
143
|
+
tag: "assign `#nested/tags` consistent with the tag hierarchy already present in the vault",
|
|
144
|
+
index: "update the relevant hub note (a Map-of-Content that `[[links]]` the cluster) so the new note is reachable",
|
|
145
|
+
backlink: "rely on computed backlinks from the `[[links]]` you write — never author backlinks by hand",
|
|
146
|
+
dedupe: "flag near-duplicate notes for human review — never auto-merge or delete",
|
|
147
|
+
summarize: "keep a short synthesized summary at the top of hub notes, refreshed when the cluster changes",
|
|
148
|
+
heal: "repair broken links after renames and surface ghost nodes (`[[links]]` to notes that don't exist yet)",
|
|
149
|
+
};
|
|
150
|
+
/**
|
|
151
|
+
* Generate the custody charter section for an agent that is `custodian-of` one
|
|
152
|
+
* or more stores. This is what makes custody a real binding behavior: the agent
|
|
153
|
+
* gets a standing instruction set to MAINTAIN the store(s), not just read them.
|
|
154
|
+
*/
|
|
155
|
+
function generateCustodyCharter(agent, storeMap) {
|
|
156
|
+
const out = [];
|
|
157
|
+
out.push("## Custody — you maintain these stores");
|
|
158
|
+
out.push("");
|
|
159
|
+
out.push("You are the **custodian** of the store(s) below: you own their upkeep, not just read access. " +
|
|
160
|
+
"Humans drop raw notes; your job is to wire them into the graph and keep it healthy.");
|
|
161
|
+
out.push("");
|
|
162
|
+
for (const storeId of agent.custodianOf ?? []) {
|
|
163
|
+
const store = storeMap.get(storeId);
|
|
164
|
+
out.push(`### ${storeId}`);
|
|
165
|
+
if (store?.path) {
|
|
166
|
+
out.push(`The vault lives at \`${store.path}\`. Maintain it with \`Read\`, \`Write\`, \`Edit\`, \`Grep\`, \`Glob\` — no database, just files.`);
|
|
167
|
+
}
|
|
168
|
+
if (store?.format === "obsidian") {
|
|
169
|
+
out.push("It is **Obsidian-format** markdown: only ever write `[[wikilinks]]`, `#tags`, `![[embeds]]`, and YAML frontmatter — nothing custom — so the vault round-trips in Obsidian.");
|
|
170
|
+
}
|
|
171
|
+
out.push("");
|
|
172
|
+
// Maintenance verbs — declared ones, or the safe default set.
|
|
173
|
+
const verbs = (agent.custodianDoes && agent.custodianDoes.length > 0)
|
|
174
|
+
? agent.custodianDoes
|
|
175
|
+
: ["link", "tag", "index", "heal", "dedupe"];
|
|
176
|
+
out.push("When a note is added or changed, perform these maintenance steps:");
|
|
177
|
+
for (const verb of verbs) {
|
|
178
|
+
const meaning = CUSTODY_VERBS[verb] ?? `apply the \`${verb}\` maintenance step`;
|
|
179
|
+
out.push(`- **${verb}** — ${meaning}`);
|
|
180
|
+
}
|
|
181
|
+
out.push("");
|
|
182
|
+
out.push("Wire add-only and conservatively: prefer leaving a human's content untouched over a risky rewrite. When unsure, flag for review rather than edit.");
|
|
183
|
+
out.push("");
|
|
184
|
+
}
|
|
185
|
+
return out;
|
|
186
|
+
}
|
|
135
187
|
/**
|
|
136
188
|
* The set of phases that compile into the embedded workflow rung — every phase
|
|
137
189
|
* that contains ≥1 agent marked `execution: workflow`. (Matches the
|
|
@@ -522,6 +574,13 @@ function generateAgents(ast) {
|
|
|
522
574
|
sections.push("");
|
|
523
575
|
}
|
|
524
576
|
}
|
|
577
|
+
// Custody — the agent OWNS the upkeep of these stores (the `brain` pattern).
|
|
578
|
+
// Generated even when the agent has no hand-written prompt, so custody is a
|
|
579
|
+
// real binding behavior, not just prose the author happened to write.
|
|
580
|
+
if (agent.custodianOf && agent.custodianOf.length > 0) {
|
|
581
|
+
const storeMap = new Map((ast.stores ?? []).map(s => [s.id, s]));
|
|
582
|
+
sections.push(...generateCustodyCharter(agent, storeMap));
|
|
583
|
+
}
|
|
525
584
|
// Retrieval strategy — rich details
|
|
526
585
|
if (agent.retrieval) {
|
|
527
586
|
const retrieval = (ast.retrievals ?? []).find(r => r.id === agent.retrieval);
|
|
@@ -1428,13 +1487,52 @@ function generateMemory(ast) {
|
|
|
1428
1487
|
if (memory.workspace) {
|
|
1429
1488
|
files.push(gitkeep(`.claude/skills/${name}/runs`));
|
|
1430
1489
|
}
|
|
1431
|
-
// Store directory markers
|
|
1490
|
+
// Store directory markers. A `brain` store is a real, user-facing Obsidian
|
|
1491
|
+
// vault — it lives at its declared `path:` (e.g. `brain/`), not buried under
|
|
1492
|
+
// `.claude/stores/`, so a human can open the folder directly in Obsidian.
|
|
1493
|
+
// We seed it with a root Map-of-Content note so the vault opens to something.
|
|
1432
1494
|
const stores = ast.stores ?? [];
|
|
1433
1495
|
for (const store of stores) {
|
|
1434
|
-
|
|
1496
|
+
if (store.type === "brain") {
|
|
1497
|
+
const vault = (store.path ?? `${store.id}/`).replace(/\/+$/, "");
|
|
1498
|
+
files.push(brainVaultSeed(store, vault));
|
|
1499
|
+
}
|
|
1500
|
+
else {
|
|
1501
|
+
files.push(gitkeep(`.claude/stores/${store.id}`));
|
|
1502
|
+
}
|
|
1435
1503
|
}
|
|
1436
1504
|
return files;
|
|
1437
1505
|
}
|
|
1506
|
+
/**
|
|
1507
|
+
* Seed a brain vault with a root index note (`_brain.md`) — an Obsidian Map-of-
|
|
1508
|
+
* Content the librarian grows over time. Gives the vault a home page and proves
|
|
1509
|
+
* the format contract (wikilinks, tags, frontmatter) from the first commit.
|
|
1510
|
+
*/
|
|
1511
|
+
function brainVaultSeed(store, vault) {
|
|
1512
|
+
const lines = [
|
|
1513
|
+
"---",
|
|
1514
|
+
"tags:",
|
|
1515
|
+
" - brain/index",
|
|
1516
|
+
"---",
|
|
1517
|
+
"",
|
|
1518
|
+
`# ${toTitle(store.id)}`,
|
|
1519
|
+
"",
|
|
1520
|
+
store.description ? `> ${store.description}` : "> The company brain.",
|
|
1521
|
+
"",
|
|
1522
|
+
"This is the root index of the brain — a Map of Content the librarian maintains.",
|
|
1523
|
+
"Drop new notes into this folder and run `/wire-in <file>` to fold them in.",
|
|
1524
|
+
"",
|
|
1525
|
+
"## Clusters",
|
|
1526
|
+
"",
|
|
1527
|
+
"_New hub notes appear here as the brain grows._",
|
|
1528
|
+
"",
|
|
1529
|
+
];
|
|
1530
|
+
return {
|
|
1531
|
+
path: `${vault}/_brain.md`,
|
|
1532
|
+
content: lines.join("\n"),
|
|
1533
|
+
category: "machine",
|
|
1534
|
+
};
|
|
1535
|
+
}
|
|
1438
1536
|
/**
|
|
1439
1537
|
* Generate memory command files for each store.
|
|
1440
1538
|
*
|