@anteros/core 0.0.41 → 0.0.44

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 (133) hide show
  1. package/database/audit.ts +9 -59
  2. package/database/collection.ts +3 -1
  3. package/database/deleteLog.ts +67 -3
  4. package/database/file.ts +4 -3
  5. package/database/mongodbadapter.ts +103 -42
  6. package/database/replication.ts +724 -58
  7. package/database/ttl.ts +68 -0
  8. package/database/workflow.ts +466 -240
  9. package/index.ts +6 -0
  10. package/lib/agent.ts +1567 -0
  11. package/lib/agentMemory.ts +689 -0
  12. package/lib/agents.ts +180 -0
  13. package/lib/ai.ts +0 -0
  14. package/lib/api.ts +246 -0
  15. package/lib/attachment.ts +351 -0
  16. package/lib/auditResult.ts +13 -0
  17. package/lib/define.ts +43 -1
  18. package/lib/endpoints.ts +80 -0
  19. package/lib/files.ts +2 -1
  20. package/lib/jsonSchema.ts +183 -0
  21. package/lib/lifecycle.ts +7 -1
  22. package/lib/load.ts +17 -0
  23. package/lib/mcp.ts +3 -2
  24. package/lib/mcpClient.ts +128 -0
  25. package/lib/messages.ts +299 -0
  26. package/lib/providers.ts +970 -0
  27. package/lib/routes.ts +14 -1
  28. package/lib/scriptWorker.ts +4 -1
  29. package/lib/scripts.ts +6 -1
  30. package/lib/skills.ts +481 -0
  31. package/lib/sockets.ts +3 -1
  32. package/lib/workflow.ts +209 -37
  33. package/lib/zip.ts +109 -0
  34. package/package.json +1 -1
  35. package/server/access.ts +96 -0
  36. package/server/agents.ts +472 -0
  37. package/server/api.ts +80 -65
  38. package/server/boot.ts +11 -0
  39. package/server/hono.ts +8 -3
  40. package/server/mcp.ts +17 -63
  41. package/server/routes.ts +19 -24
  42. package/tests/agent-mcp.test.ts +116 -0
  43. package/tests/agent-memory.test.ts +617 -0
  44. package/tests/agent-provider.test.ts +288 -0
  45. package/tests/agent-runtime.test.ts +369 -0
  46. package/tests/agent-skills.test.ts +177 -0
  47. package/tests/agent-summarize.test.ts +124 -0
  48. package/tests/agent-working-memory.test.ts +404 -0
  49. package/tests/agent.test.ts +1241 -0
  50. package/tests/agents-api.test.ts +593 -0
  51. package/tests/api-private-fields.test.ts +196 -0
  52. package/tests/api.test.ts +51 -0
  53. package/tests/attachment.test.ts +241 -0
  54. package/tests/audit-operations.test.ts +1 -1
  55. package/tests/audit-redact.test.ts +29 -3
  56. package/tests/audit-run-action.test.ts +1 -1
  57. package/tests/endpoints.test.ts +80 -0
  58. package/tests/file-collection-replication.test.ts +2 -0
  59. package/tests/fixtures/agent-tenant/agents/support.agent.ts +9 -0
  60. package/tests/fixtures/agent-tenant/agents/weather.agent.ts +21 -0
  61. package/tests/fixtures/agents-api/agents/assistant.agent.ts +46 -0
  62. package/tests/fixtures/agents-api/agents/plain.agent.ts +23 -0
  63. package/tests/fixtures/agents-api/agents/private.agent.ts +15 -0
  64. package/tests/fixtures/agents-api/collections/notes.model.ts +26 -0
  65. package/tests/fixtures/agents-api/routes/probe.route.ts +21 -0
  66. package/tests/fixtures/agents-api/services/probe.service.ts +19 -0
  67. package/tests/fixtures/attachment/data.csv +2 -0
  68. package/tests/fixtures/fake-provider.ts +128 -0
  69. package/tests/fixtures/repl-only/app.ts +3 -0
  70. package/tests/fixtures/replication-meta/agents/chat.agent.ts +16 -0
  71. package/tests/fixtures/replication-meta/collections/counters.model.ts +13 -0
  72. package/tests/fixtures/replication-meta/collections/orders.model.ts +9 -0
  73. package/tests/fixtures/replication-meta/workflows/run.workflow.ts +8 -0
  74. package/tests/fixtures/skills-tenant/agents/docs.agent.ts +23 -0
  75. package/tests/fixtures/skills-tenant/agents/incomplete.agent.ts +14 -0
  76. package/tests/fixtures/skills-tenant/broken/bad-name.md +6 -0
  77. package/tests/fixtures/skills-tenant/broken/dup-a.md +6 -0
  78. package/tests/fixtures/skills-tenant/broken/dup-b.md +6 -0
  79. package/tests/fixtures/skills-tenant/broken/empty-body.md +4 -0
  80. package/tests/fixtures/skills-tenant/broken/long-compat.md +7 -0
  81. package/tests/fixtures/skills-tenant/broken/long-description.md +6 -0
  82. package/tests/fixtures/skills-tenant/broken/mismatch/SKILL.md +6 -0
  83. package/tests/fixtures/skills-tenant/broken/no-description.md +5 -0
  84. package/tests/fixtures/skills-tenant/broken/no-name.md +5 -0
  85. package/tests/fixtures/skills-tenant/broken/plain.md +4 -0
  86. package/tests/fixtures/skills-tenant/broken/unknown-key.md +9 -0
  87. package/tests/fixtures/skills-tenant/docs/pdf-forms.md +18 -0
  88. package/tests/fixtures/skills-tenant/guide/ml.md +9 -0
  89. package/tests/fixtures/skills-tenant/guides/pdf/SKILL.md +9 -0
  90. package/tests/fixtures/skills-tenant/runbooks/incident.md +9 -0
  91. package/tests/fixtures/skills-tenant/runbooks/notes.txt +2 -0
  92. package/tests/fixtures/vars-tenant/vars/private.var.ts +4 -1
  93. package/tests/fixtures/workflow-engine/workflows/failing.workflow.ts +34 -0
  94. package/tests/fixtures/workflow-engine/workflows/flaky.workflow.ts +23 -0
  95. package/tests/fixtures/workflow-engine/workflows/hang.workflow.ts +17 -0
  96. package/tests/fixtures/workflow-engine/workflows/simple.workflow.ts +19 -0
  97. package/tests/fixtures/workflow-engine/workflows/slow.workflow.ts +23 -0
  98. package/tests/fixtures/workflow-engine/workflows/versioned.workflow.ts +19 -0
  99. package/tests/fixtures/workflow-tenant/workflows/broken.workflow.ts +3 -0
  100. package/tests/fixtures/workflow-tenant/workflows/disabled.workflow.ts +8 -0
  101. package/tests/fixtures/workflow-tenant/workflows/invoice.workflow.ts +21 -0
  102. package/tests/fixtures/workflow-tenant/workflows/no-id.workflow.ts +7 -0
  103. package/tests/fixtures/workflow-tenant/workflows/order-duplicate.workflow.ts +13 -0
  104. package/tests/fixtures/workflow-tenant/workflows/order.workflow.ts +17 -0
  105. package/tests/hono.test.ts +6 -3
  106. package/tests/locks.test.ts +120 -0
  107. package/tests/mcp.test.ts +11 -11
  108. package/tests/messages.test.ts +258 -0
  109. package/tests/replication-change-stream.test.ts +240 -0
  110. package/tests/replication-control.test.ts +4 -2
  111. package/tests/replication-initial-sync.test.ts +2 -0
  112. package/tests/replication-meta.test.ts +246 -0
  113. package/tests/replication.test.ts +2 -0
  114. package/tests/upload.test.ts +44 -12
  115. package/tests/vars-api.test.ts +1 -1
  116. package/tests/vars-replication.test.ts +5 -2
  117. package/tests/workflow-engine.test.ts +340 -0
  118. package/tests/workflow-retention.test.ts +103 -0
  119. package/tests/workflow.test.ts +186 -0
  120. package/types/agent.d.ts +1067 -0
  121. package/types/config.d.ts +16 -0
  122. package/types/file.d.ts +7 -7
  123. package/types/hook.d.ts +6 -0
  124. package/types/lifecycle.d.ts +6 -0
  125. package/types/mcp.d.ts +11 -1
  126. package/types/replication.d.ts +70 -4
  127. package/types/route.d.ts +7 -1
  128. package/types/scripts.d.ts +6 -0
  129. package/types/service.d.ts +7 -1
  130. package/types/tenant.d.ts +13 -0
  131. package/types/vars.d.ts +2 -2
  132. package/types/websocket.d.ts +6 -0
  133. package/types/workflow.d.ts +50 -3
@@ -0,0 +1,1067 @@
1
+ import type Joi from "joi";
2
+ import type { useRest } from "../database/rest";
3
+ import type { fn } from "../lib/error";
4
+ import type { jwt } from "../utils/func";
5
+ import type { Agent } from "../lib/agent";
6
+
7
+ /**
8
+ * Wire protocol spoken by a model provider.
9
+ *
10
+ * `'openai'` (the default) also covers every OpenAI-compatible gateway
11
+ * (Groq, Mistral, OpenRouter, Ollama, LM Studio, vLLM…) — only `baseUrl` changes.
12
+ */
13
+ export type AgentProtocol = "openai" | "anthropic";
14
+
15
+ /**
16
+ * @deprecated Renamed to `AgentProtocol` — the field was named after a property
17
+ * of the remote endpoint, when what it really selects is the shape of the
18
+ * request and response on the wire.
19
+ */
20
+ export type AgentCompatible = AgentProtocol;
21
+
22
+ /**
23
+ * Model provider — **where** the agent's reasoning comes from: the model, the
24
+ * credentials, the endpoint, and how it is called.
25
+ *
26
+ * ```ts
27
+ * provider: { model: 'gpt-4o-mini', apiKey: 'sk-…' } // OpenAI
28
+ * provider: { model: 'claude-sonnet-4-5', apiKey: 'sk-ant-…', protocol: 'anthropic' }
29
+ * provider: { model: 'llama3.1', protocol: 'openai', baseUrl: 'http://localhost:11434/v1' }
30
+ * ```
31
+ *
32
+ * The key falls back to the environment when omitted:
33
+ * `ANTEROS_AI_API_KEY`, then `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` according to
34
+ * `protocol`. It is therefore never required in the source.
35
+ */
36
+ export type AgentProvider = {
37
+ /** Model id sent to the API (e.g. `gpt-4o-mini`, `claude-sonnet-4-5`). */
38
+ model: string;
39
+ /** API key — falls back to the environment (see above). */
40
+ apiKey?: string;
41
+ /**
42
+ * Wire protocol of the endpoint (default `'openai'`). `'openai'` is the
43
+ * Chat Completions API — the one every OpenAI-compatible gateway
44
+ * (Groq, Mistral, Ollama…) speaks too — `'anthropic'` is the Messages API.
45
+ */
46
+ protocol?: AgentProtocol;
47
+ /**
48
+ * @deprecated Alias of `protocol`, kept so a definition written before the
49
+ * rename keeps working. `protocol` wins when both are set.
50
+ */
51
+ compatible?: AgentProtocol;
52
+ /** API root override — default `https://api.openai.com/v1` or `https://api.anthropic.com`. */
53
+ baseUrl?: string;
54
+ /** Extra headers sent with every request. */
55
+ headers?: Record<string, string>;
56
+ /** Sampling and transport — `{ temperature, maxTokens, topP, timeout, retries }`. */
57
+ options?: AgentOptions;
58
+ };
59
+
60
+ /**
61
+ * How the provider is called — declared under `provider.options`, overridable
62
+ * per call (`{ provider: { options: { … } } }`, merged).
63
+ *
64
+ * ```ts
65
+ * provider: { model: 'gpt-4o-mini', options: { temperature: 0.2, maxTokens: 1000 } }
66
+ * ```
67
+ */
68
+ export type AgentOptions = {
69
+ /** Sampling temperature. */
70
+ temperature?: number;
71
+ /** Max tokens generated per model call (default 4096). */
72
+ maxTokens?: number;
73
+ /** Nucleus sampling. */
74
+ topP?: number;
75
+ /** Request timeout in ms (default 120000). */
76
+ timeout?: number;
77
+ /** Retries on network errors, 429 and 5xx (default 2). */
78
+ retries?: number;
79
+ /**
80
+ * Mark the stable prefix of the prompt for the provider's **prompt cache**
81
+ * (default `false`). Anthropic only: a `cache_control` breakpoint is set on the
82
+ * system prompt (instructions + skills + working memory) and on the last tool,
83
+ * so the instructions are billed once instead of on every run.
84
+ *
85
+ * Ignored by an OpenAI-compatible gateway, which caches on its own.
86
+ */
87
+ cache?: boolean;
88
+ };
89
+
90
+ /** Text part of a multimodal message. */
91
+ export type AgentTextPart = { type: "text"; text: string };
92
+ /**
93
+ * An image — by **URL** or as **base64**.
94
+ *
95
+ * `url` is an `https://…` (the provider fetches it) or a `data:` URL; `data` is
96
+ * the raw base64 (no `data:` prefix), the form an upload or a `File` produces.
97
+ * One of the two is required.
98
+ */
99
+ export type AgentImagePart = { type: "image"; url?: string; data?: string; mimeType?: string };
100
+ /**
101
+ * A document — a PDF today — by **URL** or as **base64** (`data` is raw base64).
102
+ *
103
+ * Native input where the model supports it; a text extraction happens upstream
104
+ * for everything else. A **remote file URL is downloaded and inlined** before the
105
+ * call (OpenAI-compatible endpoints only accept base64), with the same 10 MB cap
106
+ * as an attachment.
107
+ */
108
+ export type AgentFilePart = {
109
+ type: "file";
110
+ /** Name shown to the model — the extension is part of what it reads. */
111
+ name?: string;
112
+ url?: string;
113
+ data?: string;
114
+ mimeType?: string;
115
+ };
116
+
117
+ /**
118
+ * A tool call, expressed as a **content part** — the spelling other LLM
119
+ * libraries use. `normalizeMessages()` hoists it onto the assistant message's
120
+ * `toolCalls`, so a conversation coming from anywhere lands on the same shape.
121
+ */
122
+ export type AgentToolCallPart = {
123
+ type: "tool-call";
124
+ id: string;
125
+ name: string;
126
+ /** Parsed arguments — `args` / `argsText` are the historical spellings. */
127
+ arguments?: unknown;
128
+ args?: Record<string, any>;
129
+ argsText?: string;
130
+ };
131
+
132
+ /**
133
+ * A tool result, expressed as a content part. Accepted on a `tool` message — or
134
+ * on a `user` message, which is how a whole tool round is replayed without
135
+ * leaving the `user` role. It is hoisted to a `tool` message.
136
+ */
137
+ export type AgentToolResultPart = {
138
+ type: "tool-result";
139
+ /** Id of the `tool-call` this answers. */
140
+ id: string;
141
+ name?: string;
142
+ /** The tool's return value — serialized for the model if it is not a string. */
143
+ result?: unknown;
144
+ /** Set instead of `result` when the tool failed. */
145
+ error?: string;
146
+ };
147
+
148
+ /** What a model actually receives — the media an attachment resolves to. */
149
+ export type AgentMediaPart = AgentTextPart | AgentImagePart | AgentFilePart;
150
+
151
+ /**
152
+ * What a caller may put in a message's `content` — the media **and** the tool
153
+ * parts, so a conversation built by another library is accepted as-is.
154
+ */
155
+ export type AgentContentPart = AgentMediaPart | AgentToolCallPart | AgentToolResultPart;
156
+
157
+ /**
158
+ * What a caller can attach to a run — resolved to content parts before the model
159
+ * sees them (see `lib/attachment.ts`).
160
+ *
161
+ * - **server-side**: a **path** (`'./invoices/2026-01.pdf'`), relative to `filesBaseDir`
162
+ * - **front-end / SDK**: a `File`/`Blob`, raw bytes, or `{ name, data }` in base64
163
+ */
164
+ export type AgentAttachment =
165
+ | string
166
+ | Uint8Array
167
+ | Blob
168
+ | {
169
+ /** File name — its extension decides how it is read. */
170
+ name?: string;
171
+ /** MIME type, when the name is not enough. */
172
+ mimeType?: string;
173
+ /** The payload: base64 (default) or UTF-8 text with `encoding: 'utf8'`. */
174
+ data: string | Uint8Array;
175
+ encoding?: "base64" | "utf8";
176
+ };
177
+
178
+ export type AgentUserMessage = { role: "user"; content: string | AgentMediaPart[]; name?: string };
179
+ /**
180
+ * A system message. `developer` is the role OpenAI-compatible endpoints use for
181
+ * an instruction that outranks a user turn — Anthropic has no such role, so it is
182
+ * folded into the system prompt there.
183
+ */
184
+ export type AgentSystemMessage = { role: "system" | "developer"; content: string; name?: string };
185
+ export type AgentAssistantMessage = {
186
+ role: "assistant";
187
+ content: string | null;
188
+ /** Set when the model asked to call tools (echoed back on the next turn). */
189
+ toolCalls?: AgentToolCall[];
190
+ /**
191
+ * The model's reasoning blocks, kept **raw** — Anthropic requires the original
192
+ * signed blocks to be echoed back when a tool round continues a thinking turn.
193
+ */
194
+ thinking?: AgentThinkingBlock[];
195
+ name?: string;
196
+ };
197
+
198
+ /** A reasoning block, exactly as the provider returned it (signature included). */
199
+ export type AgentThinkingBlock = Record<string, any>;
200
+
201
+ /**
202
+ * Extended thinking — off by default.
203
+ *
204
+ * ```ts
205
+ * await agent.generate('Prove that …', { thinking: 'high' });
206
+ * ```
207
+ *
208
+ * Mapped per protocol: Anthropic gets a real **thinking budget**, an
209
+ * OpenAI-compatible endpoint gets a `reasoning_effort` (the two knobs are not the
210
+ * same thing — a budget is a number, an effort is a level). On OpenAI-compatible
211
+ * gateways only the reasoning models honour it.
212
+ */
213
+ export type AgentThinking =
214
+ /** `true` → the provider's default: `'medium'` effort / a 4 096-token budget. */
215
+ | boolean
216
+ /** Pure level — the budget becomes 2 048 / 4 096 / 8 192 tokens. */
217
+ | "low"
218
+ | "medium"
219
+ | "high"
220
+ /** Explicit Anthropic budget (OpenAI-compatible receives `'medium'`, the closest level). */
221
+ | { budgetTokens: number };
222
+
223
+ /**
224
+ * How the model may use the tools it is given, per protocol.
225
+ *
226
+ * - `'auto'` (default) — every tool is sent, the model decides
227
+ * - `'none'` — the tools are sent but the model must answer directly
228
+ * - `'required'` — it must call a tool (Anthropic: `tool_choice: any`)
229
+ * - `{ name }` — it must call that one
230
+ *
231
+ * The framework never picks a tool itself: what reaches the model is decided by
232
+ * the tools of the run (see `tools`), and by this alone.
233
+ */
234
+ export type AgentToolChoice = "auto" | "none" | "required" | { name: string };
235
+
236
+ export type AgentToolMessage = {
237
+ role: "tool";
238
+ /** Id of the `tool_use` / `tool_call` this message answers. */
239
+ toolCallId: string;
240
+ name: string;
241
+ content: string;
242
+ isError?: boolean;
243
+ };
244
+
245
+ /** A single conversation message — the model-agnostic shape used everywhere. */
246
+ export type AgentMessage = AgentUserMessage | AgentSystemMessage | AgentAssistantMessage | AgentToolMessage;
247
+
248
+ /**
249
+ * What a caller may **pass**: the canonical messages, plus the unified content
250
+ * parts (`image` / `file` by URL or base64, `tool-call` / `tool-result`) another
251
+ * LLM library uses. `normalizeMessages()` folds them onto the canonical shape —
252
+ * a `tool-call` part becomes `toolCalls`, a `tool-result` part becomes a `tool`
253
+ * message, a `system`/`developer` message may carry a part array.
254
+ */
255
+ export type AgentMessageInput =
256
+ | { role: "system" | "developer"; content: string | AgentContentPart[]; name?: string }
257
+ | { role: "user"; content: string | AgentContentPart[]; name?: string }
258
+ | {
259
+ role: "assistant";
260
+ content?: string | AgentContentPart[] | null;
261
+ toolCalls?: AgentToolCall[];
262
+ thinking?: AgentThinkingBlock[];
263
+ name?: string;
264
+ }
265
+ | {
266
+ role: "tool";
267
+ content: string | AgentContentPart[];
268
+ toolCallId?: string;
269
+ name?: string;
270
+ isError?: boolean;
271
+ };
272
+
273
+ /** What `generate()` / `stream()` accept as input. */
274
+ export type AgentInput = string | AgentMessageInput | AgentMessageInput[];
275
+
276
+ /** A tool call requested by the model. */
277
+ export type AgentToolCall = {
278
+ id: string;
279
+ name: string;
280
+ /** Parsed arguments (`{}` when the model produced invalid JSON). */
281
+ args: Record<string, any>;
282
+ /** Raw JSON string as returned by the model. */
283
+ argsText?: string;
284
+ };
285
+
286
+ /** The outcome of executing one tool call. */
287
+ export type AgentToolResultEntry = {
288
+ id: string;
289
+ name: string;
290
+ args: Record<string, any>;
291
+ result?: any;
292
+ /** Set when the tool threw, was unknown, or its arguments were invalid. */
293
+ error?: string;
294
+ durationMs: number;
295
+ };
296
+
297
+ /** Normalized finish reason (`'max_steps'` is added by the agent itself). */
298
+ export type AgentFinishReason = "stop" | "tool_use" | "length" | "content_filter" | "aborted" | "error" | "max_steps";
299
+
300
+ export type AgentUsage = {
301
+ inputTokens: number;
302
+ outputTokens: number;
303
+ totalTokens: number;
304
+ /** How many model calls the run needed (1 + one per tool round). */
305
+ requests: number;
306
+ };
307
+
308
+ /** One model call of a run, with the tools it asked for. */
309
+ export type AgentStep = {
310
+ step: number;
311
+ text: string;
312
+ /** The model's reasoning for this step, when extended thinking is on. */
313
+ reasoning?: string;
314
+ /** Ids of the tools sent for this step — every tool the run allowed. */
315
+ tools?: string[];
316
+ toolCalls: AgentToolCall[];
317
+ toolResults: AgentToolResultEntry[];
318
+ finishReason: string;
319
+ };
320
+
321
+ /** Execution context handed to a tool. */
322
+ export type AgentToolContext = {
323
+ rest?: InstanceType<typeof useRest>;
324
+ /** The agent currently running. */
325
+ agent: Agent;
326
+ /** Tenant of the agent (`_tenant_`), when loaded from a tenant folder. */
327
+ tenant?: string;
328
+ toolCallId: string;
329
+ step: number;
330
+ signal?: AbortSignal;
331
+ /** Throw an `AppError` with a stable code. */
332
+ error: typeof fn.error;
333
+ };
334
+
335
+ /**
336
+ * A tool an agent can call.
337
+ *
338
+ * ```ts
339
+ * tools: {
340
+ * forecast: {
341
+ * id: 'forecast',
342
+ * description: 'Current weather for a city',
343
+ * inputSchema: v.object({ city: v.string().required() }),
344
+ * execute: async ({ city }, { rest }) => ({ city, celsius: 21 }),
345
+ * },
346
+ * }
347
+ * ```
348
+ *
349
+ * An MCP tool (`define.McpTool`) is accepted as-is: its `exec` is adapted and
350
+ * its `{ content: [...] }` result is flattened to text. It carries the protocol's
351
+ * `name` instead of an `id` — the resolved tool name is `id ?? name`.
352
+ */
353
+ export type AgentTool = {
354
+ _isTool_?: boolean;
355
+ _isMcpTool_?: boolean;
356
+ _tenant_?: string;
357
+ /**
358
+ * Tool id — **required**, and the name the model calls. When a tool is declared
359
+ * in a record (`tools: { forecast }`), the key must be that same id.
360
+ */
361
+ id?: string;
362
+ /** MCP tools carry a `name`; agents resolve `id ?? name`. */
363
+ name?: string;
364
+ /** Shown to the model — say what it does and when to use it. */
365
+ description?: string;
366
+ enabled?: boolean;
367
+ /** Arguments schema: Joi, zod or a plain JSON Schema (defaults to an open object). */
368
+ inputSchema?: Joi.Schema | any;
369
+ /** Handler. The returned value is serialized back to the model. */
370
+ execute?: (input: any, ctx: AgentToolContext) => any | Promise<any>;
371
+ /** MCP-compatible handler (`define.McpTool`) — adapted automatically. */
372
+ exec?: (ctx: any) => any | Promise<any>;
373
+ /**
374
+ * Give up on this tool after N ms (default: the agent's `toolTimeoutMs`). The
375
+ * model is told it timed out and the run continues — an outbound call is the one
376
+ * thing the framework cannot bound for you.
377
+ */
378
+ timeoutMs?: number;
379
+ /**
380
+ * Ask the caller before running this tool (a payment, a delete, an email).
381
+ *
382
+ * The run **pauses** with `finishReason: 'approval_required'` and
383
+ * `result.pendingApprovals`; the caller resumes with
384
+ * `{ resume: { messages: result.messages, decisions } }`. Nothing is executed
385
+ * before a decision, and a refusal is reported to the model as a tool error.
386
+ */
387
+ approval?: boolean;
388
+ };
389
+
390
+ /** What a tool must carry to be **declared** — `define.Tool` enforces the id. */
391
+ export type AgentToolDefinition = AgentTool & { id: string };
392
+
393
+ export type AgentTools = Record<string, AgentTool> | AgentTool[];
394
+
395
+ /**
396
+ * Conversation store. The agent reads the thread before a run and writes the
397
+ * whole updated conversation after it.
398
+ *
399
+ * The optional `ctx` carries what a store may need — the client to write with
400
+ * (`rest`), the tenant, the agent and the caller identity. A store that ignores
401
+ * it (a plain `Map`) stays valid.
402
+ */
403
+ export type AgentMemory = {
404
+ get(threadId: string, ctx?: AgentMemoryContext): Promise<AgentMessage[]> | AgentMessage[];
405
+ save(threadId: string, messages: AgentMessage[], ctx?: AgentMemoryContext): Promise<void> | void;
406
+ clear(threadId: string, ctx?: AgentMemoryContext): Promise<void> | void;
407
+ /** Optional — a caller's threads, most recent first (a chat UI needs it). */
408
+ list?(ctx?: AgentMemoryContext & { limit?: number }): Promise<AgentThread[]> | AgentThread[];
409
+ /**
410
+ * Optional — the agent's **working memory**: one durable value per scope key
411
+ * (a profile, a scratchpad). A store that omits it makes `workingMemory`
412
+ * inert — the agent simply has no scratchpad.
413
+ */
414
+ getState?(key: string, ctx?: AgentMemoryContext): Promise<AgentMemoryState | undefined> | AgentMemoryState | undefined;
415
+ setState?(key: string, value: AgentMemoryState, ctx?: AgentMemoryContext): Promise<void> | void;
416
+ /** Optional — forget a scratchpad. */
417
+ clearState?(key: string, ctx?: AgentMemoryContext): Promise<void> | void;
418
+ /**
419
+ * Optional — the **MongoDB collections** the store writes to. The agent loader
420
+ * publishes them so the replication engine copies them like a declared
421
+ * collection (a store outside MongoDB returns nothing).
422
+ */
423
+ collections?(): string[];
424
+ };
425
+
426
+ /** One durable value of the working memory — a markdown block, or a JSON object. */
427
+ export type AgentMemoryState = {
428
+ value: any;
429
+ updatedAt?: Date;
430
+ };
431
+
432
+ /**
433
+ * A memory processor — the extension point of a store, at the two moments a
434
+ * memory matters:
435
+ *
436
+ * - `load` — after the thread, the injected turns and the input are assembled,
437
+ * **before** the model sees them. Rewrite them (redact, translate, drop a
438
+ * branch, inject what your own search found — see the Semantic recall section
439
+ * of the Memory page), or return nothing to leave them as they are.
440
+ * - `save` — before the conversation is persisted. Rewrite it, or **throw** to
441
+ * keep it out of the store entirely (a guardrail): the run still returns its
442
+ * answer, only the write is skipped.
443
+ *
444
+ * Processors run in declaration order, and a returned array replaces the
445
+ * messages for the next one.
446
+ */
447
+ export type AgentMemoryProcessor = {
448
+ /** A label for the logs — optional. */
449
+ id?: string;
450
+ load?: (
451
+ messages: AgentMessage[],
452
+ ctx: AgentMemoryContext,
453
+ ) => AgentMessage[] | void | Promise<AgentMessage[] | void>;
454
+ save?: (
455
+ messages: AgentMessage[],
456
+ ctx: AgentMemoryContext,
457
+ ) => AgentMessage[] | void | Promise<AgentMessage[] | void>;
458
+ };
459
+
460
+ /** What a memory store knows about the run asking for a thread. */
461
+ export type AgentMemoryContext = {
462
+ agentId?: string;
463
+ tenant?: string;
464
+ /** The run's client — a Mongo-backed store reads and writes through it. */
465
+ rest?: InstanceType<typeof useRest>;
466
+ /**
467
+ * Opaque caller identity. **Namespaces** the threads of a caller
468
+ * (`threadId` becomes `resourceId:threadId`) — it is **not** an authorization
469
+ * boundary: gate `history` / `clear` with `api.access`.
470
+ */
471
+ resourceId?: string;
472
+ /** Memory thread of the run — what a processor keys a per-thread cache on. */
473
+ thread?: string;
474
+ /**
475
+ * The store of the agent running — so a processor can read and write the
476
+ * scratchpad state of a thread (see `agents.memory.summarize`). Exposed on the
477
+ * **run** context only.
478
+ */
479
+ memory?: AgentMemory;
480
+ };
481
+
482
+ /** A thread as listed by a memory store (`agents.memory.Mongo().list()`). */
483
+ export type AgentThread = {
484
+ threadId: string;
485
+ resourceId?: string;
486
+ /** Derived from the first user message, at creation. */
487
+ title?: string;
488
+ /** Number of stored messages. */
489
+ messages: number;
490
+ createdAt?: Date;
491
+ updatedAt: Date;
492
+ };
493
+
494
+ /** Shared options of the built-in stores (`agents.memory.InMemory` / `.Mongo`). */
495
+ export type AgentMemoryOptions = {
496
+ /**
497
+ * Messages kept per thread (default 100). On every save the oldest are
498
+ * dropped: a conversation must not grow until it blows the context window.
499
+ */
500
+ maxMessages?: number;
501
+ /** Namespace each thread under its `resourceId` (default `true`). */
502
+ scoped?: boolean;
503
+ };
504
+
505
+ /**
506
+ * The agent's **working memory** — a durable scratchpad it keeps up to date
507
+ * (a user profile, the state of a task):
508
+ *
509
+ * ```ts
510
+ * define.Agent({
511
+ * memory: new agents.memory.Mongo({ collection: 'support_threads' }),
512
+ * workingMemory: {
513
+ * template: '# Profile\n- Name:\n- Timezone:\n- Preferences:',
514
+ * },
515
+ * })
516
+ * ```
517
+ *
518
+ * It is injected into the system prompt, and the model updates it through an
519
+ * auto-generated `updateWorkingMemory` tool. A **`template`** is free text the
520
+ * model replaces on each update; a **`schema`** is a validated JSON object that is
521
+ * **deep merged** (`null` removes a field, an array replaces an array). One of the
522
+ * two, not both.
523
+ */
524
+ export type AgentWorkingMemory = {
525
+ /** The markdown block the model fills in (replace semantics). */
526
+ template?: string;
527
+ /** Or a schema (Joi, zod, plain JSON Schema) — the update is a **partial** merge. */
528
+ schema?: Joi.Schema | any;
529
+ /**
530
+ * `'resource'` (default) — one scratchpad per caller, shared by all their
531
+ * threads (what a profile wants). `'thread'` — one per conversation.
532
+ */
533
+ scope?: "resource" | "thread";
534
+ /** Expose the update tool to the model (default `true`; `false` for a read-only prompt). */
535
+ tool?: boolean;
536
+ };
537
+
538
+ /** Options of `agents.memory.Mongo()`. */
539
+ export type MongoAgentMemoryOptions = AgentMemoryOptions & {
540
+ /**
541
+ * **Required** — the collection holding the threads, in the tenant database.
542
+ * The tenant names it (`_memories_`, `support_threads`…); the store never
543
+ * invents one, and the replication engine copies whatever you choose.
544
+ */
545
+ collection: string;
546
+ /**
547
+ * Opt-in retention on `updatedAt` (MongoDB TTL) — a duration string like
548
+ * `'180d'` or `'24h'`. `false` → explicitly disabled (drops the TTL index),
549
+ * omitted → threads are kept forever. Same vocabulary as the audit and
550
+ * workflow retention.
551
+ */
552
+ ttl?: string | false;
553
+ };
554
+
555
+ /**
556
+ * The subset of a Redis client the store uses — `ioredis` satisfies it.
557
+ * Structural on purpose: a test (or another driver) only has to implement these.
558
+ */
559
+ export type RedisClientLike = {
560
+ get(key: string): Promise<string | null>;
561
+ set(key: string, value: string, ...args: any[]): Promise<any>;
562
+ del(...keys: string[]): Promise<any>;
563
+ mget(...keys: string[]): Promise<Array<string | null>>;
564
+ expire(key: string, seconds: number): Promise<any>;
565
+ zadd(key: string, score: number, member: string): Promise<any>;
566
+ zrevrange(key: string, start: number, stop: number): Promise<string[]>;
567
+ zrem(key: string, ...members: string[]): Promise<any>;
568
+ };
569
+
570
+ /** Options of `agents.memory.Redis()`. */
571
+ export type RedisAgentMemoryOptions = AgentMemoryOptions & {
572
+ /** An existing client — the store never closes it. Takes precedence over the connection options. */
573
+ client?: RedisClientLike;
574
+ /** `redis://` connection string (default: `REDIS_URL`, then host/port/password, then `localhost:6379`). */
575
+ url?: string;
576
+ host?: string;
577
+ port?: number;
578
+ password?: string;
579
+ /** Key namespace (default `anteros:agent-memory`). */
580
+ prefix?: string;
581
+ /**
582
+ * Expiry of a thread — a duration string like `'7d'`, refreshed on every save.
583
+ * `false` → no expiry, omitted → kept forever.
584
+ */
585
+ ttl?: string | false;
586
+ };
587
+
588
+ /** Structured output request — the answer is parsed and validated against `schema`. */
589
+ export type AgentStructuredOutput = {
590
+ schema: Joi.Schema | any;
591
+ /**
592
+ * Name of the schema on the wire — `response_format.json_schema.name`
593
+ * (default: the agent id, sanitized).
594
+ */
595
+ name?: string;
596
+ /** What the object holds, sent to the provider next to the schema. */
597
+ description?: string;
598
+ };
599
+
600
+ /**
601
+ * A tool call that is waiting for its caller's decision — see
602
+ * `AgentTool.approval`. Served on `result.pendingApprovals`.
603
+ */
604
+ export type AgentPendingApproval = {
605
+ toolCallId: string;
606
+ name: string;
607
+ args: any;
608
+ };
609
+
610
+ /**
611
+ * What a paused run needs to go on: **the conversation it stopped on**, and one
612
+ * decision per pending call.
613
+ *
614
+ * ```ts
615
+ * const paused = await agent.generate('Refund order A-1', { thread: 't1' });
616
+ * if (paused.finishReason === 'approval_required') {
617
+ * await agent.generate('ignored — the run resumes from `messages`', {
618
+ * thread: 't1',
619
+ * resume: { messages: paused.messages, decisions: { [paused.pendingApprovals![0]!.toolCallId]: true } },
620
+ * });
621
+ * }
622
+ * ```
623
+ *
624
+ * The paused step is **not replayed through the model**: the approved tools run and
625
+ * the loop continues, so approving costs exactly one model call. A call left without
626
+ * a decision stays pending — the run pauses again.
627
+ */
628
+ export type AgentResume = {
629
+ /** The conversation to continue — `result.messages` of the paused run. */
630
+ messages: AgentMessage[] | AgentMessageInput[];
631
+ /** One decision per pending call, by `toolCallId` — `true` runs it, `false` refuses it. */
632
+ decisions: Record<string, boolean>;
633
+ };
634
+
635
+ export type AgentCallOptions = {
636
+ /** Thread to read from / write to the memory (same as `memory.thread`). */
637
+ thread?: string;
638
+ /** @deprecated Alias of `thread` — kept for compatibility. */
639
+ threadId?: string;
640
+ /** Opaque caller identity (same as `memory.resource`). */
641
+ resource?: string;
642
+ /** @deprecated Alias of `resource` — kept for compatibility. */
643
+ resourceId?: string;
644
+ /** Max model calls for this run (default: the agent's `maxSteps`, then 5). */
645
+ maxSteps?: number;
646
+ /** Window on the replayed history for this run — the **last N** messages. */
647
+ lastMessages?: number;
648
+ /** Override the provider for this call — including `provider.options` (merged). */
649
+ provider?: Partial<AgentProvider>;
650
+ /** Override the instructions for this run. */
651
+ instructions?: string;
652
+ /**
653
+ * Files and images sent with this run — a path (server-side) or a
654
+ * `File`/`Blob`/base64 payload (front-end, SDK).
655
+ *
656
+ * ```ts
657
+ * await agent.generate('Summarize this invoice', { files: ['./invoices/2026-01.pdf'] });
658
+ * ```
659
+ */
660
+ files?: AgentAttachment | AgentAttachment[];
661
+ /** Where a **path** attachment is resolved from (default: the process cwd). */
662
+ filesBaseDir?: string;
663
+ /**
664
+ * Tools of this run:
665
+ *
666
+ * - `true` (default) — **every** tool the agent declares
667
+ * - `false` — **none**: the run answers directly (the definitions are not even sent)
668
+ * - `['forecast', 'echo']` — **only these** (an allow-list; an unknown name is ignored)
669
+ * - `{ … }` / `[{ … }]` — extra tools, **merged** with the agent's own
670
+ *
671
+ * ```ts
672
+ * await agent.generate('Trie ce texte.', { tools: false });
673
+ * await agent.generate('Météo ?', { tools: ['forecast'] });
674
+ * ```
675
+ */
676
+ tools?: boolean | string[] | AgentTools;
677
+ /** How the model may use the tools (default `'auto'` — it decides). */
678
+ toolChoice?: AgentToolChoice;
679
+ /**
680
+ * Turns injected into the run — **after** the thread history, **before** the
681
+ * input. They are sent to the model *and* saved into the thread, so they are
682
+ * how a conversation is seeded or a history imported:
683
+ *
684
+ * ```ts
685
+ * await agent.generate('Et pour Lyon ?', {
686
+ * thread: 'chat-1',
687
+ * messages: [
688
+ * { role: 'user', content: 'Quel temps à Paris ?' },
689
+ * { role: 'assistant', content: '18°C et ensoleillé.' },
690
+ * ],
691
+ * });
692
+ * ```
693
+ *
694
+ * The block may end on an `assistant` turn (continue a conversation, prime a
695
+ * reply); it is normalized like the input and needs no `thread` to be sent.
696
+ * To write a thread **without** a model call, use `agent.remember()`.
697
+ */
698
+ messages?: AgentInput;
699
+ /** Extended thinking for this run — overrides the agent's `thinking` default. */
700
+ thinking?: AgentThinking;
701
+ /** Abort the run — the partial result is returned, not thrown. */
702
+ signal?: AbortSignal;
703
+ /** Shortcut for `structuredOutput: { schema }`. */
704
+ schema?: Joi.Schema | any;
705
+ /** Ask for a typed object instead of free text. */
706
+ structuredOutput?: AgentStructuredOutput;
707
+ /**
708
+ * Memory of this run — the Mastra-style shorthand:
709
+ *
710
+ * ```ts
711
+ * await agent.generate('Remember my favorite color is blue.', {
712
+ * memory: { resource: 'user-123', thread: 'conversation-123' },
713
+ * })
714
+ * ```
715
+ *
716
+ * The `memory` block wins over the flat `thread` / `resource` options.
717
+ * `threadId` / `resourceId` are accepted as deprecated aliases.
718
+ */
719
+ memory?: {
720
+ /** Thread to read from and write to (with `resource`, its namespace). */
721
+ thread?: string;
722
+ /** @deprecated Alias of `thread`. */
723
+ threadId?: string;
724
+ /** Caller identity — namespaces the thread and feeds dynamic instructions. */
725
+ resource?: string;
726
+ /** @deprecated Alias of `resource`. */
727
+ resourceId?: string;
728
+ /** Read the history but do not write the run back. */
729
+ readOnly?: boolean;
730
+ };
731
+ /** Called after each model call, before its tools run. */
732
+ onStepFinish?: (step: AgentStep) => void | Promise<void>;
733
+ /**
734
+ * Give up on a single tool after N ms (default: the agent's `toolTimeoutMs`).
735
+ * A tool that times out is reported to the model as an error, and the run goes on.
736
+ */
737
+ toolTimeoutMs?: number;
738
+ /**
739
+ * Run the tools of one step **in parallel** (default `false` — sequential).
740
+ *
741
+ * Only safe when the tools do not write to the same place: two calls of the same
742
+ * tool with the same target can interleave. Read-only tools are the point.
743
+ */
744
+ parallelTools?: boolean;
745
+ /**
746
+ * Store what the run produced even when it **fails** (default `false`).
747
+ *
748
+ * Without it, a provider error at step 3 throws and the whole exchange is lost —
749
+ * the tokens are paid and the thread never hears about it. With it, the thread
750
+ * keeps the turns up to the failure, and the next call resumes from there.
751
+ */
752
+ savePartial?: boolean;
753
+ /**
754
+ * Continue a run that paused for an approval instead of starting a new one —
755
+ * see `AgentResume`.
756
+ */
757
+ resume?: AgentResume;
758
+ };
759
+
760
+ export type AgentGenerateResult<T = any> = {
761
+ text: string;
762
+ /** Present when a `schema` / `structuredOutput` was requested. */
763
+ object?: T;
764
+ /** Everything the model thought, when `thinking` was on (all steps joined). */
765
+ reasoning?: string;
766
+ toolCalls: AgentToolCall[];
767
+ toolResults: AgentToolResultEntry[];
768
+ steps: AgentStep[];
769
+ usage: AgentUsage;
770
+ finishReason: string;
771
+ /** The complete conversation, ready to be persisted. */
772
+ messages: AgentMessage[];
773
+ /**
774
+ * Calls waiting for a decision — present (and `finishReason`
775
+ * `'approval_required'`) when a tool declaring `approval: true` was called.
776
+ * Resume the run with `{ resume: { messages, decisions } }`.
777
+ */
778
+ pendingApprovals?: AgentPendingApproval[];
779
+ error?: string;
780
+ };
781
+
782
+ /** Incremental event of `agent.stream()`. */
783
+ export type AgentStreamChunk =
784
+ | { type: "text"; text: string }
785
+ /** Thinking output — never mixed with the answer. */
786
+ | { type: "reasoning"; text: string }
787
+ | { type: "tool_call"; toolCall: AgentToolCall }
788
+ | { type: "tool_result"; toolResult: AgentToolResultEntry }
789
+ | { type: "step"; step: AgentStep }
790
+ | { type: "finish"; finishReason: string; usage: AgentUsage };
791
+
792
+ /**
793
+ * `agent.stream()` result — start consuming `textStream` immediately, the
794
+ * promises resolve when the run (tool rounds included) is over.
795
+ */
796
+ export type AgentStreamResult<T = any> = {
797
+ /** Every event, tool calls and results included. */
798
+ fullStream: AsyncIterable<AgentStreamChunk>;
799
+ /** Just the text tokens, as they arrive. */
800
+ textStream: AsyncIterable<string>;
801
+ text: Promise<string>;
802
+ /** Everything the model thought, when `thinking` was on — same as `generate()`. */
803
+ reasoning: Promise<string | undefined>;
804
+ object: Promise<T | undefined>;
805
+ toolCalls: Promise<AgentToolCall[]>;
806
+ toolResults: Promise<AgentToolResultEntry[]>;
807
+ steps: Promise<AgentStep[]>;
808
+ usage: Promise<AgentUsage>;
809
+ finishReason: Promise<string>;
810
+ messages: Promise<AgentMessage[]>;
811
+ /** Stop the run — the promises resolve with what has been produced so far. */
812
+ abort: (reason?: string) => void;
813
+ };
814
+
815
+ /** Instructions — static text, or computed per call (A/B tests, dynamic prompts). */
816
+ export type AgentInstructions =
817
+ | string
818
+ | ((ctx: {
819
+ agent: Agent;
820
+ /** Memory thread of the run. */
821
+ thread?: string;
822
+ /** Caller identity — what a per-user prompt branches on. */
823
+ resource?: string;
824
+ /** @deprecated Alias of `thread`. */
825
+ threadId?: string;
826
+ /** @deprecated Alias of `resource`. */
827
+ resourceId?: string;
828
+ rest?: InstanceType<typeof useRest>;
829
+ }) => string | Promise<string>);
830
+
831
+ /** Actions exposed by the agents HTTP API (`POST /api/:tenant_id/agents/:agent/:action`). */
832
+ export type AgentActions =
833
+ /** Agent metadata (tools, model, options) — no model call. */
834
+ | "info"
835
+ /** Run the agent to completion and return the answer. */
836
+ | "generate"
837
+ /** Same run, streamed as Server-Sent Events. */
838
+ | "stream"
839
+ /** Structured output — requires `api.object` to be declared. */
840
+ | "object"
841
+ /** Read a memory thread. */
842
+ | "history"
843
+ /** List a caller's memory threads — requires a store that supports `list`. */
844
+ | "threads"
845
+ /** Forget a memory thread. */
846
+ | "clear";
847
+
848
+ /** Context handed to an `api.access` rule of an agent. */
849
+ export type AgentAccessContext = {
850
+ rest: InstanceType<typeof useRest>;
851
+ /** The agent the request targets. */
852
+ agent: InstanceType<typeof Agent>;
853
+ /** Action being attempted (`generate`, `stream`…). */
854
+ action: string;
855
+ /** Raw request body. */
856
+ body: any;
857
+ /** Input the run would receive. */
858
+ input?: AgentInput;
859
+ /** Memory thread of the run, when the body carried one. */
860
+ thread?: string;
861
+ /** @deprecated Alias of `thread`. */
862
+ threadId?: string;
863
+ error: typeof fn.error;
864
+ jwt: typeof jwt;
865
+ token: { value: string | null; decoded: Record<string, unknown> | null; provided: boolean; expired: boolean };
866
+ };
867
+
868
+ /** Per-action access rules — `'*'` wildcard, same pattern as collections and vars. */
869
+ export type AgentAccess = {
870
+ [key in AgentActions | (string & {})]?: boolean | ((ctx: AgentAccessContext) => boolean | Promise<boolean>);
871
+ };
872
+
873
+ /**
874
+ * HTTP API of an agent (declared in `define.Agent({ api })`).
875
+ *
876
+ * ```ts
877
+ * api: {
878
+ * access: {
879
+ * info: true, // public metadata
880
+ * generate: (ctx) => !!ctx.token.value, // any authenticated caller
881
+ * stream: (ctx) => !!ctx.token.value,
882
+ * },
883
+ * object: { schema: v.object({ … }) }, // enables the `object` action
884
+ * }
885
+ * ```
886
+ */
887
+ export type AgentApi = {
888
+ /** Access rules per action — **no rules = denied** (secure by default). */
889
+ access?: AgentAccess;
890
+ /**
891
+ * Schema returned by the `object` action. Declared **server-side** on purpose:
892
+ * a client never dictates the shape, and the answer is really validated.
893
+ */
894
+ /** Schema returned by the `object` action. Declared **server-side** on purpose:
895
+ * a client never dictates the shape, and the answer is really validated.
896
+ */
897
+ object?: AgentStructuredOutput;
898
+ };
899
+
900
+ /**
901
+ * How an agent declares the documents injected into its system prompt — links to
902
+ * markdown files, nothing else:
903
+ *
904
+ * - a **string** is a path or a glob — `.md` only, resolved from the **tenant
905
+ * directory** (an absolute path is used as-is): `'./docs/*.md'`, `'./guide/ml.md'`,
906
+ * `'./guides/**\/SKILL.md'`
907
+ * - a **RegExp** is matched against every file of the tenant directory (`.md` only):
908
+ * `/^runbooks\/.*\.md$/`
909
+ */
910
+ export type AgentSkillSource = string | RegExp;
911
+
912
+ /**
913
+ * A resolved skill — one markdown file with a frontmatter header:
914
+ *
915
+ * ```md
916
+ * ---
917
+ * name: pdf-forms
918
+ * description: Fill, flatten and merge PDF forms. Use for any AcroForm work.
919
+ * license: MIT
920
+ * compatibility: Needs the `pdftk` binary.
921
+ * metadata: { owner: platform }
922
+ * allowed-tools: read_pdf write_pdf
923
+ * ---
924
+ *
925
+ * # Filling a form
926
+ * …
927
+ * ```
928
+ *
929
+ * `name` and `description` are required (the name is kebab-case, 1-64 characters,
930
+ * and must match the containing directory when the file is a `SKILL.md`);
931
+ * everything else is optional. Unknown keys are kept out of the prompt and logged,
932
+ * and a file that is not markdown is never a skill — as is one whose body is empty:
933
+ * a skill *is* the text the model reads.
934
+ *
935
+ * The **`content`** is what the model reads — it is part of the system prompt of
936
+ * every run, so it costs tokens on every call: keep skills short and factual.
937
+ */
938
+ export type AgentSkill = {
939
+ /** Kebab-case identifier, unique per agent. */
940
+ name: string;
941
+ /** What the skill does and when to use it (≤ 1024 characters). */
942
+ description: string;
943
+ /** License name or reference to a bundled license file. */
944
+ license?: string;
945
+ /** Environment requirements (≤ 500 characters). */
946
+ compatibility?: string;
947
+ /** Arbitrary key-value pairs — metadata only, never injected. */
948
+ metadata?: Record<string, any>;
949
+ /**
950
+ * Tools the skill may use. Parsed and served by `getSkills()`, **not enforced**
951
+ * by the run (a skill is always in the prompt — there is no activation to scope).
952
+ */
953
+ allowedTools?: string[];
954
+ /** The markdown body — injected as-is, frontmatter stripped. */
955
+ content: string;
956
+ /** Where it came from, relative to the tenant directory. */
957
+ source: string;
958
+ };
959
+
960
+ /**
961
+ * An agent definition — `{tenant.dir}/agents/**\/*.agent.ts`, declared with
962
+ * `define.Agent({ … })`.
963
+ */
964
+ export type AgentConfig = {
965
+ _isAgent_?: boolean;
966
+ /** Injected by the loader — the tenant this definition belongs to. */
967
+ _tenant_?: string;
968
+ /** Identifier — required, unique per tenant. */
969
+ id?: string;
970
+ name?: string;
971
+ /**
972
+ * What this prompt **is** — a free string (`'2026-09-24'`, `'v3'`, a commit).
973
+ *
974
+ * Like a workflow's `version`, it is served by `info` and written on every audit
975
+ * entry, so an answer can be traced back to the prompt that produced it.
976
+ */
977
+ version?: string;
978
+ /** What the agent is for — required (served by `info`, read by humans). */
979
+ description?: string;
980
+ instructions: AgentInstructions;
981
+ provider: AgentProvider;
982
+ tools?: AgentTools;
983
+ memory?: AgentMemory;
984
+ /** Max model calls per run (default 5). */
985
+ maxSteps?: number;
986
+ /** Default deadline for one tool call, in ms — see `AgentTool.timeoutMs`. */
987
+ toolTimeoutMs?: number;
988
+ /** Run the tools of one step in parallel by default — see `AgentCallOptions`. */
989
+ parallelTools?: boolean;
990
+ /** Store a failed run's conversation by default — see `AgentCallOptions`. */
991
+ savePartial?: boolean;
992
+ /**
993
+ * Other agents exposed to this one **as tools** — the model decides when to
994
+ * delegate: `agents: [researcher]` (an agent definition, usually the default
995
+ * export of another agent file).
996
+ *
997
+ * Each one becomes a tool named after its `id`, described by its `description`,
998
+ * taking `{ input }` and answering with the sub-agent's text. A local tool with
999
+ * the same name wins.
1000
+ */
1001
+ agents?: AgentConfig | AgentConfig[];
1002
+ /**
1003
+ * Remote **MCP servers** whose tools this agent may use: `['https://host/mcp']`
1004
+ * or `{ docs: 'https://docs/mcp' }` (the name is for the logs).
1005
+ *
1006
+ * Connected once, at first use. A remote tool keeps its name; a tool the agent
1007
+ * declares itself wins over a remote one of the same name.
1008
+ */
1009
+ mcp?: string[] | Record<string, string>;
1010
+ /** Default tool choice for every run (default `'auto'`). */
1011
+ toolChoice?: AgentToolChoice;
1012
+ /** Default reasoning for every run (default off) — overridable per call. */
1013
+ thinking?: AgentThinking;
1014
+ /**
1015
+ * Default window on the replayed history — the **last N** messages of the
1016
+ * thread, whatever the store keeps. A cheap way to bound the prompt (a tool
1017
+ * round stores several messages, so count is a proxy, not a measure).
1018
+ */
1019
+ lastMessages?: number;
1020
+ /**
1021
+ * **Markdown documents injected into the system prompt** of every run —
1022
+ * resolved once at load, from `{tenant.dir}`:
1023
+ *
1024
+ * ```ts
1025
+ * define.Agent({
1026
+ * skills: ['./docs/*.md', './guide/ml.md', /^runbooks\/.*\.md$/],
1027
+ * })
1028
+ * ```
1029
+ *
1030
+ * Each file carries a frontmatter header (`name`, `description`, `license`,
1031
+ * `compatibility`, `metadata`, `allowed-tools`) and its body is what the model
1032
+ * reads. A file that is not markdown is skipped (logged); a malformed skill
1033
+ * refuses **the agent** at load — the others keep loading. See `AgentSkill`.
1034
+ */
1035
+ skills?: AgentSkillSource[];
1036
+ /** @internal — resolved from `skills` by the loader. */
1037
+ _skills_?: AgentSkill[];
1038
+ /**
1039
+ * The agent's durable scratchpad — see `AgentWorkingMemory`. Needs a store that
1040
+ * supports it (the three built-in ones do) and, for the default scope, a
1041
+ * `resource` at call time.
1042
+ */
1043
+ workingMemory?: AgentWorkingMemory;
1044
+ /**
1045
+ * Memory processors — run in declaration order around the model call:
1046
+ * `load` on what it will see, `save` on what will be stored (Mastra-style).
1047
+ */
1048
+ processors?: AgentMemoryProcessor[];
1049
+ enabled?: boolean;
1050
+ /** HTTP API — reachable at `POST /api/:tenant_id/agents/:id/:action` once declared. */
1051
+ api?: AgentApi;
1052
+ };
1053
+
1054
+ /** Alias of `AgentConfig` — what the loader registers in `cfg.agents`. */
1055
+ export type AgentDefinition = AgentConfig;
1056
+
1057
+ /** Tenant-scoped agent registry — the `agents` member of every context. */
1058
+ export type TenantAgents = {
1059
+ /** The agent instance (tenant-bound), or `undefined`. */
1060
+ get(id: string): InstanceType<typeof Agent> | undefined;
1061
+ has(id: string): boolean;
1062
+ /** Declared agent ids of this tenant. */
1063
+ ids(): string[];
1064
+ list(): InstanceType<typeof Agent>[];
1065
+ /** Re-scan `{tenant.dir}/agents` and rebuild the registry. */
1066
+ reload(): Promise<void>;
1067
+ };