@copilotkit/bot 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +190 -0
- package/dist/action-registry.d.ts +20 -0
- package/dist/action-registry.d.ts.map +1 -0
- package/dist/action-registry.js +109 -0
- package/dist/action-registry.test.d.ts +2 -0
- package/dist/action-registry.test.d.ts.map +1 -0
- package/dist/action-registry.test.js +45 -0
- package/dist/action-store.d.ts +19 -0
- package/dist/action-store.d.ts.map +1 -0
- package/dist/action-store.js +22 -0
- package/dist/action-store.test.d.ts +2 -0
- package/dist/action-store.test.d.ts.map +1 -0
- package/dist/action-store.test.js +27 -0
- package/dist/commands.d.ts +68 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +30 -0
- package/dist/create-bot.d.ts +44 -0
- package/dist/create-bot.d.ts.map +1 -0
- package/dist/create-bot.js +166 -0
- package/dist/create-bot.test.d.ts +2 -0
- package/dist/create-bot.test.d.ts.map +1 -0
- package/dist/create-bot.test.js +261 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/mint-id.d.ts +3 -0
- package/dist/mint-id.d.ts.map +1 -0
- package/dist/mint-id.js +20 -0
- package/dist/mint-id.test.d.ts +2 -0
- package/dist/mint-id.test.d.ts.map +1 -0
- package/dist/mint-id.test.js +16 -0
- package/dist/platform-adapter.d.ts +125 -0
- package/dist/platform-adapter.d.ts.map +1 -0
- package/dist/platform-adapter.js +1 -0
- package/dist/platform-adapter.test.d.ts +2 -0
- package/dist/platform-adapter.test.d.ts.map +1 -0
- package/dist/platform-adapter.test.js +32 -0
- package/dist/run-loop.d.ts +38 -0
- package/dist/run-loop.d.ts.map +1 -0
- package/dist/run-loop.js +100 -0
- package/dist/run-loop.test.d.ts +2 -0
- package/dist/run-loop.test.d.ts.map +1 -0
- package/dist/run-loop.test.js +80 -0
- package/dist/standard-schema.d.ts +52 -0
- package/dist/standard-schema.d.ts.map +1 -0
- package/dist/standard-schema.js +66 -0
- package/dist/testing/fake-adapter.d.ts +44 -0
- package/dist/testing/fake-adapter.d.ts.map +1 -0
- package/dist/testing/fake-adapter.js +123 -0
- package/dist/testing/fake-agent.d.ts +28 -0
- package/dist/testing/fake-agent.d.ts.map +1 -0
- package/dist/testing/fake-agent.js +36 -0
- package/dist/thread.d.ts +60 -0
- package/dist/thread.d.ts.map +1 -0
- package/dist/thread.js +109 -0
- package/dist/tools.d.ts +67 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +38 -0
- package/dist/tools.test.d.ts +2 -0
- package/dist/tools.test.d.ts.map +1 -0
- package/dist/tools.test.js +31 -0
- package/package.json +60 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Atai Barkai
|
|
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
|
|
13
|
+
all 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
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# @copilotkit/bot
|
|
2
|
+
|
|
3
|
+
The **platform-agnostic bot engine**. It owns everything between an incoming
|
|
4
|
+
message and a rendered reply — handler registration, the agent
|
|
5
|
+
run/tool/interrupt loop, JSX action binding, and the `PlatformAdapter`
|
|
6
|
+
contract — without knowing anything about Slack (or any other surface). A
|
|
7
|
+
platform adapter plugs in at the boundary; `@copilotkit/bot-slack` is the
|
|
8
|
+
concrete Slack one.
|
|
9
|
+
|
|
10
|
+
It builds on `@copilotkit/bot-ui` (the JSX runtime + component vocabulary,
|
|
11
|
+
re-exported from here for convenience) and AG-UI (`@ag-ui/client`,
|
|
12
|
+
`@ag-ui/core`).
|
|
13
|
+
|
|
14
|
+
## Install
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
pnpm add @copilotkit/bot @copilotkit/bot-ui
|
|
18
|
+
# plus a platform adapter, e.g.
|
|
19
|
+
pnpm add @copilotkit/bot-slack
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quickstart
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { createBot } from "@copilotkit/bot";
|
|
26
|
+
import { slack } from "@copilotkit/bot-slack"; // a concrete PlatformAdapter
|
|
27
|
+
|
|
28
|
+
const bot = createBot({
|
|
29
|
+
adapters: [slack({ botToken, appToken })],
|
|
30
|
+
agent: (threadId) => makeAgent(threadId), // AbstractAgent or (threadId) => AbstractAgent
|
|
31
|
+
tools: [...myTools], // BotTool[] forwarded on every runAgent
|
|
32
|
+
context: [...myContext], // ContextEntry[] forwarded on every runAgent
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
bot.onMention(({ thread }) => thread.runAgent());
|
|
36
|
+
bot.onMessage(({ thread }) => thread.runAgent());
|
|
37
|
+
|
|
38
|
+
await bot.start();
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`createBot(opts)` returns a `Bot`:
|
|
42
|
+
|
|
43
|
+
- `onMention(handler)` / `onMessage(handler)` — turn handlers receiving
|
|
44
|
+
`{ thread, message }`. (Routing is mention-preferred: if any mention
|
|
45
|
+
handler is registered, all turns route to it; otherwise message handlers
|
|
46
|
+
fire.)
|
|
47
|
+
- `onInteraction<TValue>(id, handler)` — explicit escape-hatch handler for a
|
|
48
|
+
known action id, bypassing the registry; `ctx.action.value` is typed `TValue`.
|
|
49
|
+
- `onInterrupt<TPayload>(eventName, handler)` — handle a captured agent
|
|
50
|
+
interrupt (LangGraph-style `on_interrupt`); receives `{ payload, thread }`
|
|
51
|
+
with `payload` typed `TPayload`.
|
|
52
|
+
- `onCommand(command)` / `onCommand(name, handler)` — register a slash command.
|
|
53
|
+
The handler gets `{ thread, command, text, options, user }`. `text` is the
|
|
54
|
+
raw args (Slack); `options` is the typed, parsed form (`defineBotCommand`
|
|
55
|
+
with an `options` Standard Schema) for surfaces with native structured args
|
|
56
|
+
(e.g. Discord). Forwarded to adapters that support commands and ignored
|
|
57
|
+
elsewhere — also pass them up front via `commands` in `CreateBotOptions`.
|
|
58
|
+
- `tool(t)` — register a `BotTool` (alternative to `opts.tools`); must be
|
|
59
|
+
added before `start()`.
|
|
60
|
+
- `start()` / `stop()` — bring adapters up / down.
|
|
61
|
+
|
|
62
|
+
`agent` is optional. If omitted, calling `thread.runAgent()` throws; supply
|
|
63
|
+
an `AbstractAgent` or a `(threadId) => AbstractAgent` factory.
|
|
64
|
+
|
|
65
|
+
## `Thread`
|
|
66
|
+
|
|
67
|
+
A `Thread` is the per-conversation handle handed to your handlers and tool
|
|
68
|
+
contexts. It accepts any `Renderable` (JSX or a string) for posting.
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
interface Thread {
|
|
72
|
+
readonly platform: string;
|
|
73
|
+
post(ui: Renderable): Promise<MessageRef>;
|
|
74
|
+
update(ref: MessageRef, ui: Renderable): Promise<MessageRef>;
|
|
75
|
+
delete(ref: MessageRef): Promise<void>;
|
|
76
|
+
stream(src: string | AsyncIterable<string>): Promise<MessageRef>;
|
|
77
|
+
runAgent(input?: {
|
|
78
|
+
context?: ContextEntry[];
|
|
79
|
+
tools?: BotTool[];
|
|
80
|
+
}): Promise<MessageRef | undefined>;
|
|
81
|
+
resume(value: unknown): Promise<MessageRef | undefined>;
|
|
82
|
+
awaitChoice<T = unknown>(ui: Renderable): Promise<T>;
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
- `post` / `update` render the JSX to IR, **bind** every event-prop handler
|
|
87
|
+
in the tree (mint a content-stable id, snapshot it, rewrite the prop to
|
|
88
|
+
`{ id }`), then hand the IR to the adapter.
|
|
89
|
+
- `runAgent` resolves the conversation's agent session, creates the adapter's
|
|
90
|
+
`RunRenderer`, and drives the run/tool/interrupt loop. Per-run `tools` /
|
|
91
|
+
`context` are merged on top of the bot-level defaults for that run only.
|
|
92
|
+
- `resume(value)` re-enters a paused interrupt run with
|
|
93
|
+
`forwardedProps.command`.
|
|
94
|
+
- `awaitChoice<T>(ui)` posts a picker and blocks until an interaction in this
|
|
95
|
+
conversation resolves it to the clicked control's value (HITL); pass `T` to
|
|
96
|
+
type the returned value.
|
|
97
|
+
|
|
98
|
+
## Tools & context
|
|
99
|
+
|
|
100
|
+
A `BotTool` is forwarded to the agent as a frontend tool; its handler runs in
|
|
101
|
+
the bot when the agent calls it. The handler `ctx` carries the `thread`, so a
|
|
102
|
+
tool can render JSX (`ctx.thread.post(<Card .../>)`) or run the agent further.
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
interface BotTool<Schema extends ObjectSchema = ObjectSchema> {
|
|
106
|
+
name: string;
|
|
107
|
+
description: string;
|
|
108
|
+
parameters: Schema; // any Standard Schema (Zod/Valibot/ArkType/…)
|
|
109
|
+
handler(args, ctx: BotToolContext): Promise<unknown> | unknown;
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Define one with the non-curried `defineBotTool`, which infers the arg types
|
|
114
|
+
from `parameters`:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
defineBotTool({
|
|
118
|
+
name: "read_thread",
|
|
119
|
+
description: "Read the messages in the current conversation.",
|
|
120
|
+
parameters: z.object({}),
|
|
121
|
+
async handler(_args, { thread }) {
|
|
122
|
+
return await thread.getMessages();
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
`parameters` (a Standard Schema) is converted to JSON Schema for the LLM and
|
|
128
|
+
validated on the way back. `BotToolContext` is `{ thread, message?, user?,
|
|
129
|
+
signal?, platform }` — a single shared type with no per-adapter generic.
|
|
130
|
+
Platform-specific power is reached only through capability-gated `thread`
|
|
131
|
+
methods (e.g. `thread.getMessages()`, `thread.lookupUser(query)`,
|
|
132
|
+
`thread.postFile(...)`), so a tool stays portable across surfaces.
|
|
133
|
+
|
|
134
|
+
A `ContextEntry` is `{ description: string; value: string }` — knowledge
|
|
135
|
+
folded into the agent's system context on each `runAgent`.
|
|
136
|
+
|
|
137
|
+
## ActionStore
|
|
138
|
+
|
|
139
|
+
Inline JSX handlers are bound by content. Each interactive node gets a
|
|
140
|
+
**content-stable, opaque** minted id — `mintId(componentName, path, props)`
|
|
141
|
+
= `"ck:" + sha1(name | path | stableStringify(props)).slice(0,16)`. Only the
|
|
142
|
+
opaque id (plus any small `bind()` args) is stamped on the native token; no
|
|
143
|
+
props, PII, or secrets go over the wire.
|
|
144
|
+
|
|
145
|
+
On a click, the `ActionRegistry` resolves the handler from a hot in-memory
|
|
146
|
+
cache; on a miss it **rehydrates** by loading the snapshot from the
|
|
147
|
+
`ActionStore`, re-rendering the named component with the frozen props, and
|
|
148
|
+
re-walking to the handler's path.
|
|
149
|
+
|
|
150
|
+
The default `ActionStore` is `InMemoryActionStore` (a `Map` with optional
|
|
151
|
+
TTL). It is lost on restart: after a restart an old button click degrades to
|
|
152
|
+
an `ActionExpiredError` ("this action expired"), which `createBot` swallows.
|
|
153
|
+
**Durable actions require an external store (Redis / DB) — not shipped in
|
|
154
|
+
v1.** Implement the `ActionStore` interface (`put` / `get` / `delete`) and
|
|
155
|
+
pass it as `actionStore` to make actions survive restarts.
|
|
156
|
+
|
|
157
|
+
## Writing a `PlatformAdapter`
|
|
158
|
+
|
|
159
|
+
To target a new surface, implement `PlatformAdapter` from this package. The
|
|
160
|
+
engine drives ingress through the `IngressSink` you receive in `start(sink)`
|
|
161
|
+
(`sink.onTurn(IncomingTurn)` / `sink.onInteraction(InteractionEvent)`) and
|
|
162
|
+
egress through your `post` / `update` / `stream` / `delete` (which receive
|
|
163
|
+
`BotNode[]` to translate to a native payload via `render`). You also provide
|
|
164
|
+
`createRunRenderer(target)` (an AG-UI `RunRenderer`: the subscriber to stream
|
|
165
|
+
into, plus accessors for captured tool calls and interrupts that the run-loop
|
|
166
|
+
reads after each `runAgent`), `decodeInteraction(raw)` (native event → opaque
|
|
167
|
+
`InteractionEvent`), `lookupUser`, a `conversationStore`
|
|
168
|
+
(`getOrCreate` → `AgentSession`), and the surface `capabilities` /
|
|
169
|
+
`ackDeadlineMs`. Optional capability methods like `getMessages(target)` and
|
|
170
|
+
`postFile(target, args)` back the matching `thread` methods when the surface
|
|
171
|
+
supports them. Slash commands are also capability-gated: an adapter forwards
|
|
172
|
+
invocations via `sink.onCommand(IncomingCommand)`, and may implement
|
|
173
|
+
`registerCommands(specs)` to publish the bot's declared commands up front
|
|
174
|
+
(e.g. Discord's application-command API); adapters that omit it are skipped.
|
|
175
|
+
See `@copilotkit/bot-slack` for a complete implementation.
|
|
176
|
+
|
|
177
|
+
## Exports
|
|
178
|
+
|
|
179
|
+
`createBot`, `Bot`, `CreateBotOptions`, `BotHandler`; `Thread`; the
|
|
180
|
+
`PlatformAdapter` boundary types (`RunRenderer`, `IngressSink`,
|
|
181
|
+
`IncomingTurn`, `InteractionEvent`, `IncomingCommand`, `SurfaceCapabilities`,
|
|
182
|
+
`ReplyTarget`, `ConversationStore`, `AgentSession`, `CapturedToolCall`,
|
|
183
|
+
`CapturedInterrupt`, `UserQuery`); `ActionStore` / `InMemoryActionStore` /
|
|
184
|
+
`ActionSnapshot` / `ActionRegistry` / `ActionExpiredError`; `BotTool` /
|
|
185
|
+
`BotToolContext` / `defineBotTool` / `BotCommand` / `CommandContext` /
|
|
186
|
+
`CommandSpec` / `defineBotCommand` / `ContextEntry` /
|
|
187
|
+
`AgentToolDescriptor` / `ObjectSchema` and the tool helpers
|
|
188
|
+
(`toAgentToolDescriptors`, `parseToolArgs`, `stringifyHandlerResult`);
|
|
189
|
+
`mintId` / `stableStringify`; `runAgentLoop`; plus the re-exported
|
|
190
|
+
`@copilotkit/bot-ui` vocabulary.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { BotNode, InteractionContext, ComponentFn, Renderable } from "@copilotkit/bot-ui";
|
|
2
|
+
import type { ActionStore } from "./action-store.js";
|
|
3
|
+
export declare class ActionExpiredError extends Error {
|
|
4
|
+
constructor(id: string);
|
|
5
|
+
}
|
|
6
|
+
export declare class ActionRegistry {
|
|
7
|
+
private store;
|
|
8
|
+
private components;
|
|
9
|
+
private hot;
|
|
10
|
+
constructor(opts: {
|
|
11
|
+
store: ActionStore;
|
|
12
|
+
});
|
|
13
|
+
registerComponent(name: string, fn: ComponentFn): void;
|
|
14
|
+
clearHotCache(): void;
|
|
15
|
+
bindTree(componentName: string, props: Record<string, unknown>, conversationKey: string): Promise<BotNode[]>;
|
|
16
|
+
bindRenderable(ui: Renderable, conversationKey: string): Promise<BotNode[]>;
|
|
17
|
+
private walk;
|
|
18
|
+
dispatch(id: string, ctx: InteractionContext): Promise<unknown>;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=action-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-registry.d.ts","sourceRoot":"","sources":["../src/action-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EAEP,kBAAkB,EAClB,WAAW,EACX,UAAU,EACX,MAAM,oBAAoB,CAAC;AAG5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,EAAE,EAAE,MAAM;CAIvB;AAcD,qBAAa,cAAc;IACzB,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,UAAU,CAAkC;IACpD,OAAO,CAAC,GAAG,CAAmC;gBAElC,IAAI,EAAE;QAAE,KAAK,EAAE,WAAW,CAAA;KAAE;IAIxC,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,GAAG,IAAI;IAItD,aAAa,IAAI,IAAI;IAMf,QAAQ,CACZ,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,OAAO,EAAE,CAAC;IAYf,cAAc,CAClB,EAAE,EAAE,UAAU,EACd,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,OAAO,EAAE,CAAC;YAgBP,IAAI;IAuCZ,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC;CAetE"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { isBound, getBoundArgs, renderToIR } from "@copilotkit/bot-ui";
|
|
2
|
+
import { mintId } from "./mint-id.js";
|
|
3
|
+
export class ActionExpiredError extends Error {
|
|
4
|
+
constructor(id) {
|
|
5
|
+
super(`Action "${id}" has expired or is no longer available.`);
|
|
6
|
+
this.name = "ActionExpiredError";
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
const EVENT_PROPS = ["onClick", "onSelect", "onSubmit"];
|
|
10
|
+
function isComponentElement(ui) {
|
|
11
|
+
return (typeof ui === "object" &&
|
|
12
|
+
ui !== null &&
|
|
13
|
+
typeof ui.type === "function");
|
|
14
|
+
}
|
|
15
|
+
export class ActionRegistry {
|
|
16
|
+
store;
|
|
17
|
+
components = new Map();
|
|
18
|
+
hot = new Map();
|
|
19
|
+
constructor(opts) {
|
|
20
|
+
this.store = opts.store;
|
|
21
|
+
}
|
|
22
|
+
registerComponent(name, fn) {
|
|
23
|
+
this.components.set(name, fn);
|
|
24
|
+
}
|
|
25
|
+
clearHotCache() {
|
|
26
|
+
this.hot.clear();
|
|
27
|
+
}
|
|
28
|
+
// Renders the named component, binds all event-prop handlers in the tree
|
|
29
|
+
// (mint id, hot-cache + ActionStore snapshot, rewrite prop to { id }), returns the bound IR.
|
|
30
|
+
async bindTree(componentName, props, conversationKey) {
|
|
31
|
+
const fn = this.components.get(componentName);
|
|
32
|
+
const root = renderToIR((fn ? fn(props) : props));
|
|
33
|
+
await this.walk(root, [], componentName, props, conversationKey);
|
|
34
|
+
return root;
|
|
35
|
+
}
|
|
36
|
+
// Binds an arbitrary Renderable for posting. If `ui` is a component element
|
|
37
|
+
// (`{ type: fn, props }`), it is registered + bound by name (cold-path
|
|
38
|
+
// re-render supported). Otherwise the IR is bound inline with `component:""`,
|
|
39
|
+
// meaning a cold-cache dispatch throws ActionExpiredError (intended
|
|
40
|
+
// degradation for inline handlers that can't be re-derived).
|
|
41
|
+
async bindRenderable(ui, conversationKey) {
|
|
42
|
+
if (isComponentElement(ui)) {
|
|
43
|
+
const fn = ui.type;
|
|
44
|
+
const name = fn.name || "anonymous";
|
|
45
|
+
this.registerComponent(name, fn);
|
|
46
|
+
return this.bindTree(name, (ui.props ?? {}), conversationKey);
|
|
47
|
+
}
|
|
48
|
+
const root = renderToIR(ui);
|
|
49
|
+
await this.walk(root, [], "", undefined, conversationKey);
|
|
50
|
+
return root;
|
|
51
|
+
}
|
|
52
|
+
async walk(nodes, base, comp, props, conv) {
|
|
53
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
54
|
+
const node = nodes[i];
|
|
55
|
+
const path = [...base, i];
|
|
56
|
+
for (const ep of EVENT_PROPS) {
|
|
57
|
+
const handler = node.props[ep];
|
|
58
|
+
if (typeof handler === "function") {
|
|
59
|
+
const fullPath = [...path, ep];
|
|
60
|
+
const id = mintId(comp, fullPath, props);
|
|
61
|
+
this.hot.set(id, handler);
|
|
62
|
+
await this.store.put(id, {
|
|
63
|
+
component: comp,
|
|
64
|
+
props,
|
|
65
|
+
path: fullPath,
|
|
66
|
+
conversationKey: conv,
|
|
67
|
+
boundArgs: isBound(handler) ? getBoundArgs(handler) : undefined,
|
|
68
|
+
});
|
|
69
|
+
node.props[ep] = { id };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
const children = node.props.children;
|
|
73
|
+
if (Array.isArray(children)) {
|
|
74
|
+
await this.walk(children, [...path, "children"], comp, props, conv);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async dispatch(id, ctx) {
|
|
79
|
+
let handler = this.hot.get(id);
|
|
80
|
+
if (!handler) {
|
|
81
|
+
const snap = await this.store.get(id);
|
|
82
|
+
if (!snap || !snap.component)
|
|
83
|
+
throw new ActionExpiredError(id);
|
|
84
|
+
const fn = this.components.get(snap.component);
|
|
85
|
+
if (!fn)
|
|
86
|
+
throw new ActionExpiredError(id);
|
|
87
|
+
const tree = renderToIR(fn(snap.props));
|
|
88
|
+
handler = pluck(tree, snap.path);
|
|
89
|
+
if (!handler)
|
|
90
|
+
throw new ActionExpiredError(id);
|
|
91
|
+
}
|
|
92
|
+
return handler({ ...ctx, action: { ...ctx.action, id } });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function pluck(tree, path) {
|
|
96
|
+
let cur = tree;
|
|
97
|
+
for (const seg of path.slice(0, -1)) {
|
|
98
|
+
if (Array.isArray(cur))
|
|
99
|
+
cur = cur[seg];
|
|
100
|
+
else if (cur && typeof cur === "object")
|
|
101
|
+
cur = cur.props?.[seg];
|
|
102
|
+
else
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
const ep = path[path.length - 1];
|
|
106
|
+
const node = cur;
|
|
107
|
+
const h = node?.props?.[ep];
|
|
108
|
+
return typeof h === "function" ? h : undefined;
|
|
109
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-registry.test.d.ts","sourceRoot":"","sources":["../src/action-registry.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { ActionRegistry, ActionExpiredError } from "./action-registry.js";
|
|
3
|
+
import { InMemoryActionStore } from "./action-store.js";
|
|
4
|
+
function Confirm(props) {
|
|
5
|
+
return {
|
|
6
|
+
type: "actions",
|
|
7
|
+
props: {
|
|
8
|
+
children: [
|
|
9
|
+
{
|
|
10
|
+
type: "button",
|
|
11
|
+
props: {
|
|
12
|
+
onClick: ({ action }) => `ok:${props.action}:${action.id}`,
|
|
13
|
+
children: "Yes",
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const ctx = {};
|
|
21
|
+
describe("ActionRegistry", () => {
|
|
22
|
+
it("binds onClick handlers and dispatches via hot cache", async () => {
|
|
23
|
+
const reg = new ActionRegistry({ store: new InMemoryActionStore() });
|
|
24
|
+
reg.registerComponent("Confirm", Confirm);
|
|
25
|
+
const ir = await reg.bindTree("Confirm", { action: "write" }, "conv1");
|
|
26
|
+
const button = ir[0].props.children[0];
|
|
27
|
+
const id = button.props.onClick.id;
|
|
28
|
+
expect(typeof id).toBe("string");
|
|
29
|
+
const out = await reg.dispatch(id, ctx);
|
|
30
|
+
expect(out).toContain("ok:write:");
|
|
31
|
+
});
|
|
32
|
+
it("cold path re-renders from snapshot when hot cache is cleared", async () => {
|
|
33
|
+
const reg = new ActionRegistry({ store: new InMemoryActionStore() });
|
|
34
|
+
reg.registerComponent("Confirm", Confirm);
|
|
35
|
+
const ir = await reg.bindTree("Confirm", { action: "write" }, "conv1");
|
|
36
|
+
const id = ir[0].props.children[0].props.onClick.id;
|
|
37
|
+
reg.clearHotCache();
|
|
38
|
+
const out = await reg.dispatch(id, ctx);
|
|
39
|
+
expect(out).toContain("ok:write:");
|
|
40
|
+
});
|
|
41
|
+
it("throws ActionExpiredError on full miss", async () => {
|
|
42
|
+
const reg = new ActionRegistry({ store: new InMemoryActionStore() });
|
|
43
|
+
await expect(reg.dispatch("ck:missing", ctx)).rejects.toBeInstanceOf(ActionExpiredError);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface ActionSnapshot {
|
|
2
|
+
component?: string;
|
|
3
|
+
props?: unknown;
|
|
4
|
+
path: (string | number)[];
|
|
5
|
+
boundArgs?: unknown;
|
|
6
|
+
conversationKey: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ActionStore {
|
|
9
|
+
put(id: string, snap: ActionSnapshot, ttlMs?: number): Promise<void>;
|
|
10
|
+
get(id: string): Promise<ActionSnapshot | undefined>;
|
|
11
|
+
delete(id: string): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
export declare class InMemoryActionStore implements ActionStore {
|
|
14
|
+
private map;
|
|
15
|
+
put(id: string, snap: ActionSnapshot, ttlMs?: number): Promise<void>;
|
|
16
|
+
get(id: string): Promise<ActionSnapshot | undefined>;
|
|
17
|
+
delete(id: string): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=action-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-store.d.ts","sourceRoot":"","sources":["../src/action-store.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;CACzB;AACD,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;IACrD,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnC;AACD,qBAAa,mBAAoB,YAAW,WAAW;IACrD,OAAO,CAAC,GAAG,CAAmE;IACxE,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMpE,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;IASpD,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGxC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export class InMemoryActionStore {
|
|
2
|
+
map = new Map();
|
|
3
|
+
async put(id, snap, ttlMs) {
|
|
4
|
+
this.map.set(id, {
|
|
5
|
+
snap,
|
|
6
|
+
expiresAt: ttlMs ? Date.now() + ttlMs : undefined,
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
async get(id) {
|
|
10
|
+
const e = this.map.get(id);
|
|
11
|
+
if (!e)
|
|
12
|
+
return undefined;
|
|
13
|
+
if (e.expiresAt !== undefined && Date.now() > e.expiresAt) {
|
|
14
|
+
this.map.delete(id);
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
return e.snap;
|
|
18
|
+
}
|
|
19
|
+
async delete(id) {
|
|
20
|
+
this.map.delete(id);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-store.test.d.ts","sourceRoot":"","sources":["../src/action-store.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import { InMemoryActionStore } from "./action-store.js";
|
|
3
|
+
describe("InMemoryActionStore", () => {
|
|
4
|
+
it("puts and gets a snapshot", async () => {
|
|
5
|
+
const s = new InMemoryActionStore();
|
|
6
|
+
await s.put("id1", { path: [0], conversationKey: "c" });
|
|
7
|
+
expect(await s.get("id1")).toMatchObject({ path: [0] });
|
|
8
|
+
});
|
|
9
|
+
it("expires entries past ttl", async () => {
|
|
10
|
+
vi.useFakeTimers();
|
|
11
|
+
try {
|
|
12
|
+
const s = new InMemoryActionStore();
|
|
13
|
+
await s.put("id1", { path: [0], conversationKey: "c" }, 1000);
|
|
14
|
+
vi.advanceTimersByTime(1500);
|
|
15
|
+
expect(await s.get("id1")).toBeUndefined();
|
|
16
|
+
}
|
|
17
|
+
finally {
|
|
18
|
+
vi.useRealTimers();
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
it("deletes entries", async () => {
|
|
22
|
+
const s = new InMemoryActionStore();
|
|
23
|
+
await s.put("id1", { path: [0], conversationKey: "c" });
|
|
24
|
+
await s.delete("id1");
|
|
25
|
+
expect(await s.get("id1")).toBeUndefined();
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { InferSchemaOutput, ObjectSchema } from "./standard-schema.js";
|
|
2
|
+
import type { Thread, PlatformUser } from "@copilotkit/bot-ui";
|
|
3
|
+
/**
|
|
4
|
+
* Context handed to a slash-command handler. `text` is the raw argument string
|
|
5
|
+
* (the form Slack delivers); `options` is the parsed, typed form, populated by
|
|
6
|
+
* surfaces that deliver structured args natively (e.g. Discord). On text-only
|
|
7
|
+
* surfaces `options` is empty — read `text` there.
|
|
8
|
+
*/
|
|
9
|
+
export interface CommandContext<TOptions = Record<string, never>> {
|
|
10
|
+
/** The conversation the command was invoked in. */
|
|
11
|
+
thread: Thread;
|
|
12
|
+
/** The invoked command name, normalized (no leading slash, lower-cased). */
|
|
13
|
+
command: string;
|
|
14
|
+
/** Raw argument string after the command name. */
|
|
15
|
+
text: string;
|
|
16
|
+
/** Parsed, typed options (empty on surfaces that only deliver `text`). */
|
|
17
|
+
options: TOptions;
|
|
18
|
+
/** The invoking user, when the surface provides it. */
|
|
19
|
+
user?: PlatformUser;
|
|
20
|
+
platform: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* A slash command. Defined like a {@link import("./tools.js").BotTool}: a name,
|
|
24
|
+
* an optional Standard Schema for typed options, and a handler. The `options`
|
|
25
|
+
* schema maps natively to surfaces with structured args (Discord) and is used
|
|
26
|
+
* to register/validate there; on text-only surfaces (Slack) args arrive via
|
|
27
|
+
* `ctx.text`.
|
|
28
|
+
*/
|
|
29
|
+
export interface BotCommand<Schema extends ObjectSchema = ObjectSchema> {
|
|
30
|
+
/** Command name without the leading slash (e.g. `"triage"`). Matched case-insensitively. */
|
|
31
|
+
name: string;
|
|
32
|
+
/** Human description — help text, and the registration label on surfaces like Discord. */
|
|
33
|
+
description?: string;
|
|
34
|
+
/** Optional Standard Schema for typed options. */
|
|
35
|
+
options?: Schema;
|
|
36
|
+
handler(ctx: CommandContext<InferSchemaOutput<Schema>>): void | Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Define a {@link BotCommand} with full type inference: `ctx.options` is
|
|
40
|
+
* inferred from `options`.
|
|
41
|
+
*
|
|
42
|
+
* ```ts
|
|
43
|
+
* const triage = defineBotCommand({
|
|
44
|
+
* name: "triage",
|
|
45
|
+
* description: "Summarize and file the current thread.",
|
|
46
|
+
* options: z.object({ priority: z.enum(["low", "high"]).optional() }),
|
|
47
|
+
* async handler({ thread, text, options }) {
|
|
48
|
+
* await thread.runAgent({ prompt: `Triage: ${text}`, });
|
|
49
|
+
* },
|
|
50
|
+
* });
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export declare function defineBotCommand<Schema extends ObjectSchema>(command: BotCommand<Schema>): BotCommand<Schema>;
|
|
54
|
+
/**
|
|
55
|
+
* Platform-neutral descriptor an adapter may use to register a command with the
|
|
56
|
+
* surface (e.g. Discord's application-command API). Produced from a
|
|
57
|
+
* {@link BotCommand} by {@link toCommandSpec}.
|
|
58
|
+
*/
|
|
59
|
+
export interface CommandSpec {
|
|
60
|
+
name: string;
|
|
61
|
+
description: string;
|
|
62
|
+
/** JSON Schema for the options, or `undefined` for a free-text command. */
|
|
63
|
+
options?: Record<string, unknown>;
|
|
64
|
+
}
|
|
65
|
+
/** Normalize a command name for matching: drop a leading slash, lower-case, trim. */
|
|
66
|
+
export declare function normalizeCommandName(name: string): string;
|
|
67
|
+
export declare function toCommandSpec(command: BotCommand): CommandSpec;
|
|
68
|
+
//# sourceMappingURL=commands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAE5E,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAE/D;;;;;GAKG;AACH,MAAM,WAAW,cAAc,CAAC,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;IAC9D,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,OAAO,EAAE,MAAM,CAAC;IAChB,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,OAAO,EAAE,QAAQ,CAAC;IAClB,uDAAuD;IACvD,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,UAAU,CAAC,MAAM,SAAS,YAAY,GAAG,YAAY;IACpE,4FAA4F;IAC5F,IAAI,EAAE,MAAM,CAAC;IACb,0FAA0F;IAC1F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,GAAG,EAAE,cAAc,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/E;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,SAAS,YAAY,EAC1D,OAAO,EAAE,UAAU,CAAC,MAAM,CAAC,GAC1B,UAAU,CAAC,MAAM,CAAC,CAEpB;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,qFAAqF;AACrF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,GAAG,WAAW,CAM9D"}
|
package/dist/commands.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { toJsonSchema } from "./standard-schema.js";
|
|
2
|
+
/**
|
|
3
|
+
* Define a {@link BotCommand} with full type inference: `ctx.options` is
|
|
4
|
+
* inferred from `options`.
|
|
5
|
+
*
|
|
6
|
+
* ```ts
|
|
7
|
+
* const triage = defineBotCommand({
|
|
8
|
+
* name: "triage",
|
|
9
|
+
* description: "Summarize and file the current thread.",
|
|
10
|
+
* options: z.object({ priority: z.enum(["low", "high"]).optional() }),
|
|
11
|
+
* async handler({ thread, text, options }) {
|
|
12
|
+
* await thread.runAgent({ prompt: `Triage: ${text}`, });
|
|
13
|
+
* },
|
|
14
|
+
* });
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export function defineBotCommand(command) {
|
|
18
|
+
return command;
|
|
19
|
+
}
|
|
20
|
+
/** Normalize a command name for matching: drop a leading slash, lower-case, trim. */
|
|
21
|
+
export function normalizeCommandName(name) {
|
|
22
|
+
return name.trim().replace(/^\//, "").toLowerCase();
|
|
23
|
+
}
|
|
24
|
+
export function toCommandSpec(command) {
|
|
25
|
+
return {
|
|
26
|
+
name: normalizeCommandName(command.name),
|
|
27
|
+
description: command.description ?? "",
|
|
28
|
+
options: command.options ? toJsonSchema(command.options) : undefined,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { PlatformAdapter } from "./platform-adapter.js";
|
|
2
|
+
import { type ActionStore } from "./action-store.js";
|
|
3
|
+
import { type BotTool, type ContextEntry } from "./tools.js";
|
|
4
|
+
import { type BotCommand, type CommandContext } from "./commands.js";
|
|
5
|
+
import { Thread } from "./thread.js";
|
|
6
|
+
import type { AbstractAgent } from "@ag-ui/client";
|
|
7
|
+
import type { InteractionContext, IncomingMessage } from "@copilotkit/bot-ui";
|
|
8
|
+
export type BotHandler = (ctx: {
|
|
9
|
+
thread: Thread;
|
|
10
|
+
message: IncomingMessage;
|
|
11
|
+
}) => void | Promise<void>;
|
|
12
|
+
export interface CreateBotOptions {
|
|
13
|
+
adapters: PlatformAdapter[];
|
|
14
|
+
agent?: AbstractAgent | ((threadId: string) => AbstractAgent);
|
|
15
|
+
actionStore?: ActionStore;
|
|
16
|
+
tools?: BotTool[];
|
|
17
|
+
context?: ContextEntry[];
|
|
18
|
+
/** Slash commands. Forwarded to adapters that support them; ignored elsewhere. */
|
|
19
|
+
commands?: BotCommand[];
|
|
20
|
+
}
|
|
21
|
+
export interface Bot {
|
|
22
|
+
onMention(h: BotHandler): void;
|
|
23
|
+
onMessage(h: BotHandler): void;
|
|
24
|
+
/** Handle clicks on a specific action `id`. `ctx.action.value` is typed as `TValue`. */
|
|
25
|
+
onInteraction<TValue = unknown>(id: string, h: (ctx: InteractionContext<TValue>) => void | Promise<void>): void;
|
|
26
|
+
/**
|
|
27
|
+
* Handle an agent interrupt (an `on_interrupt` custom event). `payload` is the
|
|
28
|
+
* event's value; pass `TPayload` to type it, e.g.
|
|
29
|
+
* `onInterrupt<{ question: string }>("ask", ...)`.
|
|
30
|
+
*/
|
|
31
|
+
onInterrupt<TPayload = unknown>(eventName: string, h: (args: {
|
|
32
|
+
payload: TPayload;
|
|
33
|
+
thread: Thread;
|
|
34
|
+
}) => void | Promise<void>): void;
|
|
35
|
+
/** Register a slash command (with optional typed options). */
|
|
36
|
+
onCommand(command: BotCommand): void;
|
|
37
|
+
/** Register a free-text slash command by name. */
|
|
38
|
+
onCommand(name: string, handler: (ctx: CommandContext) => void | Promise<void>): void;
|
|
39
|
+
tool(t: BotTool): void;
|
|
40
|
+
start(): Promise<void>;
|
|
41
|
+
stop(): Promise<void>;
|
|
42
|
+
}
|
|
43
|
+
export declare function createBot(opts: CreateBotOptions): Bot;
|
|
44
|
+
//# sourceMappingURL=create-bot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-bot.d.ts","sourceRoot":"","sources":["../src/create-bot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EAKhB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC1E,OAAO,EAGL,KAAK,OAAO,EACZ,KAAK,YAAY,EAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EAGL,KAAK,UAAU,EACf,KAAK,cAAc,EACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,MAAM,EAAmB,MAAM,aAAa,CAAC;AACtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAE9E,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,eAAe,CAAC;CAC1B,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE3B,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,KAAK,CAAC,EAAE,aAAa,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,KAAK,aAAa,CAAC,CAAC;IAC9D,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC;IAClB,OAAO,CAAC,EAAE,YAAY,EAAE,CAAC;IACzB,kFAAkF;IAClF,QAAQ,CAAC,EAAE,UAAU,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,GAAG;IAClB,SAAS,CAAC,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC/B,SAAS,CAAC,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC/B,wFAAwF;IACxF,aAAa,CAAC,MAAM,GAAG,OAAO,EAC5B,EAAE,EAAE,MAAM,EACV,CAAC,EAAE,CAAC,GAAG,EAAE,kBAAkB,CAAC,MAAM,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAC3D,IAAI,CAAC;IACR;;;;OAIG;IACH,WAAW,CAAC,QAAQ,GAAG,OAAO,EAC5B,SAAS,EAAE,MAAM,EACjB,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,QAAQ,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GACvE,IAAI,CAAC;IACR,8DAA8D;IAC9D,SAAS,CAAC,OAAO,EAAE,UAAU,GAAG,IAAI,CAAC;IACrC,kDAAkD;IAClD,SAAS,CACP,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GACrD,IAAI,CAAC;IACR,IAAI,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACvB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,gBAAgB,GAAG,GAAG,CA6MrD"}
|