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.
Files changed (121) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/LICENSE +201 -0
  3. package/README.md +161 -0
  4. package/package.json +76 -0
  5. package/src/agent-card.ts +39 -0
  6. package/src/agent.ts +283 -0
  7. package/src/agentmail-client.ts +138 -0
  8. package/src/augments/bash/index.ts +463 -0
  9. package/src/augments/bash/skill/SKILL.md +156 -0
  10. package/src/augments/budgets/budget-store.ts +513 -0
  11. package/src/augments/budgets/index.ts +134 -0
  12. package/src/augments/budgets/preamble.ts +93 -0
  13. package/src/augments/budgets/types.ts +89 -0
  14. package/src/augments/file-memory/index.ts +71 -0
  15. package/src/augments/filesystem/index.ts +533 -0
  16. package/src/augments/filesystem/skill/SKILL.md +142 -0
  17. package/src/augments/filesystem/skill/references/mount-permissions.md +81 -0
  18. package/src/augments/layered-memory/extractor/buffer.ts +56 -0
  19. package/src/augments/layered-memory/extractor/frequency.ts +79 -0
  20. package/src/augments/layered-memory/extractor/inject-handler.ts +103 -0
  21. package/src/augments/layered-memory/extractor/parse.ts +75 -0
  22. package/src/augments/layered-memory/extractor/prompt.md +26 -0
  23. package/src/augments/layered-memory/index.ts +757 -0
  24. package/src/augments/layered-memory/skill/SKILL.md +153 -0
  25. package/src/augments/layered-memory/storage/migrations/README.md +16 -0
  26. package/src/augments/layered-memory/storage/migrations/supabase-add-fact-fields.sql +9 -0
  27. package/src/augments/layered-memory/storage/sqlite-store.ts +352 -0
  28. package/src/augments/layered-memory/storage/supabase-store.ts +263 -0
  29. package/src/augments/layered-memory/storage/types.ts +98 -0
  30. package/src/augments/link/index.ts +489 -0
  31. package/src/augments/link/translate.ts +261 -0
  32. package/src/augments/notify/adapters/agentmail.ts +70 -0
  33. package/src/augments/notify/adapters/telegram.ts +60 -0
  34. package/src/augments/notify/adapters/webhook.ts +55 -0
  35. package/src/augments/notify/index.ts +284 -0
  36. package/src/augments/notify/skill/SKILL.md +150 -0
  37. package/src/augments/org-context/index.ts +721 -0
  38. package/src/augments/org-context/skill/SKILL.md +96 -0
  39. package/src/augments/skills/index.ts +103 -0
  40. package/src/augments/supabase-memory/index.ts +151 -0
  41. package/src/augments/telegram-transport/index.ts +312 -0
  42. package/src/augments/telegram-transport/polling.ts +55 -0
  43. package/src/augments/telegram-transport/webhook.ts +56 -0
  44. package/src/augments/turn-control/index.ts +61 -0
  45. package/src/augments/turn-control/skill/SKILL.md +155 -0
  46. package/src/augments/visitor-auth/email-validation.ts +66 -0
  47. package/src/augments/visitor-auth/index.ts +779 -0
  48. package/src/augments/visitor-auth/rate-limiter.ts +90 -0
  49. package/src/augments/visitor-auth/skill/SKILL.md +55 -0
  50. package/src/augments/visitor-auth/storage/sqlite-store.ts +398 -0
  51. package/src/augments/visitor-auth/storage/types.ts +164 -0
  52. package/src/augments/visitor-auth/types.ts +123 -0
  53. package/src/augments/visitor-auth/verify-page.ts +179 -0
  54. package/src/augments/web-fetch/index.ts +331 -0
  55. package/src/augments/web-fetch/skill/SKILL.md +100 -0
  56. package/src/cli/agent-index.ts +289 -0
  57. package/src/cli/augment-catalog.ts +320 -0
  58. package/src/cli/augment-resolver.ts +597 -0
  59. package/src/cli/commands/add-skill.ts +194 -0
  60. package/src/cli/commands/add.ts +87 -0
  61. package/src/cli/commands/chat.ts +207 -0
  62. package/src/cli/commands/create.ts +462 -0
  63. package/src/cli/commands/dev.ts +139 -0
  64. package/src/cli/commands/eval.ts +180 -0
  65. package/src/cli/commands/ls.ts +66 -0
  66. package/src/cli/commands/remove.ts +95 -0
  67. package/src/cli/commands/restart.ts +40 -0
  68. package/src/cli/commands/start.ts +123 -0
  69. package/src/cli/commands/status.ts +104 -0
  70. package/src/cli/commands/stop.ts +84 -0
  71. package/src/cli/commands/visitors-revoke.ts +155 -0
  72. package/src/cli/commands/visitors.ts +101 -0
  73. package/src/cli/config-parser.ts +1034 -0
  74. package/src/cli/engine-resolver.ts +68 -0
  75. package/src/cli/index.ts +178 -0
  76. package/src/cli/model-picker.ts +89 -0
  77. package/src/cli/pid-registry.ts +146 -0
  78. package/src/cli/plist-generator.ts +117 -0
  79. package/src/cli/resolve-config.ts +56 -0
  80. package/src/cli/scaffold-skills.ts +158 -0
  81. package/src/cli/scaffold.ts +291 -0
  82. package/src/cli/skill-frontmatter.ts +51 -0
  83. package/src/cli/skill-validator.ts +151 -0
  84. package/src/cli/types.ts +228 -0
  85. package/src/cli/yaml-helpers.ts +66 -0
  86. package/src/engines/_shared/cost.ts +55 -0
  87. package/src/engines/_shared/schema-normalize.ts +75 -0
  88. package/src/engines/anthropic/pricing.ts +117 -0
  89. package/src/engines/anthropic.ts +483 -0
  90. package/src/engines/openai/pricing.ts +67 -0
  91. package/src/engines/openai.ts +446 -0
  92. package/src/engines/openrouter/pricing.ts +83 -0
  93. package/src/engines/openrouter.ts +185 -0
  94. package/src/helpers.ts +24 -0
  95. package/src/http.ts +387 -0
  96. package/src/index.ts +165 -0
  97. package/src/kernel/capability-table.ts +172 -0
  98. package/src/kernel/context-allocator.ts +161 -0
  99. package/src/kernel/history-manager.ts +198 -0
  100. package/src/kernel/lifecycle-manager.ts +106 -0
  101. package/src/kernel/output-validator.ts +35 -0
  102. package/src/kernel/preamble.ts +23 -0
  103. package/src/kernel/route-collector.ts +97 -0
  104. package/src/kernel/timeout.ts +21 -0
  105. package/src/kernel/tool-selector.ts +47 -0
  106. package/src/kernel/trace-emitter.ts +66 -0
  107. package/src/kernel/transport-queue.ts +147 -0
  108. package/src/kernel/turn-loop.ts +1148 -0
  109. package/src/memory/context-synthesis.ts +83 -0
  110. package/src/memory/memory-bus.ts +61 -0
  111. package/src/memory/registry.ts +80 -0
  112. package/src/memory/tools.ts +320 -0
  113. package/src/memory/types.ts +8 -0
  114. package/src/parts.ts +30 -0
  115. package/src/scaffold-templates/identity.md +31 -0
  116. package/src/telegram-client.ts +145 -0
  117. package/src/tokenizer.ts +14 -0
  118. package/src/transports/ag-ui-events.ts +253 -0
  119. package/src/transports/visitor-token.ts +82 -0
  120. package/src/transports/web-transport.ts +948 -0
  121. package/src/types.ts +1009 -0
package/src/agent.ts ADDED
@@ -0,0 +1,283 @@
1
+ import type {
2
+ AgentCard,
3
+ AgentConfig,
4
+ AgentHandle,
5
+ AgentHealth,
6
+ ModelClient,
7
+ TurnTrigger,
8
+ TurnResult,
9
+ TransportKernel,
10
+ PeerIdentity,
11
+ OutboundMessage,
12
+ SchedulerContext,
13
+ } from "./types";
14
+ import { createTokenizer } from "./tokenizer";
15
+ import { generateAgentCard } from "./agent-card";
16
+ import { wireMemoryBus } from "./memory/memory-bus";
17
+ import { createTurnLoop } from "./kernel/turn-loop";
18
+ import { createLifecycleManager } from "./kernel/lifecycle-manager";
19
+ import { createTransportQueue } from "./kernel/transport-queue";
20
+ import { collectAugmentRoutes } from "./kernel/route-collector";
21
+ import type { CollectedRoute } from "./kernel/route-collector";
22
+
23
+ export function defineAgent(config: AgentConfig, model: ModelClient): AgentHandle {
24
+ const tokenizer = createTokenizer();
25
+
26
+ // Wire the memory bus BEFORE constructing other kernel components.
27
+ // This synthesizes context() for memory providers and adds a synthetic
28
+ // augment carrying the generic memory tools.
29
+ const wiring = wireMemoryBus(config.augments);
30
+ const effectiveAugments = wiring.syntheticToolsAugment
31
+ ? [...wiring.augmentsWithSynthesizedContext, wiring.syntheticToolsAugment]
32
+ : wiring.augmentsWithSynthesizedContext;
33
+
34
+ const effectiveConfig: AgentConfig = {
35
+ ...config,
36
+ augments: effectiveAugments,
37
+ };
38
+
39
+ // Agent card is generated from the effective config (includes memory
40
+ // capability if any augment has a memory field).
41
+ const agentCard: AgentCard = generateAgentCard(effectiveConfig);
42
+
43
+ const lifecycle = createLifecycleManager({
44
+ name: effectiveConfig.name,
45
+ augments: effectiveAugments,
46
+ model,
47
+ });
48
+ const turnLoop = createTurnLoop({
49
+ augments: effectiveAugments,
50
+ model,
51
+ tokenizer,
52
+ config: effectiveConfig,
53
+ });
54
+
55
+ const outboundHandlers = new Map<
56
+ string,
57
+ (peer: PeerIdentity, message: OutboundMessage) => Promise<void>
58
+ >();
59
+
60
+ let started = false;
61
+
62
+ async function dispatchOutbound(result: TurnResult, trigger: TurnTrigger) {
63
+ // Collect all messages to dispatch: single response + multi-destination responses
64
+ const messages: OutboundMessage[] = [];
65
+ if (result.response) messages.push(result.response);
66
+ if (result.responses) messages.push(...result.responses);
67
+ if (messages.length === 0) return;
68
+
69
+ for (const msg of messages) {
70
+ const targetAugment = msg.targetAugment ?? trigger.source;
71
+ const peer = trigger.peer;
72
+ if (!targetAugment || !peer) continue;
73
+ const handler = outboundHandlers.get(targetAugment);
74
+ if (handler) {
75
+ await handler(peer, msg);
76
+ }
77
+ }
78
+ }
79
+
80
+ const handle: AgentHandle = {
81
+ async start() {
82
+ if (started) throw new Error("Agent already started. Call stop() first.");
83
+ await lifecycle.boot();
84
+
85
+ // PR γ.1 — collect augment-registered HTTP routes AFTER boot so
86
+ // onBoot-populated route lists are visible, BEFORE any transport
87
+ // binds a port so a collision can't leave the agent half-bound.
88
+ const collected = collectAugmentRoutes(effectiveAugments);
89
+ if (collected.errors.length > 0) {
90
+ // Run shutdown to undo the boot side-effects we just performed
91
+ // (otherwise SQLite handles, file watchers, etc. leak).
92
+ try {
93
+ await lifecycle.shutdown();
94
+ } catch {
95
+ // best-effort; the original validation error wins
96
+ }
97
+ throw new Error(
98
+ `Cannot start agent — augment HTTP route validation failed:\n ` +
99
+ collected.errors.join("\n "),
100
+ );
101
+ }
102
+ const augmentRoutes: readonly CollectedRoute[] = collected.routes;
103
+
104
+ // Register transport augments
105
+ for (const aug of effectiveAugments) {
106
+ if (aug.transport) {
107
+ const queue = createTransportQueue({
108
+ concurrency: aug.transport.concurrency ?? 1,
109
+ maxQueueDepth: aug.transport.maxQueueDepth ?? 50,
110
+ rateLimitPerPeer: aug.transport.rateLimitPerPeer,
111
+ });
112
+
113
+ const transportKernel: TransportKernel = {
114
+ async handleInbound(
115
+ trigger: TurnTrigger,
116
+ opts?: { onEvent?: import("./types").KernelEventHandler },
117
+ ): Promise<TurnResult> {
118
+ return queue.enqueue(trigger, async (t) => {
119
+ lifecycle.resetIdleTimer();
120
+ const threadId = t.threadId ?? t.turnId;
121
+ const result = await turnLoop.executeTurn(t, threadId, {
122
+ onEvent: opts?.onEvent,
123
+ });
124
+
125
+ await dispatchOutbound(result, t);
126
+
127
+ // Eager compaction
128
+ const historyBudget = Math.floor(
129
+ model.maxContextTokens * ((config.contextBudget?.historyPercent ?? 40) / 100),
130
+ );
131
+ turnLoop
132
+ .getHistoryManager(threadId)
133
+ .compact(historyBudget, config.compactionStrategy ?? "truncate");
134
+
135
+ // Run onTurnEnd hooks. Awaited sequentially in declaration
136
+ // order so ADR-027's lifecycle ordering guarantee holds —
137
+ // scheduleAfterTurn must observe a fully-settled onTurnEnd.
138
+ for (const a of effectiveAugments) {
139
+ if (a.onTurnEnd) {
140
+ try {
141
+ await a.onTurnEnd(result);
142
+ } catch (err) {
143
+ console.warn(`onTurnEnd hook "${a.name}" failed: ${err}`);
144
+ }
145
+ }
146
+ }
147
+
148
+ // ADR-027: dispatch scheduleAfterTurn (same semantics as the
149
+ // inject() path) — sequential, error-isolated, transcript
150
+ // closure-bound to the just-completed turn. The hook runs
151
+ // here as well as in inject() so transport-driven turns
152
+ // (web/Telegram) get the same post-turn surface as kernel-
153
+ // injected ones; PR β's auto-save depends on both paths
154
+ // firing identically.
155
+ const completedTurnIdT = result.turnId;
156
+ const completedThreadIdT = threadId;
157
+ const ctxT: SchedulerContext = {
158
+ inject: (next) => handle.inject(next),
159
+ getCompletedTranscript: async () =>
160
+ turnLoop.getHistoryManager(completedThreadIdT).getTranscript(completedTurnIdT),
161
+ };
162
+ for (const a of effectiveAugments) {
163
+ if (a.scheduleAfterTurn) {
164
+ try {
165
+ await a.scheduleAfterTurn(result, ctxT);
166
+ } catch (err) {
167
+ console.warn(
168
+ `scheduleAfterTurn hook "${a.name}" threw: ${(err as Error).message}`,
169
+ );
170
+ }
171
+ }
172
+ }
173
+
174
+ return result;
175
+ });
176
+ },
177
+ onOutbound(callback) {
178
+ outboundHandlers.set(aug.name, callback);
179
+ },
180
+ getAgentCard() {
181
+ return agentCard;
182
+ },
183
+ getAugmentRoutes() {
184
+ return augmentRoutes;
185
+ },
186
+ };
187
+ await aug.transport.register(transportKernel, aug.name);
188
+ }
189
+ }
190
+
191
+ // Start idle timer
192
+ lifecycle.startIdleTimer(async () => {
193
+ for (const aug of effectiveAugments) {
194
+ if (aug.onIdle) {
195
+ try {
196
+ await aug.onIdle();
197
+ } catch {
198
+ // Log and continue
199
+ }
200
+ }
201
+ }
202
+ });
203
+
204
+ started = true;
205
+ },
206
+
207
+ async stop() {
208
+ if (!started) return; // no-op if not started
209
+ lifecycle.stopIdleTimer();
210
+ await lifecycle.shutdown();
211
+ started = false;
212
+ },
213
+
214
+ async ready() {
215
+ if (!started) throw new Error("Agent not started");
216
+ },
217
+
218
+ health(): AgentHealth {
219
+ return lifecycle.health();
220
+ },
221
+
222
+ card(): AgentCard {
223
+ return agentCard;
224
+ },
225
+
226
+ async inject(trigger: TurnTrigger): Promise<TurnResult> {
227
+ lifecycle.resetIdleTimer();
228
+ const threadId = trigger.threadId ?? trigger.turnId;
229
+ const result = await turnLoop.executeTurn(trigger, threadId);
230
+
231
+ await dispatchOutbound(result, trigger);
232
+
233
+ // Eager compaction
234
+ const historyBudget = Math.floor(
235
+ model.maxContextTokens * ((config.contextBudget?.historyPercent ?? 40) / 100),
236
+ );
237
+ turnLoop
238
+ .getHistoryManager(threadId)
239
+ .compact(historyBudget, config.compactionStrategy ?? "truncate");
240
+
241
+ // Run onTurnEnd hooks. Awaited sequentially in declaration order so
242
+ // that ADR-027's lifecycle ordering guarantee holds — scheduleAfterTurn
243
+ // must observe a fully-settled onTurnEnd. Errors are caught/logged so
244
+ // a single failing hook never propagates out of the inject path.
245
+ for (const a of effectiveAugments) {
246
+ if (a.onTurnEnd) {
247
+ try {
248
+ await a.onTurnEnd(result);
249
+ } catch (err) {
250
+ console.warn(`onTurnEnd hook "${a.name}" failed: ${err}`);
251
+ }
252
+ }
253
+ }
254
+
255
+ // ADR-027: dispatch scheduleAfterTurn for augments that registered it.
256
+ // SchedulerContext closes over inject (this handle's method) +
257
+ // getCompletedTranscript (closure-bound to the just-completed turn id;
258
+ // arbitrary-turnId reads stay kernel-internal at v1.0). Sequential
259
+ // execution in declaration order per ADR-027 Decision 2; errors are
260
+ // caught + logged, never propagated — background work is best-effort.
261
+ const completedTurnId = result.turnId;
262
+ const completedThreadId = threadId;
263
+ const ctx: SchedulerContext = {
264
+ inject: (t) => handle.inject(t),
265
+ getCompletedTranscript: async () =>
266
+ turnLoop.getHistoryManager(completedThreadId).getTranscript(completedTurnId),
267
+ };
268
+ for (const a of effectiveAugments) {
269
+ if (a.scheduleAfterTurn) {
270
+ try {
271
+ await a.scheduleAfterTurn(result, ctx);
272
+ } catch (err) {
273
+ console.warn(`scheduleAfterTurn hook "${a.name}" threw: ${(err as Error).message}`);
274
+ }
275
+ }
276
+ }
277
+
278
+ return result;
279
+ },
280
+ };
281
+
282
+ return handle;
283
+ }
@@ -0,0 +1,138 @@
1
+ /**
2
+ * AgentMail HTTP client — stateless infrastructure shared by the notify
3
+ * agentmail adapter and (future) the agentMail augment.
4
+ *
5
+ * Pattern matches src/telegram-client.ts: env-var or constructor-arg keyed,
6
+ * no SQLite state, no augment-system coupling.
7
+ */
8
+
9
+ import { createHttpClient } from "./http";
10
+ import type { HttpClient } from "./http";
11
+
12
+ const DEFAULT_BASE_URL = "https://api.agentmail.to/v0";
13
+
14
+ export interface AgentMailClientOptions {
15
+ apiKey: string;
16
+ /** Override AgentMail API base URL (testing/sandbox). */
17
+ apiBaseUrl?: string;
18
+ /** Timeout per request. Default 15s. */
19
+ timeoutMs?: number;
20
+ /** Test-only HTTP client override. */
21
+ http?: Pick<HttpClient, "post" | "get">;
22
+ }
23
+
24
+ export interface AgentMailInboxInfo {
25
+ inboxId: string;
26
+ /** Echoed back when the inbox exists. */
27
+ status: "ok";
28
+ }
29
+
30
+ export interface AgentMailInboxError {
31
+ status: "failed";
32
+ detail: string;
33
+ /** HTTP status if the failure originated from AgentMail (vs. network). */
34
+ httpStatus?: number;
35
+ }
36
+
37
+ export interface SendMessageInput {
38
+ inboxId: string;
39
+ to: string[];
40
+ subject: string;
41
+ text: string;
42
+ html?: string;
43
+ labels?: string[];
44
+ }
45
+
46
+ export interface SendMessageResult {
47
+ status: "sent";
48
+ messageId: string;
49
+ threadId: string;
50
+ }
51
+
52
+ export interface SendMessageError {
53
+ status: "failed";
54
+ detail: string;
55
+ /** HTTP status if the failure originated from AgentMail (vs. network). */
56
+ httpStatus?: number;
57
+ /** AgentMail-returned Retry-After if 429. */
58
+ retryAfterSec?: number;
59
+ }
60
+
61
+ export interface AgentMailClient {
62
+ send(input: SendMessageInput): Promise<SendMessageResult | SendMessageError>;
63
+ /**
64
+ * Best-effort healthcheck. Pings AgentMail's `inboxes.get` endpoint to
65
+ * confirm the inbox exists and the API key has access. Used by visitorAuth
66
+ * onBoot. Caller should warn-and-continue on failure: a transient AgentMail
67
+ * outage shouldn't block agent startup; the first real send will surface
68
+ * the same error.
69
+ */
70
+ getInbox(inboxId: string): Promise<AgentMailInboxInfo | AgentMailInboxError>;
71
+ }
72
+
73
+ export function createAgentMailClient(opts: AgentMailClientOptions): AgentMailClient {
74
+ const baseUrl = opts.apiBaseUrl ?? DEFAULT_BASE_URL;
75
+ const http =
76
+ opts.http ??
77
+ createHttpClient({
78
+ timeoutMs: opts.timeoutMs ?? 15_000,
79
+ userAgent: "auggy-agentmail-client/0.1",
80
+ });
81
+ return {
82
+ async send(input) {
83
+ const url = `${baseUrl}/inboxes/${input.inboxId}/messages`;
84
+ const body = JSON.stringify({
85
+ to: input.to,
86
+ subject: input.subject,
87
+ text: input.text,
88
+ ...(input.html ? { html: input.html } : {}),
89
+ ...(input.labels && input.labels.length > 0 ? { labels: input.labels } : {}),
90
+ });
91
+ try {
92
+ const res = await http.post(url, {
93
+ headers: {
94
+ "content-type": "application/json",
95
+ authorization: `Bearer ${opts.apiKey}`,
96
+ },
97
+ body,
98
+ });
99
+ if (res.status < 200 || res.status >= 300) {
100
+ const result: SendMessageError = {
101
+ status: "failed",
102
+ detail: `agentmail returned ${res.status}: ${res.body.slice(0, 200)}`,
103
+ httpStatus: res.status,
104
+ };
105
+ if (res.status === 429) {
106
+ const retry = res.headers.get("retry-after");
107
+ if (retry) result.retryAfterSec = Number(retry) || undefined;
108
+ }
109
+ return result;
110
+ }
111
+ const parsed = JSON.parse(res.body) as { message_id: string; thread_id: string };
112
+ return { status: "sent", messageId: parsed.message_id, threadId: parsed.thread_id };
113
+ } catch (err) {
114
+ return { status: "failed", detail: `agentmail error: ${(err as Error).message}` };
115
+ }
116
+ },
117
+ async getInbox(inboxId: string) {
118
+ const url = `${baseUrl}/inboxes/${inboxId}`;
119
+ try {
120
+ const res = await http.get(url, {
121
+ headers: {
122
+ authorization: `Bearer ${opts.apiKey}`,
123
+ },
124
+ });
125
+ if (res.status < 200 || res.status >= 300) {
126
+ return {
127
+ status: "failed" as const,
128
+ detail: `agentmail returned ${res.status}: ${res.body.slice(0, 200)}`,
129
+ httpStatus: res.status,
130
+ };
131
+ }
132
+ return { inboxId, status: "ok" as const };
133
+ } catch (err) {
134
+ return { status: "failed" as const, detail: `agentmail error: ${(err as Error).message}` };
135
+ }
136
+ },
137
+ };
138
+ }