@copilotkit/channels-core 0.6.1 → 0.6.2-canary.1785782905
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 +37 -0
- package/dist/action-registry.d.ts +23 -4
- package/dist/action-registry.d.ts.map +1 -1
- package/dist/action-registry.js +139 -26
- package/dist/action-store.d.ts +6 -0
- package/dist/action-store.d.ts.map +1 -1
- package/dist/channel-component.d.ts +18 -0
- package/dist/channel-component.d.ts.map +1 -0
- package/dist/channel-component.js +13 -0
- package/dist/channel-component.test.d.ts +2 -0
- package/dist/channel-component.test.d.ts.map +1 -0
- package/dist/channel-component.test.js +120 -0
- package/dist/component-reaction-recovery.test.d.ts +2 -0
- package/dist/component-reaction-recovery.test.d.ts.map +1 -0
- package/dist/component-reaction-recovery.test.js +29 -0
- package/dist/create-channel.d.ts +6 -5
- package/dist/create-channel.d.ts.map +1 -1
- package/dist/create-channel.js +63 -7
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/keyed-action-recovery.test.d.ts +2 -0
- package/dist/keyed-action-recovery.test.d.ts.map +1 -0
- package/dist/keyed-action-recovery.test.js +121 -0
- package/dist/thread.d.ts +3 -0
- package/dist/thread.d.ts.map +1 -1
- package/dist/thread.js +14 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -189,6 +189,43 @@ methods (e.g. `thread.getMessages()`, `thread.lookupUser(query)`,
|
|
|
189
189
|
A `ContextEntry` is `{ description: string; value: string }` — knowledge
|
|
190
190
|
folded into the agent's system context on each `runAgent`.
|
|
191
191
|
|
|
192
|
+
## Agent-rendered components
|
|
193
|
+
|
|
194
|
+
`defineChannelComponent` turns a server-rendered JSX function into an agent
|
|
195
|
+
tool. Its Standard Schema validates tool args before `render` runs. The render
|
|
196
|
+
context supplies the source `platform` and the run's `AbortSignal`.
|
|
197
|
+
|
|
198
|
+
```tsx
|
|
199
|
+
import { createChannel, defineChannelComponent } from "@copilotkit/channels";
|
|
200
|
+
import { z } from "zod";
|
|
201
|
+
|
|
202
|
+
const Approval = defineChannelComponent({
|
|
203
|
+
name: "show_approval",
|
|
204
|
+
description: "Post an approval request.",
|
|
205
|
+
parameters: z.object({ title: z.string() }),
|
|
206
|
+
render: ({ title }, { platform, signal }) => (
|
|
207
|
+
<Card title={`${title} (${platform})`}>
|
|
208
|
+
<Button key="approve" value="approve" onClick={approve}>
|
|
209
|
+
Approve
|
|
210
|
+
</Button>
|
|
211
|
+
</Card>
|
|
212
|
+
),
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
const channel = createChannel({
|
|
216
|
+
name: "approvals",
|
|
217
|
+
components: [Approval],
|
|
218
|
+
});
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
When the agent calls `show_approval`, Channels posts the rendered message as a
|
|
222
|
+
separate provider message and returns a short tool acknowledgement. The agent
|
|
223
|
+
run then continues. Interactive nodes in a component definition need stable,
|
|
224
|
+
unique JSX keys. Channels stores those keys with the source platform and action
|
|
225
|
+
value, so a click or reaction can recover the same handler after a restart.
|
|
226
|
+
Legacy `components: { Name: Component }` registrations still use positional
|
|
227
|
+
recovery for old snapshots.
|
|
228
|
+
|
|
192
229
|
## ActionStore
|
|
193
230
|
|
|
194
231
|
Inline JSX handlers are bound by content. Each interactive node gets a
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { ChannelNode, InteractionContext,
|
|
1
|
+
import type { ChannelNode, InteractionContext, Renderable, MessageReactionHandler } from "@copilotkit/channels-ui";
|
|
2
|
+
import type { ChannelComponentRenderContext } from "./channel-component.js";
|
|
2
3
|
import type { ActionContinuationContext, ActionContinuationBinding, ActionContinuationSnapshot, ActionStore } from "./action-store.js";
|
|
3
4
|
export declare class ActionExpiredError extends Error {
|
|
4
5
|
readonly code = "channel_action_expired";
|
|
@@ -31,6 +32,7 @@ export declare class ActionRegistry {
|
|
|
31
32
|
component: string;
|
|
32
33
|
props: Record<string, unknown>;
|
|
33
34
|
conversationKey: string;
|
|
35
|
+
platform?: string;
|
|
34
36
|
}): Promise<void>;
|
|
35
37
|
/**
|
|
36
38
|
* Resolve the `onReaction` handler for `messageId`: the hot cache first, then
|
|
@@ -39,10 +41,22 @@ export declare class ActionRegistry {
|
|
|
39
41
|
* handler whose closure can't be re-derived after a restart.
|
|
40
42
|
*/
|
|
41
43
|
resolveMessageReaction(messageId: string): Promise<MessageReactionHandler | undefined>;
|
|
42
|
-
registerComponent(name: string, fn:
|
|
44
|
+
registerComponent(name: string, fn: (props: Record<string, unknown>, context: ChannelComponentRenderContext) => Renderable | Promise<Renderable>, options?: {
|
|
45
|
+
requireKeys?: boolean;
|
|
46
|
+
}): void;
|
|
43
47
|
clearHotCache(): void;
|
|
44
|
-
bindTree(componentName: string, props: Record<string, unknown>, conversationKey: string, continuation?: ActionContinuationContext): Promise<ChannelNode[]>;
|
|
45
|
-
|
|
48
|
+
bindTree(componentName: string, props: Record<string, unknown>, conversationKey: string, continuation?: ActionContinuationContext, renderContext?: ChannelComponentRenderContext): Promise<ChannelNode[]>;
|
|
49
|
+
/** Bind a named component and detach its root reaction for message routing. */
|
|
50
|
+
bindRegisteredRenderable(componentName: string, props: Record<string, unknown>, conversationKey: string, continuation: ActionContinuationContext | undefined, renderContext: ChannelComponentRenderContext): Promise<{
|
|
51
|
+
root: ChannelNode[];
|
|
52
|
+
onReaction?: MessageReactionHandler;
|
|
53
|
+
reactionComponent?: {
|
|
54
|
+
component: string;
|
|
55
|
+
props: Record<string, unknown>;
|
|
56
|
+
platform: string;
|
|
57
|
+
};
|
|
58
|
+
}>;
|
|
59
|
+
bindRenderable(ui: Renderable, conversationKey: string, continuation?: ActionContinuationContext, renderContext?: ChannelComponentRenderContext): Promise<{
|
|
46
60
|
root: ChannelNode[];
|
|
47
61
|
onReaction?: MessageReactionHandler;
|
|
48
62
|
/**
|
|
@@ -53,9 +67,14 @@ export declare class ActionRegistry {
|
|
|
53
67
|
reactionComponent?: {
|
|
54
68
|
component: string;
|
|
55
69
|
props: Record<string, unknown>;
|
|
70
|
+
platform: string;
|
|
56
71
|
};
|
|
57
72
|
}>;
|
|
58
73
|
private walk;
|
|
74
|
+
/** Traverse children and provider-native named slots through one path model. */
|
|
75
|
+
private walkValue;
|
|
76
|
+
/** Bind one node reached through a singular provider-native slot. */
|
|
77
|
+
private walkNode;
|
|
59
78
|
/**
|
|
60
79
|
* Run the click handler for `id` and return the clicked element's `value`
|
|
61
80
|
* (so callers can resolve a HITL `awaitChoice` waiter even when the platform
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action-registry.d.ts","sourceRoot":"","sources":["../src/action-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EAEX,kBAAkB,
|
|
1
|
+
{"version":3,"file":"action-registry.d.ts","sourceRoot":"","sources":["../src/action-registry.ts"],"names":[],"mappings":"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"}
|
package/dist/action-registry.js
CHANGED
|
@@ -54,6 +54,7 @@ export class ActionRegistry {
|
|
|
54
54
|
props: snap.props,
|
|
55
55
|
path: [],
|
|
56
56
|
conversationKey: snap.conversationKey,
|
|
57
|
+
platform: snap.platform,
|
|
57
58
|
});
|
|
58
59
|
}
|
|
59
60
|
/**
|
|
@@ -69,26 +70,52 @@ export class ActionRegistry {
|
|
|
69
70
|
const snap = await this.store.get(reactionKey(messageId));
|
|
70
71
|
if (!snap?.component)
|
|
71
72
|
return undefined;
|
|
72
|
-
const
|
|
73
|
-
if (!
|
|
73
|
+
const registered = this.components.get(snap.component);
|
|
74
|
+
if (!registered)
|
|
74
75
|
return undefined;
|
|
75
|
-
const root = renderToIR(
|
|
76
|
+
const root = renderToIR(await registered.render(snap.props, {
|
|
77
|
+
platform: (snap.platform ??
|
|
78
|
+
"slack"),
|
|
79
|
+
signal: new AbortController().signal,
|
|
80
|
+
}));
|
|
76
81
|
return takeMessageReaction(root);
|
|
77
82
|
}
|
|
78
|
-
registerComponent(name, fn) {
|
|
79
|
-
this.components.set(name,
|
|
83
|
+
registerComponent(name, fn, options = {}) {
|
|
84
|
+
this.components.set(name, {
|
|
85
|
+
render: fn,
|
|
86
|
+
requireKeys: options.requireKeys ?? false,
|
|
87
|
+
});
|
|
80
88
|
}
|
|
81
89
|
clearHotCache() {
|
|
82
90
|
this.hot.clear();
|
|
83
91
|
}
|
|
84
92
|
// Renders the named component, binds all event-prop handlers in the tree
|
|
85
93
|
// (mint id, hot-cache + ActionStore snapshot, rewrite prop to { id }), returns the bound IR.
|
|
86
|
-
async bindTree(componentName, props, conversationKey, continuation
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
94
|
+
async bindTree(componentName, props, conversationKey, continuation, renderContext = {
|
|
95
|
+
platform: "slack",
|
|
96
|
+
signal: new AbortController().signal,
|
|
97
|
+
}) {
|
|
98
|
+
const registered = this.components.get(componentName);
|
|
99
|
+
const rendered = registered
|
|
100
|
+
? await registered.render(props, renderContext)
|
|
101
|
+
: props;
|
|
102
|
+
const root = renderToIR(rendered);
|
|
103
|
+
const interactiveKeys = new Set();
|
|
104
|
+
await this.walk(root, [], componentName, props, conversationKey, continuation, registered?.requireKeys ?? false, renderContext.platform, interactiveKeys);
|
|
90
105
|
return root;
|
|
91
106
|
}
|
|
107
|
+
/** Bind a named component and detach its root reaction for message routing. */
|
|
108
|
+
async bindRegisteredRenderable(componentName, props, conversationKey, continuation, renderContext) {
|
|
109
|
+
const root = await this.bindTree(componentName, props, conversationKey, continuation, renderContext);
|
|
110
|
+
const onReaction = takeMessageReaction(root);
|
|
111
|
+
return {
|
|
112
|
+
root,
|
|
113
|
+
onReaction,
|
|
114
|
+
reactionComponent: onReaction
|
|
115
|
+
? { component: componentName, props, platform: renderContext.platform }
|
|
116
|
+
: undefined,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
92
119
|
// Binds an arbitrary Renderable for posting. If `ui` is a component element
|
|
93
120
|
// (`{ type: fn, props }`), it is registered + bound by name (cold-path
|
|
94
121
|
// re-render supported). Otherwise the IR is bound inline with `component:""`,
|
|
@@ -96,7 +123,10 @@ export class ActionRegistry {
|
|
|
96
123
|
// degradation for inline handlers that can't be re-derived). A top-level
|
|
97
124
|
// `<Message onReaction>` handler is pulled off the IR (so it never reaches the
|
|
98
125
|
// adapter) and returned for the caller to associate with the posted message.
|
|
99
|
-
async bindRenderable(ui, conversationKey, continuation
|
|
126
|
+
async bindRenderable(ui, conversationKey, continuation, renderContext = {
|
|
127
|
+
platform: "slack",
|
|
128
|
+
signal: new AbortController().signal,
|
|
129
|
+
}) {
|
|
100
130
|
let root;
|
|
101
131
|
let component;
|
|
102
132
|
let props;
|
|
@@ -104,31 +134,50 @@ export class ActionRegistry {
|
|
|
104
134
|
const fn = ui.type;
|
|
105
135
|
component = fn.name || "anonymous";
|
|
106
136
|
props = (ui.props ?? {});
|
|
107
|
-
this.registerComponent(component, fn);
|
|
108
|
-
root = await this.bindTree(component, props, conversationKey, continuation
|
|
137
|
+
this.registerComponent(component, (componentProps) => fn(componentProps));
|
|
138
|
+
root = await this.bindTree(component, props, conversationKey, continuation, {
|
|
139
|
+
platform: renderContext.platform,
|
|
140
|
+
signal: renderContext.signal,
|
|
141
|
+
});
|
|
109
142
|
}
|
|
110
143
|
else {
|
|
111
144
|
root = renderToIR(ui);
|
|
112
|
-
await this.walk(root, [], "", undefined, conversationKey, continuation);
|
|
145
|
+
await this.walk(root, [], "", undefined, conversationKey, continuation, false, renderContext.platform, new Set());
|
|
113
146
|
}
|
|
114
147
|
const onReaction = takeMessageReaction(root);
|
|
115
148
|
return {
|
|
116
149
|
root,
|
|
117
150
|
onReaction,
|
|
118
|
-
reactionComponent: onReaction && component && props
|
|
151
|
+
reactionComponent: onReaction && component && props
|
|
152
|
+
? { component, props, platform: renderContext.platform }
|
|
153
|
+
: undefined,
|
|
119
154
|
};
|
|
120
155
|
}
|
|
121
|
-
async walk(nodes, base, comp, props, conv, continuation) {
|
|
156
|
+
async walk(nodes, base, comp, props, conv, continuation, requireKeys, platform, interactiveKeys, exactPath) {
|
|
122
157
|
for (let i = 0; i < nodes.length; i++) {
|
|
123
158
|
const node = nodes[i];
|
|
124
|
-
const path = [...base, i];
|
|
159
|
+
const path = exactPath ?? [...base, i];
|
|
160
|
+
const eventProps = EVENT_PROPS.filter((eventProp) => typeof node.props[eventProp] === "function");
|
|
161
|
+
if (requireKeys && eventProps.length > 0) {
|
|
162
|
+
if (node.key === undefined ||
|
|
163
|
+
(typeof node.key === "string" && node.key.trim().length === 0)) {
|
|
164
|
+
throw new Error(`${formatComponentPath(comp, path)}.${eventProps[0]} requires a non-empty JSX key`);
|
|
165
|
+
}
|
|
166
|
+
if (interactiveKeys.has(node.key)) {
|
|
167
|
+
throw new Error(`duplicate interactive JSX key "${node.key}"`);
|
|
168
|
+
}
|
|
169
|
+
interactiveKeys.add(node.key);
|
|
170
|
+
}
|
|
125
171
|
for (const ep of EVENT_PROPS) {
|
|
126
172
|
const handler = node.props[ep];
|
|
127
173
|
if (typeof handler === "function") {
|
|
128
174
|
const fullPath = [...path, ep];
|
|
175
|
+
const locator = requireKeys && node.key !== undefined
|
|
176
|
+
? { key: node.key, eventProp: ep }
|
|
177
|
+
: undefined;
|
|
129
178
|
const id = continuation
|
|
130
179
|
? `ck:${globalThis.crypto.randomUUID()}`
|
|
131
|
-
: mintId(comp, fullPath, props);
|
|
180
|
+
: mintId(comp, locator ? [locator.key, locator.eventProp] : fullPath, props);
|
|
132
181
|
this.hot.set(id, {
|
|
133
182
|
handler: handler,
|
|
134
183
|
value: node.props.value,
|
|
@@ -138,6 +187,9 @@ export class ActionRegistry {
|
|
|
138
187
|
component: comp,
|
|
139
188
|
props,
|
|
140
189
|
path: fullPath,
|
|
190
|
+
...(locator ? { locator } : {}),
|
|
191
|
+
platform,
|
|
192
|
+
actionValue: node.props.value,
|
|
141
193
|
conversationKey: conv,
|
|
142
194
|
boundArgs: isBound(handler) ? getBoundArgs(handler) : undefined,
|
|
143
195
|
...(continuation
|
|
@@ -147,12 +199,29 @@ export class ActionRegistry {
|
|
|
147
199
|
node.props[ep] = { id };
|
|
148
200
|
}
|
|
149
201
|
}
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
202
|
+
for (const [propName, propValue] of Object.entries(node.props)) {
|
|
203
|
+
if (EVENT_PROPS.includes(propName))
|
|
204
|
+
continue;
|
|
205
|
+
await this.walkValue(propValue, [...path, propName], comp, props, conv, continuation, requireKeys, platform, interactiveKeys);
|
|
153
206
|
}
|
|
154
207
|
}
|
|
155
208
|
}
|
|
209
|
+
/** Traverse children and provider-native named slots through one path model. */
|
|
210
|
+
async walkValue(value, path, comp, props, conv, continuation, requireKeys, platform, interactiveKeys) {
|
|
211
|
+
if (isChannelNode(value)) {
|
|
212
|
+
await this.walkNode(value, path, comp, props, conv, continuation, requireKeys, platform, interactiveKeys);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (!Array.isArray(value))
|
|
216
|
+
return;
|
|
217
|
+
for (let index = 0; index < value.length; index++) {
|
|
218
|
+
await this.walkValue(value[index], [...path, index], comp, props, conv, continuation, requireKeys, platform, interactiveKeys);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/** Bind one node reached through a singular provider-native slot. */
|
|
222
|
+
async walkNode(node, path, comp, props, conv, continuation, requireKeys, platform, interactiveKeys) {
|
|
223
|
+
await this.walk([node], [], comp, props, conv, continuation, requireKeys, platform, interactiveKeys, path);
|
|
224
|
+
}
|
|
156
225
|
/**
|
|
157
226
|
* Run the click handler for `id` and return the clicked element's `value`
|
|
158
227
|
* (so callers can resolve a HITL `awaitChoice` waiter even when the platform
|
|
@@ -175,16 +244,26 @@ export class ActionRegistry {
|
|
|
175
244
|
const snap = await this.store.get(id);
|
|
176
245
|
if (!snap || !snap.component)
|
|
177
246
|
throw new ActionExpiredError(id);
|
|
178
|
-
const
|
|
179
|
-
if (!
|
|
247
|
+
const registered = this.components.get(snap.component);
|
|
248
|
+
if (!registered)
|
|
180
249
|
throw new ActionExpiredError(id);
|
|
181
|
-
const tree = renderToIR(
|
|
182
|
-
|
|
183
|
-
|
|
250
|
+
const tree = renderToIR(await registered.render(snap.props, {
|
|
251
|
+
platform: (snap.platform ??
|
|
252
|
+
ctx.platform),
|
|
253
|
+
signal: new AbortController().signal,
|
|
254
|
+
}));
|
|
255
|
+
handler = snap.locator
|
|
256
|
+
? findHandlerByLocator(tree, snap.locator)
|
|
257
|
+
: pluck(tree, snap.path);
|
|
258
|
+
value = snap.locator ? snap.actionValue : pluckValue(tree, snap.path);
|
|
184
259
|
if (!handler)
|
|
185
260
|
throw new ActionExpiredError(id);
|
|
186
261
|
}
|
|
187
|
-
|
|
262
|
+
const actionValue = value === undefined ? ctx.action.value : value;
|
|
263
|
+
await handler({
|
|
264
|
+
...ctx,
|
|
265
|
+
action: { ...ctx.action, id, value: actionValue },
|
|
266
|
+
});
|
|
188
267
|
return value;
|
|
189
268
|
}
|
|
190
269
|
/** Read and validate one continuation capability without consuming it. */
|
|
@@ -219,6 +298,40 @@ function assertContinuationBinding(actual, actionId, expected) {
|
|
|
219
298
|
throw new ActionContinuationMismatchError();
|
|
220
299
|
}
|
|
221
300
|
}
|
|
301
|
+
function isChannelNode(value) {
|
|
302
|
+
return (typeof value === "object" &&
|
|
303
|
+
value !== null &&
|
|
304
|
+
"type" in value &&
|
|
305
|
+
"props" in value &&
|
|
306
|
+
typeof value.props === "object" &&
|
|
307
|
+
value.props !== null);
|
|
308
|
+
}
|
|
309
|
+
function formatComponentPath(component, path) {
|
|
310
|
+
return path.reduce((result, segment) => typeof segment === "number"
|
|
311
|
+
? `${result}[${segment}]`
|
|
312
|
+
: `${result}.${segment}`, component);
|
|
313
|
+
}
|
|
314
|
+
function findHandlerByLocator(tree, locator) {
|
|
315
|
+
const matches = [];
|
|
316
|
+
const visit = (value) => {
|
|
317
|
+
if (Array.isArray(value)) {
|
|
318
|
+
for (const item of value)
|
|
319
|
+
visit(item);
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
if (!isChannelNode(value))
|
|
323
|
+
return;
|
|
324
|
+
if (value.key === locator.key) {
|
|
325
|
+
const handler = value.props[locator.eventProp];
|
|
326
|
+
if (typeof handler === "function")
|
|
327
|
+
matches.push(handler);
|
|
328
|
+
}
|
|
329
|
+
for (const propValue of Object.values(value.props))
|
|
330
|
+
visit(propValue);
|
|
331
|
+
};
|
|
332
|
+
visit(tree);
|
|
333
|
+
return matches.length === 1 ? matches[0] : undefined;
|
|
334
|
+
}
|
|
222
335
|
/** Store key for a message's durable reaction snapshot (distinct from minted action ids). */
|
|
223
336
|
function reactionKey(messageId) {
|
|
224
337
|
return `reaction:${messageId}`;
|
package/dist/action-store.d.ts
CHANGED
|
@@ -22,6 +22,12 @@ export interface ActionSnapshot {
|
|
|
22
22
|
component?: string;
|
|
23
23
|
props?: unknown;
|
|
24
24
|
path: (string | number)[];
|
|
25
|
+
locator?: {
|
|
26
|
+
key: string | number;
|
|
27
|
+
eventProp: "onClick" | "onSelect" | "onSubmit";
|
|
28
|
+
};
|
|
29
|
+
platform?: string;
|
|
30
|
+
actionValue?: unknown;
|
|
25
31
|
boundArgs?: unknown;
|
|
26
32
|
conversationKey: string;
|
|
27
33
|
continuation?: ActionContinuationSnapshot;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action-store.d.ts","sourceRoot":"","sources":["../src/action-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAE9E,iEAAiE;AACjE,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,eAAe,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,aAAa,CAAC;CACtB;AAED,gFAAgF;AAChF,MAAM,WAAW,yBAAyB;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,2BAA2B,CAAC;CACxC;AAED,+EAA+E;AAC/E,MAAM,MAAM,yBAAyB,GAAG,IAAI,CAC1C,yBAAyB,EACzB,aAAa,GAAG,iBAAiB,GAAG,UAAU,CAC/C,CAAC;AAEF,8EAA8E;AAC9E,MAAM,WAAW,0BAA2B,SAAQ,yBAAyB;IAC3E,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,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;IACxB,YAAY,CAAC,EAAE,0BAA0B,CAAC;CAC3C;AACD,iHAAiH;AACjH,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,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;IACzD,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnC;AACD,iHAAiH;AACjH,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;IAGjC,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;CAU/D"}
|
|
1
|
+
{"version":3,"file":"action-store.d.ts","sourceRoot":"","sources":["../src/action-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAE9E,iEAAiE;AACjE,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,eAAe,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE,aAAa,CAAC;CACtB;AAED,gFAAgF;AAChF,MAAM,WAAW,yBAAyB;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,2BAA2B,CAAC;CACxC;AAED,+EAA+E;AAC/E,MAAM,MAAM,yBAAyB,GAAG,IAAI,CAC1C,yBAAyB,EACzB,aAAa,GAAG,iBAAiB,GAAG,UAAU,CAC/C,CAAC;AAEF,8EAA8E;AAC9E,MAAM,WAAW,0BAA2B,SAAQ,yBAAyB;IAC3E,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,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,OAAO,CAAC,EAAE;QACR,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;QACrB,SAAS,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;KAChD,CAAC;IACF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,0BAA0B,CAAC;CAC3C;AACD,iHAAiH;AACjH,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,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC,CAAC;IACzD,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnC;AACD,iHAAiH;AACjH,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;IAGjC,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;CAU/D"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Renderable } from "@copilotkit/channels-ui";
|
|
2
|
+
import type { InferSchemaOutput, ObjectSchema } from "./standard-schema.js";
|
|
3
|
+
export type ChannelComponentPlatform = "slack" | "teams" | "discord" | "telegram" | "whatsapp";
|
|
4
|
+
export interface ChannelComponentRenderContext {
|
|
5
|
+
platform: ChannelComponentPlatform;
|
|
6
|
+
signal: AbortSignal;
|
|
7
|
+
}
|
|
8
|
+
export interface ChannelComponentDefinition<Schema extends ObjectSchema = ObjectSchema> {
|
|
9
|
+
name: string;
|
|
10
|
+
description: string;
|
|
11
|
+
parameters: Schema;
|
|
12
|
+
render(props: InferSchemaOutput<Schema>, context: ChannelComponentRenderContext): Renderable | Promise<Renderable>;
|
|
13
|
+
}
|
|
14
|
+
/** Define an agent-rendered Channel component with schema-inferred props. */
|
|
15
|
+
export declare function defineChannelComponent<Schema extends ObjectSchema>(component: ChannelComponentDefinition<Schema>): ChannelComponentDefinition<Schema>;
|
|
16
|
+
/** Distinguish component-tool descriptors from legacy JSX component functions. */
|
|
17
|
+
export declare function isChannelComponentDefinition(value: unknown): value is ChannelComponentDefinition;
|
|
18
|
+
//# sourceMappingURL=channel-component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-component.d.ts","sourceRoot":"","sources":["../src/channel-component.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAE5E,MAAM,MAAM,wBAAwB,GAChC,OAAO,GACP,OAAO,GACP,SAAS,GACT,UAAU,GACV,UAAU,CAAC;AAEf,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,wBAAwB,CAAC;IACnC,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,MAAM,WAAW,0BAA0B,CACzC,MAAM,SAAS,YAAY,GAAG,YAAY;IAE1C,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CACJ,KAAK,EAAE,iBAAiB,CAAC,MAAM,CAAC,EAChC,OAAO,EAAE,6BAA6B,GACrC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CACrC;AAED,6EAA6E;AAC7E,wBAAgB,sBAAsB,CAAC,MAAM,SAAS,YAAY,EAChE,SAAS,EAAE,0BAA0B,CAAC,MAAM,CAAC,GAC5C,0BAA0B,CAAC,MAAM,CAAC,CAEpC;AAED,kFAAkF;AAClF,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,0BAA0B,CAWrC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Define an agent-rendered Channel component with schema-inferred props. */
|
|
2
|
+
export function defineChannelComponent(component) {
|
|
3
|
+
return component;
|
|
4
|
+
}
|
|
5
|
+
/** Distinguish component-tool descriptors from legacy JSX component functions. */
|
|
6
|
+
export function isChannelComponentDefinition(value) {
|
|
7
|
+
return (typeof value === "object" &&
|
|
8
|
+
value !== null &&
|
|
9
|
+
typeof value.name === "string" &&
|
|
10
|
+
typeof value.description === "string" &&
|
|
11
|
+
typeof value.render === "function" &&
|
|
12
|
+
typeof value.parameters?.["~standard"] === "object");
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channel-component.test.d.ts","sourceRoot":"","sources":["../src/channel-component.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { Section } from "@copilotkit/channels-ui";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { expect, test, vi } from "vitest";
|
|
4
|
+
import { createChannel } from "./create-channel.js";
|
|
5
|
+
import { defineChannelComponent } from "./channel-component.js";
|
|
6
|
+
import { FakeAdapter } from "./testing/fake-adapter.js";
|
|
7
|
+
import { FakeAgent } from "./testing/fake-agent.js";
|
|
8
|
+
function setup(args) {
|
|
9
|
+
const adapter = new FakeAdapter({ platform: "intelligence" });
|
|
10
|
+
let iterations = 0;
|
|
11
|
+
const agent = new FakeAgent([
|
|
12
|
+
(subscriber) => {
|
|
13
|
+
iterations += 1;
|
|
14
|
+
subscriber.onToolCallEndEvent?.({
|
|
15
|
+
event: { toolCallId: "component-call-1" },
|
|
16
|
+
toolCallName: args.component.name,
|
|
17
|
+
toolCallArgs: args.toolArgs ?? {},
|
|
18
|
+
});
|
|
19
|
+
},
|
|
20
|
+
() => {
|
|
21
|
+
iterations += 1;
|
|
22
|
+
},
|
|
23
|
+
]);
|
|
24
|
+
const channel = createChannel({
|
|
25
|
+
identifyUser: "platform",
|
|
26
|
+
adapters: [adapter],
|
|
27
|
+
agent: () => agent,
|
|
28
|
+
components: [args.component],
|
|
29
|
+
});
|
|
30
|
+
channel.onMessage(async ({ thread }) => {
|
|
31
|
+
await thread.runAgent();
|
|
32
|
+
});
|
|
33
|
+
return {
|
|
34
|
+
adapter,
|
|
35
|
+
channel,
|
|
36
|
+
iterations: () => iterations,
|
|
37
|
+
async run() {
|
|
38
|
+
await channel.ɵruntime.start();
|
|
39
|
+
await adapter.getSink().onTurn({
|
|
40
|
+
conversationKey: "conversation-1",
|
|
41
|
+
replyTarget: {},
|
|
42
|
+
userText: "show the order",
|
|
43
|
+
platform: args.platform ?? "slack",
|
|
44
|
+
});
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
test("a channel component validates args, renders asynchronously, and posts through the thread", async () => {
|
|
49
|
+
const render = vi.fn(async (props, context) => {
|
|
50
|
+
await Promise.resolve();
|
|
51
|
+
return Section({
|
|
52
|
+
children: `${context.platform}:${props.orderId}:${context.signal.aborted}`,
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
const component = defineChannelComponent({
|
|
56
|
+
name: "show_order",
|
|
57
|
+
description: "Show one order",
|
|
58
|
+
parameters: z.object({ orderId: z.string() }),
|
|
59
|
+
render,
|
|
60
|
+
});
|
|
61
|
+
const harness = setup({
|
|
62
|
+
component,
|
|
63
|
+
platform: "teams",
|
|
64
|
+
toolArgs: { orderId: "order-42" },
|
|
65
|
+
});
|
|
66
|
+
await harness.run();
|
|
67
|
+
expect(render).toHaveBeenCalledWith({ orderId: "order-42" }, { platform: "teams", signal: expect.any(AbortSignal) });
|
|
68
|
+
expect(harness.adapter.posted).toEqual([
|
|
69
|
+
[
|
|
70
|
+
{
|
|
71
|
+
type: "section",
|
|
72
|
+
props: {
|
|
73
|
+
children: [
|
|
74
|
+
{
|
|
75
|
+
type: "text",
|
|
76
|
+
props: { value: "teams:order-42:false" },
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
]);
|
|
83
|
+
expect(harness.iterations()).toBe(2);
|
|
84
|
+
});
|
|
85
|
+
test("invalid component arguments do not render or post", async () => {
|
|
86
|
+
const render = vi.fn(() => Section({ children: "should not render" }));
|
|
87
|
+
const component = defineChannelComponent({
|
|
88
|
+
name: "show_order",
|
|
89
|
+
description: "Show one order",
|
|
90
|
+
parameters: z.object({ orderId: z.string() }),
|
|
91
|
+
render,
|
|
92
|
+
});
|
|
93
|
+
const harness = setup({ component, toolArgs: { orderId: 42 } });
|
|
94
|
+
await harness.run();
|
|
95
|
+
expect(render).not.toHaveBeenCalled();
|
|
96
|
+
expect(harness.adapter.posted).toEqual([]);
|
|
97
|
+
expect(harness.iterations()).toBe(2);
|
|
98
|
+
});
|
|
99
|
+
test("channel start rejects component and tool name collisions", async () => {
|
|
100
|
+
const component = defineChannelComponent({
|
|
101
|
+
name: "show_order",
|
|
102
|
+
description: "Show one order",
|
|
103
|
+
parameters: z.object({}),
|
|
104
|
+
render: () => Section({ children: "order" }),
|
|
105
|
+
});
|
|
106
|
+
const channel = createChannel({
|
|
107
|
+
identifyUser: "platform",
|
|
108
|
+
adapters: [new FakeAdapter()],
|
|
109
|
+
components: [component],
|
|
110
|
+
tools: [
|
|
111
|
+
{
|
|
112
|
+
name: "show_order",
|
|
113
|
+
description: "Conflicting tool",
|
|
114
|
+
parameters: z.object({}),
|
|
115
|
+
handler: () => "conflict",
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
});
|
|
119
|
+
await expect(channel.ɵruntime.start()).rejects.toThrow('duplicate channel tool or component name "show_order"');
|
|
120
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"component-reaction-recovery.test.d.ts","sourceRoot":"","sources":["../src/component-reaction-recovery.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { expect, test } from "vitest";
|
|
2
|
+
import { Message } from "@copilotkit/channels-ui";
|
|
3
|
+
import { ActionRegistry } from "./action-registry.js";
|
|
4
|
+
import { InMemoryActionStore } from "./action-store.js";
|
|
5
|
+
const reactionHandler = () => undefined;
|
|
6
|
+
test("registered component reactions recover with their source platform", async () => {
|
|
7
|
+
const store = new InMemoryActionStore();
|
|
8
|
+
const renderPlatforms = [];
|
|
9
|
+
const register = (registry) => {
|
|
10
|
+
registry.registerComponent("deployment", (_props, context) => {
|
|
11
|
+
renderPlatforms.push(context.platform);
|
|
12
|
+
return Message({ children: "done", onReaction: reactionHandler });
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
const first = new ActionRegistry({ store });
|
|
16
|
+
register(first);
|
|
17
|
+
const bound = await first.bindRegisteredRenderable("deployment", {}, "conversation-1", undefined, { platform: "teams", signal: new AbortController().signal });
|
|
18
|
+
expect(bound.onReaction).toBe(reactionHandler);
|
|
19
|
+
await first.persistMessageReaction("message-1", {
|
|
20
|
+
component: "deployment",
|
|
21
|
+
props: {},
|
|
22
|
+
conversationKey: "conversation-1",
|
|
23
|
+
platform: "teams",
|
|
24
|
+
});
|
|
25
|
+
const restarted = new ActionRegistry({ store });
|
|
26
|
+
register(restarted);
|
|
27
|
+
expect(await restarted.resolveMessageReaction("message-1")).toBe(reactionHandler);
|
|
28
|
+
expect(renderPlatforms).toEqual(["teams", "teams"]);
|
|
29
|
+
});
|
package/dist/create-channel.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { Transcripts } from "./transcripts.js";
|
|
|
10
10
|
import type { TranscriptsConfig } from "./transcripts.js";
|
|
11
11
|
import type { ChannelIdentifyUser } from "./identity.js";
|
|
12
12
|
import type { StandardSchemaV1, InferSchemaOutput } from "./standard-schema.js";
|
|
13
|
+
import type { ChannelComponentDefinition } from "./channel-component.js";
|
|
13
14
|
export type LockConflictDecision = "drop" | "force";
|
|
14
15
|
/**
|
|
15
16
|
* How overlapping turns on the same `conversationKey` are handled.
|
|
@@ -63,6 +64,7 @@ export declare function resolveChannelConcurrency(cfg: {
|
|
|
63
64
|
* with the props persisted in the store, so the specific shape isn't needed here.
|
|
64
65
|
*/
|
|
65
66
|
export type ChannelComponent = (props: never) => ReturnType<ComponentFn>;
|
|
67
|
+
export type ChannelComponentRegistration = ChannelComponent | ChannelComponentDefinition;
|
|
66
68
|
export type ChannelHandler<TState = unknown> = (ctx: {
|
|
67
69
|
thread: StatefulThread<TState>;
|
|
68
70
|
message: ChannelMessage;
|
|
@@ -238,12 +240,11 @@ export interface CreateChannelOptions<TStateSchema extends StandardSchemaV1 | un
|
|
|
238
240
|
tools?: ChannelTool[];
|
|
239
241
|
context?: ContextEntry[];
|
|
240
242
|
/**
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
* restart degrades to "action expired".
|
|
243
|
+
* Agent-rendered component definitions or legacy named JSX components.
|
|
244
|
+
* Definitions become tools. Both forms let the channel recover keyed handlers
|
|
245
|
+
* after a restart when the configured store is durable.
|
|
245
246
|
*/
|
|
246
|
-
components?:
|
|
247
|
+
components?: ChannelComponentRegistration[] | Record<string, ChannelComponent>;
|
|
247
248
|
/** Slash commands. Forwarded to adapters that support them; ignored elsewhere. */
|
|
248
249
|
commands?: ChannelCommand[];
|
|
249
250
|
/** Persistence, per-thread state schema, transcripts, and lock/dedup tuning. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-channel.d.ts","sourceRoot":"","sources":["../src/create-channel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EAUf,iBAAiB,EAClB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEzD,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE5D,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,OAAO,KAAK,EACV,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,aAAa,EACb,eAAe,EACf,UAAU,EAGV,WAAW,EACX,UAAU,
|
|
1
|
+
{"version":3,"file":"create-channel.d.ts","sourceRoot":"","sources":["../src/create-channel.ts"],"names":[],"mappings":"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"}
|
package/dist/create-channel.js
CHANGED
|
@@ -8,6 +8,7 @@ import { sanitizeAgentEventStream } from "./sanitize-agent-events.js";
|
|
|
8
8
|
import { normalizeEmoji, toCanonicalEmoji, renderToIR, } from "@copilotkit/channels-ui";
|
|
9
9
|
import { Transcripts } from "./transcripts.js";
|
|
10
10
|
import { resolveChannelUser } from "./identity.js";
|
|
11
|
+
import { isChannelComponentDefinition } from "./channel-component.js";
|
|
11
12
|
import { ChannelTelemetry } from "./telemetry/channel-telemetry.js";
|
|
12
13
|
import { errorClass, normalizePlatform } from "./telemetry/sanitize-error.js";
|
|
13
14
|
import { createRequire } from "node:module";
|
|
@@ -279,8 +280,59 @@ export function createChannel(opts) {
|
|
|
279
280
|
return run;
|
|
280
281
|
}
|
|
281
282
|
const toolMap = new Map();
|
|
282
|
-
|
|
283
|
-
|
|
283
|
+
let componentConfigurationError;
|
|
284
|
+
for (const tool of opts.tools ?? []) {
|
|
285
|
+
if (toolMap.has(tool.name)) {
|
|
286
|
+
componentConfigurationError = new Error(`duplicate channel tool or component name "${tool.name}"`);
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
toolMap.set(tool.name, tool);
|
|
290
|
+
}
|
|
291
|
+
const componentEntries = [];
|
|
292
|
+
const configuredComponents = Array.isArray(opts.components)
|
|
293
|
+
? opts.components.map((component) => ({
|
|
294
|
+
name: component.name,
|
|
295
|
+
component,
|
|
296
|
+
}))
|
|
297
|
+
: Object.entries(opts.components ?? {}).map(([name, component]) => ({
|
|
298
|
+
name,
|
|
299
|
+
component,
|
|
300
|
+
}));
|
|
301
|
+
const componentNames = new Set();
|
|
302
|
+
for (const { name, component } of configuredComponents) {
|
|
303
|
+
if (!name)
|
|
304
|
+
continue;
|
|
305
|
+
if (componentNames.has(name) || toolMap.has(name)) {
|
|
306
|
+
componentConfigurationError = new Error(`duplicate channel tool or component name "${name}"`);
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
componentNames.add(name);
|
|
310
|
+
if (isChannelComponentDefinition(component)) {
|
|
311
|
+
componentEntries.push({
|
|
312
|
+
name,
|
|
313
|
+
requireKeys: true,
|
|
314
|
+
render: (props, renderContext) => component.render(props, renderContext),
|
|
315
|
+
});
|
|
316
|
+
toolMap.set(name, {
|
|
317
|
+
name,
|
|
318
|
+
description: component.description,
|
|
319
|
+
parameters: component.parameters,
|
|
320
|
+
async handler(args, toolContext) {
|
|
321
|
+
await toolContext.thread.postRegisteredComponent(name, args, {
|
|
322
|
+
platform: toolContext.platform,
|
|
323
|
+
signal: toolContext.signal ?? new AbortController().signal,
|
|
324
|
+
});
|
|
325
|
+
return `Rendered component "${name}".`;
|
|
326
|
+
},
|
|
327
|
+
});
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
componentEntries.push({
|
|
331
|
+
name,
|
|
332
|
+
requireKeys: false,
|
|
333
|
+
render: (props) => component(props),
|
|
334
|
+
});
|
|
335
|
+
}
|
|
284
336
|
const context = opts.context ?? [];
|
|
285
337
|
const mentionHandlers = [];
|
|
286
338
|
const messageHandlers = [];
|
|
@@ -704,6 +756,8 @@ export function createChannel(opts) {
|
|
|
704
756
|
// and real adapters would connect/port-bind twice.
|
|
705
757
|
if (started)
|
|
706
758
|
return;
|
|
759
|
+
if (componentConfigurationError)
|
|
760
|
+
throw componentConfigurationError;
|
|
707
761
|
if (adapters.some((adapter) => adapter.capabilities.supportsMessageEvents) &&
|
|
708
762
|
mentionHandlers.length === 0 &&
|
|
709
763
|
messageHandlers.length === 0) {
|
|
@@ -729,20 +783,22 @@ export function createChannel(opts) {
|
|
|
729
783
|
retentionMs: cfg.actionRetentionMs ?? 7 * 24 * 60 * 60 * 1000,
|
|
730
784
|
});
|
|
731
785
|
registry = registryInstance;
|
|
732
|
-
for (const
|
|
733
|
-
if (!
|
|
786
|
+
for (const component of componentEntries) {
|
|
787
|
+
if (!component.name) {
|
|
734
788
|
console.warn("[channel] createChannel: skipping anonymous component — give it a name to enable durable actions after restart.");
|
|
735
789
|
continue;
|
|
736
790
|
}
|
|
737
|
-
registryInstance.registerComponent(
|
|
791
|
+
registryInstance.registerComponent(component.name, component.render, {
|
|
792
|
+
requireKeys: component.requireKeys,
|
|
793
|
+
});
|
|
738
794
|
}
|
|
739
795
|
toolDescriptors = toAgentToolDescriptors([...toolMap.values()]);
|
|
740
796
|
tel.capture("oss.channel.configured", {
|
|
741
797
|
platforms: adapters.map((a) => normalizePlatform(a.platform)),
|
|
742
798
|
adapterCount: adapters.length,
|
|
743
799
|
store: storeKind(backend),
|
|
744
|
-
hasComponents:
|
|
745
|
-
componentsCount:
|
|
800
|
+
hasComponents: componentEntries.length > 0,
|
|
801
|
+
componentsCount: componentEntries.length,
|
|
746
802
|
toolsCount: toolMap.size,
|
|
747
803
|
commandsCount: commandHandlers.size,
|
|
748
804
|
contextCount: context.length,
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,9 @@ export { createChannel } from "./create-channel.js";
|
|
|
2
2
|
export { isolateAgentInstance, resolveChannelConcurrency, } from "./create-channel.js";
|
|
3
3
|
export { sanitizeAgentEventStream } from "./sanitize-agent-events.js";
|
|
4
4
|
export { HttpAgent } from "@ag-ui/client";
|
|
5
|
-
export type { Channel, CreateChannelOptions, ReplyContinuationOptions, ChannelHandler, WelcomeHandler, ThreadStartHandler, ReactionEvent, ReactionHandler, ModalSubmitEvent, ModalSubmitHandler, ModalCloseEvent, ModalCloseHandler, StoreConfig, LockConflictDecision, ChannelConcurrency, StatefulThread, ChannelComponent, } from "./create-channel.js";
|
|
5
|
+
export type { Channel, CreateChannelOptions, ReplyContinuationOptions, ChannelHandler, WelcomeHandler, ThreadStartHandler, ReactionEvent, ReactionHandler, ModalSubmitEvent, ModalSubmitHandler, ModalCloseEvent, ModalCloseHandler, StoreConfig, LockConflictDecision, ChannelConcurrency, StatefulThread, ChannelComponent, ChannelComponentRegistration, } from "./create-channel.js";
|
|
6
|
+
export { defineChannelComponent } from "./channel-component.js";
|
|
7
|
+
export type { ChannelComponentDefinition, ChannelComponentPlatform, ChannelComponentRenderContext, } from "./channel-component.js";
|
|
6
8
|
export { ChannelIdentityResolutionError, ChannelIdentityResultError, resolveChannelUser, } from "./identity.js";
|
|
7
9
|
export type { ChannelConversation, ChannelEvent, ChannelIdentifyUser, ChannelIdentityContext, ChannelInstallation, ChannelTenant, IngressIdentityContext, } from "./identity.js";
|
|
8
10
|
export { Thread } from "./thread.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EACL,oBAAoB,EACpB,yBAAyB,GAC1B,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAGtE,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,YAAY,EACV,OAAO,EACP,oBAAoB,EACpB,wBAAwB,EACxB,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,WAAW,EACX,oBAAoB,EACpB,kBAAkB,EAClB,cAAc,EACd,gBAAgB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@ export { sanitizeAgentEventStream } from "./sanitize-agent-events.js";
|
|
|
8
8
|
// The usual `agent` for a Channel is an AG-UI agent over HTTP. Re-exported so
|
|
9
9
|
// wiring one up needs no second import (any `AbstractAgent` still works).
|
|
10
10
|
export { HttpAgent } from "@ag-ui/client";
|
|
11
|
+
export { defineChannelComponent } from "./channel-component.js";
|
|
11
12
|
export { ChannelIdentityResolutionError, ChannelIdentityResultError, resolveChannelUser, } from "./identity.js";
|
|
12
13
|
// Thread
|
|
13
14
|
export { Thread } from "./thread.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyed-action-recovery.test.d.ts","sourceRoot":"","sources":["../src/keyed-action-recovery.test.tsx"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { jsx as _jsx } from "@copilotkit/channels-core/jsx-runtime";
|
|
2
|
+
import { createNativeNode } from "@copilotkit/channels-ui";
|
|
3
|
+
import { expect, test, vi } from "vitest";
|
|
4
|
+
import { ActionRegistry } from "./action-registry.js";
|
|
5
|
+
import { InMemoryActionStore } from "./action-store.js";
|
|
6
|
+
function SlackButton(props) {
|
|
7
|
+
return createNativeNode("slack", "element", "button", props);
|
|
8
|
+
}
|
|
9
|
+
async function unusedChoice() {
|
|
10
|
+
throw new Error("awaitChoice is not used by this fixture");
|
|
11
|
+
}
|
|
12
|
+
async function emptyState() {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
function interactionContext(id) {
|
|
16
|
+
const thread = {
|
|
17
|
+
platform: "slack",
|
|
18
|
+
post: async () => ({ id: "message-1" }),
|
|
19
|
+
update: async (ref) => ref,
|
|
20
|
+
delete: async () => undefined,
|
|
21
|
+
awaitChoice: unusedChoice,
|
|
22
|
+
runAgent: async () => undefined,
|
|
23
|
+
resume: async () => undefined,
|
|
24
|
+
stream: async () => ({ id: "message-1" }),
|
|
25
|
+
postFile: async () => ({ ok: true, fileId: "file-1" }),
|
|
26
|
+
getMessages: async () => [],
|
|
27
|
+
lookupUser: async () => undefined,
|
|
28
|
+
setSuggestedPrompts: async () => ({ ok: true }),
|
|
29
|
+
setTitle: async () => ({ ok: true }),
|
|
30
|
+
react: async () => ({ ok: true }),
|
|
31
|
+
unreact: async () => ({ ok: true }),
|
|
32
|
+
postEphemeral: async () => null,
|
|
33
|
+
subscribe: async () => undefined,
|
|
34
|
+
unsubscribe: async () => undefined,
|
|
35
|
+
isSubscribed: async () => false,
|
|
36
|
+
setState: async () => undefined,
|
|
37
|
+
state: emptyState,
|
|
38
|
+
};
|
|
39
|
+
const actor = { id: "user-1", kind: "human" };
|
|
40
|
+
return {
|
|
41
|
+
thread,
|
|
42
|
+
message: {
|
|
43
|
+
text: "",
|
|
44
|
+
user: null,
|
|
45
|
+
actor,
|
|
46
|
+
ref: { id: "message-1" },
|
|
47
|
+
platform: "slack",
|
|
48
|
+
},
|
|
49
|
+
action: { id, value: { decision: "provider-mutated" } },
|
|
50
|
+
values: {},
|
|
51
|
+
user: null,
|
|
52
|
+
actor,
|
|
53
|
+
platform: "slack",
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function actionId(tree, key) {
|
|
57
|
+
const elements = tree[0]?.props.elements;
|
|
58
|
+
if (!Array.isArray(elements)) {
|
|
59
|
+
throw new Error("expected native action elements");
|
|
60
|
+
}
|
|
61
|
+
const node = elements.find((candidate) => typeof candidate === "object" &&
|
|
62
|
+
candidate !== null &&
|
|
63
|
+
"key" in candidate &&
|
|
64
|
+
candidate.key === key);
|
|
65
|
+
if (!node || typeof node !== "object" || !("props" in node)) {
|
|
66
|
+
throw new Error(`expected action with key ${key}`);
|
|
67
|
+
}
|
|
68
|
+
const handler = node.props.onClick;
|
|
69
|
+
if (typeof handler !== "object" ||
|
|
70
|
+
handler === null ||
|
|
71
|
+
!("id" in handler) ||
|
|
72
|
+
typeof handler.id !== "string") {
|
|
73
|
+
throw new Error(`expected bound action with key ${key}`);
|
|
74
|
+
}
|
|
75
|
+
return handler.id;
|
|
76
|
+
}
|
|
77
|
+
test("cold recovery finds the same keyed handler after async sibling reorder", async () => {
|
|
78
|
+
const store = new InMemoryActionStore();
|
|
79
|
+
const handled = vi.fn();
|
|
80
|
+
let reordered = false;
|
|
81
|
+
const render = async () => {
|
|
82
|
+
await Promise.resolve();
|
|
83
|
+
const approve = (_jsx(SlackButton, { text: "Approve", value: { decision: reordered ? "changed" : "approve" }, onClick: (context) => handled("approve", context.action.value) }, "approve-order"));
|
|
84
|
+
const reject = (_jsx(SlackButton, { text: "Reject", value: { decision: "reject" }, onClick: (context) => handled("reject", context.action.value) }, "reject-order"));
|
|
85
|
+
const unrelated = (_jsx(SlackButton, { text: "Inspect", value: { decision: "inspect" }, onClick: (context) => handled("inspect", context.action.value) }, "inspect-order"));
|
|
86
|
+
return createNativeNode("slack", "block", "actions", {
|
|
87
|
+
elements: reordered ? [unrelated, reject, approve] : [approve, reject],
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
const first = new ActionRegistry({ store });
|
|
91
|
+
first.registerComponent("order_actions", render, { requireKeys: true });
|
|
92
|
+
const tree = await first.bindTree("order_actions", {}, "conversation-1", undefined, { platform: "slack", signal: new AbortController().signal });
|
|
93
|
+
const id = actionId(tree, "approve-order");
|
|
94
|
+
reordered = true;
|
|
95
|
+
const restarted = new ActionRegistry({ store });
|
|
96
|
+
restarted.registerComponent("order_actions", render, {
|
|
97
|
+
requireKeys: true,
|
|
98
|
+
});
|
|
99
|
+
const value = await restarted.dispatch(id, interactionContext(id));
|
|
100
|
+
expect(value).toEqual({ decision: "approve" });
|
|
101
|
+
expect(handled).toHaveBeenCalledOnce();
|
|
102
|
+
expect(handled).toHaveBeenCalledWith("approve", { decision: "approve" });
|
|
103
|
+
});
|
|
104
|
+
test("component tools reject a handler without a stable JSX key", async () => {
|
|
105
|
+
const registry = new ActionRegistry({ store: new InMemoryActionStore() });
|
|
106
|
+
registry.registerComponent("missing_key", () => createNativeNode("slack", "element", "button", {
|
|
107
|
+
text: "Approve",
|
|
108
|
+
onClick: () => undefined,
|
|
109
|
+
}), { requireKeys: true });
|
|
110
|
+
await expect(registry.bindTree("missing_key", {}, "conversation-1")).rejects.toThrow("missing_key[0].onClick requires a non-empty JSX key");
|
|
111
|
+
});
|
|
112
|
+
test("component tools reject duplicate interactive keys", async () => {
|
|
113
|
+
const registry = new ActionRegistry({ store: new InMemoryActionStore() });
|
|
114
|
+
registry.registerComponent("duplicate_key", () => createNativeNode("slack", "block", "actions", {
|
|
115
|
+
elements: [
|
|
116
|
+
_jsx(SlackButton, { text: "Approve", value: { decision: "approve" }, onClick: () => undefined }, "decision"),
|
|
117
|
+
_jsx(SlackButton, { text: "Reject", value: { decision: "reject" }, onClick: () => undefined }, "decision"),
|
|
118
|
+
],
|
|
119
|
+
}), { requireKeys: true });
|
|
120
|
+
await expect(registry.bindTree("duplicate_key", {}, "conversation-1")).rejects.toThrow('duplicate interactive JSX key "decision"');
|
|
121
|
+
});
|
package/dist/thread.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type { AbstractAgent } from "@ag-ui/client";
|
|
|
7
7
|
import type { StateStore } from "./state/state-store.js";
|
|
8
8
|
import type { StandardSchemaV1 } from "./standard-schema.js";
|
|
9
9
|
import type { MemoryGrant } from "./memory.js";
|
|
10
|
+
import type { ChannelComponentRenderContext } from "./channel-component.js";
|
|
10
11
|
/** A Channel run requested Memory without an attached Intelligence backend. */
|
|
11
12
|
export declare class ChannelMemoryUnavailableError extends Error {
|
|
12
13
|
readonly code = "channel_memory_unavailable";
|
|
@@ -98,6 +99,8 @@ export declare class Thread implements ThreadInterface {
|
|
|
98
99
|
*/
|
|
99
100
|
private bindReaction;
|
|
100
101
|
post(ui: Renderable): Promise<MessageRef>;
|
|
102
|
+
/** @internal Post a registered component through the normal bind and adapter path. */
|
|
103
|
+
postRegisteredComponent(componentName: string, props: Record<string, unknown>, renderContext: ChannelComponentRenderContext): Promise<MessageRef>;
|
|
101
104
|
update(ref: MessageRef, ui: Renderable): Promise<MessageRef>;
|
|
102
105
|
delete(ref: MessageRef): Promise<void>;
|
|
103
106
|
stream(src: string | AsyncIterable<string>): Promise<MessageRef>;
|
package/dist/thread.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thread.d.ts","sourceRoot":"","sources":["../src/thread.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAK3D,OAAO,KAAK,EACV,gBAAgB,EAChB,UAAU,EACV,UAAU,EACV,aAAa,EACb,eAAe,EACf,aAAa,EACb,eAAe,EACf,MAAM,IAAI,eAAe,EACzB,UAAU,EACV,eAAe,EAChB,MAAM,yBAAyB,CAAC;AAIjC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,KAAK,EACV,WAAW,EAEX,YAAY,EACZ,mBAAmB,EACpB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,OAAO,KAAK,EAAE,WAAW,EAAyB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"thread.d.ts","sourceRoot":"","sources":["../src/thread.ts"],"names":[],"mappings":"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"}
|
package/dist/thread.js
CHANGED
|
@@ -64,7 +64,10 @@ export class Thread {
|
|
|
64
64
|
this.store = deps.state;
|
|
65
65
|
}
|
|
66
66
|
async bindForPost(ui) {
|
|
67
|
-
return this.deps.registry.bindRenderable(ui, this.deps.conversationKey, this.activeContinuation
|
|
67
|
+
return this.deps.registry.bindRenderable(ui, this.deps.conversationKey, this.activeContinuation, {
|
|
68
|
+
platform: this.platform,
|
|
69
|
+
signal: new AbortController().signal,
|
|
70
|
+
});
|
|
68
71
|
}
|
|
69
72
|
trackOperation(operation) {
|
|
70
73
|
if (!this.deps.adapter.trackThreadOperation) {
|
|
@@ -107,6 +110,15 @@ export class Thread {
|
|
|
107
110
|
return ref;
|
|
108
111
|
});
|
|
109
112
|
}
|
|
113
|
+
/** @internal Post a registered component through the normal bind and adapter path. */
|
|
114
|
+
postRegisteredComponent(componentName, props, renderContext) {
|
|
115
|
+
return this.trackOperation(async () => {
|
|
116
|
+
const bound = await this.deps.registry.bindRegisteredRenderable(componentName, props, this.deps.conversationKey, this.activeContinuation, renderContext);
|
|
117
|
+
const ref = await this.deps.adapter.post(this.deps.replyTarget, bound.root);
|
|
118
|
+
await this.bindReaction(ref.id, bound);
|
|
119
|
+
return ref;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
110
122
|
update(ref, ui) {
|
|
111
123
|
return this.trackOperation(async () => {
|
|
112
124
|
const bound = await this.bindForPost(ui);
|
|
@@ -478,6 +490,7 @@ export class Thread {
|
|
|
478
490
|
message: this.deps.message,
|
|
479
491
|
user: this.deps.user,
|
|
480
492
|
actor: this.deps.actor,
|
|
493
|
+
signal: session.agent.abortController?.signal ?? new AbortController().signal,
|
|
481
494
|
platform: this.platform,
|
|
482
495
|
}),
|
|
483
496
|
handleInterrupt: async (interrupt) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@copilotkit/channels-core",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2-canary.1785782905",
|
|
4
4
|
"description": "Platform-agnostic JSX channel engine for CopilotKit (createChannel, Thread, PlatformAdapter, ActionStore).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -50,10 +50,10 @@
|
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@ag-ui/client": "0.0.57",
|
|
52
52
|
"@ag-ui/core": "0.0.57",
|
|
53
|
-
"
|
|
54
|
-
"@copilotkit/core": "
|
|
55
|
-
"@copilotkit/shared": "
|
|
56
|
-
"
|
|
53
|
+
"@copilotkit/channels-ui": "0.6.2-canary.1785782905",
|
|
54
|
+
"@copilotkit/core": "1.65.1-canary.1785782905",
|
|
55
|
+
"@copilotkit/shared": "1.65.1-canary.1785782905",
|
|
56
|
+
"zod-to-json-schema": "^3.24.1"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@types/node": "^22.10.0",
|