@msg91comm/m91 1.1.0 → 1.2.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/CHANGELOG.md +35 -0
- package/README.md +241 -19
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/integrations/claude-agent-sdk.d.ts +63 -0
- package/dist/src/integrations/claude-agent-sdk.d.ts.map +1 -0
- package/dist/src/integrations/claude-agent-sdk.js +109 -0
- package/dist/src/integrations/claude-agent-sdk.js.map +1 -0
- package/dist/src/integrations/google-adk.d.ts +47 -0
- package/dist/src/integrations/google-adk.d.ts.map +1 -0
- package/dist/src/integrations/google-adk.js +84 -0
- package/dist/src/integrations/google-adk.js.map +1 -0
- package/dist/src/integrations/index.d.ts +7 -3
- package/dist/src/integrations/index.d.ts.map +1 -1
- package/dist/src/integrations/index.js +6 -2
- package/dist/src/integrations/index.js.map +1 -1
- package/dist/src/integrations/langchain.d.ts +56 -5
- package/dist/src/integrations/langchain.d.ts.map +1 -1
- package/dist/src/integrations/langchain.js +56 -2
- package/dist/src/integrations/langchain.js.map +1 -1
- package/dist/src/integrations/mastra.d.ts +75 -0
- package/dist/src/integrations/mastra.d.ts.map +1 -0
- package/dist/src/integrations/mastra.js +109 -0
- package/dist/src/integrations/mastra.js.map +1 -0
- package/dist/src/integrations/openai-agents.d.ts +61 -0
- package/dist/src/integrations/openai-agents.d.ts.map +1 -0
- package/dist/src/integrations/openai-agents.js +127 -0
- package/dist/src/integrations/openai-agents.js.map +1 -0
- package/dist/src/integrations/specs.d.ts +42 -0
- package/dist/src/integrations/specs.d.ts.map +1 -1
- package/dist/src/integrations/specs.js +61 -0
- package/dist/src/integrations/specs.js.map +1 -1
- package/dist/src/integrations/vercel-ai.d.ts +44 -6
- package/dist/src/integrations/vercel-ai.d.ts.map +1 -1
- package/dist/src/integrations/vercel-ai.js +38 -3
- package/dist/src/integrations/vercel-ai.js.map +1 -1
- package/dist/src/integrations/zod-shapes.d.ts +19 -0
- package/dist/src/integrations/zod-shapes.d.ts.map +1 -0
- package/dist/src/integrations/zod-shapes.js +44 -0
- package/dist/src/integrations/zod-shapes.js.map +1 -0
- package/dist/src/platform.d.ts +1 -1
- package/dist/src/platform.d.ts.map +1 -1
- package/dist/src/platform.js +1 -1
- package/dist/src/platform.js.map +1 -1
- package/dist/src/url-builder.js +1 -1
- package/dist/src/url-builder.js.map +1 -1
- package/examples/basic-alert.ts +1 -1
- package/examples/claude-agent-sdk.ts +23 -0
- package/examples/deep-agents.ts +31 -0
- package/examples/google-adk.ts +39 -0
- package/examples/mastra.ts +41 -0
- package/examples/openai-agents.ts +33 -0
- package/examples/webhook-urls.ts +1 -1
- package/package.json +63 -11
- package/src/index.ts +1 -1
- package/src/integrations/claude-agent-sdk.ts +158 -0
- package/src/integrations/google-adk.ts +122 -0
- package/src/integrations/index.ts +7 -3
- package/src/integrations/langchain.ts +109 -6
- package/src/integrations/mastra.ts +178 -0
- package/src/integrations/openai-agents.ts +173 -0
- package/src/integrations/specs.ts +104 -0
- package/src/integrations/vercel-ai.ts +59 -7
- package/src/integrations/zod-shapes.ts +55 -0
- package/src/platform.ts +1 -1
- package/src/url-builder.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.2.0
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **OpenAI Agents SDK** (`@msg91comm/m91/integrations/openai-agents`): `runWithM91Approvals` answers every `needsApproval` interruption on a phone and resumes the run; `approveInterruptionsWithM91` for your own loop; `createM91Tools`.
|
|
7
|
+
- **Claude Agent SDK** (`@msg91comm/m91/integrations/claude-agent-sdk`): `createM91CanUseTool` (a `canUseTool` callback), `createM91PreToolUseHook` (gates in every permission mode), and `createM91McpServer` (the two tools as an in-process MCP server).
|
|
8
|
+
- **Google ADK for TypeScript** (`@msg91comm/m91/integrations/google-adk`): `createM91BeforeToolCallback` gates named tools; `createM91Tools` as `FunctionTool`s.
|
|
9
|
+
- **Mastra** (`@msg91comm/m91/integrations/mastra`): `generateWithM91Approvals` answers `requireApproval` suspensions; `createM91Tools`.
|
|
10
|
+
- **Vercel AI SDK**: `createM91ToolApproval`, a `toolApproval` function that rings a phone before the tools you name run.
|
|
11
|
+
- **LangChain `humanInTheLoopMiddleware` and Deep Agents** (`@msg91comm/m91/integrations/langchain`): `invokeWithM91Approvals` answers each `interruptOn` pause on a phone and resumes with `Command({ resume: { decisions } })`; `resolveHITLWithM91` builds the decisions for your own loop.
|
|
12
|
+
- `decideToolCall`, `GateOptions`: the shared fail-closed gate every adapter above uses.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- Vercel AI SDK tool factories return `Tool` / `ToolSet`, and LangChain's return `DynamicStructuredTool`, instead of `unknown`, so they type-check in `generateText({ tools })` and `createAgent({ tools })`. Runtime behaviour is unchanged.
|
|
16
|
+
- The `ai` peer range is `>=5.0.0`, matching the `inputSchema` tool format.
|
|
17
|
+
- Docs and docstrings use the published package name (`@msg91comm/m91`) and current APIs (`createAgent`, `isStepCount`).
|
|
18
|
+
|
|
19
|
+
## Fixed
|
|
20
|
+
- `@msg91comm/m91/integrations/vercel-ai`: `createM91ApprovalTool`, `createM91AlertTool` and `createM91Tools` are now typed as the AI SDK's `Tool` / `ToolSet` instead of `unknown`, so they type-check inside `generateText({ tools })`. Runtime behaviour is unchanged.
|
|
21
|
+
- The `ai` peer dependency range is now `>=5.0.0`, matching the `inputSchema` tool format these factories use.
|
|
22
|
+
|
|
23
|
+
## 1.1.1
|
|
24
|
+
|
|
25
|
+
Metadata only — no code change.
|
|
26
|
+
|
|
27
|
+
`homepage` pointed at `https://m91.msg91.com`, which does not resolve: that
|
|
28
|
+
host is the static site, and every backend route lives on `m91-api.msg91.com`.
|
|
29
|
+
npm renders `homepage` as a link on the package page and freezes it per
|
|
30
|
+
version, so 1.1.0's link is dead for the life of that version. It now points at
|
|
31
|
+
the documentation.
|
|
32
|
+
|
|
33
|
+
The package also went out as `@msg91comm/m91` while the repository still said
|
|
34
|
+
`"name": "m91"`, so the next publish would have gone to a different package.
|
|
35
|
+
The repository now matches what is published.
|
|
36
|
+
|
|
37
|
+
|
|
3
38
|
## 1.1.0
|
|
4
39
|
|
|
5
40
|
Three gaps where the server had grown a field and the SDK had not, so the only
|
package/README.md
CHANGED
|
@@ -28,13 +28,17 @@ dependencies, imported only when you call one:
|
|
|
28
28
|
| `@msg91comm/m91` — client, platform, URL builder | nothing |
|
|
29
29
|
| `@msg91comm/m91/integrations/langchain` | `@langchain/core` |
|
|
30
30
|
| `@msg91comm/m91/integrations/langgraph` | nothing — it wraps the client, not the graph |
|
|
31
|
-
| `@msg91comm/m91/integrations/vercel-ai` | `ai` |
|
|
31
|
+
| `@msg91comm/m91/integrations/vercel-ai` | `ai` (v5 or later) |
|
|
32
|
+
| `@msg91comm/m91/integrations/openai-agents` | `@openai/agents` |
|
|
33
|
+
| `@msg91comm/m91/integrations/claude-agent-sdk` | `@anthropic-ai/claude-agent-sdk`, `zod` |
|
|
34
|
+
| `@msg91comm/m91/integrations/google-adk` | `@google/adk`, `zod` |
|
|
35
|
+
| `@msg91comm/m91/integrations/mastra` | `@mastra/core`, `zod` |
|
|
32
36
|
|
|
33
37
|
---
|
|
34
38
|
|
|
35
39
|
## Quick start
|
|
36
40
|
|
|
37
|
-
You need a **send link** — `https://m91.msg91.com/s/<token>` — from the channel
|
|
41
|
+
You need a **send link** — `https://m91-api.msg91.com/s/<token>` — from the channel
|
|
38
42
|
in the M91 app. The token is in the URL path, so the link *is* the credential:
|
|
39
43
|
keep it in the environment, never in the repository.
|
|
40
44
|
|
|
@@ -79,6 +83,58 @@ only when somebody tapped **Approve**. A rejection, a timeout and an unreachable
|
|
|
79
83
|
channel all resolve `false` or throw — there is no configuration under which
|
|
80
84
|
silence means yes.
|
|
81
85
|
|
|
86
|
+
### Asking for a reason, not just a tap
|
|
87
|
+
|
|
88
|
+
An option can require something before it can be chosen. `required` blocks the
|
|
89
|
+
button until it is supplied; `optional` offers a skippable field.
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
await client.send('Agent wants to refund ₹48,000 on order ORD-40122', {
|
|
93
|
+
customId: 'refund-ORD-40122',
|
|
94
|
+
responseOptions: [
|
|
95
|
+
{ label: 'Approve', effect: 'SHARED' },
|
|
96
|
+
{ label: 'Reject', effect: 'SHARED', required: ['note'] },
|
|
97
|
+
{ label: 'Need the delivery photo', effect: 'SHARED', optional: ['attachment'] },
|
|
98
|
+
],
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Only two things can be asked for: `'note'` and `'attachment'`. Requiring a note
|
|
103
|
+
on the rejection is the difference between your code learning *no* and learning
|
|
104
|
+
*why* — worth it on any branch that has to explain itself to a customer later.
|
|
105
|
+
|
|
106
|
+
Two more fields on `send`:
|
|
107
|
+
|
|
108
|
+
- **`attachments`** — https links to files the alert is about, shown on the
|
|
109
|
+
alert screen. URLs, never bytes.
|
|
110
|
+
- **`other`** — whether the free-text "Something else" reply is offered. Leave
|
|
111
|
+
it unset and the server's default (on) stands; set `false` when the answer
|
|
112
|
+
must be one of your options.
|
|
113
|
+
|
|
114
|
+
### `callbackUrl` instead of polling
|
|
115
|
+
|
|
116
|
+
`waitForDecision` and `requestApproval` poll. For a service that is already
|
|
117
|
+
listening on a URL, hand M91 the URL instead and the outcome is POSTed to it
|
|
118
|
+
when somebody answers:
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
await client.send('Agent wants to refund ₹48,000 on order ORD-40122', {
|
|
122
|
+
customId: 'refund-ORD-40122',
|
|
123
|
+
callbackUrl: 'https://your-service.example.com/hooks/m91',
|
|
124
|
+
responseOptions: [{ label: 'Approve', effect: 'SHARED' },
|
|
125
|
+
{ label: 'Reject', effect: 'SHARED', required: ['note'] }],
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**It will not reach your laptop.** The callback is fetched by M91's servers and
|
|
130
|
+
private addresses are refused — `localhost`, `127.0.0.1`, `10.x`, `192.168.x`
|
|
131
|
+
and the rest — so local development and anything inside a VPC has to poll. Use
|
|
132
|
+
a tunnel, or keep `waitForDecision` for development and the callback in
|
|
133
|
+
production.
|
|
134
|
+
|
|
135
|
+
You still own the timeout either way. There is no server-side expiry: decide
|
|
136
|
+
what silence means before you send, and make it the safe answer.
|
|
137
|
+
|
|
82
138
|
---
|
|
83
139
|
|
|
84
140
|
## Severity
|
|
@@ -112,7 +168,7 @@ console.log(builder.build('CPU above 95% on prod-server-3', {
|
|
|
112
168
|
customId: 'cpu-high-prod3',
|
|
113
169
|
respond: ['On it', 'Escalate'],
|
|
114
170
|
}));
|
|
115
|
-
// https://m91.msg91.com/s/TOKEN?title=CPU+above+95%25+on+prod-server-3&severity=HIGH
|
|
171
|
+
// https://m91-api.msg91.com/s/TOKEN?title=CPU+above+95%25+on+prod-server-3&severity=HIGH
|
|
116
172
|
// &customId=cpu-high-prod3&respond=On+it%2CEscalate
|
|
117
173
|
```
|
|
118
174
|
|
|
@@ -173,12 +229,20 @@ stale device token is the difference between waking somebody and waking nobody.
|
|
|
173
229
|
|
|
174
230
|
The cases an API key exists for — per-user alerting, per-tenant channels, roster
|
|
175
231
|
sync — and the much larger set where a send link is the right answer, are worked
|
|
176
|
-
through at **<https://m91.msg91.com/docs>**.
|
|
232
|
+
through at **<https://m91-api.msg91.com/docs>**.
|
|
177
233
|
|
|
178
234
|
---
|
|
179
235
|
|
|
180
236
|
## LangChain
|
|
181
237
|
|
|
238
|
+
An agent that must not act alone needs a human, and the hard part is not pausing
|
|
239
|
+
— it is **reaching somebody**. `interrupt()` stops the graph and waits for a
|
|
240
|
+
resume that only arrives if a person happens to be watching a dashboard. M91
|
|
241
|
+
fills that half: the tool call rings every phone on a channel at full volume,
|
|
242
|
+
through silent mode and Do Not Disturb, escalating to a voice call if nobody
|
|
243
|
+
answers. The tool returns `"approved"` only when a human tapped **Approve**;
|
|
244
|
+
a rejection, a timeout and an unreachable channel are all a no.
|
|
245
|
+
|
|
182
246
|
```ts
|
|
183
247
|
import { M91Client } from '@msg91comm/m91';
|
|
184
248
|
import { createM91ApprovalTool, createM91AlertTool } from '@msg91comm/m91/integrations/langchain';
|
|
@@ -190,7 +254,7 @@ const tools = [
|
|
|
190
254
|
await createM91AlertTool(client), // wakes somebody, carries on
|
|
191
255
|
...myTools,
|
|
192
256
|
];
|
|
193
|
-
const agent =
|
|
257
|
+
const agent = createAgent({ model: 'openai:gpt-5-mini', tools, systemPrompt: SYSTEM }); // from 'langchain'
|
|
194
258
|
```
|
|
195
259
|
|
|
196
260
|
The factories are async because `@langchain/core` is imported only when you call
|
|
@@ -218,6 +282,37 @@ await createM91ApprovalTool(client, {
|
|
|
218
282
|
|
|
219
283
|
---
|
|
220
284
|
|
|
285
|
+
### `humanInTheLoopMiddleware` and Deep Agents
|
|
286
|
+
|
|
287
|
+
LangChain's `humanInTheLoopMiddleware` (and Deep Agents' `interruptOn`, which is
|
|
288
|
+
built on it) pauses the run before the tools you name and waits for
|
|
289
|
+
`{ decisions: [...] }`. `invokeWithM91Approvals` answers each pause on a phone
|
|
290
|
+
and resumes the same thread:
|
|
291
|
+
|
|
292
|
+
```ts
|
|
293
|
+
import { createDeepAgent } from 'deepagents';
|
|
294
|
+
import { MemorySaver } from '@langchain/langgraph';
|
|
295
|
+
import { invokeWithM91Approvals } from '@msg91comm/m91/integrations/langchain';
|
|
296
|
+
|
|
297
|
+
const agent = createDeepAgent({
|
|
298
|
+
interruptOn: { write_file: true, edit_file: true, execute: true },
|
|
299
|
+
checkpointer: new MemorySaver(),
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
const result = await invokeWithM91Approvals(
|
|
303
|
+
agent,
|
|
304
|
+
{ messages: [{ role: 'user', content: 'Write the release notes to RELEASE.md.' }] },
|
|
305
|
+
{ configurable: { thread_id: 'release-v2.4' } },
|
|
306
|
+
client,
|
|
307
|
+
);
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
The same call works for `createAgent({ middleware: [humanInTheLoopMiddleware({ interruptOn })] })`.
|
|
311
|
+
Running your own loop? `resolveHITLWithM91(result.__interrupt__[0].value, client)`
|
|
312
|
+
returns the `{ decisions }` to pass to `new Command({ resume })`.
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
221
316
|
## LangGraph
|
|
222
317
|
|
|
223
318
|
LangGraph knows how to stop a graph and resume it. What it has no opinion about
|
|
@@ -255,22 +350,146 @@ The node reads `title` (or `question`) and `customId` from state, and writes
|
|
|
255
350
|
|
|
256
351
|
## Vercel AI SDK
|
|
257
352
|
|
|
353
|
+
Two ways in. **Tools** the model calls when it decides to ask:
|
|
354
|
+
|
|
258
355
|
```ts
|
|
259
|
-
import {
|
|
356
|
+
import { generateText, isStepCount } from 'ai';
|
|
357
|
+
import { createM91Tools } from '@msg91comm/m91/integrations/vercel-ai';
|
|
260
358
|
|
|
261
359
|
const { text } = await generateText({
|
|
262
|
-
model: openai
|
|
263
|
-
tools: {
|
|
264
|
-
|
|
265
|
-
m91_send_alert: await createM91AlertTool(client),
|
|
266
|
-
},
|
|
267
|
-
stopWhen: stepCountIs(10),
|
|
360
|
+
model: 'openai/gpt-5-mini',
|
|
361
|
+
tools: { ...(await createM91Tools(client)) },
|
|
362
|
+
stopWhen: isStepCount(10),
|
|
268
363
|
prompt: 'Verify the GDPR deletion request for acct_8821 and action it.',
|
|
269
364
|
});
|
|
270
365
|
```
|
|
271
366
|
|
|
367
|
+
**A gate** on your own tools, through the AI SDK's `toolApproval` option. The
|
|
368
|
+
named tools ring a phone before they run; the model cannot skip it.
|
|
369
|
+
|
|
370
|
+
```ts
|
|
371
|
+
import { createM91ToolApproval } from '@msg91comm/m91/integrations/vercel-ai';
|
|
372
|
+
|
|
373
|
+
await generateText({
|
|
374
|
+
model: 'openai/gpt-5-mini',
|
|
375
|
+
tools: { issueRefund, deleteUser },
|
|
376
|
+
toolApproval: createM91ToolApproval(client, { tools: ['issueRefund', 'deleteUser'] }),
|
|
377
|
+
stopWhen: isStepCount(10),
|
|
378
|
+
prompt: 'Refund ORD-40122.',
|
|
379
|
+
});
|
|
380
|
+
```
|
|
381
|
+
|
|
272
382
|
---
|
|
273
383
|
|
|
384
|
+
## OpenAI Agents SDK
|
|
385
|
+
|
|
386
|
+
Mark risky tools `needsApproval: true`. `runWithM91Approvals` answers every
|
|
387
|
+
pause on a phone and resumes the same run.
|
|
388
|
+
|
|
389
|
+
```ts
|
|
390
|
+
import { Agent, tool } from '@openai/agents';
|
|
391
|
+
import { runWithM91Approvals } from '@msg91comm/m91/integrations/openai-agents';
|
|
392
|
+
|
|
393
|
+
const issueRefund = tool({
|
|
394
|
+
name: 'issue_refund',
|
|
395
|
+
description: 'Refund an order.',
|
|
396
|
+
parameters: z.object({ orderId: z.string(), amount: z.number() }),
|
|
397
|
+
needsApproval: true,
|
|
398
|
+
execute: async ({ orderId, amount }) => refunds.create(orderId, amount),
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
const agent = new Agent({ name: 'Support', instructions: 'Resolve refund requests.', tools: [issueRefund] });
|
|
402
|
+
const result = await runWithM91Approvals(agent, 'Refund ORD-40122, it never arrived.', client);
|
|
403
|
+
console.log(result.finalOutput);
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
Running your own loop? `approveInterruptionsWithM91(result, client)` decides the
|
|
407
|
+
pending approvals on `result.state`; then call `run(agent, result.state)`.
|
|
408
|
+
`createM91Tools(client)` gives the agent the two tools as well.
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
## Claude Agent SDK
|
|
413
|
+
|
|
414
|
+
```ts
|
|
415
|
+
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
416
|
+
import { createM91CanUseTool, createM91McpServer } from '@msg91comm/m91/integrations/claude-agent-sdk';
|
|
417
|
+
|
|
418
|
+
for await (const message of query({
|
|
419
|
+
prompt: 'Delete the stale feature branches and deploy main.',
|
|
420
|
+
options: {
|
|
421
|
+
// Bash and Write ring a phone first; every other tool runs as normal.
|
|
422
|
+
canUseTool: createM91CanUseTool(client, { tools: ['Bash', 'Write'] }),
|
|
423
|
+
// Optional: let Claude decide to ask, or to wake somebody.
|
|
424
|
+
mcpServers: { m91: await createM91McpServer(client) },
|
|
425
|
+
},
|
|
426
|
+
})) {
|
|
427
|
+
if (message.type === 'result') console.log(message);
|
|
428
|
+
}
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
`canUseTool` only runs when the SDK would otherwise ask. To gate a tool in every
|
|
432
|
+
permission mode, including `bypassPermissions`, pass
|
|
433
|
+
`hooks: { PreToolUse: createM91PreToolUseHook(client, { tools: ['Bash'] }) }`.
|
|
434
|
+
|
|
435
|
+
---
|
|
436
|
+
|
|
437
|
+
## Google ADK
|
|
438
|
+
|
|
439
|
+
```ts
|
|
440
|
+
import { LlmAgent } from '@google/adk';
|
|
441
|
+
import { createM91BeforeToolCallback, createM91Tools } from '@msg91comm/m91/integrations/google-adk';
|
|
442
|
+
|
|
443
|
+
const agent = new LlmAgent({
|
|
444
|
+
name: 'support_agent',
|
|
445
|
+
model: 'gemini-2.5-flash',
|
|
446
|
+
instruction: 'Resolve refund requests.',
|
|
447
|
+
tools: [issueRefund, ...(await createM91Tools(client))],
|
|
448
|
+
// issue_refund rings a phone first. A rejection skips the tool and tells the agent why.
|
|
449
|
+
beforeToolCallback: createM91BeforeToolCallback(client, { tools: ['issue_refund'] }),
|
|
450
|
+
});
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
---
|
|
454
|
+
|
|
455
|
+
## Mastra
|
|
456
|
+
|
|
457
|
+
```ts
|
|
458
|
+
import { Agent } from '@mastra/core/agent';
|
|
459
|
+
import { createTool } from '@mastra/core/tools';
|
|
460
|
+
import { generateWithM91Approvals } from '@msg91comm/m91/integrations/mastra';
|
|
461
|
+
|
|
462
|
+
const issueRefund = createTool({
|
|
463
|
+
id: 'issue-refund',
|
|
464
|
+
description: 'Refund an order.',
|
|
465
|
+
inputSchema: z.object({ orderId: z.string(), amount: z.number() }),
|
|
466
|
+
requireApproval: true,
|
|
467
|
+
execute: async ({ orderId, amount }) => refunds.create(orderId, amount),
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
const agent = new Agent({
|
|
471
|
+
id: 'support', name: 'Support', instructions: 'Resolve refund requests.',
|
|
472
|
+
model: 'openai/gpt-5-mini', tools: { issueRefund },
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
const output = await generateWithM91Approvals(agent, 'Refund ORD-40122.', client);
|
|
476
|
+
console.log(output.text);
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
Mastra keeps suspended runs in storage, so configure a storage provider on your
|
|
480
|
+
`Mastra` instance.
|
|
481
|
+
|
|
482
|
+
---
|
|
483
|
+
|
|
484
|
+
### Which tools ring a phone
|
|
485
|
+
|
|
486
|
+
Every gate takes `tools`: a list of tool names, or a predicate
|
|
487
|
+
(`(name, input) => boolean`). Gates fail closed: a rejection, no answer within
|
|
488
|
+
`timeoutSeconds` (default 300) or an M91 error means the tool does not run, and
|
|
489
|
+
the agent is told why. Write the lock-screen text yourself with
|
|
490
|
+
`describe: (call) => ({ title, description })`.
|
|
491
|
+
|
|
492
|
+
---
|
|
274
493
|
|
|
275
494
|
## Error handling
|
|
276
495
|
|
|
@@ -361,7 +580,7 @@ alert, and repeating it is how one condition wakes a team four times.
|
|
|
361
580
|
| `close({ customId? , alertId? }): Promise<boolean>` | Close it. Exactly one id. |
|
|
362
581
|
| `requestApproval(title, options): Promise<boolean>` | Send, wait, close. |
|
|
363
582
|
|
|
364
|
-
`SendOptions`: `{ description?, severity?, customId?, isAutoClose?, responseOptions? }`
|
|
583
|
+
`SendOptions`: `{ description?, severity?, customId?, isAutoClose?, responseOptions?, other?, attachments?, callbackUrl? }`
|
|
365
584
|
`WaitOptions`: `{ timeoutMs?, pollIntervalMs?, throwOnTimeout? }`
|
|
366
585
|
`ApprovalOptions`: `{ customId, description?, severity?, approveLabel?, rejectLabel?, timeoutMs?, pollIntervalMs?, timeoutMeansReject? }`
|
|
367
586
|
|
|
@@ -375,6 +594,7 @@ alert, and repeating it is how one condition wakes a team four times.
|
|
|
375
594
|
| `alertPerson(phone, title, options?): Promise<AlertResponse>` | Wake one person. |
|
|
376
595
|
| `getAlert(alertId, phone): Promise<AlertDetail \| null>` | Read what they decided. |
|
|
377
596
|
| `closeAlert(phone, { customId? , alertId? }): Promise<boolean>` | Close their alert. |
|
|
597
|
+
| `listChannels(): Promise<ProvisionedChannel[]>` | Every channel this key owns. |
|
|
378
598
|
| `createChannel(name, alertOwner?): Promise<ProvisionedChannel>` | **Not idempotent.** Store the result. |
|
|
379
599
|
| `addMembers(channelId, phones): Promise<InviteResult>` | Up to 500 per call. |
|
|
380
600
|
| `removeMember(channelId, phone): Promise<boolean>` | Take them off the roster. |
|
|
@@ -390,7 +610,7 @@ alert, and repeating it is how one condition wakes a team four times.
|
|
|
390
610
|
### Types
|
|
391
611
|
|
|
392
612
|
`Severity` · `Effect` · `AlertStatus` · `RecipientState` · `ResponseOption` ·
|
|
393
|
-
`Decision` · `AlertResponse` · `AlertDetail` · `ChannelInfo` · `Recipient` ·
|
|
613
|
+
`Collectable` · `Attachment` · `Decision` · `AlertResponse` · `AlertDetail` · `ChannelInfo` · `Recipient` ·
|
|
394
614
|
`ProvisionedChannel` · `InviteResult` · `ApiKeyInfo`
|
|
395
615
|
|
|
396
616
|
Helpers: `isApproved(decision, approveLabel?)` · `validatePhone(phone)` ·
|
|
@@ -408,11 +628,12 @@ Compiles with `tsc` and runs the suite on Node's built-in test runner — no tes
|
|
|
408
628
|
framework to install. Every test swaps `fetch`; nothing here makes a network
|
|
409
629
|
call, and nothing wakes anybody.
|
|
410
630
|
|
|
411
|
-
For the SDK against a real socket — a
|
|
412
|
-
timeout —
|
|
631
|
+
For the SDK against a real socket — a real send, a real poll loop, a real
|
|
632
|
+
timeout — point an example at your own channel. This wakes a phone:
|
|
413
633
|
|
|
414
634
|
```bash
|
|
415
|
-
|
|
635
|
+
export M91_SEND_LINK='https://m91-api.msg91.com/s/<token>'
|
|
636
|
+
npx tsx examples/basic-alert.ts
|
|
416
637
|
```
|
|
417
638
|
|
|
418
639
|
---
|
|
@@ -421,6 +642,7 @@ cd ../demo && python3 run_node_demo.py
|
|
|
421
642
|
|
|
422
643
|
The `examples/` directory included with the SDK covers a basic alert, the approval
|
|
423
644
|
gate, a LangChain agent, the LangGraph `interrupt()` pattern, a Vercel AI SDK
|
|
424
|
-
agent,
|
|
645
|
+
agent, OpenAI Agents, Claude Agent SDK, Google ADK and Mastra agents gated by
|
|
646
|
+
M91, and webhook URLs for Grafana and friends.
|
|
425
647
|
|
|
426
|
-
Full API documentation: **<https://m91.msg91.com/docs>**
|
|
648
|
+
Full API documentation: **<https://m91-api.msg91.com/docs>**
|
package/dist/src/index.d.ts
CHANGED
|
@@ -45,5 +45,5 @@ export type { AlertDetail, AlertResponse, AlertStatus, ApiKeyInfo, ChannelInfo,
|
|
|
45
45
|
* written twice — and it drifted, reporting 1.0.0 while the package shipped as
|
|
46
46
|
* 1.1.0. A test is what makes two copies safe.
|
|
47
47
|
*/
|
|
48
|
-
export declare const VERSION = "1.
|
|
48
|
+
export declare const VERSION = "1.2.0";
|
|
49
49
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/src/index.js
CHANGED
|
@@ -41,5 +41,5 @@ export { SEVERITIES, EFFECTS, ESCALATING_SEVERITIES, isApproved, validatePhone,
|
|
|
41
41
|
* written twice — and it drifted, reporting 1.0.0 while the package shipped as
|
|
42
42
|
* 1.1.0. A test is what makes two copies safe.
|
|
43
43
|
*/
|
|
44
|
-
export const VERSION = '1.
|
|
44
|
+
export const VERSION = '1.2.0';
|
|
45
45
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Agent SDK (`@anthropic-ai/claude-agent-sdk`): M91 as the permission
|
|
3
|
+
* prompt, and M91 as tools.
|
|
4
|
+
*
|
|
5
|
+
* **Permission prompt.** `createM91CanUseTool` is a `canUseTool` callback: the
|
|
6
|
+
* tools you name ring a phone and run only if a person approves. Everything
|
|
7
|
+
* else is allowed through untouched.
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
11
|
+
* import { M91Client } from '@msg91comm/m91';
|
|
12
|
+
* import { createM91CanUseTool } from '@msg91comm/m91/integrations/claude-agent-sdk';
|
|
13
|
+
*
|
|
14
|
+
* const client = new M91Client(process.env.M91_SEND_LINK!);
|
|
15
|
+
*
|
|
16
|
+
* for await (const message of query({
|
|
17
|
+
* prompt: 'Clean up the stale feature branches and deploy main.',
|
|
18
|
+
* options: { canUseTool: createM91CanUseTool(client, { tools: ['Bash', 'Write'] }) },
|
|
19
|
+
* })) {
|
|
20
|
+
* if (message.type === 'result') console.log(message);
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* `canUseTool` only runs when the SDK would otherwise ask. To gate a tool
|
|
25
|
+
* whatever the permission mode (including `bypassPermissions`), use the
|
|
26
|
+
* `PreToolUse` hook from `createM91PreToolUseHook` instead.
|
|
27
|
+
*
|
|
28
|
+
* **Tools.** `createM91McpServer` exposes `m91_request_approval` and
|
|
29
|
+
* `m91_send_alert` as an in-process MCP server, for when the agent itself
|
|
30
|
+
* should decide to ask or to wake somebody.
|
|
31
|
+
*/
|
|
32
|
+
import type { CanUseTool, HookCallbackMatcher, McpSdkServerConfigWithInstance } from '@anthropic-ai/claude-agent-sdk';
|
|
33
|
+
import type { M91Client } from '../client.js';
|
|
34
|
+
import { type AlertDefaults, type ApprovalDefaults, type GateOptions } from './specs.js';
|
|
35
|
+
/** Tool names, or a predicate. Required: an agent that rings for every `Read` gets muted. */
|
|
36
|
+
type ClaudeGateOptions = GateOptions & {
|
|
37
|
+
tools: NonNullable<GateOptions['tools']>;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* A `canUseTool` callback that asks a person on their phone before the tools
|
|
41
|
+
* in `options.tools` run.
|
|
42
|
+
*/
|
|
43
|
+
export declare function createM91CanUseTool(client: M91Client, options: ClaudeGateOptions): CanUseTool;
|
|
44
|
+
/**
|
|
45
|
+
* A `PreToolUse` hook matcher that asks a person before the tools in
|
|
46
|
+
* `options.tools` run, in every permission mode. Pass it as
|
|
47
|
+
* `options.hooks.PreToolUse`.
|
|
48
|
+
*
|
|
49
|
+
* ```ts
|
|
50
|
+
* query({ prompt, options: { hooks: { PreToolUse: createM91PreToolUseHook(client, { tools: ['Bash'] }) } } })
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare function createM91PreToolUseHook(client: M91Client, options: ClaudeGateOptions & {
|
|
54
|
+
timeoutSeconds?: number;
|
|
55
|
+
}): HookCallbackMatcher[];
|
|
56
|
+
/**
|
|
57
|
+
* `m91_request_approval` and `m91_send_alert` as an in-process MCP server.
|
|
58
|
+
* Register it under `mcpServers.m91`; the tools appear to Claude as
|
|
59
|
+
* `mcp__m91__m91_request_approval` and `mcp__m91__m91_send_alert`.
|
|
60
|
+
*/
|
|
61
|
+
export declare function createM91McpServer(client: M91Client, defaults?: ApprovalDefaults & AlertDefaults): Promise<McpSdkServerConfigWithInstance>;
|
|
62
|
+
export {};
|
|
63
|
+
//# sourceMappingURL=claude-agent-sdk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-agent-sdk.d.ts","sourceRoot":"","sources":["../../../src/integrations/claude-agent-sdk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EACV,UAAU,EACV,mBAAmB,EACnB,8BAA8B,EAE/B,MAAM,gCAAgC,CAAC;AAExC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAML,KAAK,aAAa,EAElB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAKjB,MAAM,YAAY,CAAC;AAGpB,6FAA6F;AAC7F,KAAK,iBAAiB,GAAG,WAAW,GAAG;IAAE,KAAK,EAAE,WAAW,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAA;CAAE,CAAC;AAEpF;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,iBAAiB,GAAG,UAAU,CAU7F;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,iBAAiB,GAAG;IAAE,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GACvD,mBAAmB,EAAE,CAyBvB;AAsBD;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,SAAS,EACjB,QAAQ,GAAE,gBAAgB,GAAG,aAAkB,GAC9C,OAAO,CAAC,8BAA8B,CAAC,CAczC"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Agent SDK (`@anthropic-ai/claude-agent-sdk`): M91 as the permission
|
|
3
|
+
* prompt, and M91 as tools.
|
|
4
|
+
*
|
|
5
|
+
* **Permission prompt.** `createM91CanUseTool` is a `canUseTool` callback: the
|
|
6
|
+
* tools you name ring a phone and run only if a person approves. Everything
|
|
7
|
+
* else is allowed through untouched.
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
11
|
+
* import { M91Client } from '@msg91comm/m91';
|
|
12
|
+
* import { createM91CanUseTool } from '@msg91comm/m91/integrations/claude-agent-sdk';
|
|
13
|
+
*
|
|
14
|
+
* const client = new M91Client(process.env.M91_SEND_LINK!);
|
|
15
|
+
*
|
|
16
|
+
* for await (const message of query({
|
|
17
|
+
* prompt: 'Clean up the stale feature branches and deploy main.',
|
|
18
|
+
* options: { canUseTool: createM91CanUseTool(client, { tools: ['Bash', 'Write'] }) },
|
|
19
|
+
* })) {
|
|
20
|
+
* if (message.type === 'result') console.log(message);
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* `canUseTool` only runs when the SDK would otherwise ask. To gate a tool
|
|
25
|
+
* whatever the permission mode (including `bypassPermissions`), use the
|
|
26
|
+
* `PreToolUse` hook from `createM91PreToolUseHook` instead.
|
|
27
|
+
*
|
|
28
|
+
* **Tools.** `createM91McpServer` exposes `m91_request_approval` and
|
|
29
|
+
* `m91_send_alert` as an in-process MCP server, for when the agent itself
|
|
30
|
+
* should decide to ask or to wake somebody.
|
|
31
|
+
*/
|
|
32
|
+
import { ALERT_DESCRIPTION, ALERT_TOOL_NAME, APPROVAL_DESCRIPTION, APPROVAL_TOOL_NAME, decideToolCall, needsHuman, runAlert, runApproval, } from './specs.js';
|
|
33
|
+
import { alertShape, approvalShape, loadZod } from './zod-shapes.js';
|
|
34
|
+
/**
|
|
35
|
+
* A `canUseTool` callback that asks a person on their phone before the tools
|
|
36
|
+
* in `options.tools` run.
|
|
37
|
+
*/
|
|
38
|
+
export function createM91CanUseTool(client, options) {
|
|
39
|
+
return async (toolName, input, { toolUseID }) => {
|
|
40
|
+
if (!(await needsHuman(options, toolName, input))) {
|
|
41
|
+
return { behavior: 'allow', updatedInput: input };
|
|
42
|
+
}
|
|
43
|
+
const decision = await decideToolCall(client, { toolName, input, callId: toolUseID }, options);
|
|
44
|
+
return decision.approved
|
|
45
|
+
? { behavior: 'allow', updatedInput: input }
|
|
46
|
+
: { behavior: 'deny', message: decision.reason };
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* A `PreToolUse` hook matcher that asks a person before the tools in
|
|
51
|
+
* `options.tools` run, in every permission mode. Pass it as
|
|
52
|
+
* `options.hooks.PreToolUse`.
|
|
53
|
+
*
|
|
54
|
+
* ```ts
|
|
55
|
+
* query({ prompt, options: { hooks: { PreToolUse: createM91PreToolUseHook(client, { tools: ['Bash'] }) } } })
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export function createM91PreToolUseHook(client, options) {
|
|
59
|
+
return [
|
|
60
|
+
{
|
|
61
|
+
// The hook must outlive the wait for a human, or the SDK gives up first.
|
|
62
|
+
timeout: (options.timeoutSeconds ?? 300) + 60,
|
|
63
|
+
hooks: [
|
|
64
|
+
async (input) => {
|
|
65
|
+
if (input.hook_event_name !== 'PreToolUse')
|
|
66
|
+
return {};
|
|
67
|
+
if (!(await needsHuman(options, input.tool_name, input.tool_input)))
|
|
68
|
+
return {};
|
|
69
|
+
const decision = await decideToolCall(client, { toolName: input.tool_name, input: input.tool_input, callId: input.tool_use_id }, options);
|
|
70
|
+
return {
|
|
71
|
+
hookSpecificOutput: {
|
|
72
|
+
hookEventName: 'PreToolUse',
|
|
73
|
+
permissionDecision: decision.approved ? 'allow' : 'deny',
|
|
74
|
+
permissionDecisionReason: decision.reason,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
];
|
|
81
|
+
}
|
|
82
|
+
async function claudeSdk() {
|
|
83
|
+
try {
|
|
84
|
+
const specifier = '@anthropic-ai/claude-agent-sdk';
|
|
85
|
+
return (await import(specifier));
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
throw new Error('The Claude Agent SDK integration needs @anthropic-ai/claude-agent-sdk. Install it with: ' +
|
|
89
|
+
'npm install @anthropic-ai/claude-agent-sdk', { cause: error });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const text = (value) => ({ content: [{ type: 'text', text: value }] });
|
|
93
|
+
/**
|
|
94
|
+
* `m91_request_approval` and `m91_send_alert` as an in-process MCP server.
|
|
95
|
+
* Register it under `mcpServers.m91`; the tools appear to Claude as
|
|
96
|
+
* `mcp__m91__m91_request_approval` and `mcp__m91__m91_send_alert`.
|
|
97
|
+
*/
|
|
98
|
+
export async function createM91McpServer(client, defaults = {}) {
|
|
99
|
+
const { tool, createSdkMcpServer } = await claudeSdk();
|
|
100
|
+
const z = await loadZod('Claude Agent SDK');
|
|
101
|
+
return createSdkMcpServer({
|
|
102
|
+
name: 'm91',
|
|
103
|
+
tools: [
|
|
104
|
+
tool(APPROVAL_TOOL_NAME, APPROVAL_DESCRIPTION, approvalShape(z), async (args) => text(await runApproval(client, args, defaults))),
|
|
105
|
+
tool(ALERT_TOOL_NAME, ALERT_DESCRIPTION, alertShape(z), async (args) => text(await runAlert(client, args, defaults))),
|
|
106
|
+
],
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=claude-agent-sdk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-agent-sdk.js","sourceRoot":"","sources":["../../../src/integrations/claude-agent-sdk.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google Agent Development Kit for TypeScript (`@google/adk`): M91 as a
|
|
3
|
+
* before-tool gate, and M91 as tools.
|
|
4
|
+
*
|
|
5
|
+
* **Gate.** `createM91BeforeToolCallback` goes in an `LlmAgent`'s
|
|
6
|
+
* `beforeToolCallback`. Calls to the tools you name ring a phone first; if the
|
|
7
|
+
* person rejects (or does not answer), the tool does not run and the agent
|
|
8
|
+
* gets the reason back as the tool's result.
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { LlmAgent } from '@google/adk';
|
|
12
|
+
* import { M91Client } from '@msg91comm/m91';
|
|
13
|
+
* import { createM91BeforeToolCallback, createM91Tools } from '@msg91comm/m91/integrations/google-adk';
|
|
14
|
+
*
|
|
15
|
+
* const client = new M91Client(process.env.M91_SEND_LINK!);
|
|
16
|
+
*
|
|
17
|
+
* const agent = new LlmAgent({
|
|
18
|
+
* name: 'support_agent',
|
|
19
|
+
* model: 'gemini-2.5-flash',
|
|
20
|
+
* instruction: 'Resolve refund requests.',
|
|
21
|
+
* tools: [issueRefund, ...(await createM91Tools(client))],
|
|
22
|
+
* beforeToolCallback: createM91BeforeToolCallback(client, { tools: ['issue_refund'] }),
|
|
23
|
+
* });
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* The gate waits for the answer in-process. For approvals that take hours,
|
|
27
|
+
* use ADK's own Tool Confirmation and resume the session when M91's answer
|
|
28
|
+
* arrives.
|
|
29
|
+
*/
|
|
30
|
+
import type { BaseTool, SingleBeforeToolCallback } from '@google/adk';
|
|
31
|
+
import type { M91Client } from '../client.js';
|
|
32
|
+
import { type AlertDefaults, type ApprovalDefaults, type GateOptions } from './specs.js';
|
|
33
|
+
/**
|
|
34
|
+
* A `beforeToolCallback` that asks a person on their phone before the tools
|
|
35
|
+
* in `options.tools` run. Returning a result object skips the tool, which is
|
|
36
|
+
* how a rejection reaches the model.
|
|
37
|
+
*/
|
|
38
|
+
export declare function createM91BeforeToolCallback(client: M91Client, options: GateOptions & {
|
|
39
|
+
tools: NonNullable<GateOptions['tools']>;
|
|
40
|
+
}): SingleBeforeToolCallback;
|
|
41
|
+
/** The blocking approval tool, `m91_request_approval`, as an ADK `FunctionTool`. */
|
|
42
|
+
export declare function createM91ApprovalTool(client: M91Client, defaults?: ApprovalDefaults): Promise<BaseTool>;
|
|
43
|
+
/** The fire-and-forget alert tool, `m91_send_alert`, as an ADK `FunctionTool`. */
|
|
44
|
+
export declare function createM91AlertTool(client: M91Client, defaults?: AlertDefaults): Promise<BaseTool>;
|
|
45
|
+
/** Both tools, for an agent's `tools` array. */
|
|
46
|
+
export declare function createM91Tools(client: M91Client, defaults?: ApprovalDefaults & AlertDefaults): Promise<BaseTool[]>;
|
|
47
|
+
//# sourceMappingURL=google-adk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"google-adk.d.ts","sourceRoot":"","sources":["../../../src/integrations/google-adk.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAgB,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAEpF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAML,KAAK,aAAa,EAElB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAKjB,MAAM,YAAY,CAAC;AAGpB;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,WAAW,GAAG;IAAE,KAAK,EAAE,WAAW,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAA;CAAE,GAClE,wBAAwB,CAS1B;AAkBD,oFAAoF;AACpF,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,SAAS,EACjB,QAAQ,GAAE,gBAAqB,GAC9B,OAAO,CAAC,QAAQ,CAAC,CASnB;AAED,kFAAkF;AAClF,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,SAAS,EACjB,QAAQ,GAAE,aAAkB,GAC3B,OAAO,CAAC,QAAQ,CAAC,CASnB;AAED,gDAAgD;AAChD,wBAAsB,cAAc,CAClC,MAAM,EAAE,SAAS,EACjB,QAAQ,GAAE,gBAAgB,GAAG,aAAkB,GAC9C,OAAO,CAAC,QAAQ,EAAE,CAAC,CAErB"}
|