@stratusagent/channel-slack 0.11.6 → 0.11.8
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 +78 -8
- package/dist/index.d.ts +55 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +814 -83
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Slack adapter for Stratus agents. **One Slack app per agent** — Slack has no way to give a single bot several identities with real avatars, presence, and DMs, so each agent gets its own app, and the adapter runs one Socket Mode connection per agent (no public ingress needed; Mac Minis behind NAT are fine).
|
|
4
4
|
|
|
5
|
-
- **Resumable conversations**: session keys are `slack:<agent>:<team>:<channel>:<thread_ts ?? ts>` (DMs: the DM channel id) — a thread is a conversation, it survives daemon restarts, and two agents sharing a thread keep fully separate sessions. A turn parked on a human when the daemon died is re-asked after the restart, and when it finishes its reply is posted into the thread as a fresh message — the placeholder it would have edited belonged to the old process.
|
|
5
|
+
- **Resumable conversations**: session keys are `slack:<agent>:<team>:<channel>:<thread_ts ?? ts>` (DMs: the DM channel id) — a thread is a conversation, it survives daemon restarts, and two agents sharing a thread keep fully separate sessions. A turn parked on a human when the daemon died is re-asked after the restart, and a turn that was still running is continued from where it got to (unless the agent runs on a harness — `codex` or a Claude subscription — where it is failed instead, since the harness may already have acted on it); either way, when it finishes its reply is posted into the thread as a fresh message — the placeholder it would have edited belonged to the old process.
|
|
6
6
|
- **Replies post when they are ready**: by default the reply is posted once, finished, with Slack's loading status (`assistant.threads.setStatus` — "is thinking…", "is running shell.run…") shown while the turn works, so the notification carries the answer. The `stream` reply mode (`slack.replies` in the [config](../../docs/guides/slack.md#how-replies-appear)) keeps the placeholder-and-edit rendering instead: post `…`, edit as deltas arrive (throttled for `chat.update` limits), show `⚙ tool…` status lines, finalize with the full reply. Either way a reply that outgrows one message is split across several.
|
|
7
7
|
- **Markdown is translated to Slack's mrkdwn** on the way out — `**bold**` to `*bold*`, `*italic*` to `_italic_`, `~~struck~~` to `~struck~`, `[text](https://…)` to `<https://…|text>`, and a `#` heading to a bold line, since mrkdwn has no headings. The reply is read once into tokens and written back from those, so what counts as code is decided one time rather than by each rule in turn. Three things follow from that. **A code run is one token**: nothing reads or rewrites inside it, an unclosed fence included, and the newlines in a fence are not line breaks — so a `#` on a line of somebody's shell script is not a heading. A run of N backticks opens code and only a run of exactly N closes it, which is what lets ``a span with a ` in it`` and a ````-fence holding a ```-fence work; one or two unmatched backticks are text, as they are in Markdown, but a pair is a span however far apart, because Slack pairs them too. **A delimiter is matched to its partner**, across whatever sits between — ``**the `fs.read` tool**`` is bold around a snippet, not four literal asterisks — though never across a line, so a marker still being streamed stays literal until its closing half arrives. A run is spent rather than matched whole, so the three asterisks ending `**bold and *italic***` close the italic with one and the bold with two; what no pair spends stays on the line as text, and what a run cannot buy — the third tilde of `~~~struck~~~`, a style that does not exist — is never charged for. **A wrapper is only written where its own character is not already loose** in what it wraps: Slack has one delimiter per style and no way to nest it, so a heading that already contains bold keeps the bold it has rather than gaining a second pair that would print instead of render, and `# glob *.ts` keeps an asterisk that was never a marker. A heading sheds its hashes and its trailing spaces from the prose that holds them, never from a snippet. Anything whose spelling already matches — lists, block quotes, inline code — is untouched. **A pipe table**, which mrkdwn has no syntax for, becomes a code block with its columns padded to line up (alignment colons respected, `**`/`` ` `` markers dropped from cells, since nothing renders inside code); a grid wider than 60 characters — past what a phone shows unwrapped — or one with a link in it (a link inside a code block is not clickable) becomes one line per row, `*Header*: value · *Header*: value`. A one-column table has nothing to line up and becomes a bulleted list under its bold header. A table inside a fence is somebody's text and stays as written, and a grid longer than one message is split like any code block, closed at the cut and reopened in the next, with its header and rule repeated so every part names its columns.
|
|
8
|
-
- **Mention to start, then it stays in the thread.** A mention opens a conversation; every reply in that thread reaches the agent without tagging it again, and free-form DMs are unchanged. Who an untagged reply is for is decided by [Who a message is for](#who-a-message-is-for). Inbound mentions of other users are humanized to `@Display Name` for the model; redeliveries are deduped so a slow turn never runs twice.
|
|
8
|
+
- **Mention to start, then it stays in the thread.** A mention opens a conversation; every reply in that thread reaches the agent without tagging it again, and free-form DMs are unchanged. Who an untagged reply is for is decided by [Who a message is for](#who-a-message-is-for). Inbound mentions of other users are humanized to `@Display Name` for the model; redeliveries are deduped so a slow turn never runs twice — in memory while the process remembers them, and after a restart by the gateway: each message is dispatched with its own key (`idempotencyKey`), a repeat runs no second turn, and the adapter posts that message's own reply and files (a file changed since that turn is skipped, since the path may now hold another file), or its failure if the turn failed — once more, if the process died after posting it, which beats a reply never posted.
|
|
9
9
|
- **Approval buttons** for the gateway's `remote` permission mode: a gated tool call parks the turn and asks in its thread with **Allow once** / **Always allow** / **Deny**. See [Approving tool calls](#approving-tool-calls) — clicks are authorized by *who clicked*, never by who can see the message, and **Always allow** is offered only where the daemon would actually remember it.
|
|
10
10
|
- **Tokens are gateway infrastructure secrets**, stored in the `channels` namespace of `~/.stratus/credentials.json` — never in an agent's credential allowlist:
|
|
11
11
|
|
|
@@ -13,7 +13,7 @@ Slack adapter for Stratus agents. **One Slack app per agent** — Slack has no w
|
|
|
13
13
|
{
|
|
14
14
|
"channels": {
|
|
15
15
|
"slack": {
|
|
16
|
-
"
|
|
16
|
+
"blair": { "appToken": "xapp-…", "botToken": "xoxb-…" }
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
}
|
|
@@ -43,7 +43,10 @@ they are the whole model:
|
|
|
43
43
|
whenever the turn it starts gets around to answering: an addendum typed
|
|
44
44
|
straight after it reaches the agent you just named, and the agent that
|
|
45
45
|
*had* the thread stands down at the same instant rather than whenever its
|
|
46
|
-
own app next catches up.
|
|
46
|
+
own app next catches up. A message an agent already took stays that
|
|
47
|
+
agent's: Slack redelivering it after a restart goes back to the agent
|
|
48
|
+
that accepted it, not to whoever has spoken since — and if that agent's
|
|
49
|
+
app has not come back, the others stand down rather than run it again.
|
|
47
50
|
5. **How an agent listens is the soul's to say.** Rules 3 and 4 are the
|
|
48
51
|
`thread` mode, the default. A soul with `listens: mentions` takes only
|
|
49
52
|
messages that name it, and hears the rest. One with `listens: judge`
|
|
@@ -146,8 +149,10 @@ and the warning names the scope. Text files share the message's 30-second
|
|
|
146
149
|
download deadline with its images.
|
|
147
150
|
**Every other attachment is unreadable** — a PDF, a Word file, an image that
|
|
148
151
|
was too large, a text file over the caps — so the message reaches the agent
|
|
149
|
-
naming those files
|
|
150
|
-
|
|
152
|
+
naming those files, each with why it was not read (not a kind of file read
|
|
153
|
+
here, too large, not downloaded or not in time, not readable, or not opened
|
|
154
|
+
because the message was overheard), which is what lets it answer honestly
|
|
155
|
+
instead of as though it had read them, or guessing at why. Such a file dropped
|
|
151
156
|
in with nothing said is not a question, and gets no reply.
|
|
152
157
|
Sessions are still per agent: an agent hears a thread from the mention
|
|
153
158
|
that brought it in, and what was said before that — to the other agent, or
|
|
@@ -178,6 +183,29 @@ answering; teaching it to choose — to answer only when it has something to
|
|
|
178
183
|
add, and to let "thanks, we've got it" be a sentence it read — is the rest
|
|
179
184
|
of [roadmap step 31](../../docs/roadmap/31-reading-the-room.md).
|
|
180
185
|
|
|
186
|
+
### What the agent is told about the room
|
|
187
|
+
|
|
188
|
+
Every turn says which kind of conversation it came from — a direct message
|
|
189
|
+
(and with whom, when that is one of the operator's principals: a display
|
|
190
|
+
name is text anyone can set, and this lands in the system prompt), a group DM, a private channel, or a public channel — with
|
|
191
|
+
its member count, whether the message is in a thread, and whether the
|
|
192
|
+
channel is shared with people outside the workspace (`is_ext_shared` /
|
|
193
|
+
`is_org_shared` from the lookup, or the event's own `is_ext_shared_channel`,
|
|
194
|
+
which is current on every turn). Never the
|
|
195
|
+
channel's name, for the same reason: whoever created or renamed the
|
|
196
|
+
channel chose it. The agent writes for that audience: a public channel of a thousand
|
|
197
|
+
people is not the DM it was in a minute ago, and what it was told in one does
|
|
198
|
+
not belong in the other. The kind comes from the event; the count
|
|
199
|
+
comes from `conversations.info` with `include_num_members`, the same call and
|
|
200
|
+
the same `channels:read` / `groups:read` / `mpim:read` scopes
|
|
201
|
+
[outbound destinations](#speaking-first-the-outbound-seam) use. It is made on every turn outside a DM and never cached, so a channel
|
|
202
|
+
made public, or shared with another workspace, since the last message is
|
|
203
|
+
described as it is now; turns in one conversation that overlap share one
|
|
204
|
+
call. A DM needs no lookup. An app
|
|
205
|
+
without those scopes still tells the agent the kind when the event carries
|
|
206
|
+
it, and otherwise only that it is in Slack. The words it is told are in
|
|
207
|
+
[Agents](../../docs/concepts/agents.md#soul-files).
|
|
208
|
+
|
|
181
209
|
## Installing
|
|
182
210
|
|
|
183
211
|
This package is an **optional peer** of the CLI — `stratus` ships without any
|
|
@@ -251,7 +279,7 @@ Who may answer is configured per agent, in `~/.stratus/config.json`:
|
|
|
251
279
|
"mode": "remote",
|
|
252
280
|
"slackChannel": "C07OPS",
|
|
253
281
|
"agents": {
|
|
254
|
-
"
|
|
282
|
+
"blair": { "slackApprovers": ["U01DYLAN", "U01OPS"] }
|
|
255
283
|
}
|
|
256
284
|
}
|
|
257
285
|
}
|
|
@@ -301,6 +329,48 @@ needs `settings.interactivity.is_enabled` set to `true` once, under **App
|
|
|
301
329
|
Manifest**. No request URL is needed — the clicks arrive over the same Socket
|
|
302
330
|
Mode connection.
|
|
303
331
|
|
|
332
|
+
### Credential requests
|
|
333
|
+
|
|
334
|
+
The same approvers answer an agent's `credential.request`. The gateway hands
|
|
335
|
+
the request to the adapter's `requestCredential`, the channel contract's
|
|
336
|
+
method for this, and waits for it: the adapter resolves once the form is
|
|
337
|
+
posted, and rejects, posting nothing, when the agent has no approvers, the
|
|
338
|
+
turn is a DM with someone who is not one of them or a private channel or
|
|
339
|
+
group DM none of them is a member of (`conversations.info`, then
|
|
340
|
+
`conversations.members`, under the read scopes the manifest already has),
|
|
341
|
+
the turn is not one the adapter dispatched from a Slack message, or Slack
|
|
342
|
+
refuses the post. A public channel is open to everyone in the workspace and
|
|
343
|
+
is not checked. When Slack will not answer the membership question, the
|
|
344
|
+
form is refused rather than posted on a guess. Where to post comes from that turn (its conversation and
|
|
345
|
+
thread), never from session metadata, which a control API caller can write. The gateway then
|
|
346
|
+
drops the request and tells the agent why. The request is
|
|
347
|
+
posted in the conversation the agent asked in (never an `approvalChannel`:
|
|
348
|
+
the person who can answer is the one the agent is talking to) with an **Add
|
|
349
|
+
credential** button. A click by an approver opens a modal (`views.open`);
|
|
350
|
+
anyone else is told, privately, that they are not an approver. The modal
|
|
351
|
+
carries only the request id in `private_metadata`, so where the key goes and
|
|
352
|
+
for whom is the gateway's record of the request, never something a
|
|
353
|
+
submission says about itself.
|
|
354
|
+
|
|
355
|
+
The submission is checked against the approvers again and handed to the
|
|
356
|
+
gateway's `provideCredential`, which stores it add-only and grants it in the
|
|
357
|
+
requesting agent's soul. A refusal (a name already stored, an empty value, a
|
|
358
|
+
request no longer pending) comes back as a `response_action: errors` ack,
|
|
359
|
+
which keeps the modal open with the reason under the field. A refusal the
|
|
360
|
+
gateway marks `retired` (the name was stored since, or the request is gone)
|
|
361
|
+
also rewrites the request message without its button, since no answer could
|
|
362
|
+
land on it any more. On success the
|
|
363
|
+
request message is rewritten to say what was added, for whom, and by whom,
|
|
364
|
+
or, when the key was stored but the soul could not be written, that the
|
|
365
|
+
agent cannot use it until the name is added by hand. The submission is
|
|
366
|
+
acked within Slack's three-second window: an answer slower than that closes
|
|
367
|
+
the modal first, and a refusal arriving after is sent to the submitter as
|
|
368
|
+
an ephemeral message in the request's thread.
|
|
369
|
+
The value is read from the submission and passed to the gateway; the adapter
|
|
370
|
+
never logs it, posts it, or dispatches it. Modals need no scope beyond the bot's,
|
|
371
|
+
and arrive over the same Socket Mode connection as clicks. The full flow is
|
|
372
|
+
in [Slack](../../docs/guides/slack.md#adding-a-credential-from-slack).
|
|
373
|
+
|
|
304
374
|
## Who counts as the operator
|
|
305
375
|
|
|
306
376
|
Everything above decides *which agent* a message is for. None of it decides
|
|
@@ -316,7 +386,7 @@ So a message is the operator's only when the operator has said so:
|
|
|
316
386
|
"principals": {
|
|
317
387
|
"slackUsers": ["U01DYLAN"],
|
|
318
388
|
"agents": {
|
|
319
|
-
"
|
|
389
|
+
"blair": { "slackUsers": ["U01DYLAN", "U01OPS"] },
|
|
320
390
|
"bea": { "slackUsers": [] }
|
|
321
391
|
}
|
|
322
392
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ChannelAdapter } from '@stratusagent/channels';
|
|
1
|
+
import { type AdmitPolicy, type ChannelAdapter } from '@stratusagent/channels';
|
|
2
2
|
/** One agent's Slack identity: its own app (avatar, presence) and tokens. */
|
|
3
3
|
export interface SlackAgentConfig {
|
|
4
4
|
agentId: string;
|
|
@@ -38,7 +38,7 @@ export interface SlackAgentConfig {
|
|
|
38
38
|
* would be in the transcript, which is what this mode exists to keep
|
|
39
39
|
* out. The label above is provenance; this is authorization.
|
|
40
40
|
*/
|
|
41
|
-
admit?:
|
|
41
|
+
admit?: AdmitPolicy;
|
|
42
42
|
/**
|
|
43
43
|
* How replies appear. `final` (the default) shows Slack's loading status
|
|
44
44
|
* while the turn runs and posts the reply once, finished; `stream` posts
|
|
@@ -52,6 +52,22 @@ export interface SlackSocketEventArgs {
|
|
|
52
52
|
body?: {
|
|
53
53
|
team_id?: string;
|
|
54
54
|
event_id?: string;
|
|
55
|
+
/** Events API: the conversation is shared with another organization (Slack Connect). Current on every event. */
|
|
56
|
+
is_ext_shared_channel?: boolean;
|
|
57
|
+
/** `block_actions` or `view_submission`, for the interactions this adapter handles. */
|
|
58
|
+
type?: string;
|
|
59
|
+
/** block_actions only: what `views.open` needs to put a form in front of the clicker. */
|
|
60
|
+
trigger_id?: string;
|
|
61
|
+
/** view_submission only: the form, what it was opened for, and what was entered. */
|
|
62
|
+
view?: {
|
|
63
|
+
callback_id?: string;
|
|
64
|
+
private_metadata?: string;
|
|
65
|
+
state?: {
|
|
66
|
+
values?: Record<string, Record<string, {
|
|
67
|
+
value?: string | null;
|
|
68
|
+
}>>;
|
|
69
|
+
};
|
|
70
|
+
};
|
|
55
71
|
/** block_actions only: who clicked. The one field authorization reads. */
|
|
56
72
|
user?: {
|
|
57
73
|
id?: string;
|
|
@@ -143,7 +159,7 @@ export interface SlackFileDownload {
|
|
|
143
159
|
*/
|
|
144
160
|
export type SlackFileFetcher = (url: string, botToken: string, signal: AbortSignal, maxBytes: number) => Promise<SlackFileDownload>;
|
|
145
161
|
export interface SlackSocketLike {
|
|
146
|
-
on(eventName: string, listener: (args: SlackSocketEventArgs) =>
|
|
162
|
+
on(eventName: string, listener: (args: SlackSocketEventArgs) => unknown): void;
|
|
147
163
|
start(): Promise<unknown>;
|
|
148
164
|
disconnect(): Promise<void>;
|
|
149
165
|
}
|
|
@@ -242,11 +258,35 @@ export interface SlackWebLike {
|
|
|
242
258
|
*/
|
|
243
259
|
info(args: {
|
|
244
260
|
channel: string;
|
|
261
|
+
include_num_members?: boolean;
|
|
245
262
|
}): Promise<{
|
|
246
263
|
channel?: {
|
|
247
264
|
id?: string;
|
|
265
|
+
name?: string;
|
|
248
266
|
is_member?: boolean;
|
|
249
267
|
is_im?: boolean;
|
|
268
|
+
is_private?: boolean;
|
|
269
|
+
is_mpim?: boolean;
|
|
270
|
+
num_members?: number;
|
|
271
|
+
/** Shared with another organization through Slack Connect. */
|
|
272
|
+
is_ext_shared?: boolean;
|
|
273
|
+
/** Shared across workspaces of an Enterprise Grid organization. */
|
|
274
|
+
is_org_shared?: boolean;
|
|
275
|
+
};
|
|
276
|
+
}>;
|
|
277
|
+
/**
|
|
278
|
+
* Who is in a conversation, a page at a time. Asked before a credential
|
|
279
|
+
* form goes into a private one, so it never lands where no approver can
|
|
280
|
+
* see it. Same read scopes as `info`.
|
|
281
|
+
*/
|
|
282
|
+
members(args: {
|
|
283
|
+
channel: string;
|
|
284
|
+
cursor?: string;
|
|
285
|
+
limit?: number;
|
|
286
|
+
}): Promise<{
|
|
287
|
+
members?: string[];
|
|
288
|
+
response_metadata?: {
|
|
289
|
+
next_cursor?: string;
|
|
250
290
|
};
|
|
251
291
|
}>;
|
|
252
292
|
};
|
|
@@ -263,6 +303,18 @@ export interface SlackWebLike {
|
|
|
263
303
|
};
|
|
264
304
|
}>;
|
|
265
305
|
};
|
|
306
|
+
/**
|
|
307
|
+
* Opens a modal, which is how a credential is entered: its answer comes
|
|
308
|
+
* back as a `view_submission` to this app and never becomes a message in
|
|
309
|
+
* the conversation. Optional so a fake without it still runs; an adapter
|
|
310
|
+
* without it tells the clicker it cannot open the form.
|
|
311
|
+
*/
|
|
312
|
+
views?: {
|
|
313
|
+
open(args: {
|
|
314
|
+
trigger_id: string;
|
|
315
|
+
view: SlackBlock;
|
|
316
|
+
}): Promise<unknown>;
|
|
317
|
+
};
|
|
266
318
|
}
|
|
267
319
|
export interface SlackAdapterOptions {
|
|
268
320
|
agents: SlackAgentConfig[];
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAgCA,OAAO,EAIL,KAAK,WAAW,EAChB,KAAK,cAAc,EAOpB,MAAM,wBAAwB,CAAC;AAyFhC,6EAA6E;AAC7E,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;;;;OASG;IACH,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;CAC9B;AAKD,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE;QACL,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,gHAAgH;QAChH,qBAAqB,CAAC,EAAE,OAAO,CAAC;QAChC,uFAAuF;QACvF,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,yFAAyF;QACzF,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,oFAAoF;QACpF,IAAI,CAAC,EAAE;YACL,WAAW,CAAC,EAAE,MAAM,CAAC;YACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;YAC1B,KAAK,CAAC,EAAE;gBAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;oBAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;iBAAE,CAAC,CAAC,CAAA;aAAE,CAAC;SAChF,CAAC;QACF,0EAA0E;QAC1E,IAAI,CAAC,EAAE;YAAE,EAAE,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACvB,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAC;QAC7B,OAAO,CAAC,EAAE;YAAE,EAAE,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QAC1B;;;;;WAKG;QACH,OAAO,CAAC,EAAE;YAAE,EAAE,CAAC,EAAE,MAAM,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,UAAU,EAAE,CAAA;SAAE,CAAC;KACtE,CAAC;IACF,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,6EAA6E;AAC7E,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEjD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAC5B;AAED,0DAA0D;AAC1D,MAAM,WAAW,gBAAgB;IAC/B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAEpI,MAAM,WAAW,eAAe;IAC9B,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,OAAO,GAAG,IAAI,CAAC;IAC/E,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1B,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,KAAK,CAAC;QACZ,MAAM,CAAC,EAAE;YACP,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;gBAAE,EAAE,CAAC,EAAE,MAAM,CAAA;aAAE,CAAC,CAAC,CAAC;YAChD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;gBAAE,EAAE,CAAC,EAAE,MAAM,CAAA;aAAE,CAAC,CAAC,CAAC;SAClD,CAAC;KACH,CAAC,CAAC;CACJ;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE;QAAE,IAAI,IAAI,OAAO,CAAC;YAAE,OAAO,CAAC,EAAE,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC;IAClE,IAAI,EAAE;QACJ,WAAW,CAAC,IAAI,EAAE;YAAE,OAAO,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,UAAU,EAAE,CAAA;SAAE,GAAG,OAAO,CAAC;YAAE,EAAE,CAAC,EAAE,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QAC5I,MAAM,CAAC,IAAI,EAAE;YAAE,OAAO,EAAE,MAAM,CAAC;YAAC,EAAE,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,UAAU,EAAE,CAAA;SAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QACrG;;;;WAIG;QACH,MAAM,CAAC,IAAI,EAAE;YAAE,OAAO,EAAE,MAAM,CAAC;YAAC,EAAE,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QAChE;;;WAGG;QACH,aAAa,CAAC,IAAI,EAAE;YAAE,OAAO,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;KAC5G,CAAC;IACF;;;;;OAKG;IACH,SAAS,CAAC,EAAE;QACV,OAAO,EAAE;YACP,SAAS,CAAC,IAAI,EAAE;gBAAE,UAAU,EAAE,MAAM,CAAC;gBAAC,SAAS,EAAE,MAAM,CAAC;gBAAC,MAAM,EAAE,MAAM,CAAA;aAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;SAC9F,CAAC;KACH,CAAC;IACF,KAAK,EAAE;QAGL,QAAQ,CAAC,IAAI,EAAE;YAAE,UAAU,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;KACzI,CAAC;IACF,aAAa,EAAE;QACb;;;;;WAKG;QACH,IAAI,CAAC,IAAI,EAAE;YAAE,OAAO,EAAE,MAAM,CAAC;YAAC,mBAAmB,CAAC,EAAE,OAAO,CAAA;SAAE,GAAG,OAAO,CAAC;YACtE,OAAO,CAAC,EAAE;gBACR,EAAE,CAAC,EAAE,MAAM,CAAC;gBACZ,IAAI,CAAC,EAAE,MAAM,CAAC;gBACd,SAAS,CAAC,EAAE,OAAO,CAAC;gBACpB,KAAK,CAAC,EAAE,OAAO,CAAC;gBAChB,UAAU,CAAC,EAAE,OAAO,CAAC;gBACrB,OAAO,CAAC,EAAE,OAAO,CAAC;gBAClB,WAAW,CAAC,EAAE,MAAM,CAAC;gBACrB,8DAA8D;gBAC9D,aAAa,CAAC,EAAE,OAAO,CAAC;gBACxB,mEAAmE;gBACnE,aAAa,CAAC,EAAE,OAAO,CAAC;aACzB,CAAC;SACH,CAAC,CAAC;QACH;;;;WAIG;QACH,OAAO,CAAC,IAAI,EAAE;YAAE,OAAO,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC;YAC3E,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;YACnB,iBAAiB,CAAC,EAAE;gBAAE,WAAW,CAAC,EAAE,MAAM,CAAA;aAAE,CAAC;SAC9C,CAAC,CAAC;KACJ,CAAC;IACF,KAAK,EAAE;QACL,IAAI,CAAC,IAAI,EAAE;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC;YAAE,IAAI,CAAC,EAAE;gBAAE,IAAI,CAAC,EAAE,MAAM,CAAC;gBAAC,OAAO,CAAC,EAAE;oBAAE,YAAY,CAAC,EAAE,MAAM,CAAC;oBAAC,SAAS,CAAC,EAAE,MAAM,CAAA;iBAAE,CAAA;aAAE,CAAA;SAAE,CAAC,CAAC;KAC9H,CAAC;IACF;;;;;OAKG;IACH,KAAK,CAAC,EAAE;QACN,IAAI,CAAC,IAAI,EAAE;YAAE,UAAU,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,UAAU,CAAA;SAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;KACxE,CAAC;CACH;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAC3B,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC7B,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,iFAAiF;IACjF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mEAAmE;IACnE,kBAAkB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,eAAe,CAAC;IAC3D,8DAA8D;IAC9D,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,YAAY,CAAC;IACrD;;;;OAIG;IACH,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAgBD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,GAAI,WAAW,OAAO,KAAK,KAAG,gBAgChE,CAAC;AA0xDF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,yBAAyB,GAAI,SAAS,mBAAmB,KAAG,cAqiFxE,CAAC"}
|