determinate 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +236 -0
- package/dist/agent.d.ts +17 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +131 -0
- package/dist/agent.js.map +1 -0
- package/dist/context/assembler.d.ts +19 -0
- package/dist/context/assembler.d.ts.map +1 -0
- package/dist/context/assembler.js +87 -0
- package/dist/context/assembler.js.map +1 -0
- package/dist/context/budget.d.ts +10 -0
- package/dist/context/budget.d.ts.map +1 -0
- package/dist/context/budget.js +13 -0
- package/dist/context/budget.js.map +1 -0
- package/dist/context/tokenizer.d.ts +6 -0
- package/dist/context/tokenizer.d.ts.map +1 -0
- package/dist/context/tokenizer.js +34 -0
- package/dist/context/tokenizer.js.map +1 -0
- package/dist/errors.d.ts +29 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +44 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/oauth/anthropic.d.ts +5 -0
- package/dist/oauth/anthropic.d.ts.map +1 -0
- package/dist/oauth/anthropic.js +84 -0
- package/dist/oauth/anthropic.js.map +1 -0
- package/dist/oauth/index.d.ts +18 -0
- package/dist/oauth/index.d.ts.map +1 -0
- package/dist/oauth/index.js +44 -0
- package/dist/oauth/index.js.map +1 -0
- package/dist/oauth/openai.d.ts +14 -0
- package/dist/oauth/openai.d.ts.map +1 -0
- package/dist/oauth/openai.js +328 -0
- package/dist/oauth/openai.js.map +1 -0
- package/dist/oauth/pkce.d.ts +9 -0
- package/dist/oauth/pkce.d.ts.map +1 -0
- package/dist/oauth/pkce.js +22 -0
- package/dist/oauth/pkce.js.map +1 -0
- package/dist/oauth/token-store.d.ts +10 -0
- package/dist/oauth/token-store.d.ts.map +1 -0
- package/dist/oauth/token-store.js +36 -0
- package/dist/oauth/token-store.js.map +1 -0
- package/dist/oauth/types.d.ts +32 -0
- package/dist/oauth/types.d.ts.map +1 -0
- package/dist/oauth/types.js +2 -0
- package/dist/oauth/types.js.map +1 -0
- package/dist/providers/anthropic.d.ts +9 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +101 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/factory.d.ts +4 -0
- package/dist/providers/factory.d.ts.map +1 -0
- package/dist/providers/factory.js +25 -0
- package/dist/providers/factory.js.map +1 -0
- package/dist/providers/openai.d.ts +9 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +57 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/parse-action.d.ts +3 -0
- package/dist/providers/parse-action.d.ts.map +1 -0
- package/dist/providers/parse-action.js +18 -0
- package/dist/providers/parse-action.js.map +1 -0
- package/dist/providers/types.d.ts +22 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +2 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/schema/action-schema.d.ts +9 -0
- package/dist/schema/action-schema.d.ts.map +1 -0
- package/dist/schema/action-schema.js +33 -0
- package/dist/schema/action-schema.js.map +1 -0
- package/dist/schema/history-schema.d.ts +11 -0
- package/dist/schema/history-schema.d.ts.map +1 -0
- package/dist/schema/history-schema.js +18 -0
- package/dist/schema/history-schema.js.map +1 -0
- package/dist/types.d.ts +75 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 cahaseler
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# determinate
|
|
2
|
+
|
|
3
|
+
A TypeScript library that treats LLMs as next-action predictors instead of conversational partners.
|
|
4
|
+
|
|
5
|
+
## The Problem
|
|
6
|
+
|
|
7
|
+
Current agentic frameworks are built around a conversation metaphor: an append-only chat history, a static list of tools, and a loop that generates the next message given everything that came before. This works well for coding assistants where the environment is deterministic and stable, but falls apart in dynamic environments where:
|
|
8
|
+
|
|
9
|
+
- **State goes stale.** If you inject environment state each turn across a 50-turn interaction, you have 50 snapshots in context, 49 of which are wrong. The oldest, most incorrect snapshot has the strongest positional signal.
|
|
10
|
+
- **Context fills with noise.** Failed tool calls, redundant observations, and retry loops consume tokens without contributing to decisions. Half the conversation history in a typical agentic run is the agent's own mistakes.
|
|
11
|
+
- **Tools are over-injected.** For a system with 180 possible actions, all 180 schemas are injected every call, even when only 20 are relevant in the current state.
|
|
12
|
+
- **The model does housekeeping instead of reasoning.** The model spends capacity reconciling stale state, filtering irrelevant tools, and formatting output instead of making the actual decision.
|
|
13
|
+
|
|
14
|
+
For the full argument, see [Beyond the Sacred Conversation](beyond-the-sacred-conversation.md).
|
|
15
|
+
|
|
16
|
+
## The Approach
|
|
17
|
+
|
|
18
|
+
The unit of work is not a conversation turn. It is a **state-to-action decision**.
|
|
19
|
+
|
|
20
|
+
`determinate` is a decision engine, not a framework. It does not own the loop, manage side effects, or implement tool handlers. You provide the situation; it returns an action.
|
|
21
|
+
|
|
22
|
+
Each call to `nextAction()`:
|
|
23
|
+
|
|
24
|
+
1. Filters tools to only those valid in the current state
|
|
25
|
+
2. Generates a constrained output schema — the model *cannot* choose an invalid action
|
|
26
|
+
3. Assembles an optimized context with explicit token budgets per section
|
|
27
|
+
4. Makes a single LLM call with structured output (constrained decoding)
|
|
28
|
+
5. Returns the chosen action with validated parameters
|
|
29
|
+
|
|
30
|
+
No conversation history accumulates inside the library. Context is an intentional budget, not a dumping ground.
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
bun add determinate
|
|
36
|
+
# zod is a peer dependency
|
|
37
|
+
bun add zod
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { z } from "zod";
|
|
42
|
+
import { createAgent } from "determinate";
|
|
43
|
+
|
|
44
|
+
const agent = createAgent({
|
|
45
|
+
provider: {
|
|
46
|
+
type: "openai", // or "anthropic", "vllm", "openrouter"
|
|
47
|
+
model: "gpt-5-nano",
|
|
48
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
49
|
+
},
|
|
50
|
+
state: z.object({
|
|
51
|
+
order: z.object({
|
|
52
|
+
status: z.enum(["pending", "approved", "shipped"]),
|
|
53
|
+
riskScore: z.number(),
|
|
54
|
+
items: z.array(z.object({ name: z.string(), qty: z.number() })),
|
|
55
|
+
}),
|
|
56
|
+
}),
|
|
57
|
+
tools: [
|
|
58
|
+
{
|
|
59
|
+
name: "approve_order",
|
|
60
|
+
description: "Approve a pending order",
|
|
61
|
+
params: z.object({ note: z.string() }),
|
|
62
|
+
validWhen: (s) => s.order.status === "pending" && s.order.riskScore < 0.7,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: "escalate_order",
|
|
66
|
+
description: "Escalate order for human review",
|
|
67
|
+
params: z.object({ reason: z.string() }),
|
|
68
|
+
validWhen: (s) => s.order.status === "pending" && s.order.riskScore >= 0.7,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: "ship_order",
|
|
72
|
+
description: "Ship an approved order",
|
|
73
|
+
params: z.object({ carrier: z.enum(["fedex", "ups", "usps"]) }),
|
|
74
|
+
validWhen: (s) => s.order.status === "approved",
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
instructions: (s) =>
|
|
78
|
+
`You are an order processing agent. Evaluate order risk and take appropriate action.
|
|
79
|
+
Current risk score: ${s.order.riskScore}`,
|
|
80
|
+
context: {
|
|
81
|
+
budgets: { instructions: 5000, state: 5000, history: 10000, tools: 3000 },
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Your loop — you own it
|
|
86
|
+
agent.setState({
|
|
87
|
+
order: { status: "pending", riskScore: 0.3, items: [{ name: "Widget", qty: 2 }] },
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const result = await agent.nextAction();
|
|
91
|
+
// { action: { tool: "approve_order", params: { note: "Low risk, standard order" } },
|
|
92
|
+
// meta: { tokensUsed: { input: 180, output: 30 }, model: "gpt-5-nano", latency: 892 } }
|
|
93
|
+
|
|
94
|
+
// You execute the action, update state, call nextAction() again
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Core Concepts
|
|
98
|
+
|
|
99
|
+
### State
|
|
100
|
+
|
|
101
|
+
Define your environment state as a Zod schema. The library validates it, serializes it for the model, and passes it to your tool predicates and instruction function. You replace it entirely each turn via `setState()` — no stale snapshots accumulating.
|
|
102
|
+
|
|
103
|
+
### Tools with Conditional Validity
|
|
104
|
+
|
|
105
|
+
Each tool has a `validWhen` predicate evaluated against current state. Only valid tools are presented to the model, and the constrained output schema makes it physically impossible for the model to choose an invalid tool. This is least-privilege enforced structurally, not by hoping the model follows instructions.
|
|
106
|
+
|
|
107
|
+
### Token Budgets
|
|
108
|
+
|
|
109
|
+
You set explicit token budgets per section (instructions, state, history, tools). If any section exceeds its budget, the call is rejected with a `BudgetExceededError` — no silent truncation. This makes context overflow a build-time problem you fix once, not a runtime surprise.
|
|
110
|
+
|
|
111
|
+
### History
|
|
112
|
+
|
|
113
|
+
You manage history. The library defines the format, validates it, and translates it into provider-native tool-calling messages (exploiting model training on tool-calling patterns). You control what history to include, how to compress it, and when to drop entries.
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
agent.setHistory([
|
|
117
|
+
{
|
|
118
|
+
tool: "request_info",
|
|
119
|
+
params: { field: "shipping_address" },
|
|
120
|
+
result: "Customer provided: 123 Main St",
|
|
121
|
+
success: true,
|
|
122
|
+
},
|
|
123
|
+
]);
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Instructions
|
|
127
|
+
|
|
128
|
+
A function from state to string. Called each turn, so you can provide different instructions for different situations without any framework machinery.
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
instructions: (s) => {
|
|
132
|
+
if (s.order.riskScore > 0.9) return "This is an extremely high-risk order. Escalate immediately.";
|
|
133
|
+
if (s.order.status === "approved") return "Select carrier based on package weight and destination.";
|
|
134
|
+
return "Evaluate the order against standard fulfillment policy.";
|
|
135
|
+
},
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Providers
|
|
139
|
+
|
|
140
|
+
| Provider | How | Structured Output |
|
|
141
|
+
|----------|-----|-------------------|
|
|
142
|
+
| OpenAI | OpenAI SDK | `response_format: json_schema` |
|
|
143
|
+
| Anthropic | Raw fetch adapter | `output_config.format: json_schema` |
|
|
144
|
+
| vLLM | OpenAI SDK + custom base URL | Constrained decoding (xgrammar/outlines) |
|
|
145
|
+
| OpenRouter | OpenAI SDK + custom base URL | Depends on upstream model |
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
// Local vLLM
|
|
149
|
+
provider: { type: "vllm", model: "Qwen/Qwen3.5-4B", apiKey: "not-needed", baseUrl: "http://localhost:8000/v1" }
|
|
150
|
+
|
|
151
|
+
// Anthropic
|
|
152
|
+
provider: { type: "anthropic", model: "claude-haiku-4-5-20251001", apiKey: process.env.ANTHROPIC_API_KEY }
|
|
153
|
+
|
|
154
|
+
// OpenRouter
|
|
155
|
+
provider: { type: "openrouter", model: "anthropic/claude-sonnet-4-5", apiKey: process.env.OPENROUTER_API_KEY }
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Cost Tracking
|
|
159
|
+
|
|
160
|
+
The library returns token counts in `meta.tokensUsed` (may be `{ input: 0, output: 0 }` if the provider doesn't report usage). For cost estimation, pass your own pricing:
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
const agent = createAgent({
|
|
164
|
+
// ...
|
|
165
|
+
pricing: { input: 0.05, output: 0.4 }, // per 1M tokens
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const result = await agent.nextAction();
|
|
169
|
+
result.meta.cost; // number | undefined
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Timeouts and Cancellation
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
// Per-call timeout
|
|
176
|
+
const result = await agent.nextAction({ timeout: 10000 });
|
|
177
|
+
|
|
178
|
+
// AbortSignal
|
|
179
|
+
const controller = new AbortController();
|
|
180
|
+
const result = await agent.nextAction({ signal: controller.signal });
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Verbose Mode
|
|
184
|
+
|
|
185
|
+
For debugging, get the full assembled context:
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
const result = await agent.nextAction({ verbose: true });
|
|
189
|
+
// result.context.messages — what was sent to the LLM
|
|
190
|
+
// result.context.outputSchema — the JSON schema constraining the output
|
|
191
|
+
// result.context.validTools — which tools were available
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Errors
|
|
195
|
+
|
|
196
|
+
All errors are typed and actionable:
|
|
197
|
+
|
|
198
|
+
| Error | When |
|
|
199
|
+
|-------|------|
|
|
200
|
+
| `ValidationError` | State doesn't match schema, history format invalid |
|
|
201
|
+
| `BudgetExceededError` | A section exceeds its token budget |
|
|
202
|
+
| `NoValidToolsError` | No tool's `validWhen` returned true |
|
|
203
|
+
| `ProviderError` | Auth failure, rate limit, network error |
|
|
204
|
+
| `OutputError` | Model returned invalid action (shouldn't happen with constrained output) |
|
|
205
|
+
| `AbortError` | Call cancelled or timed out |
|
|
206
|
+
|
|
207
|
+
## OAuth
|
|
208
|
+
|
|
209
|
+
Built-in device code flows for subscription-based access (ChatGPT Plus, Claude Pro):
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
import { getOAuthProvider, getOAuthApiKey } from "determinate";
|
|
213
|
+
|
|
214
|
+
// Trigger login flow
|
|
215
|
+
const provider = getOAuthProvider("openai"); // returns undefined if not registered
|
|
216
|
+
await provider?.login(callbacks);
|
|
217
|
+
|
|
218
|
+
// Later, credentials are used automatically
|
|
219
|
+
const agent = createAgent({
|
|
220
|
+
provider: { type: "openai", model: "gpt-5-nano", oauth: true },
|
|
221
|
+
// no apiKey needed — uses stored credentials
|
|
222
|
+
// ...
|
|
223
|
+
});
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Requirements
|
|
227
|
+
|
|
228
|
+
- **Runtime:** Bun (or Node.js with compatible APIs)
|
|
229
|
+
- **TypeScript:** 5.x
|
|
230
|
+
- **Zod:** >= 4.0.0 (peer dependency)
|
|
231
|
+
|
|
232
|
+
## Philosophy
|
|
233
|
+
|
|
234
|
+
This library exists because we believe the conversation metaphor is the wrong abstraction for most agentic systems. An LLM making decisions in a dynamic environment is solving a classification problem with context, not having a conversation. The architecture should reflect that.
|
|
235
|
+
|
|
236
|
+
For the full argument: [Beyond the Sacred Conversation](beyond-the-sacred-conversation.md).
|
package/dist/agent.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type ValidatedHistoryEntry } from "./schema/history-schema";
|
|
2
|
+
import type { ActionResult, AgentConfig, HistoryEntry, NextActionOptions, VerboseActionResult } from "./types";
|
|
3
|
+
export declare class Agent<TState> {
|
|
4
|
+
private config;
|
|
5
|
+
private state;
|
|
6
|
+
private history;
|
|
7
|
+
private provider;
|
|
8
|
+
private tokenizer;
|
|
9
|
+
constructor(config: AgentConfig<TState>);
|
|
10
|
+
private resolveProvider;
|
|
11
|
+
setState(state: TState): void;
|
|
12
|
+
getState(): TState;
|
|
13
|
+
setHistory(history: HistoryEntry[]): void;
|
|
14
|
+
getHistory(): ValidatedHistoryEntry[];
|
|
15
|
+
nextAction(options?: NextActionOptions): Promise<ActionResult | VerboseActionResult>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=agent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,qBAAqB,EAAmB,MAAM,yBAAyB,CAAC;AACtF,OAAO,KAAK,EACX,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,EACnB,MAAM,SAAS,CAAC;AAEjB,qBAAa,KAAK,CAAC,MAAM;IACxB,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,SAAS,CAAY;gBAEjB,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC;YAUzB,eAAe;IAsB7B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAU7B,QAAQ,IAAI,MAAM;IAOlB,UAAU,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,IAAI;IAIzC,UAAU,IAAI,qBAAqB,EAAE;IAI/B,UAAU,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,YAAY,GAAG,mBAAmB,CAAC;CA4F1F"}
|
package/dist/agent.js
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { assembleContext } from "./context/assembler";
|
|
2
|
+
import { createTokenizer } from "./context/tokenizer";
|
|
3
|
+
import { AbortError, OutputError, ProviderError, ValidationError } from "./errors";
|
|
4
|
+
import { getOAuthApiKey } from "./oauth/index";
|
|
5
|
+
import { createProvider } from "./providers/factory";
|
|
6
|
+
import { validateHistory } from "./schema/history-schema";
|
|
7
|
+
export class Agent {
|
|
8
|
+
config;
|
|
9
|
+
state;
|
|
10
|
+
history = [];
|
|
11
|
+
provider;
|
|
12
|
+
tokenizer;
|
|
13
|
+
constructor(config) {
|
|
14
|
+
this.config = config;
|
|
15
|
+
this.tokenizer = createTokenizer(config.provider.type, config.provider.model);
|
|
16
|
+
// Create provider immediately if we have an apiKey or OAuth isn't requested
|
|
17
|
+
if (config.provider.apiKey || !config.provider.oauth) {
|
|
18
|
+
this.provider = createProvider(config.provider);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async resolveProvider() {
|
|
22
|
+
if (this.provider)
|
|
23
|
+
return this.provider;
|
|
24
|
+
// OAuth is requested but no apiKey — try stored credentials
|
|
25
|
+
const oauthProviderId = this.config.provider.type === "openai" ? "openai" : "anthropic";
|
|
26
|
+
const result = await getOAuthApiKey(oauthProviderId);
|
|
27
|
+
if (!result) {
|
|
28
|
+
throw new ProviderError(this.config.provider.type, `No stored OAuth credentials for ${oauthProviderId}. Run the login flow first using the exported OAuth providers.`);
|
|
29
|
+
}
|
|
30
|
+
this.provider = createProvider({
|
|
31
|
+
...this.config.provider,
|
|
32
|
+
apiKey: result.apiKey,
|
|
33
|
+
});
|
|
34
|
+
return this.provider;
|
|
35
|
+
}
|
|
36
|
+
setState(state) {
|
|
37
|
+
const result = this.config.state.safeParse(state);
|
|
38
|
+
if (!result.success) {
|
|
39
|
+
throw new ValidationError(`Invalid state: ${result.error.issues.map((i) => i.message).join(", ")}`);
|
|
40
|
+
}
|
|
41
|
+
this.state = result.data;
|
|
42
|
+
}
|
|
43
|
+
getState() {
|
|
44
|
+
if (this.state === undefined) {
|
|
45
|
+
throw new ValidationError("State has not been set");
|
|
46
|
+
}
|
|
47
|
+
return this.state;
|
|
48
|
+
}
|
|
49
|
+
setHistory(history) {
|
|
50
|
+
this.history = validateHistory(history);
|
|
51
|
+
}
|
|
52
|
+
getHistory() {
|
|
53
|
+
return this.history;
|
|
54
|
+
}
|
|
55
|
+
async nextAction(options) {
|
|
56
|
+
const state = this.getState();
|
|
57
|
+
let signal = options?.signal;
|
|
58
|
+
if (options?.timeout) {
|
|
59
|
+
const timeoutSignal = AbortSignal.timeout(options.timeout);
|
|
60
|
+
signal = signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal;
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
const assembled = assembleContext({
|
|
64
|
+
state,
|
|
65
|
+
tools: this.config.tools,
|
|
66
|
+
history: this.history,
|
|
67
|
+
instructions: this.config.instructions,
|
|
68
|
+
budgets: this.config.context.budgets,
|
|
69
|
+
tokenizer: this.tokenizer,
|
|
70
|
+
providerType: this.config.provider.type,
|
|
71
|
+
});
|
|
72
|
+
const provider = await this.resolveProvider();
|
|
73
|
+
const start = performance.now();
|
|
74
|
+
const response = await provider.sendRequest({
|
|
75
|
+
messages: assembled.messages,
|
|
76
|
+
outputSchema: assembled.outputSchema,
|
|
77
|
+
model: this.config.provider.model,
|
|
78
|
+
options: this.config.provider.options,
|
|
79
|
+
signal,
|
|
80
|
+
});
|
|
81
|
+
const latency = performance.now() - start;
|
|
82
|
+
if (!assembled.validTools.includes(response.action.tool)) {
|
|
83
|
+
throw new OutputError(`Model returned tool "${response.action.tool}" which is not in the valid set: [${assembled.validTools.join(", ")}]`, JSON.stringify(response.action));
|
|
84
|
+
}
|
|
85
|
+
const toolDef = this.config.tools.find((t) => t.name === response.action.tool);
|
|
86
|
+
if (toolDef) {
|
|
87
|
+
// Strip the tool_name discriminant injected by the output schema
|
|
88
|
+
const { tool_name: _, ...cleanParams } = response.action.params;
|
|
89
|
+
const paramsResult = toolDef.params.safeParse(cleanParams);
|
|
90
|
+
if (!paramsResult.success) {
|
|
91
|
+
throw new OutputError(`Params for tool "${response.action.tool}" failed validation: ${paramsResult.error.issues.map((i) => i.message).join(", ")}`, JSON.stringify(response.action));
|
|
92
|
+
}
|
|
93
|
+
response.action.params = paramsResult.data;
|
|
94
|
+
}
|
|
95
|
+
let cost;
|
|
96
|
+
if (this.config.pricing) {
|
|
97
|
+
const { input, output } = response.meta.tokensUsed;
|
|
98
|
+
cost =
|
|
99
|
+
(input / 1_000_000) * this.config.pricing.input +
|
|
100
|
+
(output / 1_000_000) * this.config.pricing.output;
|
|
101
|
+
}
|
|
102
|
+
const result = {
|
|
103
|
+
action: response.action,
|
|
104
|
+
meta: {
|
|
105
|
+
tokensUsed: response.meta.tokensUsed,
|
|
106
|
+
cost,
|
|
107
|
+
model: response.meta.model,
|
|
108
|
+
latency,
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
if (options?.verbose) {
|
|
112
|
+
return {
|
|
113
|
+
...result,
|
|
114
|
+
context: {
|
|
115
|
+
messages: assembled.messages,
|
|
116
|
+
outputSchema: assembled.outputSchema,
|
|
117
|
+
validTools: assembled.validTools,
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
return result;
|
|
122
|
+
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
if (signal?.aborted) {
|
|
125
|
+
throw new AbortError(options?.timeout ? `Timeout after ${options.timeout}ms` : "Operation was aborted");
|
|
126
|
+
}
|
|
127
|
+
throw err;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { HistoryEntry, ProviderConfig, TokenBudgets, ToolDefinition } from "../types";
|
|
2
|
+
import type { Tokenizer } from "./tokenizer";
|
|
3
|
+
interface AssembleInput<TState> {
|
|
4
|
+
state: TState;
|
|
5
|
+
tools: ToolDefinition<TState>[];
|
|
6
|
+
history: HistoryEntry[];
|
|
7
|
+
instructions: (state: TState) => string;
|
|
8
|
+
budgets: TokenBudgets;
|
|
9
|
+
tokenizer: Tokenizer;
|
|
10
|
+
providerType: ProviderConfig["type"];
|
|
11
|
+
}
|
|
12
|
+
interface AssembledPayload {
|
|
13
|
+
messages: unknown[];
|
|
14
|
+
outputSchema: Record<string, unknown>;
|
|
15
|
+
validTools: string[];
|
|
16
|
+
}
|
|
17
|
+
export declare function assembleContext<TState>(input: AssembleInput<TState>): AssembledPayload;
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=assembler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assembler.d.ts","sourceRoot":"","sources":["../../src/context/assembler.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE3F,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,UAAU,aAAa,CAAC,MAAM;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC;IAChC,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACxC,OAAO,EAAE,YAAY,CAAC;IACtB,SAAS,EAAE,SAAS,CAAC;IACrB,YAAY,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;CACrC;AAED,UAAU,gBAAgB;IACzB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,UAAU,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,gBAAgB,CA8FtF"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { NoValidToolsError } from "../errors";
|
|
3
|
+
import { generateActionSchema } from "../schema/action-schema";
|
|
4
|
+
import { enforceBudgets } from "./budget";
|
|
5
|
+
export function assembleContext(input) {
|
|
6
|
+
const { state, tools, history, instructions, budgets, tokenizer, providerType } = input;
|
|
7
|
+
// 1. Filter tools by validWhen
|
|
8
|
+
const validTools = tools.filter((t) => t.validWhen(state));
|
|
9
|
+
if (validTools.length === 0) {
|
|
10
|
+
throw new NoValidToolsError();
|
|
11
|
+
}
|
|
12
|
+
// 2. Generate output schema
|
|
13
|
+
const outputSchema = generateActionSchema(validTools);
|
|
14
|
+
// 3. Build instructions text
|
|
15
|
+
const instructionsText = instructions(state);
|
|
16
|
+
const toolInstructions = validTools
|
|
17
|
+
.filter((t) => t.instructions)
|
|
18
|
+
.map((t) => `[${t.name}]: ${t.instructions}`)
|
|
19
|
+
.join("\n");
|
|
20
|
+
const fullInstructions = toolInstructions
|
|
21
|
+
? `${instructionsText}\n\nTool-specific instructions:\n${toolInstructions}`
|
|
22
|
+
: instructionsText;
|
|
23
|
+
// 4. Build tool descriptions
|
|
24
|
+
const toolDescriptions = validTools.map((t) => `- ${t.name}: ${t.description}`).join("\n");
|
|
25
|
+
// 5. Serialize state
|
|
26
|
+
const stateText = JSON.stringify(state, null, 2);
|
|
27
|
+
// 6. Build history messages (needed for accurate budget counting)
|
|
28
|
+
const historyMessages = [];
|
|
29
|
+
for (const entry of history) {
|
|
30
|
+
const callId = randomUUID();
|
|
31
|
+
if (providerType === "anthropic") {
|
|
32
|
+
historyMessages.push({
|
|
33
|
+
role: "assistant",
|
|
34
|
+
content: [{ type: "tool_use", id: callId, name: entry.tool, input: entry.params }],
|
|
35
|
+
});
|
|
36
|
+
historyMessages.push({
|
|
37
|
+
role: "user",
|
|
38
|
+
content: [
|
|
39
|
+
{
|
|
40
|
+
type: "tool_result",
|
|
41
|
+
tool_use_id: callId,
|
|
42
|
+
content: entry.result,
|
|
43
|
+
is_error: entry.success === false,
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
historyMessages.push({
|
|
50
|
+
role: "assistant",
|
|
51
|
+
tool_calls: [
|
|
52
|
+
{
|
|
53
|
+
id: callId,
|
|
54
|
+
type: "function",
|
|
55
|
+
function: { name: entry.tool, arguments: JSON.stringify(entry.params) },
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
});
|
|
59
|
+
historyMessages.push({
|
|
60
|
+
role: "tool",
|
|
61
|
+
tool_call_id: callId,
|
|
62
|
+
content: entry.result,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// 7. Enforce budgets
|
|
67
|
+
const historyText = historyMessages.length > 0 ? JSON.stringify(historyMessages) : "";
|
|
68
|
+
enforceBudgets({
|
|
69
|
+
instructions: fullInstructions,
|
|
70
|
+
state: stateText,
|
|
71
|
+
history: historyText,
|
|
72
|
+
tools: toolDescriptions,
|
|
73
|
+
}, budgets, tokenizer);
|
|
74
|
+
// 8. Build final messages
|
|
75
|
+
const messages = [];
|
|
76
|
+
messages.push({
|
|
77
|
+
role: "system",
|
|
78
|
+
content: `${fullInstructions}\n\nAvailable actions:\n${toolDescriptions}\n\nRespond with a JSON object choosing one action and its parameters.`,
|
|
79
|
+
});
|
|
80
|
+
messages.push(...historyMessages);
|
|
81
|
+
messages.push({
|
|
82
|
+
role: "user",
|
|
83
|
+
content: `Current state:\n${stateText}\n\nChoose the next action.`,
|
|
84
|
+
});
|
|
85
|
+
return { messages, outputSchema, validTools: validTools.map((t) => t.name) };
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=assembler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assembler.js","sourceRoot":"","sources":["../../src/context/assembler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { TokenBudgets } from "../types";
|
|
2
|
+
import type { Tokenizer } from "./tokenizer";
|
|
3
|
+
export interface SectionContents {
|
|
4
|
+
instructions: string;
|
|
5
|
+
state: string;
|
|
6
|
+
history: string;
|
|
7
|
+
tools: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function enforceBudgets(sections: SectionContents, budgets: TokenBudgets, tokenizer: Tokenizer): Record<keyof TokenBudgets, number>;
|
|
10
|
+
//# sourceMappingURL=budget.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budget.d.ts","sourceRoot":"","sources":["../../src/context/budget.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,WAAW,eAAe;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,cAAc,CAC7B,QAAQ,EAAE,eAAe,EACzB,OAAO,EAAE,YAAY,EACrB,SAAS,EAAE,SAAS,GAClB,MAAM,CAAC,MAAM,YAAY,EAAE,MAAM,CAAC,CAYpC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BudgetExceededError } from "../errors";
|
|
2
|
+
export function enforceBudgets(sections, budgets, tokenizer) {
|
|
3
|
+
const counts = {};
|
|
4
|
+
for (const key of ["instructions", "state", "history", "tools"]) {
|
|
5
|
+
const count = tokenizer.count(sections[key]);
|
|
6
|
+
counts[key] = count;
|
|
7
|
+
if (count > budgets[key]) {
|
|
8
|
+
throw new BudgetExceededError(key, count, budgets[key]);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
return counts;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=budget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budget.js","sourceRoot":"","sources":["../../src/context/budget.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAWhD,MAAM,UAAU,cAAc,CAC7B,QAAyB,EACzB,OAAqB,EACrB,SAAoB;IAEpB,MAAM,MAAM,GAA2B,EAAE,CAAC;IAE1C,KAAK,MAAM,GAAG,IAAI,CAAC,cAAc,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,CAAU,EAAE,CAAC;QAC1E,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7C,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACpB,IAAI,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,mBAAmB,CAAC,GAAG,EAAE,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;QACzD,CAAC;IACF,CAAC;IAED,OAAO,MAA4C,CAAC;AACrD,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ProviderConfig } from "../types";
|
|
2
|
+
export interface Tokenizer {
|
|
3
|
+
count(input: string | Record<string, unknown>): number;
|
|
4
|
+
}
|
|
5
|
+
export declare function createTokenizer(providerType: ProviderConfig["type"], model: string): Tokenizer;
|
|
6
|
+
//# sourceMappingURL=tokenizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenizer.d.ts","sourceRoot":"","sources":["../../src/context/tokenizer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE/C,MAAM,WAAW,SAAS;IACzB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC;CACvD;AA+BD,wBAAgB,eAAe,CAAC,YAAY,EAAE,cAAc,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,CAM9F"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { encoding_for_model, get_encoding } from "tiktoken";
|
|
2
|
+
function serialize(input) {
|
|
3
|
+
if (typeof input === "string")
|
|
4
|
+
return input;
|
|
5
|
+
return JSON.stringify(input);
|
|
6
|
+
}
|
|
7
|
+
class TiktokenTokenizer {
|
|
8
|
+
encoder;
|
|
9
|
+
constructor(model) {
|
|
10
|
+
try {
|
|
11
|
+
this.encoder = encoding_for_model(model);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
this.encoder = get_encoding("cl100k_base");
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
count(input) {
|
|
18
|
+
return this.encoder.encode(serialize(input)).length;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
class CharApproximationTokenizer {
|
|
22
|
+
charsPerToken = 3;
|
|
23
|
+
count(input) {
|
|
24
|
+
return Math.ceil(serialize(input).length / this.charsPerToken);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export function createTokenizer(providerType, model) {
|
|
28
|
+
if (providerType === "openai" || providerType === "openrouter" || providerType === "vllm") {
|
|
29
|
+
return new TiktokenTokenizer(model);
|
|
30
|
+
}
|
|
31
|
+
// Anthropic doesn't use tiktoken — approximate by character count
|
|
32
|
+
return new CharApproximationTokenizer();
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=tokenizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenizer.js","sourceRoot":"","sources":["../../src/context/tokenizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAsB,MAAM,UAAU,CAAC;AAOhF,SAAS,SAAS,CAAC,KAAuC;IACzD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5C,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,iBAAiB;IACd,OAAO,CAAC;IAEhB,YAAY,KAAa;QACxB,IAAI,CAAC;YACJ,IAAI,CAAC,OAAO,GAAG,kBAAkB,CAAC,KAAsB,CAAC,CAAC;QAC3D,CAAC;QAAC,MAAM,CAAC;YACR,IAAI,CAAC,OAAO,GAAG,YAAY,CAAC,aAAa,CAAC,CAAC;QAC5C,CAAC;IACF,CAAC;IAED,KAAK,CAAC,KAAuC;QAC5C,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;IACrD,CAAC;CACD;AAED,MAAM,0BAA0B;IACd,aAAa,GAAG,CAAC,CAAC;IAEnC,KAAK,CAAC,KAAuC;QAC5C,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC;IAChE,CAAC;CACD;AAED,MAAM,UAAU,eAAe,CAAC,YAAoC,EAAE,KAAa;IAClF,IAAI,YAAY,KAAK,QAAQ,IAAI,YAAY,KAAK,YAAY,IAAI,YAAY,KAAK,MAAM,EAAE,CAAC;QAC3F,OAAO,IAAI,iBAAiB,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC;IACD,kEAAkE;IAClE,OAAO,IAAI,0BAA0B,EAAE,CAAC;AACzC,CAAC"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export declare class ValidationError extends Error {
|
|
2
|
+
name: "ValidationError";
|
|
3
|
+
}
|
|
4
|
+
export declare class BudgetExceededError extends Error {
|
|
5
|
+
readonly section: string;
|
|
6
|
+
readonly actual: number;
|
|
7
|
+
readonly budget: number;
|
|
8
|
+
name: "BudgetExceededError";
|
|
9
|
+
constructor(section: string, actual: number, budget: number);
|
|
10
|
+
}
|
|
11
|
+
export declare class NoValidToolsError extends Error {
|
|
12
|
+
name: "NoValidToolsError";
|
|
13
|
+
constructor();
|
|
14
|
+
}
|
|
15
|
+
export declare class ProviderError extends Error {
|
|
16
|
+
readonly provider: string;
|
|
17
|
+
name: "ProviderError";
|
|
18
|
+
constructor(provider: string, message: string);
|
|
19
|
+
}
|
|
20
|
+
export declare class OutputError extends Error {
|
|
21
|
+
readonly rawOutput: string;
|
|
22
|
+
name: "OutputError";
|
|
23
|
+
constructor(message: string, rawOutput: string);
|
|
24
|
+
}
|
|
25
|
+
export declare class AbortError extends Error {
|
|
26
|
+
name: "AbortError";
|
|
27
|
+
constructor(message?: string);
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,eAAgB,SAAQ,KAAK;IAChC,IAAI,EAAG,iBAAiB,CAAU;CAC3C;AAED,qBAAa,mBAAoB,SAAQ,KAAK;aAG5B,OAAO,EAAE,MAAM;aACf,MAAM,EAAE,MAAM;aACd,MAAM,EAAE,MAAM;IAJtB,IAAI,EAAG,qBAAqB,CAAU;gBAE9B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM;CAI/B;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAClC,IAAI,EAAG,mBAAmB,CAAU;;CAI7C;AAED,qBAAa,aAAc,SAAQ,KAAK;aAGtB,QAAQ,EAAE,MAAM;IAFxB,IAAI,EAAG,eAAe,CAAU;gBAExB,QAAQ,EAAE,MAAM,EAChC,OAAO,EAAE,MAAM;CAIhB;AAED,qBAAa,WAAY,SAAQ,KAAK;aAIpB,SAAS,EAAE,MAAM;IAHzB,IAAI,EAAG,aAAa,CAAU;gBAEtC,OAAO,EAAE,MAAM,EACC,SAAS,EAAE,MAAM;CAIlC;AAED,qBAAa,UAAW,SAAQ,KAAK;IAC3B,IAAI,EAAG,YAAY,CAAU;gBAC1B,OAAO,GAAE,MAAgC;CAGrD"}
|