auggy 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/CHANGELOG.md +96 -0
- package/LICENSE +201 -0
- package/README.md +161 -0
- package/package.json +76 -0
- package/src/agent-card.ts +39 -0
- package/src/agent.ts +283 -0
- package/src/agentmail-client.ts +138 -0
- package/src/augments/bash/index.ts +463 -0
- package/src/augments/bash/skill/SKILL.md +156 -0
- package/src/augments/budgets/budget-store.ts +513 -0
- package/src/augments/budgets/index.ts +134 -0
- package/src/augments/budgets/preamble.ts +93 -0
- package/src/augments/budgets/types.ts +89 -0
- package/src/augments/file-memory/index.ts +71 -0
- package/src/augments/filesystem/index.ts +533 -0
- package/src/augments/filesystem/skill/SKILL.md +142 -0
- package/src/augments/filesystem/skill/references/mount-permissions.md +81 -0
- package/src/augments/layered-memory/extractor/buffer.ts +56 -0
- package/src/augments/layered-memory/extractor/frequency.ts +79 -0
- package/src/augments/layered-memory/extractor/inject-handler.ts +103 -0
- package/src/augments/layered-memory/extractor/parse.ts +75 -0
- package/src/augments/layered-memory/extractor/prompt.md +26 -0
- package/src/augments/layered-memory/index.ts +757 -0
- package/src/augments/layered-memory/skill/SKILL.md +153 -0
- package/src/augments/layered-memory/storage/migrations/README.md +16 -0
- package/src/augments/layered-memory/storage/migrations/supabase-add-fact-fields.sql +9 -0
- package/src/augments/layered-memory/storage/sqlite-store.ts +352 -0
- package/src/augments/layered-memory/storage/supabase-store.ts +263 -0
- package/src/augments/layered-memory/storage/types.ts +98 -0
- package/src/augments/link/index.ts +489 -0
- package/src/augments/link/translate.ts +261 -0
- package/src/augments/notify/adapters/agentmail.ts +70 -0
- package/src/augments/notify/adapters/telegram.ts +60 -0
- package/src/augments/notify/adapters/webhook.ts +55 -0
- package/src/augments/notify/index.ts +284 -0
- package/src/augments/notify/skill/SKILL.md +150 -0
- package/src/augments/org-context/index.ts +721 -0
- package/src/augments/org-context/skill/SKILL.md +96 -0
- package/src/augments/skills/index.ts +103 -0
- package/src/augments/supabase-memory/index.ts +151 -0
- package/src/augments/telegram-transport/index.ts +312 -0
- package/src/augments/telegram-transport/polling.ts +55 -0
- package/src/augments/telegram-transport/webhook.ts +56 -0
- package/src/augments/turn-control/index.ts +61 -0
- package/src/augments/turn-control/skill/SKILL.md +155 -0
- package/src/augments/visitor-auth/email-validation.ts +66 -0
- package/src/augments/visitor-auth/index.ts +779 -0
- package/src/augments/visitor-auth/rate-limiter.ts +90 -0
- package/src/augments/visitor-auth/skill/SKILL.md +55 -0
- package/src/augments/visitor-auth/storage/sqlite-store.ts +398 -0
- package/src/augments/visitor-auth/storage/types.ts +164 -0
- package/src/augments/visitor-auth/types.ts +123 -0
- package/src/augments/visitor-auth/verify-page.ts +179 -0
- package/src/augments/web-fetch/index.ts +331 -0
- package/src/augments/web-fetch/skill/SKILL.md +100 -0
- package/src/cli/agent-index.ts +289 -0
- package/src/cli/augment-catalog.ts +320 -0
- package/src/cli/augment-resolver.ts +597 -0
- package/src/cli/commands/add-skill.ts +194 -0
- package/src/cli/commands/add.ts +87 -0
- package/src/cli/commands/chat.ts +207 -0
- package/src/cli/commands/create.ts +462 -0
- package/src/cli/commands/dev.ts +139 -0
- package/src/cli/commands/eval.ts +180 -0
- package/src/cli/commands/ls.ts +66 -0
- package/src/cli/commands/remove.ts +95 -0
- package/src/cli/commands/restart.ts +40 -0
- package/src/cli/commands/start.ts +123 -0
- package/src/cli/commands/status.ts +104 -0
- package/src/cli/commands/stop.ts +84 -0
- package/src/cli/commands/visitors-revoke.ts +155 -0
- package/src/cli/commands/visitors.ts +101 -0
- package/src/cli/config-parser.ts +1034 -0
- package/src/cli/engine-resolver.ts +68 -0
- package/src/cli/index.ts +178 -0
- package/src/cli/model-picker.ts +89 -0
- package/src/cli/pid-registry.ts +146 -0
- package/src/cli/plist-generator.ts +117 -0
- package/src/cli/resolve-config.ts +56 -0
- package/src/cli/scaffold-skills.ts +158 -0
- package/src/cli/scaffold.ts +291 -0
- package/src/cli/skill-frontmatter.ts +51 -0
- package/src/cli/skill-validator.ts +151 -0
- package/src/cli/types.ts +228 -0
- package/src/cli/yaml-helpers.ts +66 -0
- package/src/engines/_shared/cost.ts +55 -0
- package/src/engines/_shared/schema-normalize.ts +75 -0
- package/src/engines/anthropic/pricing.ts +117 -0
- package/src/engines/anthropic.ts +483 -0
- package/src/engines/openai/pricing.ts +67 -0
- package/src/engines/openai.ts +446 -0
- package/src/engines/openrouter/pricing.ts +83 -0
- package/src/engines/openrouter.ts +185 -0
- package/src/helpers.ts +24 -0
- package/src/http.ts +387 -0
- package/src/index.ts +165 -0
- package/src/kernel/capability-table.ts +172 -0
- package/src/kernel/context-allocator.ts +161 -0
- package/src/kernel/history-manager.ts +198 -0
- package/src/kernel/lifecycle-manager.ts +106 -0
- package/src/kernel/output-validator.ts +35 -0
- package/src/kernel/preamble.ts +23 -0
- package/src/kernel/route-collector.ts +97 -0
- package/src/kernel/timeout.ts +21 -0
- package/src/kernel/tool-selector.ts +47 -0
- package/src/kernel/trace-emitter.ts +66 -0
- package/src/kernel/transport-queue.ts +147 -0
- package/src/kernel/turn-loop.ts +1148 -0
- package/src/memory/context-synthesis.ts +83 -0
- package/src/memory/memory-bus.ts +61 -0
- package/src/memory/registry.ts +80 -0
- package/src/memory/tools.ts +320 -0
- package/src/memory/types.ts +8 -0
- package/src/parts.ts +30 -0
- package/src/scaffold-templates/identity.md +31 -0
- package/src/telegram-client.ts +145 -0
- package/src/tokenizer.ts +14 -0
- package/src/transports/ag-ui-events.ts +253 -0
- package/src/transports/visitor-token.ts +82 -0
- package/src/transports/web-transport.ts +948 -0
- package/src/types.ts +1009 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent index — `~/.auggy/agents.json`.
|
|
3
|
+
*
|
|
4
|
+
* Maps agent name → { localDir, createdAt, cloud }. Load-bearing facility
|
|
5
|
+
* metadata: every CLI command that finds an agent on disk goes through here.
|
|
6
|
+
*
|
|
7
|
+
* Pattern mirrors `pid-registry.ts`: atomic write via temp+rename, defensive
|
|
8
|
+
* recovery on corruption, no in-memory caching across invocations.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
closeSync,
|
|
13
|
+
fstatSync,
|
|
14
|
+
mkdirSync,
|
|
15
|
+
openSync,
|
|
16
|
+
readSync,
|
|
17
|
+
renameSync,
|
|
18
|
+
unlinkSync,
|
|
19
|
+
writeFileSync,
|
|
20
|
+
writeSync,
|
|
21
|
+
} from "node:fs";
|
|
22
|
+
import { homedir } from "node:os";
|
|
23
|
+
import { join } from "node:path";
|
|
24
|
+
import type { IndexFile, IndexEntry } from "./types";
|
|
25
|
+
|
|
26
|
+
const SCHEMA_VERSION = 1 as const;
|
|
27
|
+
|
|
28
|
+
// Lock acquisition tuning. Spin with short busy-waits up to LOCK_TIMEOUT_MS,
|
|
29
|
+
// matching the cadence in pid-registry's other file-coordination paths.
|
|
30
|
+
const LOCK_TIMEOUT_MS = 5_000;
|
|
31
|
+
const LOCK_POLL_MS = 50;
|
|
32
|
+
|
|
33
|
+
interface IndexOptions {
|
|
34
|
+
/** Override `~/.auggy/` for tests. Production callers omit. */
|
|
35
|
+
auggyDir?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getAuggyDir(opts: IndexOptions = {}): string {
|
|
39
|
+
return opts.auggyDir ?? join(homedir(), ".auggy");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function indexPath(opts: IndexOptions): string {
|
|
43
|
+
return join(getAuggyDir(opts), "agents.json");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function ensureDir(opts: IndexOptions): void {
|
|
47
|
+
mkdirSync(getAuggyDir(opts), { recursive: true });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function emptyIndex(): IndexFile {
|
|
51
|
+
return { version: SCHEMA_VERSION, agents: {} };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function lockPath(opts: IndexOptions): string {
|
|
55
|
+
return join(getAuggyDir(opts), "agents.json.lock");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface LockHandle {
|
|
59
|
+
release: () => void;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
interface LockFileContents {
|
|
63
|
+
pid: number;
|
|
64
|
+
acquired: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Try to atomically create the lock file. Returns a release handle on
|
|
69
|
+
* success, or null if the lock is already held (EEXIST). Any other I/O
|
|
70
|
+
* error propagates.
|
|
71
|
+
*
|
|
72
|
+
* Extracted from `acquireLock` so the happy path and the force-recovery
|
|
73
|
+
* retry path share a single implementation.
|
|
74
|
+
*/
|
|
75
|
+
function tryAcquire(path: string, body: string): LockHandle | null {
|
|
76
|
+
try {
|
|
77
|
+
const fd = openSync(path, "wx");
|
|
78
|
+
try {
|
|
79
|
+
writeSync(fd, body);
|
|
80
|
+
} finally {
|
|
81
|
+
closeSync(fd);
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
release: () => {
|
|
85
|
+
try {
|
|
86
|
+
unlinkSync(path);
|
|
87
|
+
} catch {
|
|
88
|
+
// Already gone — nothing to do.
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
} catch (err) {
|
|
93
|
+
if ((err as NodeJS.ErrnoException).code !== "EEXIST") throw err;
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Acquire a cross-process advisory lock on `agents.json.lock`.
|
|
100
|
+
*
|
|
101
|
+
* Uses `openSync(path, "wx")` for atomic exclusive create — same primitive
|
|
102
|
+
* used by `pid-registry` for atomic per-agent manifests. On EEXIST: busy-wait
|
|
103
|
+
* `LOCK_POLL_MS` and retry until `LOCK_TIMEOUT_MS` elapses. Once the deadline
|
|
104
|
+
* passes we assume the previous holder crashed, force-unlink the lock, and
|
|
105
|
+
* retry once. If that retry still fails (e.g. permissions/IO error), throw.
|
|
106
|
+
*
|
|
107
|
+
* Why time-based recovery instead of PID liveness checks: reading the lock
|
|
108
|
+
* file's content to extract a holder PID requires a second `openSync(path, "r")`,
|
|
109
|
+
* which CodeQL flags as TOCTOU against the EEXIST signal even though we only
|
|
110
|
+
* extract a number. Time-based recovery uses a single primitive (`openSync(wx)`),
|
|
111
|
+
* so there is no path-based race surface to flag.
|
|
112
|
+
*
|
|
113
|
+
* Synchronous on purpose — the rest of this module is sync, and CLI mutators
|
|
114
|
+
* are short-running. Blocking the event loop for at most 5s is acceptable.
|
|
115
|
+
*/
|
|
116
|
+
function acquireLock(opts: IndexOptions = {}): LockHandle {
|
|
117
|
+
ensureDir(opts);
|
|
118
|
+
const path = lockPath(opts);
|
|
119
|
+
const deadline = Date.now() + LOCK_TIMEOUT_MS;
|
|
120
|
+
const body = JSON.stringify({
|
|
121
|
+
pid: process.pid,
|
|
122
|
+
acquired: new Date().toISOString(),
|
|
123
|
+
} satisfies LockFileContents);
|
|
124
|
+
|
|
125
|
+
for (;;) {
|
|
126
|
+
const handle = tryAcquire(path, body);
|
|
127
|
+
if (handle) return handle;
|
|
128
|
+
|
|
129
|
+
if (Date.now() >= deadline) {
|
|
130
|
+
// Force-recover: assume the previous holder crashed.
|
|
131
|
+
try {
|
|
132
|
+
unlinkSync(path);
|
|
133
|
+
} catch {
|
|
134
|
+
// Race: someone else cleaned it up. Recovery retry below will succeed.
|
|
135
|
+
}
|
|
136
|
+
const recovered = tryAcquire(path, body);
|
|
137
|
+
if (recovered) return recovered;
|
|
138
|
+
throw new Error(
|
|
139
|
+
`Could not acquire agents.json lock at ${path} after ${LOCK_TIMEOUT_MS}ms ` +
|
|
140
|
+
`(another process holding it).`,
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
Bun.sleepSync(LOCK_POLL_MS);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Read the index. Returns an empty index when the file doesn't exist.
|
|
150
|
+
*
|
|
151
|
+
* On JSON parse failure, backs up the corrupt file to
|
|
152
|
+
* `agents.json.corrupt-<ISO>` and returns an empty index — operator can
|
|
153
|
+
* recover from the backup if needed.
|
|
154
|
+
*
|
|
155
|
+
* Throws on unknown schema versions (forward-compat guard).
|
|
156
|
+
*/
|
|
157
|
+
export function readIndex(opts: IndexOptions = {}): IndexFile {
|
|
158
|
+
const path = indexPath(opts);
|
|
159
|
+
|
|
160
|
+
// Open via fd to avoid existsSync+readFileSync TOCTOU.
|
|
161
|
+
let fd: number;
|
|
162
|
+
try {
|
|
163
|
+
fd = openSync(path, "r");
|
|
164
|
+
} catch (err) {
|
|
165
|
+
if ((err as NodeJS.ErrnoException).code === "ENOENT") {
|
|
166
|
+
return emptyIndex();
|
|
167
|
+
}
|
|
168
|
+
throw err;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
let raw: string;
|
|
172
|
+
try {
|
|
173
|
+
const stats = fstatSync(fd);
|
|
174
|
+
const buf = Buffer.alloc(stats.size);
|
|
175
|
+
if (stats.size > 0) {
|
|
176
|
+
readSync(fd, buf, 0, stats.size, 0);
|
|
177
|
+
}
|
|
178
|
+
raw = buf.toString("utf-8");
|
|
179
|
+
} finally {
|
|
180
|
+
try {
|
|
181
|
+
closeSync(fd);
|
|
182
|
+
} catch {
|
|
183
|
+
// best-effort
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
let parsed: unknown;
|
|
188
|
+
try {
|
|
189
|
+
parsed = JSON.parse(raw);
|
|
190
|
+
} catch (_err) {
|
|
191
|
+
const ts = new Date().toISOString().replace(/[:.]/g, "-");
|
|
192
|
+
const backupPath = join(getAuggyDir(opts), `agents.json.corrupt-${ts}`);
|
|
193
|
+
renameSync(path, backupPath);
|
|
194
|
+
console.warn(
|
|
195
|
+
`[agent-index] corrupt agents.json detected; backed up to ${backupPath}. Recreating empty index.`,
|
|
196
|
+
);
|
|
197
|
+
return emptyIndex();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (
|
|
201
|
+
!parsed ||
|
|
202
|
+
typeof parsed !== "object" ||
|
|
203
|
+
(parsed as { version?: number }).version !== SCHEMA_VERSION
|
|
204
|
+
) {
|
|
205
|
+
const got = (parsed as { version?: number } | null)?.version;
|
|
206
|
+
throw new Error(
|
|
207
|
+
`agents.json has unknown schema version ${got} (expected ${SCHEMA_VERSION}). ` +
|
|
208
|
+
`This file may be from a newer version of auggy.`,
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return parsed as IndexFile;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Write the index atomically: write to `agents.json.tmp`, then rename
|
|
217
|
+
* over the target. Same hygiene as `pid-registry.ts`.
|
|
218
|
+
*/
|
|
219
|
+
export function writeIndex(idx: IndexFile, opts: IndexOptions = {}): void {
|
|
220
|
+
ensureDir(opts);
|
|
221
|
+
const target = indexPath(opts);
|
|
222
|
+
const tmp = `${target}.tmp`;
|
|
223
|
+
writeFileSync(tmp, JSON.stringify(idx, null, 2));
|
|
224
|
+
renameSync(tmp, target);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Add an agent to the index. Throws if `name` is already registered.
|
|
229
|
+
*
|
|
230
|
+
* Holds an advisory lock for the read-modify-write window so two concurrent
|
|
231
|
+
* CLI invocations don't lose each other's updates.
|
|
232
|
+
*/
|
|
233
|
+
export function addAgent(name: string, localDir: string, opts: IndexOptions = {}): void {
|
|
234
|
+
const lock = acquireLock(opts);
|
|
235
|
+
try {
|
|
236
|
+
const idx = readIndex(opts);
|
|
237
|
+
if (idx.agents[name]) {
|
|
238
|
+
throw new Error(
|
|
239
|
+
`Agent "${name}" already registered at ${idx.agents[name].localDir}. ` +
|
|
240
|
+
`Choose a different name or remove the existing one with \`auggy remove ${name}\`.`,
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
idx.agents[name] = {
|
|
244
|
+
localDir,
|
|
245
|
+
createdAt: new Date().toISOString(),
|
|
246
|
+
cloud: null,
|
|
247
|
+
};
|
|
248
|
+
writeIndex(idx, opts);
|
|
249
|
+
} finally {
|
|
250
|
+
lock.release();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Remove an agent from the index. Idempotent — no-op when not present.
|
|
256
|
+
*
|
|
257
|
+
* Holds an advisory lock for the read-modify-write window. The lock is
|
|
258
|
+
* released even when no entry exists.
|
|
259
|
+
*/
|
|
260
|
+
export function removeAgent(name: string, opts: IndexOptions = {}): void {
|
|
261
|
+
const lock = acquireLock(opts);
|
|
262
|
+
try {
|
|
263
|
+
const idx = readIndex(opts);
|
|
264
|
+
if (!idx.agents[name]) return;
|
|
265
|
+
delete idx.agents[name];
|
|
266
|
+
writeIndex(idx, opts);
|
|
267
|
+
} finally {
|
|
268
|
+
lock.release();
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Look up an agent by name. Returns null if not registered.
|
|
274
|
+
*/
|
|
275
|
+
export function getAgent(name: string, opts: IndexOptions = {}): IndexEntry | null {
|
|
276
|
+
const idx = readIndex(opts);
|
|
277
|
+
return idx.agents[name] ?? null;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* List all registered agents with their names.
|
|
282
|
+
*/
|
|
283
|
+
export function listAgents(opts: IndexOptions = {}): Array<IndexEntry & { name: string }> {
|
|
284
|
+
const idx = readIndex(opts);
|
|
285
|
+
return Object.entries(idx.agents).map(([name, entry]) => ({
|
|
286
|
+
name,
|
|
287
|
+
...entry,
|
|
288
|
+
}));
|
|
289
|
+
}
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Augment catalog — metadata for all built-in augments.
|
|
3
|
+
*
|
|
4
|
+
* Used by `auggy create` (interactive selection) and `auggy add`
|
|
5
|
+
* (add to existing agent). Each entry describes what the augment
|
|
6
|
+
* does, its default config, and whether it ships with a bundled skill
|
|
7
|
+
* folder under `src/augments/<type>/skill/`.
|
|
8
|
+
*
|
|
9
|
+
* Skills are no longer carried as inline string templates here. Per
|
|
10
|
+
* ADR-025 + PR α task 4, scaffold copies `src/augments/<name>/skill/`
|
|
11
|
+
* into the agent dir directly; the catalog only records *whether* a
|
|
12
|
+
* bundled skill exists so the create UI can label entries accurately.
|
|
13
|
+
* The `scaffold-skills` module is the single source of truth for the
|
|
14
|
+
* type → folder mapping.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export interface CatalogEntry {
|
|
18
|
+
/** Display name for selection UI. */
|
|
19
|
+
label: string;
|
|
20
|
+
/** Short description shown in the selector. */
|
|
21
|
+
description: string;
|
|
22
|
+
/** The augment type identifier in agent.yaml. */
|
|
23
|
+
type: string;
|
|
24
|
+
/** Default instance name in agent.yaml. */
|
|
25
|
+
defaultName: string;
|
|
26
|
+
/** Default options for agent.yaml. */
|
|
27
|
+
defaultOptions: Record<string, unknown>;
|
|
28
|
+
/** Whether this augment is always included (not deselectable). */
|
|
29
|
+
required: boolean;
|
|
30
|
+
/** Env vars this augment needs (shown in .env.example). */
|
|
31
|
+
envVars?: string[];
|
|
32
|
+
/**
|
|
33
|
+
* Whether this augment ships a bundled `src/augments/<type>/skill/` folder
|
|
34
|
+
* the scaffold copies into the agent dir. Authoritative state lives on
|
|
35
|
+
* disk; this flag is informational for the create UI.
|
|
36
|
+
*/
|
|
37
|
+
hasSkill: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const AUGMENT_CATALOG: CatalogEntry[] = [
|
|
41
|
+
// NOTE on identity: the agent's identity preamble is mounted via the
|
|
42
|
+
// top-level `identity: ./identity.md` shorthand (see config-parser §α-5),
|
|
43
|
+
// not via a catalog entry. Keeping an explicit fileMemory@system entry
|
|
44
|
+
// here AND emitting the shorthand would trigger α-5's conflict check on
|
|
45
|
+
// the very first scaffold. The scaffold and the create command emit the
|
|
46
|
+
// shorthand directly; the catalog never carries an identity row.
|
|
47
|
+
{
|
|
48
|
+
label: "fileMemory (learned)",
|
|
49
|
+
description: "Mutable memory — agent writes learned behaviors here",
|
|
50
|
+
type: "fileMemory",
|
|
51
|
+
defaultName: "learned",
|
|
52
|
+
defaultOptions: {
|
|
53
|
+
label: "learned",
|
|
54
|
+
source: "./learned.md",
|
|
55
|
+
mutable: true,
|
|
56
|
+
origin: "system",
|
|
57
|
+
priority: "high",
|
|
58
|
+
placement: "preamble",
|
|
59
|
+
eviction: "drop",
|
|
60
|
+
},
|
|
61
|
+
required: true,
|
|
62
|
+
hasSkill: false,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
label: "layeredMemory",
|
|
66
|
+
description: "Peer-scoped episodic memory with provenance (SQLite or Supabase)",
|
|
67
|
+
type: "layeredMemory",
|
|
68
|
+
defaultName: "memory",
|
|
69
|
+
defaultOptions: {
|
|
70
|
+
backend: "sqlite",
|
|
71
|
+
dbPath: "./memory.sqlite",
|
|
72
|
+
namespace: "ep",
|
|
73
|
+
retentionDays: 90,
|
|
74
|
+
},
|
|
75
|
+
required: true,
|
|
76
|
+
hasSkill: true,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
label: "filesystem",
|
|
80
|
+
description: "Read/write files — skills directory + workspace",
|
|
81
|
+
type: "filesystem",
|
|
82
|
+
defaultName: "files",
|
|
83
|
+
defaultOptions: {
|
|
84
|
+
mounts: [
|
|
85
|
+
{ name: "skills", path: "./skills", writable: false },
|
|
86
|
+
{ name: "workspace", path: "./workspace", writable: true, deletable: true },
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
required: true,
|
|
90
|
+
hasSkill: true,
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
label: "webTransport",
|
|
94
|
+
description: "AG-UI chat endpoint (HTTP + SSE)",
|
|
95
|
+
type: "webTransport",
|
|
96
|
+
defaultName: "web",
|
|
97
|
+
defaultOptions: {
|
|
98
|
+
port: 8080,
|
|
99
|
+
auth: { type: "bearer", token: "${AUGGY_WEB_TOKEN}" },
|
|
100
|
+
visitorTokens: {
|
|
101
|
+
// signingKey is NOT set here — visitorAuth is the single source of truth
|
|
102
|
+
// and the resolver injects it. Setting it here would trigger the
|
|
103
|
+
// duplicate-key warning on every boot.
|
|
104
|
+
agentBinding: "${AUGGY_AGENT_ID}",
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
required: false,
|
|
108
|
+
envVars: ["AUGGY_WEB_TOKEN", "AUGGY_AGENT_ID"],
|
|
109
|
+
hasSkill: false,
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
label: "webFetch",
|
|
113
|
+
description: "Fetch URLs, read web pages, call HTTP APIs",
|
|
114
|
+
type: "webFetch",
|
|
115
|
+
defaultName: "fetch",
|
|
116
|
+
defaultOptions: {
|
|
117
|
+
timeoutMs: 15000,
|
|
118
|
+
},
|
|
119
|
+
required: false,
|
|
120
|
+
hasSkill: true,
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
label: "supabaseMemory",
|
|
124
|
+
description: "Episodic memory backed by Supabase (visitor profiles, events)",
|
|
125
|
+
type: "supabaseMemory",
|
|
126
|
+
defaultName: "episodic",
|
|
127
|
+
defaultOptions: {
|
|
128
|
+
namespace: "episode",
|
|
129
|
+
table: "agent_memories",
|
|
130
|
+
mutable: true,
|
|
131
|
+
origin: "peer-derived",
|
|
132
|
+
priority: "normal",
|
|
133
|
+
placement: "preamble",
|
|
134
|
+
eviction: "drop",
|
|
135
|
+
supabaseUrl: "${SUPABASE_URL}",
|
|
136
|
+
supabaseKey: "${SUPABASE_SERVICE_KEY}",
|
|
137
|
+
},
|
|
138
|
+
required: false,
|
|
139
|
+
envVars: ["SUPABASE_URL", "SUPABASE_SERVICE_KEY"],
|
|
140
|
+
hasSkill: false,
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
label: "orgContext",
|
|
144
|
+
description: "Connect to org knowledge API (manifest + org_fetch)",
|
|
145
|
+
type: "orgContext",
|
|
146
|
+
defaultName: "org",
|
|
147
|
+
defaultOptions: {
|
|
148
|
+
// Default to file:// scheme pointing at the scaffolded example dir
|
|
149
|
+
// (per α-6 + spec §Decision 9). Operators wanting an HTTP-served
|
|
150
|
+
// manifest replace this with `${ORG_CONTEXT_URL}` and provide the env.
|
|
151
|
+
baseUrl: "file://./org-context",
|
|
152
|
+
},
|
|
153
|
+
required: false,
|
|
154
|
+
hasSkill: true,
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
// ADR-030: model-facing skill surface. Emits a single system-placement
|
|
158
|
+
// context block listing each mounted skill's name + description (read
|
|
159
|
+
// from each SKILL.md's YAML frontmatter, agentskills.io standard).
|
|
160
|
+
// Activation is fs_read via the filesystem augment. Required because
|
|
161
|
+
// without it no skills are surfaced to the model; operators wanting an
|
|
162
|
+
// agent with literally zero skill discovery can edit agent.yaml after
|
|
163
|
+
// scaffolding.
|
|
164
|
+
label: "skills",
|
|
165
|
+
description: "Lists mounted skills for the model (ADR-030 skill surface)",
|
|
166
|
+
type: "skills",
|
|
167
|
+
defaultName: "skills",
|
|
168
|
+
defaultOptions: {
|
|
169
|
+
dir: "./skills",
|
|
170
|
+
},
|
|
171
|
+
required: true,
|
|
172
|
+
// The augment itself carries no skill — it IS the skill surface.
|
|
173
|
+
hasSkill: false,
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
label: "bash",
|
|
177
|
+
description: "Execute shell commands with configurable risk levels",
|
|
178
|
+
type: "bash",
|
|
179
|
+
defaultName: "bash",
|
|
180
|
+
defaultOptions: {
|
|
181
|
+
risk: "restricted",
|
|
182
|
+
allowedCommands: ["echo", "ls", "cat", "pwd", "date"],
|
|
183
|
+
},
|
|
184
|
+
required: false,
|
|
185
|
+
hasSkill: true,
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
label: "budgets",
|
|
189
|
+
description: "Per-trust-level turn budgets + dailyBudgetUsd ceiling (SQLite)",
|
|
190
|
+
type: "budgets",
|
|
191
|
+
defaultName: "budgets",
|
|
192
|
+
defaultOptions: {
|
|
193
|
+
dbPath: "./budgets.db",
|
|
194
|
+
caps: {
|
|
195
|
+
public: {
|
|
196
|
+
recognized: { maxTurnsPerThread: 20, maxTurnsPerDay: 50, maxUsdPerDay: 1 },
|
|
197
|
+
anonymous: { maxTurnsPerThread: 5 },
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
anonymousGlobalLimit: 30,
|
|
201
|
+
dailyBudgetUsd: 5,
|
|
202
|
+
},
|
|
203
|
+
required: false,
|
|
204
|
+
hasSkill: false,
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
label: "notify",
|
|
208
|
+
description:
|
|
209
|
+
"Outbound messaging to operator-defined destinations (webhook + telegram + agentmail adapters)",
|
|
210
|
+
type: "notify",
|
|
211
|
+
defaultName: "notify",
|
|
212
|
+
defaultOptions: {
|
|
213
|
+
destinations: [{ name: "creator", transport: "webhook", url: "${ORG_NOTIFY_URL}" }],
|
|
214
|
+
rateLimit: {
|
|
215
|
+
cooldownMs: 120_000,
|
|
216
|
+
globalMaxPerHour: 5,
|
|
217
|
+
dedupWindowMs: 300_000,
|
|
218
|
+
dedupThreshold: 0.6,
|
|
219
|
+
perPeerCooldownMs: 30_000,
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
required: false,
|
|
223
|
+
envVars: ["ORG_NOTIFY_URL"],
|
|
224
|
+
hasSkill: true,
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
label: "telegramTransport",
|
|
228
|
+
description: "Bidirectional Telegram I/O — long-poll OR webhook inbound, four-path identity",
|
|
229
|
+
type: "telegramTransport",
|
|
230
|
+
defaultName: "telegram",
|
|
231
|
+
defaultOptions: {
|
|
232
|
+
botToken: "${TELEGRAM_BOT_TOKEN}",
|
|
233
|
+
inbound: {
|
|
234
|
+
mode: "polling",
|
|
235
|
+
polling: { timeoutSec: 30 },
|
|
236
|
+
// To switch to webhook mode, replace the polling block with:
|
|
237
|
+
// mode: "webhook"
|
|
238
|
+
// webhook: { publicUrl: "${TELEGRAM_WEBHOOK_URL}", port: 8081, secretToken: "${TELEGRAM_WEBHOOK_SECRET}" }
|
|
239
|
+
},
|
|
240
|
+
auth: {
|
|
241
|
+
creatorUserIds: [],
|
|
242
|
+
anonymousIdentityMode: "ephemeral",
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
required: false,
|
|
246
|
+
envVars: ["TELEGRAM_BOT_TOKEN"],
|
|
247
|
+
hasSkill: false,
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
label: "Turn Control",
|
|
251
|
+
description:
|
|
252
|
+
"Lets the agent pause and request input from the user. Recommended for chat-shaped agents (web/telegram).",
|
|
253
|
+
type: "turnControl",
|
|
254
|
+
defaultName: "turn-control",
|
|
255
|
+
defaultOptions: {},
|
|
256
|
+
required: false,
|
|
257
|
+
hasSkill: true,
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
label: "link",
|
|
261
|
+
description:
|
|
262
|
+
"Peer-to-peer A2A v0.2 transport (auggy ↔ auggy / A2A-speaking peers) via @auggy/link",
|
|
263
|
+
type: "link",
|
|
264
|
+
defaultName: "link",
|
|
265
|
+
defaultOptions: {
|
|
266
|
+
port: 8081,
|
|
267
|
+
dbPath: "./link.db",
|
|
268
|
+
agentCard: {
|
|
269
|
+
id: "${AUGGY_AGENT_ID}",
|
|
270
|
+
name: "${AUGGY_AGENT_NAME}",
|
|
271
|
+
description: "augment-1 link endpoint",
|
|
272
|
+
endpointUrl: "${AUGGY_LINK_PUBLIC_URL}",
|
|
273
|
+
},
|
|
274
|
+
peers: {},
|
|
275
|
+
},
|
|
276
|
+
required: false,
|
|
277
|
+
envVars: ["AUGGY_AGENT_ID", "AUGGY_AGENT_NAME", "AUGGY_LINK_PUBLIC_URL"],
|
|
278
|
+
hasSkill: false,
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
label: "Visitor Auth",
|
|
282
|
+
description:
|
|
283
|
+
"Email magic-link verification — promotes anonymous visitors to recognized identity",
|
|
284
|
+
type: "visitorAuth",
|
|
285
|
+
defaultName: "visitor-auth",
|
|
286
|
+
defaultOptions: {
|
|
287
|
+
publicUrl: "${AUGGY_PUBLIC_URL}",
|
|
288
|
+
dbPath: "./visitor-auth.db",
|
|
289
|
+
agentMail: {
|
|
290
|
+
apiKey: "${AGENTMAIL_API_KEY}",
|
|
291
|
+
inboxId: "${AGENTMAIL_INBOX_ID}",
|
|
292
|
+
subjectPrefix: "[Verify] ",
|
|
293
|
+
},
|
|
294
|
+
signingKey: "${VISITOR_SIGNING_KEY}",
|
|
295
|
+
agentBinding: "${AUGGY_AGENT_ID}",
|
|
296
|
+
rateLimit: { perHour: 1, perDay: 3 },
|
|
297
|
+
reverifyAfterDays: 90,
|
|
298
|
+
tokenTtlMinutes: 15,
|
|
299
|
+
layeredMemoryDbPath: "./memory.db",
|
|
300
|
+
},
|
|
301
|
+
required: false,
|
|
302
|
+
envVars: [
|
|
303
|
+
"AGENTMAIL_API_KEY",
|
|
304
|
+
"AGENTMAIL_INBOX_ID",
|
|
305
|
+
"AUGGY_PUBLIC_URL",
|
|
306
|
+
"VISITOR_SIGNING_KEY",
|
|
307
|
+
"AUGGY_AGENT_ID",
|
|
308
|
+
],
|
|
309
|
+
hasSkill: true,
|
|
310
|
+
},
|
|
311
|
+
];
|
|
312
|
+
|
|
313
|
+
/** Get catalog entries that are not yet installed (by type + defaultName). */
|
|
314
|
+
export function getAvailableAugments(
|
|
315
|
+
installed: Array<{ type: string; name: string }>,
|
|
316
|
+
): CatalogEntry[] {
|
|
317
|
+
return AUGMENT_CATALOG.filter(
|
|
318
|
+
(entry) => !installed.some((i) => i.type === entry.type && i.name === entry.defaultName),
|
|
319
|
+
);
|
|
320
|
+
}
|