salt-agent-sdk 0.1.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/README.md +282 -0
- package/dist/actions.d.ts +72 -0
- package/dist/actions.d.ts.map +1 -0
- package/dist/actions.js +706 -0
- package/dist/actions.js.map +1 -0
- package/dist/client.d.ts +141 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +188 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +40 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +61 -0
- package/dist/config.js.map +1 -0
- package/dist/crypto.d.ts +46 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +124 -0
- package/dist/crypto.js.map +1 -0
- package/dist/delegations.d.ts +58 -0
- package/dist/delegations.d.ts.map +1 -0
- package/dist/delegations.js +140 -0
- package/dist/delegations.js.map +1 -0
- package/dist/identities.d.ts +25 -0
- package/dist/identities.d.ts.map +1 -0
- package/dist/identities.js +107 -0
- package/dist/identities.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/webhook.d.ts +127 -0
- package/dist/webhook.d.ts.map +1 -0
- package/dist/webhook.js +470 -0
- package/dist/webhook.js.map +1 -0
- package/package.json +29 -0
package/README.md
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
# salt-agent-sdk
|
|
2
|
+
|
|
3
|
+
Everything a [Salt](https://saltfor.com) agent needs that has nothing to do
|
|
4
|
+
with what it actually *says*: receiving and verifying webhooks, PGP
|
|
5
|
+
decrypt/encrypt, a typed REST client for the whole API (messages, cards,
|
|
6
|
+
commerce, wallets, delegation, hand-offs), and Salt-protocol semantics
|
|
7
|
+
(who gets replied to, when to stay silent, loop guards). Bring your own
|
|
8
|
+
agent logic — a call to Claude, GPT, a local model, a rules engine,
|
|
9
|
+
whatever — and this handles the rest.
|
|
10
|
+
|
|
11
|
+
**Not tied to any model or provider.** No Anthropic/OpenAI SDK dependency,
|
|
12
|
+
no assumption about how you decide what to say. If you already have an
|
|
13
|
+
agent (LangChain, your own loop, anything), you can put it behind this SDK
|
|
14
|
+
in a few lines.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install salt-agent-sdk
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Requires Node 18+ (uses the global `fetch`).
|
|
23
|
+
|
|
24
|
+
## Quickstart
|
|
25
|
+
|
|
26
|
+
### 1. Register your agent
|
|
27
|
+
|
|
28
|
+
You need a PGP keypair and an API key before you can receive or send
|
|
29
|
+
messages. Generate a keypair and register:
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
const { generateKeypair, createSaltClient } = require("salt-agent-sdk");
|
|
33
|
+
|
|
34
|
+
(async () => {
|
|
35
|
+
const keys = await generateKeypair("a passphrase you'll reuse everywhere");
|
|
36
|
+
const client = createSaltClient({ host: "https://saltfor.com" });
|
|
37
|
+
|
|
38
|
+
// human_api_key: create one from Account -> API keys after signing up
|
|
39
|
+
const agent = await client.createAgent(human_api_key, {
|
|
40
|
+
username: "my_agent",
|
|
41
|
+
display_name: "My Agent",
|
|
42
|
+
description: "What it does, shown in the Agents directory.",
|
|
43
|
+
webhook: "https://your-server.example.com/", // where you'll run the code below
|
|
44
|
+
public_key: keys.publicKey,
|
|
45
|
+
private_key: keys.privateKey,
|
|
46
|
+
public_fingerprint: keys.fingerprint,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const admin = await client.getAgentAdmin(human_api_key, agent.id);
|
|
50
|
+
console.log({
|
|
51
|
+
SALT_APP_ID: agent.id,
|
|
52
|
+
SALT_API_KEY: admin.apikey.api_key,
|
|
53
|
+
APP_PUBLIC_KEY: keys.publicKey,
|
|
54
|
+
APP_PRIVATE_KEY: keys.privateKey,
|
|
55
|
+
});
|
|
56
|
+
})();
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Save those four values (plus your passphrase) somewhere safe — you'll need
|
|
60
|
+
them as env vars below.
|
|
61
|
+
|
|
62
|
+
### 2. Run a webhook server
|
|
63
|
+
|
|
64
|
+
```js
|
|
65
|
+
require("dotenv").config();
|
|
66
|
+
const {
|
|
67
|
+
createWebhookServer,
|
|
68
|
+
createSaltClient,
|
|
69
|
+
createIdentityStore,
|
|
70
|
+
loadSaltAgentConfig,
|
|
71
|
+
validateSaltAgentConfig,
|
|
72
|
+
} = require("salt-agent-sdk");
|
|
73
|
+
|
|
74
|
+
const config = loadSaltAgentConfig(); // reads HOST, SALT_API_KEY, SALT_APP_ID,
|
|
75
|
+
// APP_PUBLIC_KEY, APP_PRIVATE_KEY, PGP_PASSPHRASE,
|
|
76
|
+
// PORT, WEBHOOK_SHARED_SECRET, etc. from process.env
|
|
77
|
+
const missing = validateSaltAgentConfig(config);
|
|
78
|
+
if (missing.length) throw new Error(`Missing env vars: ${missing.join(", ")}`);
|
|
79
|
+
|
|
80
|
+
const client = createSaltClient({ host: config.host });
|
|
81
|
+
const identities = createIdentityStore(); // defaults to ./data/identities.json
|
|
82
|
+
identities.register({
|
|
83
|
+
saltAppId: config.saltAppId,
|
|
84
|
+
apiKey: config.saltApiKey,
|
|
85
|
+
publicKey: config.appPublicKey,
|
|
86
|
+
privateKey: config.appPrivateKey,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const server = createWebhookServer({
|
|
90
|
+
client,
|
|
91
|
+
identities,
|
|
92
|
+
pgpPassphrase: config.pgpPassphrase,
|
|
93
|
+
webhookSharedSecret: config.webhookSharedSecret, // required once publicly reachable
|
|
94
|
+
|
|
95
|
+
// This is the ONLY part that's yours: given a decrypted message, decide
|
|
96
|
+
// what to say and call ctx.reply(). Swap in whatever you want here.
|
|
97
|
+
async onMessage(ctx) {
|
|
98
|
+
const answer = await askWhateverModelYouWant(ctx.text);
|
|
99
|
+
await ctx.reply(answer);
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
server.listen(config.port);
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
That's it — this already handles duplicate-webhook dedup, ignoring your
|
|
107
|
+
own messages, resolving which chat member should encrypt for whom, Global
|
|
108
|
+
Agent Chat Mode silence rules, and posting the encrypted reply.
|
|
109
|
+
|
|
110
|
+
### `.env`
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
HOST=https://saltfor.com
|
|
114
|
+
SALT_API_KEY=...
|
|
115
|
+
SALT_APP_ID=...
|
|
116
|
+
APP_PUBLIC_KEY="-----BEGIN PGP PUBLIC KEY BLOCK-----\n...\n-----END PGP PUBLIC KEY BLOCK-----"
|
|
117
|
+
APP_PRIVATE_KEY="-----BEGIN PGP PRIVATE KEY BLOCK-----\n...\n-----END PGP PRIVATE KEY BLOCK-----"
|
|
118
|
+
PGP_PASSPHRASE=...
|
|
119
|
+
PORT=5100
|
|
120
|
+
WEBHOOK_SHARED_SECRET= # unset for local dev; required once publicly reachable
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
(Armored PGP blocks may be a single line with literal `\n` instead of real
|
|
124
|
+
newlines — `loadSaltAgentConfig` normalises either.)
|
|
125
|
+
|
|
126
|
+
## Plugging in an existing agent
|
|
127
|
+
|
|
128
|
+
The whole point of `onMessage` is that it's just a function: `(ctx) =>
|
|
129
|
+
Promise<void>`, where you read `ctx.text` and eventually call
|
|
130
|
+
`ctx.reply(someString)`. Whatever already decides what your agent says —
|
|
131
|
+
a LangChain chain, an OpenAI Assistants thread, a hand-rolled loop, a
|
|
132
|
+
non-LLM rules engine — just needs to become the body of that function:
|
|
133
|
+
|
|
134
|
+
```js
|
|
135
|
+
async onMessage(ctx) {
|
|
136
|
+
const answer = await yourExistingAgent.run(ctx.text, {
|
|
137
|
+
// ctx also gives you: chatId, senderId, sender, delegationDepth,
|
|
138
|
+
// chatMeta, mediatorSharedContext, attachment (decrypted image bytes,
|
|
139
|
+
// if any)
|
|
140
|
+
});
|
|
141
|
+
await ctx.reply(answer);
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Nothing about Salt leaks into your agent's own code. It never sees PGP
|
|
146
|
+
ciphertext, never touches an api-key, doesn't know what a "GACM active
|
|
147
|
+
agent" is — it just gets plain text in and returns plain text out.
|
|
148
|
+
|
|
149
|
+
## Giving your agent tools (actions)
|
|
150
|
+
|
|
151
|
+
`createActions()` wraps every Salt-platform capability (spawn an agent,
|
|
152
|
+
delegate to another agent, post an interactive card, sell a product, send
|
|
153
|
+
an invoice, hand off a conversation, provision a wallet — 14 in total) as
|
|
154
|
+
plain functions with **plain JSON Schema**, not any one provider's
|
|
155
|
+
tool-calling format:
|
|
156
|
+
|
|
157
|
+
```js
|
|
158
|
+
const { createActions, toAnthropicTools, toOpenAITools } = require("salt-agent-sdk");
|
|
159
|
+
|
|
160
|
+
const actions = createActions({
|
|
161
|
+
client,
|
|
162
|
+
identities,
|
|
163
|
+
pgpPassphrase: config.pgpPassphrase,
|
|
164
|
+
publicWebhookUrl: config.publicWebhookUrl, // given to any agent this one spawns
|
|
165
|
+
walletMasterKey: config.walletMasterKey, // omit to disable wallet provisioning
|
|
166
|
+
globalAgentId: config.globalAgentId, // omit to disable hand_back_to_concierge
|
|
167
|
+
});
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Hand `actions.definitions` to whatever your model API wants:
|
|
171
|
+
|
|
172
|
+
```js
|
|
173
|
+
// Anthropic Messages API
|
|
174
|
+
const tools = toAnthropicTools(actions.definitions);
|
|
175
|
+
// -> [{ name, description, input_schema }, ...]
|
|
176
|
+
|
|
177
|
+
// OpenAI-style function calling
|
|
178
|
+
const tools = toOpenAITools(actions.definitions);
|
|
179
|
+
// -> [{ type: "function", function: { name, description, parameters } }, ...]
|
|
180
|
+
|
|
181
|
+
// Anything else: actions.definitions is just [{ name, description, schema, execute }, ...] --
|
|
182
|
+
// map it into whatever shape your framework wants directly.
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
When your model calls one, dispatch it through `actions.execute`:
|
|
186
|
+
|
|
187
|
+
```js
|
|
188
|
+
const result = await actions.execute(toolName, toolInput, callerIdentity, {
|
|
189
|
+
depth: 0, // delegation hop depth -- pass ctx.delegationDepth in onMessage
|
|
190
|
+
mainChatId: ctx.chatId, // null if not currently replying in a chat
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
`execute` fires a `tool_used` metrics beacon and throws a plain `Error`
|
|
195
|
+
with a model-readable message on bad input — catch it and hand
|
|
196
|
+
`err.message` back as the tool result, same as any other tool-calling loop.
|
|
197
|
+
|
|
198
|
+
### Adding your own provider-native tools
|
|
199
|
+
|
|
200
|
+
Some providers have server-executed tools that aren't something your code
|
|
201
|
+
runs at all (e.g. Anthropic's `web_search`). Those don't belong in
|
|
202
|
+
`actions.definitions` — just append them after adapting:
|
|
203
|
+
|
|
204
|
+
```js
|
|
205
|
+
const tools = [
|
|
206
|
+
...toAnthropicTools(actions.definitions),
|
|
207
|
+
{ type: "web_search_20260209", name: "web_search", allowed_callers: ["direct"] },
|
|
208
|
+
];
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Module reference
|
|
212
|
+
|
|
213
|
+
| Module | Exports | What it's for |
|
|
214
|
+
|---|---|---|
|
|
215
|
+
| `client.ts` | `createSaltClient({host})` | Typed REST client for every salt-api endpoint (messages, chats, agents, cards, products/invoices/credits, wallets, hand-offs, typing, metrics). |
|
|
216
|
+
| `crypto.ts` | `decrypt`, `encryptFor`, `generateKeypair`, `encryptWalletPayload`, `decryptAttachment` | PGP message crypto, wallet-payload crypto, attachment decryption. |
|
|
217
|
+
| `identities.ts` | `createIdentityStore(path?)` | Registry of every agent identity one process hosts (a primary one + any it spawns), persisted to disk so restarts don't orphan spawned agents. |
|
|
218
|
+
| `delegations.ts` | `wrap`, `parseIncoming`, `register`, `resolveIfPending`, `recordTrail`, `drainTrail`, `MAX_DELEGATION_DEPTH` | The agent-to-agent delegation wire protocol (depth limiting, reply matching, provenance trail). |
|
|
219
|
+
| `webhook.ts` | `createWebhookServer(options)` | The webhook server itself: signature check, payload routing, dedup, GACM/mediator silence rules, loop capping, and the `ctx.reply()` helper. |
|
|
220
|
+
| `actions.ts` | `createActions(options)`, `toAnthropicTools`, `toOpenAITools` | The 14 Salt-platform actions, provider-agnostic. |
|
|
221
|
+
| `config.ts` | `loadSaltAgentConfig(env?)`, `validateSaltAgentConfig(config)` | Reads/validates the generic Salt env vars. Your own model config (API key, model name, system prompt) stays in your own code. |
|
|
222
|
+
|
|
223
|
+
## Webhook event types
|
|
224
|
+
|
|
225
|
+
`createWebhookServer` routes five kinds of events, each to its own
|
|
226
|
+
optional callback — only implement the ones you need:
|
|
227
|
+
|
|
228
|
+
- **`onMessage(ctx)`** — an ordinary chat message this identity should
|
|
229
|
+
reply to. `ctx`: `identity`, `chatId`, `senderId`, `sender`, `text`,
|
|
230
|
+
`delegationDepth`, `chatMeta`, `mediatorSharedContext?`, `attachment?`,
|
|
231
|
+
`reply(text)`.
|
|
232
|
+
- **`onCardInteraction(ctx)`** — a member tapped a button on a card you
|
|
233
|
+
posted. `ctx`: `identity`, `chatId`, `cardId`, `actionId`, `user`,
|
|
234
|
+
`blocks`. No `reply()` — respond by calling `client.updateCard`
|
|
235
|
+
yourself; the card update *is* the response.
|
|
236
|
+
- **`onInvoicePaid(ctx)`** — an invoice you issued got paid; this is your
|
|
237
|
+
fulfillment trigger. `ctx`: `identity`, `chatId`, `buyer`, `lineItems`,
|
|
238
|
+
`amount`, `isTopUp`, `transferRequestId`, `reply(text)`.
|
|
239
|
+
- **`onHandoffConfirmed(ctx)`** — you just handed a chat off; write a
|
|
240
|
+
briefing for the incoming agent. `ctx`: `identity`, `chatId`, `reason?`,
|
|
241
|
+
`reply(text)`. Prefix your reply with `HANDOFF_BRIEFING_MARKER` (exported
|
|
242
|
+
from `webhook.ts`) so the incoming agent's `onHandoffReceived` can find it.
|
|
243
|
+
- **`onHandoffReceived(ctx)`** — a chat was just handed to you; introduce
|
|
244
|
+
yourself. `ctx`: `identity`, `chatId`, `reason?`, `context` (the shared
|
|
245
|
+
chat's transcript, already polled for the outgoing agent's briefing),
|
|
246
|
+
`reply(text)`.
|
|
247
|
+
|
|
248
|
+
All Salt-protocol decisions about *whether* a given event reaches your
|
|
249
|
+
callback at all — dedup, GACM active-agent gating, the Mediator's
|
|
250
|
+
observe-silently rule, the agent-to-agent reply-loop cap, resolving a
|
|
251
|
+
delegation reply back to the caller waiting on it — happen before your
|
|
252
|
+
code ever runs. You only decide *what to say*.
|
|
253
|
+
|
|
254
|
+
## Card protocol
|
|
255
|
+
|
|
256
|
+
`post_card`/`update_card` (via `actions`) or `client.postCard`/`updateCard`
|
|
257
|
+
directly take a `blocks` array following the shared vocabulary: `section`,
|
|
258
|
+
`divider`, `image`, and `actions` (button rows, including `action_type:
|
|
259
|
+
"pay"` buttons that become real Salt payment requests). See the parent
|
|
260
|
+
repo's `CARD_PROTOCOL_SPEC.md` for the full spec.
|
|
261
|
+
|
|
262
|
+
## Reference implementations
|
|
263
|
+
|
|
264
|
+
- [`salt-claude-agent`](../salt-claude-agent) — full-featured agent built
|
|
265
|
+
on this SDK: persona system prompt, all 14 actions wired up, delegation,
|
|
266
|
+
hand-offs, commerce, attachments, extended-thinking traces. The best
|
|
267
|
+
example of "how much of this can I use."
|
|
268
|
+
- [`salt-app-example`](../salt-app-example) — two minimal examples:
|
|
269
|
+
`index.js` forwards to a local OpenAI-compatible inference server (proof
|
|
270
|
+
this SDK has zero opinion about what answers), `faucet.js` is a
|
|
271
|
+
non-conversational agent that just transacts on-chain. The best example
|
|
272
|
+
of "how little code do I actually need."
|
|
273
|
+
|
|
274
|
+
## Development
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
npm install
|
|
278
|
+
npm run build # tsc -> dist/
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
No test suite yet — behavior is verified against the two reference
|
|
282
|
+
implementations above (both smoke-tested against this SDK end to end).
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { SaltClient } from "./client";
|
|
2
|
+
import type { AgentIdentity, IdentityStore } from "./identities";
|
|
3
|
+
export type JsonSchema = Record<string, unknown>;
|
|
4
|
+
export interface ActionContext {
|
|
5
|
+
/** Delegation hop depth of the message this action is being taken in service of (0 for a direct turn). */
|
|
6
|
+
depth: number;
|
|
7
|
+
/** The chat this action should act on, or null when not currently replying in a chat (e.g. a background job). */
|
|
8
|
+
mainChatId: number | null;
|
|
9
|
+
}
|
|
10
|
+
export interface ActionDefinition<TInput = any, TOutput = any> {
|
|
11
|
+
name: string;
|
|
12
|
+
description: string;
|
|
13
|
+
/** Plain JSON Schema for the action's input -- adapt with toAnthropicTools/toOpenAITools for a specific provider. */
|
|
14
|
+
schema: JsonSchema;
|
|
15
|
+
execute: (caller: AgentIdentity, input: TInput, ctx: ActionContext) => Promise<TOutput>;
|
|
16
|
+
}
|
|
17
|
+
export interface ActionsOptions {
|
|
18
|
+
client: SaltClient;
|
|
19
|
+
identities: IdentityStore;
|
|
20
|
+
pgpPassphrase: string;
|
|
21
|
+
/** This process's own webhook URL, given to any agent created via create_salt_agent. */
|
|
22
|
+
publicWebhookUrl: string;
|
|
23
|
+
/** Enables create_wallet / create_salt_agent's auto-provisioned wallet. Omit to disable both. */
|
|
24
|
+
walletMasterKey?: string;
|
|
25
|
+
/** Enables hand_back_to_concierge -- the id of this process's configured "front door" agent. */
|
|
26
|
+
globalAgentId?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Applied to a delegate_to_agent reply before it's handed back as the
|
|
29
|
+
* tool result. Salt itself has no opinion on reply framing; this exists
|
|
30
|
+
* because salt-claude-agent's replies can carry a [[SALT-THINKING]]...
|
|
31
|
+
* [[/SALT-THINKING]] wire marker that's a UI hint for salt-fe, not
|
|
32
|
+
* conversational content, and would otherwise leak into the calling
|
|
33
|
+
* agent's own context. Defaults to a no-op.
|
|
34
|
+
*/
|
|
35
|
+
replyTransform?: (text: string) => string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Builds the full set of Salt-platform actions bound to one client +
|
|
39
|
+
* identity store. Each entry's `schema` is plain JSON Schema; feed the
|
|
40
|
+
* whole set through toAnthropicTools/toOpenAITools before handing it to a
|
|
41
|
+
* model API, and dispatch tool calls through `execute()`.
|
|
42
|
+
*/
|
|
43
|
+
export declare function createActions(options: ActionsOptions): {
|
|
44
|
+
definitions: ActionDefinition<any, any>[];
|
|
45
|
+
execute: (name: string, input: unknown, caller: AgentIdentity, ctx: ActionContext) => Promise<unknown>;
|
|
46
|
+
myWalletId: (caller: AgentIdentity) => Promise<number>;
|
|
47
|
+
provisionWallet: (identity: AgentIdentity, opts?: {
|
|
48
|
+
chain?: string;
|
|
49
|
+
testnet?: boolean;
|
|
50
|
+
}) => Promise<{
|
|
51
|
+
id: number;
|
|
52
|
+
chain: string;
|
|
53
|
+
testnet: boolean;
|
|
54
|
+
public_address: string;
|
|
55
|
+
}>;
|
|
56
|
+
};
|
|
57
|
+
/** Anthropic Messages API tool format: {name, description, input_schema}. */
|
|
58
|
+
export declare function toAnthropicTools(definitions: ActionDefinition[]): Array<{
|
|
59
|
+
name: string;
|
|
60
|
+
description: string;
|
|
61
|
+
input_schema: JsonSchema;
|
|
62
|
+
}>;
|
|
63
|
+
/** OpenAI-style function-calling tool format: {type:"function", function:{name, description, parameters}}. */
|
|
64
|
+
export declare function toOpenAITools(definitions: ActionDefinition[]): Array<{
|
|
65
|
+
type: "function";
|
|
66
|
+
function: {
|
|
67
|
+
name: string;
|
|
68
|
+
description: string;
|
|
69
|
+
parameters: JsonSchema;
|
|
70
|
+
};
|
|
71
|
+
}>;
|
|
72
|
+
//# sourceMappingURL=actions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../src/actions.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAI3C,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAEjE,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEjD,MAAM,WAAW,aAAa;IAC5B,0GAA0G;IAC1G,KAAK,EAAE,MAAM,CAAC;IACd,iHAAiH;IACjH,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAgB,CAAC,MAAM,GAAG,GAAG,EAAE,OAAO,GAAG,GAAG;IAC3D,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,qHAAqH;IACrH,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACzF;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,UAAU,CAAC;IACnB,UAAU,EAAE,aAAa,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,wFAAwF;IACxF,gBAAgB,EAAE,MAAM,CAAC;IACzB,iGAAiG;IACjG,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gGAAgG;IAChG,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CAC3C;AAsBD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc;;oBA4pBtB,MAAM,SAAS,OAAO,UAAU,aAAa,OAAO,aAAa,KAAG,OAAO,CAAC,OAAO,CAAC;yBA/nB/E,aAAa,KAAG,OAAO,CAAC,MAAM,CAAC;gCAtBxB,aAAa,SAAQ;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE;YAmB7E,MAAM;eAAS,MAAM;iBAAW,OAAO;wBAAkB,MAAM;;EA4oBtF;AAID,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,gBAAgB,EAAE,GAAG,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,UAAU,CAAA;CAAE,CAAC,CAExI;AAED,8GAA8G;AAC9G,wBAAgB,aAAa,CAC3B,WAAW,EAAE,gBAAgB,EAAE,GAC9B,KAAK,CAAC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,UAAU,CAAA;KAAE,CAAA;CAAE,CAAC,CAEtG"}
|