@anvia/core 1.5.1 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/README.md +32 -1073
  2. package/dist/agent/agent-tool.js +4 -4
  3. package/dist/agent/agent.js +17 -17
  4. package/dist/agent/index.js +20 -20
  5. package/dist/agent/resolve-options.js +6 -6
  6. package/dist/agent/resolved-agent.js +18 -18
  7. package/dist/agent/team/agent-team.js +20 -20
  8. package/dist/agent/team/index.js +20 -20
  9. package/dist/agent/tool-catalog.js +6 -6
  10. package/dist/{chunk-QHGYCYV6.js → chunk-3FIX7GLM.js} +2 -2
  11. package/dist/{chunk-SIXS4CCU.js → chunk-4IOC4SP6.js} +4 -4
  12. package/dist/chunk-4IOC4SP6.js.map +1 -0
  13. package/dist/{chunk-JMMZICU7.js → chunk-4OSA3MMA.js} +39 -5
  14. package/dist/chunk-4OSA3MMA.js.map +1 -0
  15. package/dist/{chunk-YOLOFTAY.js → chunk-57AUTEQ6.js} +57 -5
  16. package/dist/chunk-57AUTEQ6.js.map +1 -0
  17. package/dist/{chunk-4VDDYI6P.js → chunk-5CQRCTIN.js} +2 -2
  18. package/dist/{chunk-F4BOW76L.js → chunk-5TGVDMGM.js} +4 -4
  19. package/dist/{chunk-TK2VXQVZ.js → chunk-7G3YQQVE.js} +3 -3
  20. package/dist/{chunk-NDAGW32C.js → chunk-7OHHMYRO.js} +2 -2
  21. package/dist/{chunk-3SUEXGRA.js → chunk-7V3DKQTI.js} +4 -4
  22. package/dist/{chunk-DXW37JLZ.js → chunk-AMPQJMDG.js} +5 -5
  23. package/dist/{chunk-ZQC6M22O.js → chunk-AQ47TF5K.js} +2 -2
  24. package/dist/{chunk-PZ7ATNR6.js → chunk-BI3T62GL.js} +9 -9
  25. package/dist/chunk-BI3T62GL.js.map +1 -0
  26. package/dist/{chunk-HAAGSIZH.js → chunk-CB2L2NIQ.js} +2 -2
  27. package/dist/{chunk-KX2TRKO2.js → chunk-GNBPXJC6.js} +2 -2
  28. package/dist/{chunk-YMTBHHLQ.js → chunk-HG6ASDNI.js} +3 -3
  29. package/dist/{chunk-JMNIWUFV.js → chunk-HILMAFZK.js} +2 -2
  30. package/dist/{chunk-PWWKX2HA.js → chunk-LGMH6PCB.js} +7 -7
  31. package/dist/{chunk-PPMK7CG4.js → chunk-LW6F3IGP.js} +2 -2
  32. package/dist/{chunk-UYODZ7OE.js → chunk-LWF6AHDG.js} +2 -2
  33. package/dist/{chunk-2JZV4LUS.js → chunk-MKY7KTZY.js} +3 -13
  34. package/dist/chunk-MKY7KTZY.js.map +1 -0
  35. package/dist/{chunk-RFFOL6ON.js → chunk-MWG2PMTE.js} +18 -5
  36. package/dist/chunk-MWG2PMTE.js.map +1 -0
  37. package/dist/{chunk-AJC6YRVB.js → chunk-O7NN7PF4.js} +3 -3
  38. package/dist/{chunk-GDY2Z2SZ.js → chunk-PFJN5OIQ.js} +2 -2
  39. package/dist/{chunk-XAVSWLX5.js → chunk-PV6KJWYO.js} +2 -2
  40. package/dist/chunk-PZC5UHIN.js +190 -0
  41. package/dist/chunk-PZC5UHIN.js.map +1 -0
  42. package/dist/{chunk-V5DWNXVH.js → chunk-W2XPREWL.js} +135 -26
  43. package/dist/chunk-W2XPREWL.js.map +1 -0
  44. package/dist/chunk-W6V63YPY.js +96 -0
  45. package/dist/chunk-W6V63YPY.js.map +1 -0
  46. package/dist/{chunk-LJGQ6VQB.js → chunk-XJ5X3NI4.js} +89 -30
  47. package/dist/chunk-XJ5X3NI4.js.map +1 -0
  48. package/dist/{chunk-HHMLE3TZ.js → chunk-YCTXTOOQ.js} +7 -16
  49. package/dist/chunk-YCTXTOOQ.js.map +1 -0
  50. package/dist/{chunk-7TUAAGVR.js → chunk-YKWUFYXT.js} +2 -2
  51. package/dist/{chunk-5SJBSCG2.js → chunk-YSR6KLCS.js} +8 -2
  52. package/dist/chunk-YSR6KLCS.js.map +1 -0
  53. package/dist/completion/generate-completion.js +3 -3
  54. package/dist/completion/index.js +3 -3
  55. package/dist/completion/stream-accumulator.js +1 -1
  56. package/dist/embeddings/embed.js +1 -1
  57. package/dist/embeddings/index.js +1 -1
  58. package/dist/evals/advanced-metrics.js +6 -6
  59. package/dist/evals/agent-target.js +2 -1
  60. package/dist/evals/cli.js +6 -6
  61. package/dist/evals/execution.js +3 -1
  62. package/dist/evals/index.js +12 -12
  63. package/dist/evals/judge.js +5 -5
  64. package/dist/evals/metrics.js +6 -6
  65. package/dist/evals/runner.js +5 -5
  66. package/dist/extractor/extractor.js +4 -4
  67. package/dist/extractor/index.js +4 -4
  68. package/dist/index.js +23 -23
  69. package/dist/internal/agent-runtime/agent-run.js +13 -13
  70. package/dist/internal/agent-runtime/continuation-state.js +3 -3
  71. package/dist/internal/agent-runtime/interaction-suspension.js +3 -3
  72. package/dist/internal/agent-runtime/memory.js +5 -5
  73. package/dist/internal/agent-runtime/retrieval.js +5 -5
  74. package/dist/internal/agent-runtime/tool-execution.js +8 -8
  75. package/dist/internal/agent.js +18 -18
  76. package/dist/internal/team-runtime/member.js +3 -3
  77. package/dist/internal/team-runtime/run.js +19 -19
  78. package/dist/memory/compaction.js +4 -4
  79. package/dist/memory/index.js +4 -4
  80. package/dist/observability/group.js +3 -3
  81. package/dist/observability/index.js +3 -3
  82. package/dist/pipeline/index.js +6 -6
  83. package/dist/pipeline/pipeline.js +6 -6
  84. package/dist/schema/standard-json-schema.js +1 -1
  85. package/dist/skills/index.js +7 -7
  86. package/dist/skills/load.js +7 -7
  87. package/dist/skills/tools.js +6 -6
  88. package/dist/streaming/index.d.ts +2 -1
  89. package/dist/streaming/index.js +2 -1
  90. package/dist/streaming/readable-stream.js +2 -1
  91. package/dist/tool/dynamic-tools.js +5 -5
  92. package/dist/tool/index.js +5 -5
  93. package/dist/vector-store/index.js +4 -4
  94. package/dist/vector-store/ingest.js +2 -2
  95. package/dist/vector-store/retrieve.js +2 -2
  96. package/package.json +1 -1
  97. package/dist/chunk-2JZV4LUS.js.map +0 -1
  98. package/dist/chunk-54ZZJRP2.js +0 -57
  99. package/dist/chunk-54ZZJRP2.js.map +0 -1
  100. package/dist/chunk-5SJBSCG2.js.map +0 -1
  101. package/dist/chunk-HHMLE3TZ.js.map +0 -1
  102. package/dist/chunk-JMMZICU7.js.map +0 -1
  103. package/dist/chunk-LJGQ6VQB.js.map +0 -1
  104. package/dist/chunk-NNEKBZSM.js +0 -66
  105. package/dist/chunk-NNEKBZSM.js.map +0 -1
  106. package/dist/chunk-PZ7ATNR6.js.map +0 -1
  107. package/dist/chunk-RFFOL6ON.js.map +0 -1
  108. package/dist/chunk-SIXS4CCU.js.map +0 -1
  109. package/dist/chunk-V5DWNXVH.js.map +0 -1
  110. package/dist/chunk-YOLOFTAY.js.map +0 -1
  111. /package/dist/{chunk-QHGYCYV6.js.map → chunk-3FIX7GLM.js.map} +0 -0
  112. /package/dist/{chunk-4VDDYI6P.js.map → chunk-5CQRCTIN.js.map} +0 -0
  113. /package/dist/{chunk-F4BOW76L.js.map → chunk-5TGVDMGM.js.map} +0 -0
  114. /package/dist/{chunk-TK2VXQVZ.js.map → chunk-7G3YQQVE.js.map} +0 -0
  115. /package/dist/{chunk-NDAGW32C.js.map → chunk-7OHHMYRO.js.map} +0 -0
  116. /package/dist/{chunk-3SUEXGRA.js.map → chunk-7V3DKQTI.js.map} +0 -0
  117. /package/dist/{chunk-DXW37JLZ.js.map → chunk-AMPQJMDG.js.map} +0 -0
  118. /package/dist/{chunk-ZQC6M22O.js.map → chunk-AQ47TF5K.js.map} +0 -0
  119. /package/dist/{chunk-HAAGSIZH.js.map → chunk-CB2L2NIQ.js.map} +0 -0
  120. /package/dist/{chunk-KX2TRKO2.js.map → chunk-GNBPXJC6.js.map} +0 -0
  121. /package/dist/{chunk-YMTBHHLQ.js.map → chunk-HG6ASDNI.js.map} +0 -0
  122. /package/dist/{chunk-JMNIWUFV.js.map → chunk-HILMAFZK.js.map} +0 -0
  123. /package/dist/{chunk-PWWKX2HA.js.map → chunk-LGMH6PCB.js.map} +0 -0
  124. /package/dist/{chunk-PPMK7CG4.js.map → chunk-LW6F3IGP.js.map} +0 -0
  125. /package/dist/{chunk-UYODZ7OE.js.map → chunk-LWF6AHDG.js.map} +0 -0
  126. /package/dist/{chunk-AJC6YRVB.js.map → chunk-O7NN7PF4.js.map} +0 -0
  127. /package/dist/{chunk-GDY2Z2SZ.js.map → chunk-PFJN5OIQ.js.map} +0 -0
  128. /package/dist/{chunk-XAVSWLX5.js.map → chunk-PV6KJWYO.js.map} +0 -0
  129. /package/dist/{chunk-7TUAAGVR.js.map → chunk-YKWUFYXT.js.map} +0 -0
package/README.md CHANGED
@@ -1,1108 +1,67 @@
1
1
  # @anvia/core
2
2
 
3
- Small, explicit, embeddable runtime contracts for Anvia agents, tools, structured extraction, pipelines, streaming, RAG, MCP, skills, and observability.
3
+ Build provider-independent AI agents and workflows with typed tools, streaming, structured output,
4
+ and application-owned memory. Core runs the model/tool loop while your application chooses providers,
5
+ credentials, storage, and delivery.
4
6
 
5
- This package is provider-neutral. Pair it with a provider adapter such as `@anvia/openai`, `@anvia/anthropic`, or `@anvia/gemini` to create runnable model objects, then pass those objects into agents, extractors, pipelines, or direct completion helpers.
6
-
7
- ## Design Philosophy
8
-
9
- `@anvia/core` owns the model/tool loop and the runtime contracts around it. Your application owns provider client construction, credentials, product data access, permissions, storage, deployment, observability backends, and response shape.
10
-
11
- The package is dependency-injection oriented: create provider models, typed tools, memory stores, vector indexes, observers, and services in application code, then pass the relevant objects into agents, prompt requests, runners, or adapters. Core receives those objects and coordinates the run without taking over product architecture.
12
-
13
- ## Installation
14
-
15
- Anvia 1.0 is currently published on npm under the `rc` tag. The unqualified `latest` tag remains
16
- on the maintenance-only v0 line until 1.0 reaches general availability. Keep Core and every Anvia
17
- adapter on the same release channel.
18
-
19
- ```sh
20
- pnpm add @anvia/core@rc
21
- ```
22
-
23
- ### Bun
24
-
25
- Bun 1.3.14 is the currently tested and supported runtime baseline. Install Core on the same
26
- release channel as the rest of your Anvia packages:
7
+ ## Install
27
8
 
28
9
  ```sh
29
- bun add @anvia/core@rc
10
+ pnpm add @anvia/core @anvia/openai zod
30
11
  ```
31
12
 
32
- The repository itself remains a pnpm workspace; Bun support applies to applications consuming the
33
- published package.
13
+ This example uses OpenAI. Other provider adapters work with the same Core APIs.
34
14
 
35
- See the repository's [1.0 release policy](../../docs/releases/v1.md) for channel and support
36
- details.
15
+ ## Quickstart
37
16
 
38
- In this monorepo, the package is available through the workspace:
39
-
40
- ```sh
41
- pnpm --filter @anvia/core build
42
- ```
43
-
44
- ## Usage
17
+ Set `OPENAI_API_KEY` in your server environment, then create an agent with a typed tool:
45
18
 
46
19
  ```ts
47
- import { z } from "zod";
48
20
  import { Agent, createTool } from "@anvia/core";
49
21
  import { OpenAIClient } from "@anvia/openai";
22
+ import { z } from "zod";
50
23
 
51
- const client = new OpenAIClient({
52
- apiKey,
53
- });
54
-
55
- const model = client.completionModel({ modelId: "gpt-5", api: "responses" });
56
-
24
+ const apiKey = process.env.OPENAI_API_KEY;
25
+ if (!apiKey) throw new Error("Set OPENAI_API_KEY.");
26
+ const client = new OpenAIClient({ apiKey });
57
27
  const lookupOrder = createTool({
58
28
  name: "lookup_order",
59
- description: "Look up an order by id.",
29
+ description: "Look up an order by ID.",
60
30
  inputSchema: z.object({ orderId: z.string() }),
61
31
  execute: async ({ orderId }) => ({ orderId, status: "processing" }),
62
32
  });
63
33
 
64
34
  const agent = new Agent({
65
35
  id: "support",
66
- model,
36
+ model: client.completionModel({ modelId: "gpt-5", api: "responses" }),
67
37
  instructions: "Help customers with order questions.",
68
- maxTurns: 4,
69
38
  tools: [lookupOrder],
39
+ maxTurns: 4,
70
40
  });
71
41
 
72
42
  const result = await agent.generate({ prompt: "What is happening with order A123?" });
73
43
  if (result.type === "response") console.log(result.output);
74
44
  ```
75
45
 
76
- ## Agent teams
77
-
78
- `AgentTeam` coordinates ordinary `Agent` definitions and gives each spawned instance its own
79
- conversation and inbox. The coordinator chooses when to spawn members and can create multiple
80
- instances of the same definition. Tools keep the usual `tools: []` shape; members receive only their
81
- own configured tools and the runtime's communication tools.
82
-
83
- ```ts
84
- import { Agent, AgentTeam } from "@anvia/core/agent";
85
-
86
- // model, readFileTool, and searchTool are application-owned objects.
87
- const researcher = new Agent({
88
- id: "researcher",
89
- model,
90
- description: "Research a specific question and report evidence.",
91
- instructions: "Ask your parent for clarification when needed.",
92
- tools: [readFileTool, searchTool],
93
- });
94
-
95
- const reviewer = new Agent({
96
- id: "reviewer",
97
- model,
98
- description: "Review findings for errors and unsupported claims.",
99
- tools: [readFileTool],
100
- });
101
-
102
- const team = new AgentTeam({
103
- id: "research-team",
104
- model,
105
- instructions: [
106
- "Delegate research to researcher instances.",
107
- "Send their findings to a reviewer and incorporate its feedback.",
108
- "Answer the user after the work is complete.",
109
- ].join("\n"),
110
- members: [researcher, reviewer],
111
- limits: {
112
- maxDepth: 3,
113
- maxConcurrentAgents: 4,
114
- maxAgentInstances: 12,
115
- maxTotalTurns: 100,
116
- maxBufferedEvents: 1024,
117
- },
118
- });
119
-
120
- const result = await team.generate({ prompt: "Investigate the proposed architecture." });
121
- if (result.type === "response") console.log(result.output);
122
- console.log(result.teamRunId, result.usage, result.members);
123
- ```
124
-
125
- The coordinator supports normal Agent configuration, including `outputSchema`, context, tools,
126
- guardrails, middleware, and lifecycle callbacks. Its schema determines the team's output type.
127
- Each member preserves its own configuration. Run-level options apply to the coordinator; the
128
- abort signal, interaction resolver, and team limits apply to the whole execution.
129
-
130
- The runtime adds these model-callable tools:
131
-
132
- | Tool | Available to | Input |
133
- | ------------------- | --------------------------------- | ----------------------------- |
134
- | `spawn_<member.id>` | Coordinator and permitted parents | `{ prompt, name? }` |
135
- | `send_message` | All instances | `{ to, content, replyTo? }` |
136
- | `wait_for_agent` | All instances | `{ instanceId?, timeoutMs? }` |
137
- | `list_agents` | All instances | `{}` |
138
- | `cancel_agent` | Instances with spawn permissions | `{ instanceId, reason? }` |
139
-
140
- Spawning returns an `instanceId` immediately. A definition's `agentId` stays the same across its
141
- instances; each assignment or resumed interaction has a distinct `runId`. Member IDs must contain
142
- 1–58 letters, digits, underscores, or hyphens so generated tool names are valid. Configured tools
143
- must not use reserved coordination names.
144
-
145
- Routing is between a parent and its children by default. A member can use `to: "parent"`;
146
- parents address children by instance ID. Enable sibling communication and recursive spawning
147
- with explicit permissions:
148
-
149
- ```ts
150
- const team = new AgentTeam({
151
- id: "research-team",
152
- model,
153
- members: [researcher, reviewer],
154
- communication: { siblings: true },
155
- spawning: [{ from: researcher, to: [researcher, reviewer] }],
156
- limits: { maxDepth: 3, maxConcurrentAgents: 4, maxAgentInstances: 12, maxTotalTurns: 100 },
157
- });
158
- ```
159
-
160
- `members` is the definition catalog available to the coordinator. A spawn rule allows every
161
- instance of `from` to create only the definitions in `to`, using their generated spawn tools.
162
- Use the same Agent objects registered in `members`; duplicate rules or targets are rejected.
163
- Rules and communication options are snapshotted at construction. Without rules, only the
164
- coordinator can spawn. A self-reference enables recursion; each child has its own conversation
165
- and its definition's tools, without inheriting the parent's tools.
166
-
167
- With `communication.siblings: true`, instances sharing the same parent instance can discover,
168
- message, and wait for one another. Cousins and other branches remain inaccessible. `list_agents`
169
- includes self, parent, direct children, and enabled siblings, with `parentInstanceId` and `depth`.
170
- Sibling access does not grant cancellation or approval authority. Only a parent can cancel its
171
- direct child; cancellation also stops that child's entire subtree, including idle descendants.
172
-
173
- `send_message` returns a queued receipt, not a reply. Messages arrive at safe turn/tool boundaries,
174
- are explicitly attributed to their sender, and cannot satisfy a tool approval. Sending to an idle
175
- member starts a follow-up run with its retained conversation. Failed or cancelled members cannot
176
- receive follow-ups. Each inbox accepts at most 128 pending inputs.
177
-
178
- `wait_for_agent` wakes on a relevant message, outcome, cancellation, or timeout (default 30 seconds,
179
- maximum 5 minutes). Waiting agents and agents awaiting application input release their concurrency
180
- slot. Every parent waits automatically for its own children before finalizing and receives their
181
- outcomes as attributed messages. A member failure is reported to its immediate parent; failure or
182
- blocking cancels its descendants. A coordinator error or team cancellation rejects the execution.
183
- Guardrail blocking remains a `blocked` outcome. The final `members` array includes all descendants,
184
- with `parentInstanceId` and `depth` to reconstruct the tree.
185
-
186
- The limits in the first example are the defaults. `maxDepth` is a positive integer; the coordinator
187
- is depth 0 and direct children are depth 1. Concurrent and total instance limits include the coordinator.
188
- The instance limit counts all instances created during this execution, including completed or
189
- cancelled ones. The turn budget covers all instances and follow-ups; provider retries remain
190
- governed by normal retry settings. Reaching the turn budget fails the team. Reaching the instance
191
- or depth limit rejects the spawn tool call so the parent can use existing members. All budgets are
192
- shared across the whole tree; spawning does not reset them. Tools within one
193
- instance execute sequentially; different instances can execute concurrently.
194
-
195
- ### Team streaming and steering
196
-
197
- ```ts
198
- const stream = team.stream({ prompt: "Investigate the proposed architecture." });
199
-
200
- // Call from the application's user-input handler while the team is running:
201
- // stream.steer({ prompt: "Also consider deployment complexity." });
202
- // stream.cancel("User cancelled.");
203
-
204
- for await (const event of stream.events) {
205
- if (event.type === "agent_event" && event.event.type === "text_delta") {
206
- console.log(event.instanceId, event.event.delta);
207
- }
208
- }
209
- const outcome = await stream.result;
210
- ```
211
-
212
- `events` is an attributed union of agent events, member state changes, queued/delivered messages,
213
- interactions, and the terminal team outcome. New members emit `agent_queued` before acquiring a
214
- concurrency slot. `textStream` yields coordinator text only, including
215
- provisional turns before its final answer. `text` resolves to the final text. Choose one event or text
216
- consumer; final promises can be read alongside it. Reading only a final promise consumes the run
217
- without buffering unused events. Non-streaming models also work and emit complete response text
218
- instead of provider deltas. Closing the event iterator cancels unfinished work.
219
-
220
- Streaming buffers at most `limits.maxBufferedEvents` unread events (default 1,024). If a consumer
221
- falls behind and fills that buffer, the team cancels its running work, discards unread events, and
222
- rejects both the iterator and final promises with `AgentTeamLimitError` whose `limit` is
223
- `"maxBufferedEvents"`. This bounds queued event count, not the byte size of individual events.
224
- Increase the limit when your application needs to tolerate longer pauses between reads.
225
- Result-only consumption does not buffer events and is unaffected by this limit.
226
-
227
- ### Team approvals and questions
228
-
229
- Only the application resolves interactions. A coordinator or reviewer cannot approve another
230
- member's protected tool through a message. Existing tool `requiresApproval` and question tools use
231
- the same interaction contracts as `Agent`:
232
-
233
- ```ts
234
- import type { AgentTeamInteraction } from "@anvia/core/agent";
235
- import type { AgentInteractionResponse } from "@anvia/core/agent/interactions";
236
-
237
- // Implement this with your application's authenticated approval/question UI.
238
- declare function askUser(request: AgentTeamInteraction): Promise<AgentInteractionResponse>;
239
-
240
- const result = await team.generate({
241
- prompt: "Investigate and propose changes.",
242
- resolveInteraction: (request) => askUser(request),
243
- });
244
- ```
245
-
246
- The resolver receives `teamRunId`, `instanceId`, `runId`, the interaction request, and an abort
247
- signal. A missing, throwing, or invalid resolver fails the team with `AgentTeamInteractionError`
248
- before the protected action executes. Approval denial follows the ordinary Agent behavior: the
249
- tool is skipped and its member can continue. Other members may continue while an interaction is
250
- pending. The application must decide who may approve; agent-generated content is not authorization.
251
-
252
- Teams currently live in one process for one execution. Each `generate` or `stream` call creates
253
- fresh instances. Pass `messages` instead of `prompt` to supply coordinator history. Member
254
- conversations are retained only within that execution; team session persistence and durable
255
- checkpoint/resume are not provided. Cancellation signals reach running models, tools, and
256
- resolvers; work owned by the application must honor its signal to stop external activity.
257
- Studio provides a team playground with member activity, messages, and approval/question controls.
258
-
259
- ## Direct Completions
260
-
261
- Use `generateCompletion` for one provider call without Agent turns, memory, or local tool
262
- execution. The model and input are part of one options object:
263
-
264
- ```ts
265
- import { generateCompletion } from "@anvia/core";
266
- import { OpenAIClient } from "@anvia/openai";
267
-
268
- const model = new OpenAIClient({ apiKey }).completionModel({ modelId: "gpt-5", api: "responses" });
269
-
270
- const result = await generateCompletion({
271
- model,
272
- prompt: "Summarize Anvia in one sentence.",
273
- instructions: "Answer clearly and concisely.",
274
- });
275
-
276
- console.log(result.output); // string
277
- ```
278
-
279
- Use `messages` when the application already owns the transcript. Exactly one of `prompt` or
280
- `messages` is required:
281
-
282
- ```ts
283
- import { generateCompletion, type Message } from "@anvia/core";
284
-
285
- const result = await generateCompletion({
286
- model,
287
- messages: [
288
- { role: "system", content: "You are concise." },
289
- { role: "user", content: "Explain Anvia." },
290
- ] satisfies readonly Message[],
291
- maxTokens: 300,
292
- providerOptions: {
293
- reasoning: { effort: "low" },
294
- },
295
- });
296
- ```
297
-
298
- Messages are plain, readonly `{ role, content }` objects. Use structural literals with
299
- `satisfies Message` or `satisfies readonly Message[]`; there is no message factory namespace.
300
- Multimodal user content uses `text`, `image`, and `file` parts, while assistant tool calls use
301
- `{ type: "tool-call", toolCallId, toolName, input }`. At external boundaries, validate with
302
- `parseMessage`, `parseMessages`, `messageSchema`, or an application-specific
303
- `createMessageSchema({ metadataSchema })`.
304
-
305
- Add `outputSchema` to the same function for typed, schema-validated output:
306
-
307
- ```ts
308
- import { generateCompletion } from "@anvia/core";
309
- import { z } from "zod";
310
-
311
- const result = await generateCompletion({
312
- model,
313
- prompt: "Extract: Acme reports a high-priority checkout failure.",
314
- outputSchema: z.object({
315
- customer: z.string(),
316
- priority: z.enum(["low", "medium", "high"]),
317
- }),
318
- });
319
-
320
- console.log(result.output.priority); // fully typed
321
- ```
322
-
323
- `outputSchema` accepts any [Standard Schema](https://standardschema.dev) — Zod, Valibot, ArkType, and
324
- other implementing libraries, including transformed schemas whose validation input and output
325
- differ (for example `string -> number`). Zod uses its built-in JSON Schema conversion. Valibot
326
- requires the optional converter `@valibot/to-json-schema` (install it alongside `valibot`);
327
- unsupported Valibot actions surface as conversion errors before any model call. Other libraries
328
- must either implement the Standard JSON Schema interface (`~standard.jsonSchema`) or convert to a
329
- supported library. Providers receive the schema's input representation — the raw response is what
330
- `~standard.validate` accepts — while the validated, possibly transformed value becomes
331
- `result.output`. For Zod, the output representation is used only when it describes the same
332
- accepted values as the input side, so its strict-object refinements (`additionalProperties:
333
- false`, required by provider strict modes such as OpenAI structured outputs) are preserved;
334
- otherwise the input representation is sent. Object nodes without an explicit
335
- `additionalProperties` are completed with `false` in every provider payload for the same reason.
336
- Schema validation of the provider output is synchronous; asynchronous schemas are rejected with a
337
- `CompletionStructuredOutputError` in the `schema` phase.
338
-
339
- `CompletionResult` consistently contains `output`, the original `text`, normalized `content`,
340
- `usage`, and `rawResponse`, plus optional message, context, source, provider-tool, and finish-reason
341
- metadata. First-party adapters normalize provider termination into `finishReason` (`stop`, `length`,
342
- `content-filter`, `tool-calls`, or `other`) while preserving the provider value separately as
343
- `providerFinishReason`.
344
-
345
- Schema-backed completion checks termination before JSON parsing. `finishReason: "length"` throws
346
- `CompletionStructuredOutputError` with `phase: "truncated"`; `finishReason: "content-filter"`
347
- throws it with `phase: "content-filter"` and is not retried by the default policy. Ordinary text
348
- completion remains intentional: partial text is returned unchanged with its finish reason so the
349
- application decides whether to display, continue, or discard it. A direct structured stream can
350
- similarly end with one typed error event after any already-emitted deltas; it does not silently
351
- retract public stream progress.
352
-
353
- Use `streamCompletion` for the streaming form:
354
-
355
- ```ts
356
- import { streamCompletion } from "@anvia/core";
357
-
358
- for await (const event of streamCompletion({
359
- model,
360
- prompt: "Write a short launch note.",
361
- })) {
362
- if (event.type === "text_delta") process.stdout.write(event.delta);
363
- if (event.type === "final") console.log(event.result.usage);
364
- if (event.type === "error") console.error(event.error);
365
- }
366
- ```
367
-
368
- Tool-call deltas are always emitted when a provider supplies them; there is no opt-in flag. A
369
- high-level stream emits at most one terminal `error` event and then closes. Provider model adapters
370
- use the lower-level `CompletionModelStreamEvent`, whose terminal event is `{ type: "final",
371
- response }`; `streamCompletion` normalizes it to `{ type: "final", result }`.
372
-
373
- Client requests carry core `Message[]`, so an endpoint can validate and pass them directly:
374
-
375
- ```ts
376
- import { completionToClientStream, parseClientStreamRequest } from "@anvia/client";
377
- import { streamCompletion } from "@anvia/core";
378
- import { createClientStreamResponse } from "@anvia/server";
379
-
380
- const body = parseClientStreamRequest(await request.json());
381
- const events = completionToClientStream({
382
- events: streamCompletion({ model, messages: body.messages }),
383
- });
384
- return createClientStreamResponse({ events });
385
- ```
386
-
387
- Core does not own UI messages, HTTP transports, or the public wire protocol. Those boundaries live
388
- in `@anvia/client`, `@anvia/server`, and framework packages such as `@anvia/react`.
389
-
390
- ## Retries, Provider Options, and Cancellation
391
-
392
- Direct completion and media calls accept `retries?: RetryOptions | false`. An omitted or `false`
393
- value makes one provider attempt; `{}` enables the default retry policy. Only retry-safe provider
394
- calls are repeated.
395
-
396
- ```ts
397
- const controller = new AbortController();
398
-
399
- const result = await generateCompletion({
400
- model,
401
- prompt: "Summarize this incident.",
402
- retries: { maxAttempts: 3, initialDelayMs: 100, maxDelayMs: 1_000 },
403
- abortSignal: controller.signal,
404
- controls: { reasoningEffort: "medium" },
405
- });
406
- ```
407
-
408
- Completion models can advertise typed, provider-neutral `controls`. Reasoning-capable adapters use
409
- the `reasoningEffort` select control, so supported values are inferred from the model returned by
410
- `completionModel()`. An Agent can set control defaults, and a generate or stream call can override
411
- them for that run:
412
-
413
- ```ts
414
- const agent = new Agent({ model, controls: { reasoningEffort: "medium" } });
415
- await agent.generate({ prompt: "Solve this.", controls: { reasoningEffort: "high" } });
416
- ```
417
-
418
- Omitting a control means “Default”: the Agent default applies when configured, otherwise the
419
- provider chooses. Invalid control names and values are rejected before the provider call.
420
-
421
- `providerOptions` remains the escape hatch for a strict JSON object passed to an adapter. Runtime values such as
422
- `undefined`, non-finite numbers, cycles, a top-level array, and class instances are rejected rather
423
- than coerced. Nested arrays are valid JSON. Canonical Anvia fields such as model, input,
424
- temperature, tools, controls, dimensions, text, and voice take precedence over conflicting provider keys.
425
- Cancellation is forwarded to provider SDK calls and is never retried.
426
-
427
- ## Agents
428
-
429
- Agents own their default retry policy. A run with no `retries` value inherits the Agent setting;
430
- `false` disables it for that run; an object replaces it for that run. Retries apply to the current
431
- completion only, so completed tools and earlier turns are never replayed. `maxAttempts` is the
432
- total number of model attempts for that completion, including the initial attempt; it is not the
433
- number of additional retries.
434
-
435
- ```ts
436
- const agent = new Agent({
437
- id: "support",
438
- model,
439
- retries: { maxAttempts: 3 },
440
- });
441
-
442
- await agent.generate({ prompt: "Try normally." });
443
- await agent.generate({ prompt: "Do not retry this run.", retries: false });
444
- await agent.generate({
445
- prompt: "Use one custom policy.",
446
- retries: { maxAttempts: 2, initialDelayMs: 0, maxDelayMs: 0 },
447
- });
448
- ```
449
-
450
- When an Agent has `outputSchema`, truncation, JSON parsing, and schema-validation failures use the
451
- same retry budget when retries are explicitly enabled. Each retry starts from the original request
452
- rather than recursively accumulating failed attempts. Truncated output and reasoning are omitted;
453
- parse and schema repairs include only a bounded, text-only preview of the latest failed response.
454
- The correction asks for shorter raw JSON matching the schema, without Markdown or commentary.
455
- Transport failures and structured-output failures therefore cannot exceed `maxAttempts` in total.
456
- Agent response outcomes expose the last generation's `finishReason` and `providerFinishReason`, and
457
- the same fields remain attached to each assistant message's Anvia generation metadata.
458
-
459
- Malformed, incomplete, or non-JSON provider tool arguments fail with
460
- `CompletionProviderOutputError`. The default policy retries only explicitly retry-safe provider
461
- output failures when retries are enabled; it does not make arbitrary `SyntaxError` instances
462
- retryable. Tool calls are validated as a complete set before any tool executes, and filtered tool
463
- calls are never retried by default. Raw provider arguments are not retained in the error or retry
464
- metadata. A no-argument tool call must still contain the JSON value `{}`; blank argument text is
465
- malformed provider output and is never replaced with invented input.
466
-
467
- Streaming attempts are retried only before any provider event has been exposed. Once progress has
468
- been emitted, Anvia returns the failure without starting another provider attempt, because replaying
469
- the stream could duplicate text, tool-call deltas, or other observable events. Applications that
470
- want post-progress recovery must buffer attempts and define their own reset or resume protocol.
46
+ Replace the sample tool body with your application's data access. Agent outcomes distinguish a
47
+ response, a guardrail block, and an interaction that needs application input.
471
48
 
472
- Structured output remains strict: Core trims surrounding whitespace, parses JSON, and validates the
473
- result with the configured Zod schema. As a compatibility fallback for OpenAI-compatible providers
474
- that violate strict JSON mode, Core also accepts a response consisting entirely of one lowercase
475
- `json` Markdown fence or one unlabeled Markdown fence. It does not search prose for JSON, accept
476
- content before or after a fence, or bypass schema validation. `AgentStructuredOutputError` reports
477
- the `truncated`, `content-filter`, `parse`, or `schema` phase, attempt counts, output length/format
478
- metadata, per-attempt and cumulative usage, finish reasons, and the original `cause` when one
479
- exists; its message never contains the rejected model response. The `completion.retry` observer
480
- event records the same safe diagnostics and whether failed output was omitted or previewed, never
481
- the model output itself.
482
-
483
- Agent outcomes are discriminated by `type`. Successful responses include typed `output` and `text`;
484
- guardrail blocks return `type: "blocked"`, `stage`, `reason`, and an optional `message`; tool
485
- approvals and first-class questions return a JSON-safe `type: "interaction"` outcome.
486
-
487
- ```ts
488
- const outcome = await agent.generate({ prompt: "Help with this request." });
489
-
490
- switch (outcome.type) {
491
- case "response":
492
- console.log(outcome.output);
493
- break;
494
- case "blocked":
495
- console.log(outcome.stage, outcome.reason);
496
- break;
497
- case "interaction": {
498
- const response =
499
- outcome.interaction.type === "tool-approval"
500
- ? { type: "tool-approval" as const, approved: true }
501
- : {
502
- type: "tool-question" as const,
503
- answers: outcome.interaction.questions.map((question) => ({
504
- questionId: question.id,
505
- value: "application-provided answer",
506
- })),
507
- };
508
- const resumed = await agent.resume(outcome.continuation, response);
509
- console.log(resumed.type, resumed.resumedFrom);
510
- break;
511
- }
512
- }
513
- ```
49
+ ## What you can build
514
50
 
515
- Keep continuations server-side. A resumed phase receives a new `runId`; Core validates the
516
- continuation and current Agent/tool registration but does not provide a durable continuation store
517
- or exactly-once execution. Use `createQuestionTool({ name, description })` when a model must ask for
518
- structured free-text or choice answers.
519
-
520
- Import JSON-safe interaction contracts and parsers from their browser-safe subpath. This entrypoint
521
- does not load the Agent runtime, MCP clients, or Node infrastructure:
522
-
523
- ```ts
524
- import {
525
- type AgentInteractionResponse,
526
- parseAgentInteractionRequest,
527
- parseAgentInteractionResponse,
528
- } from "@anvia/core/agent/interactions";
529
- ```
530
-
531
- An Agent with `outputSchema` carries that output type through `generate`, `stream`, `asTool`, and
532
- Pipeline Agent stages. `stream()` returns a handle with one event consumer plus final-value
533
- promises, steering, and cancellation. Terminal agent events are the flattened outcomes themselves;
534
- there is no extra `final.result` wrapper:
535
-
536
- ```ts
537
- const stream = agent.stream({ prompt: "Help with this request." });
538
-
539
- for await (const event of stream.events) {
540
- if (event.type === "text_delta") process.stdout.write(event.delta);
541
- if (event.type === "response") console.log(event.output);
542
- if (event.type === "interaction") console.log(event.interaction);
543
- if (event.type === "blocked") console.log(event.reason);
544
- }
545
-
546
- const outcome = await stream.result;
547
- console.log(outcome.type, await stream.text);
548
- ```
549
-
550
- Use `stream.textStream` instead of `stream.events` when only text deltas are needed. Event and text
551
- iteration share one underlying stream and therefore cannot be consumed independently.
552
-
553
- Pass `abortSignal` on a run to cancel the active provider call, tools, and nested Agent tools.
554
- Run observers receive terminal errors with `status: "cancelled"` for cancellation and
555
- `status: "failed"` for other failures.
556
-
557
- ### Prompt attribution
558
-
559
- Set `trace.promptRef` on `generate()` or `stream()` to identify the prompt used by a run and
560
- default its generation attribution. The core `AgentRunPromptRef` contract is
561
- `{ readonly name: string; readonly version?: number }`; use the registry's resolved numeric version
562
- when available, not the requested label. This identity does not contain templates or variables.
563
-
564
- For runs that change prompts between turns, completion-request middleware is the explicit
565
- generation override boundary:
566
-
567
- ```ts
568
- await agent.generate({
569
- prompt: "Summarize the ticket",
570
- trace: { promptRef: { name: "support", version: 7 } },
571
- middlewares: [
572
- {
573
- onCompletionRequest: ({ request, turn }) => {
574
- if (turn === 0) {
575
- return { request, promptRef: { name: "planner", version: 3 } };
576
- }
577
- return { request, promptRef: null };
578
- },
579
- },
580
- ],
581
- });
582
- ```
51
+ - Agents with typed tools, approvals, questions, guardrails, and streaming responses.
52
+ - Agent teams with isolated conversations, delegation, and coordinated cancellation.
53
+ - Direct completions and schema-validated extraction without an agent loop.
54
+ - Sequential or parallel pipelines and evaluations with built-in or custom metrics.
55
+ - Stateful conversations using memory adapters and explicit compaction policies.
56
+ - RAG with document chunking, embeddings, and vector-store contracts.
57
+ - Image generation, speech, transcription, MCP tools, and local Agent Skills.
583
58
 
584
- The middleware receives the current `promptRef`. Returning a reference overrides that generation;
585
- `null` clears it, while omission preserves it. Each turn starts again with the run default, and
586
- later middleware sees earlier overrides. Set attribution alongside any request changes that select
587
- another prompt: attribution itself does not change the model input. It is a runtime side channel,
588
- not a provider request field or global current-prompt setting.
59
+ Use `@anvia/client`, `@anvia/server`, and `@anvia/react` to deliver agent output to an application UI.
60
+ Use provider, memory, vector-store, and observability adapters for the infrastructure you choose.
589
61
 
590
- ## Memory
62
+ ## Learn more
591
63
 
592
- Configure durable conversation memory on the Agent, then run through a session:
593
-
594
- ```ts
595
- import { Agent, type MemoryStore, type Message } from "@anvia/core";
596
- import type { MemoryAppendOptions, MemoryScope } from "@anvia/core/memory";
597
-
598
- class AppMemoryStore implements MemoryStore {
599
- private readonly sessions = new Map<string, Message[]>();
600
-
601
- async load({ scope }: { scope: MemoryScope }): Promise<Message[]> {
602
- return [...(this.sessions.get(scope.sessionId) ?? [])];
603
- }
604
-
605
- async append(input: MemoryAppendOptions): Promise<void> {
606
- const current = this.sessions.get(input.scope.sessionId) ?? [];
607
- this.sessions.set(input.scope.sessionId, [...current, ...input.messages]);
608
- }
609
-
610
- async clear({ scope }: { scope: MemoryScope }): Promise<void> {
611
- this.sessions.delete(scope.sessionId);
612
- }
613
- }
614
-
615
- const memory = new AppMemoryStore();
616
- const agent = new Agent({
617
- id: "support",
618
- model: model,
619
- memory: { store: memory },
620
- });
621
-
622
- const session = { sessionId: "thread_123", userId: "user_456" };
623
- await agent.generate({ prompt: "Remember my plan.", session });
624
- await agent.generate({ prompt: "What is my plan?", session });
625
- ```
626
-
627
- Memory defaults to `savePolicy: "message"`, which saves the user prompt, each completed assistant message, and each completed tool result as soon as they are ready. You can choose `"turn"` or `"run"` at configuration time:
628
-
629
- ```ts
630
- new Agent({
631
- id: "support",
632
- model,
633
- memory: { store: memory, savePolicy: "turn" },
634
- });
635
- ```
636
-
637
- Without `session`, the same Agent is stateless. Pass `{ messages }` when the caller already owns a
638
- complete transcript; transcripts cannot be combined with persisted sessions.
639
-
640
- Compaction is an explicit Agent policy over a store capability. The store preserves the canonical
641
- message history for `load()` and inspectors, and persists one latest compaction checkpoint
642
- separately. Future model calls receive the checkpoint summary (a system message tagged with
643
- `metadata.anvia.memoryCompaction`) plus the unsummarized tail, not the individual messages already
644
- covered by that summary:
645
-
646
- ```ts
647
- import { createSummaryMemoryCompactor } from "@anvia/core/memory";
648
-
649
- const compactor = createSummaryMemoryCompactor({
650
- model: summaryModel,
651
- maxTokens: 1024,
652
- retries: { maxAttempts: 2 },
653
- });
654
-
655
- const agent = new Agent({
656
- id: "support",
657
- model,
658
- memory: {
659
- store: memory,
660
- savePolicy: "message",
661
- compaction: {
662
- trigger: { afterTokens: 32_000 },
663
- retention: { recentTurns: 4 },
664
- compactor,
665
- conflictRetries: false,
666
- },
667
- },
668
- });
669
-
670
- for await (const event of agent.stream({ prompt: "What did we decide?", session })) {
671
- if (event.type === "memory_compaction") {
672
- console.log({
673
- messages: event.compactedMessageCount,
674
- beforeTokens: event.originalTokenCount,
675
- afterTokens: event.resultTokenCount,
676
- usage: event.usage,
677
- });
678
- }
679
- }
680
- ```
681
-
682
- ### Retention API design
683
-
684
- - **Public option:** `retention: { recentTurns: N }`.
685
- - **Counting unit:** a user-led turn, not an individual message. Each `user` message starts a turn;
686
- every following `assistant`, `tool`, or `system` message belongs to it until the next `user`
687
- message. Tool calls and results therefore cannot be split at the compaction boundary.
688
- - **Default:** omitting `retention` resolves to `{ recentTurns: 1 }`.
689
- - **Validation:** `N` must be a nonnegative safe integer. Fractions, negative values, infinity, and
690
- specifying both retention modes are rejected.
691
- - **Boundaries:** exactly the last `N` complete stored turns remain unsummarized. `N = 0` summarizes
692
- the whole stored transcript when it contains a user-led turn. If at most `N` turns exist,
693
- compaction is a no-op.
694
- - **Leading messages:** messages before the first `user` do not count as a turn. When older turns
695
- are summarized, this leading prefix is summarized with them.
696
- - **Trigger interaction:** the stored transcript plus incoming user message is compared with
697
- `afterTokens`, but the incoming message is not part of the stored-turn retention count.
698
- `afterTokens` remains only the automatic trigger threshold and does not cap retained turns.
699
- - **Compatibility:** deprecated `{ recentTokens: N }` remains supported with its previous
700
- whole-turn semantics. It is mutually exclusive with `recentTurns`.
701
-
702
- The built-in token counter is a lightweight provider-neutral estimate. Pass `tokenCounter` when you
703
- have a model-specific tokenizer or counting service. A custom counter may be async, can be called
704
- multiple times, and must return deterministic, monotonic, nonnegative safe integers:
705
-
706
- ```ts
707
- compaction: {
708
- trigger: { afterTokens: 32_000 },
709
- retention: { recentTurns: 4 },
710
- tokenCounter: (messages) => tokenizer.count(messages),
711
- compactor,
712
- }
713
- ```
714
-
715
- For migration, the deprecated `retention: { recentTokens: N }` form remains supported with its
716
- previous semantics: it keeps as many complete recent user-led turns as fit the token budget and
717
- always keeps the newest turn. `recentTokens` must be positive and less than `afterTokens`.
718
- `recentTurns` and `recentTokens` are mutually exclusive; use `recentTurns` for deterministic
719
- count-based retention. Omitting `retention` previously used
720
- `Math.floor(afterTokens / 4)` as a token budget and now retains one turn. To preserve the old
721
- implicit behavior during migration, set that value explicitly as `recentTokens`.
722
-
723
- You can force the configured policy without starting an Agent run. The result makes a no-op
724
- explicit rather than throwing:
725
-
726
- ```ts
727
- const result = await agent.compactMemory({ session });
728
-
729
- if (result.type === "compacted") {
730
- console.log(result.originalTokenCount, result.resultTokenCount);
731
- } else {
732
- console.log(result.reason); // "nothing_to_compact"
733
- }
734
- ```
735
-
736
- The automatic trigger is a threshold, not a hard storage limit. Omitting `compaction` keeps the
737
- full canonical history model-facing. Summary-provider retries belong to the compactor; full
738
- snapshot-to-checkpoint conflict retries are separately opt-in. A streamed `memory_compaction`
739
- event is emitted only after the store atomically commits the checkpoint.
740
-
741
- ## Structured Extraction
742
-
743
- ```ts
744
- import { extract } from "@anvia/core/extractor";
745
- import { z } from "zod";
746
-
747
- const ticketSchema = z.object({
748
- customer: z.string(),
749
- priority: z.enum(["low", "medium", "high"]),
750
- summary: z.string(),
751
- });
752
-
753
- const { output: ticket } = await extract({
754
- model,
755
- text: "Acme Co. reports checkout failures. Priority is high.",
756
- outputSchema: ticketSchema,
757
- retries: { maxAttempts: 2 },
758
- });
759
- ```
760
-
761
- ## Pipelines
762
-
763
- ```ts
764
- import { Pipeline } from "@anvia/core/pipeline";
765
- import { z } from "zod";
766
-
767
- const pipeline = new Pipeline({ id: "support-flow", inputSchema: z.string() })
768
- .agent({
769
- id: "draft",
770
- agent,
771
- suspension: "reject",
772
- request: ({ input }) => ({ prompt: `Draft a reply for this ticket:\n\n${input}` }),
773
- })
774
- .extract({
775
- id: "parse",
776
- model,
777
- outputSchema: ticketSchema,
778
- text: ({ input }) => input,
779
- });
780
-
781
- const { runId, output } = await pipeline.run({
782
- input: "Customer cannot complete checkout.",
783
- });
784
- ```
785
-
786
- Pipelines use the same named observability configuration as Agents. Pipeline observers receive the
787
- root run and every composed, parallel, Agent, extraction, and custom stage. A primary stage trace is
788
- propagated into an Agent stage so its generations and tools become children of that stage. This
789
- requires the Pipeline and Agent `primaryTrace` names to match. When they differ, Anvia leaves the
790
- Agent trace unchanged without raising an error:
791
-
792
- ```ts
793
- const pipeline = new Pipeline({
794
- id: "support-flow",
795
- inputSchema: z.string(),
796
- observability: {
797
- observers: { telemetry: pipelineObserver },
798
- primaryTrace: "telemetry",
799
- },
800
- }).agent({
801
- id: "draft",
802
- agent,
803
- suspension: "reject",
804
- request: ({ input }) => ({ prompt: input }),
805
- });
806
-
807
- const result = await pipeline.run({
808
- input: "Customer cannot complete checkout.",
809
- trace: { sessionId: "support-session" },
810
- });
811
-
812
- console.log(result.trace);
813
- ```
814
-
815
- The `observer` passed to `pipeline.run()` remains a separate stage-event sink intended for Studio,
816
- logging, and other operational consumers. Its errors are isolated unless `failOnObserverError` is
817
- enabled.
818
-
819
- `observability.errorPolicy` controls named observer failures instead. It accepts `"ignore"` or
820
- `"throw"` and defaults to `"ignore"`. With `"throw"`, the run rejects with a
821
- `PipelineObserverDispatchError` containing the failed phase and per-observer failures.
822
-
823
- Pipeline run and batch options also accept `trace.promptRef`. This identifies the pipeline root;
824
- it does not stamp every stage or Agent with that prompt. Select each Agent's relevant prompt with
825
- `request: ({ input }) => ({ prompt: input, trace: { promptRef } })`, especially when a pipeline uses
826
- multiple prompts. Trace parent propagation and prompt identity are independent.
827
-
828
- ## Documents
829
-
830
- Applications own file discovery, file reads, document parsing, source metadata, and per-file error
831
- policy. Core only provides deterministic in-memory text chunking:
832
-
833
- ```ts
834
- import { readFile } from "node:fs/promises";
835
- import { chunkText } from "@anvia/core/documents";
836
-
837
- const text = await readFile("guide.txt", "utf8");
838
- const chunks = chunkText({
839
- text,
840
- strategy: "recursive",
841
- maxSize: 1_000,
842
- overlap: 100,
843
- separators: ["\n\n", "\n", " "],
844
- });
845
- ```
846
-
847
- Use `strategy: "fixed"` for deterministic sliding windows. Recursive chunking requires an explicit
848
- separator order and falls back to fixed-size splitting when none of those separators can divide an
849
- oversized section. Chunk offsets use JavaScript string indices and always identify the exact source
850
- slice.
851
-
852
- For the common vector ingestion path, embed and upsert raw text with one call:
853
-
854
- ```ts
855
- import { ingestVectorText } from "@anvia/core/vector-store";
856
-
857
- await ingestVectorText({
858
- store,
859
- document: {
860
- id: "incident-42",
861
- text,
862
- metadata: { tenant: "acme" },
863
- },
864
- embeddingModel,
865
- chunking: {
866
- strategy: "recursive",
867
- maxSize: 1_000,
868
- overlap: 100,
869
- separators: ["\n\n", "\n", " "],
870
- },
871
- });
872
- ```
873
-
874
- `ingestVectorDocuments()` accepts the same document shape for batches. Without `chunking`, each
875
- source document is embedded as one chunk. Chunk embeddings remain grouped under the source document
876
- ID so re-ingestion replaces its complete vector representation instead of leaving stale chunks.
877
-
878
- ## Media
879
-
880
- Media helpers follow the same one-object API and share `providerOptions`, `retries`, and
881
- `abortSignal`:
882
-
883
- ```ts
884
- import { generateImage, generateSpeech, transcribe } from "@anvia/core";
885
-
886
- const image = await generateImage({
887
- model: client.imageGenerationModel({ modelId: "gpt-image-1" }),
888
- prompt: "A compact robot drawing an architecture diagram.",
889
- width: 1024,
890
- height: 1024,
891
- });
892
- console.log(image.images[0].data);
893
-
894
- const speech = await generateSpeech({
895
- model: client.speechGenerationModel({ modelId: "gpt-4o-mini-tts" }),
896
- text: "Hello from Anvia.",
897
- voice: "alloy",
898
- });
899
-
900
- const transcript = await transcribe({
901
- model: client.transcriptionModel({ modelId: "gpt-4o-mini-transcribe" }),
902
- audio: {
903
- data: speech.audio.data,
904
- filename: "speech.mp3",
905
- mediaType: speech.audio.mediaType,
906
- },
907
- });
908
- console.log(transcript.text);
909
- ```
910
-
911
- ## MCP
912
-
913
- MCP clients live in the optional `@anvia/mcp` package. Core retains the lightweight registration
914
- contracts that let agents receive immutable MCP server snapshots without owning or closing their
915
- underlying transports:
916
-
917
- ```sh
918
- pnpm add @anvia/mcp
919
- ```
920
-
921
- Import `McpClient` and `McpClientGroup` from `@anvia/mcp`, connect them, and pass the resulting
922
- `servers` to `new Agent({ mcpServers })`. See the `@anvia/mcp` README for transport configuration,
923
- connection ownership, URL safety, and cleanup.
924
-
925
- ## Evaluations
926
-
927
- Import evaluation APIs from `@anvia/core/evals`. A suite contains cases, a target, and one or more
928
- metrics. Required metric failures make a case fail; infrastructure or evaluation errors produce an
929
- `invalid` outcome.
930
-
931
- ```ts
932
- import { contains, exactMatch, runEvalSuite } from "@anvia/core/evals";
933
-
934
- const result = await runEvalSuite({
935
- name: "support answers",
936
- cases: [
937
- {
938
- id: "refund-window",
939
- input: "When can I request a refund?",
940
- expected: "Refunds are available for 30 days.",
941
- },
942
- ],
943
- target: async (input) => answerSupportQuestion(input),
944
- metrics: [exactMatch(), contains({ expected: "30 days" })],
945
- });
946
-
947
- console.log(result.cases);
948
- console.log(result.results[0]?.scores.exact_match);
949
- ```
950
-
951
- `runEvalSuite` infers the case input, target output, expected value, metric names, and score types.
952
- Metrics that implicitly read `case.expected`, `case.context`, or `case.retrievalContext` require
953
- those fields at compile time. Supplying an explicit metric value or selector removes the matching
954
- case requirement.
955
-
956
- Set `run: { promptRef: { name: "support", version: 7 } }` on `runEvalSuite()` to attribute the
957
- evaluation run. The reference is preserved in `result.run` and the reporter's run-start, per-metric,
958
- and run-end contexts. It does not automatically attribute the target Agent or judge generations;
959
- configure those runs' `trace.promptRef` separately when relevant.
960
-
961
- ### Built-in metrics
962
-
963
- Deterministic metrics do not call a model:
964
-
965
- - `exactMatch`, `contains`, `notContains`, `containsAll`, and `containsAny`
966
- - `matches`, `doesNotMatch`, `maxLength`, and `requiredFields`
967
- - `semanticSimilarity`, which uses an embedding model
968
-
969
- Judge metrics call a completion model and record their evaluation token usage:
970
-
971
- - `llmJudge` and `llmScore`
972
- - `answerRelevancy`, `promptAlignment`, `jsonCorrectness`, and `summarization`
973
- - `hallucination`, `faithfulness`, and `abstention`
974
- - `gEval`, `turnRelevancy`, and `knowledgeRetention`
975
-
976
- Numeric thresholds use values from `0` through `1`. `strictMode` requires a perfect higher-is-better
977
- score or a zero lower-is-better score. Judge metrics default to including a final explanation;
978
- setting `includeReason: false` saves that extra judge call when the explanation is unnecessary.
979
-
980
- ### Typed custom metrics
981
-
982
- Use `createEvalTypes` to bind input, output, and expected types once when defining multiple custom
983
- metrics:
984
-
985
- ```ts
986
- import { createEvalTypes, EvalOutcome } from "@anvia/core/evals";
987
-
988
- const supportEvals = createEvalTypes<string, { answer: string }, string>();
989
-
990
- const noHandoff = supportEvals.defineMetric({
991
- name: "no_handoff",
992
- dataType: "BOOLEAN",
993
- evaluate: ({ output, signal }) =>
994
- signal.aborted || output.answer.includes("contact support")
995
- ? EvalOutcome.fail(false)
996
- : EvalOutcome.pass(true),
997
- });
998
- ```
999
-
1000
- Custom metrics return `EvalOutcome.pass`, `EvalOutcome.fail`, or `EvalOutcome.invalid`. Thrown metric
1001
- errors are converted to structured invalid outcomes containing the error kind and original error.
1002
-
1003
- ### Execution controls
1004
-
1005
- Evaluation targets receive an optional third argument containing the case `AbortSignal`.
1006
-
1007
- ```ts
1008
- const result = await runEvalSuite({
1009
- // cases, target, and metrics...
1010
- name: "release gate",
1011
- cases,
1012
- target: async (input, testCase, context) =>
1013
- generateAnswer(input, { signal: context?.signal, metadata: testCase.metadata }),
1014
- metrics,
1015
- targetConcurrency: 4,
1016
- metricConcurrency: 2,
1017
- caseTimeoutMs: 30_000,
1018
- signal: deploymentSignal,
1019
- failFast: true,
1020
- caseIds: ["refund-window", "billing-owner"],
1021
- shard: { index: 0, count: 4 },
1022
- onProgress(event) {
1023
- console.log(event.type);
1024
- },
1025
- });
1026
- ```
1027
-
1028
- - `concurrency` remains the shorthand for both target and metric concurrency.
1029
- - `targetConcurrency` and `metricConcurrency` can limit them independently.
1030
- - `caseTimeoutMs` covers the target and its metrics. Cooperative targets and metrics should observe
1031
- the supplied signal; the runner still stops awaiting work that ignores it.
1032
- - Aborting the suite-level `signal` stops scheduling cases and rejects the run with the abort reason.
1033
- - `caseIds`, `caseFilter`, and `shard` select cases before execution.
1034
- - `failFast` throws `EvalFailFastError` after the first completed required failure or invalid case.
1035
- - `onProgress` receives case, target, metric, and case-completion events.
1036
-
1037
- Use `selectEvalCaseIds(previousResult)` to select failed and invalid cases for a rerun.
1038
-
1039
- ### Results, usage, and failures
1040
-
1041
- Every case result includes `targetStatus`, target and total durations, per-case usage and optional
1042
- cost. Every metric result includes its duration, optional cost, outcome, and reporter errors. A
1043
- successful target that returns `undefined` has `targetStatus: "succeeded"` and retains its `output`
1044
- property, so it is distinct from a failed target.
1045
-
1046
- The suite aggregates metric totals, case totals, target and evaluation usage, costs, duration, and
1047
- all reporter errors. Invalid outcomes may include these `kind` values: `target`, `metric`,
1048
- `configuration`, `provider`, or `timeout`.
1049
-
1050
- ### CI output and expectations
1051
-
1052
- `runEvalCli` prints a result and can set `process.exitCode`. Exit code `1` means a required metric
1053
- failed; `2` means a required metric was invalid. Use `defineEvalExpectations` with a defined suite to
1054
- type-check case and metric names.
1055
-
1056
- ```ts
1057
- import { defineEvalExpectations, defineEvalSuite, exactMatch, runEvalCli } from "@anvia/core/evals";
1058
-
1059
- const suite = defineEvalSuite({
1060
- name: "release gate",
1061
- cases: [{ id: "refund", input: "refund", expected: "30 days" }],
1062
- target: async () => "30 days",
1063
- metrics: [exactMatch({ name: "correct" })],
1064
- });
1065
-
1066
- await runEvalCli({
1067
- ...suite,
1068
- expectations: defineEvalExpectations(suite, {
1069
- outcomes: { refund: { correct: "pass" } },
1070
- }),
1071
- exitCode: true,
1072
- maxValueLength: 2_000,
1073
- redact: (value, context) => (context.kind === "input" ? "[redacted]" : value),
1074
- });
1075
- ```
1076
-
1077
- Use `formatEvalResult` for pure pretty or JSON formatting without writing to stdout. Both
1078
- `formatEvalResult` and `runEvalCli` support value truncation and redaction. JSON output otherwise
1079
- contains cases, outputs, metadata, and error details, so redact sensitive data before writing it to
1080
- shared CI logs.
1081
-
1082
- ## Public Areas
1083
-
1084
- - `agent`: typed Agent runtime, retries, and stream events
1085
- - `agent/interactions`: browser-safe interaction contracts, schemas, assertions, and parsers
1086
- - `tool`: typed tool creation and tool sets
1087
- - `completion`: direct completion helpers and provider-neutral model contracts
1088
- - `memory`: durable session memory interfaces and in-memory store
1089
- - `extractor`: schema-first structured extraction
1090
- - `pipeline`: typed sequential and parallel workflows
1091
- - `embeddings`: embedding helpers and document embedding utilities
1092
- - `vector-store`: in-memory vector search and vector search tools
1093
- - `streaming`: normalized stream helpers
1094
- - `mcp`: lightweight MCP tool and server registration contracts used by Agent
1095
- - `skills`: local skill loading
1096
- - `observability`: observer interfaces for runs, generations, and tool calls
1097
- - `evals`: evaluation helpers and reporters
1098
- - `redaction`: PII patterns and deep value redaction shared by observability adapters
1099
- - `documents`: deterministic in-memory text chunking
1100
- - `speech-generation`, `image-generation`, `transcription`: provider-neutral media interfaces
1101
-
1102
- ## Development
1103
-
1104
- ```sh
1105
- pnpm --filter @anvia/core typecheck
1106
- pnpm --filter @anvia/core test
1107
- pnpm --filter @anvia/core build
1108
- ```
64
+ - [Core guide and API examples](https://github.com/anvia-hq/anvia/blob/main/docs/packages/core.md)
65
+ - [Runnable cookbook](https://github.com/anvia-hq/anvia/tree/main/cookbook)
66
+ - [Package catalog](https://github.com/anvia-hq/anvia#readme)
67
+ - [Contributing](https://github.com/anvia-hq/anvia/blob/main/CONTRIBUTING.md)