@antglobal/copilot-cards-core 0.0.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -3
- package/dist/index.cjs.js +2100 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +853 -0
- package/dist/index.esm.js +2072 -0
- package/dist/index.esm.js.map +1 -0
- package/package.json +34 -7
- package/LEGAL.md +0 -7
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,853 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Action Runner — executes ActionStep chains defined in card schemas.
|
|
3
|
+
*
|
|
4
|
+
* Supported action types:
|
|
5
|
+
* - **request**: HTTP request (fetch / API call)
|
|
6
|
+
* - **toast**: Display a toast notification
|
|
7
|
+
* - **url**: Navigate to a URL
|
|
8
|
+
* - **setVariable**: Update a variable in the card context
|
|
9
|
+
* - **emit**: Emit a custom event to the host environment
|
|
10
|
+
* - **copy**: Copy text to clipboard
|
|
11
|
+
*
|
|
12
|
+
* Each step can have `onSuccess` / `onFail` branches for chaining.
|
|
13
|
+
*/
|
|
14
|
+
interface ActionStep {
|
|
15
|
+
type: 'request' | 'emit' | 'url' | 'setVariable' | 'toast' | 'copy' | (string & {});
|
|
16
|
+
params: Record<string, any>;
|
|
17
|
+
/** Action chain to run on success (optional). */
|
|
18
|
+
onSuccess?: ActionStep[];
|
|
19
|
+
/** Action chain to run on failure (optional). */
|
|
20
|
+
onFail?: ActionStep[];
|
|
21
|
+
}
|
|
22
|
+
type ActionStepHandler = (step: ActionStep, context: ActionRunnerContext) => Promise<void> | void;
|
|
23
|
+
interface ActionRunnerContext {
|
|
24
|
+
/** Platform-level HTTP fetcher (web fetch / mini-program request) */
|
|
25
|
+
fetch?: (url: string, init?: RequestInit) => Promise<Response>;
|
|
26
|
+
/** Toast display callback */
|
|
27
|
+
showToast?: (message: string, level?: string, duration?: number) => void;
|
|
28
|
+
/** URL navigation callback */
|
|
29
|
+
navigate?: (url: string, target?: string) => void;
|
|
30
|
+
/** Variable setter — updates the live variables store */
|
|
31
|
+
setVariable?: (key: string, value: any) => void;
|
|
32
|
+
/** Emit event to the host application */
|
|
33
|
+
emit?: (eventName: string, payload?: any) => void;
|
|
34
|
+
/** Copy text to clipboard */
|
|
35
|
+
copyText?: (text: string) => void;
|
|
36
|
+
/** Variables context for resolving expressions in action params */
|
|
37
|
+
variables?: Record<string, any>;
|
|
38
|
+
/** Current bot ID — used to look up bot-scoped handlers */
|
|
39
|
+
botId?: string;
|
|
40
|
+
/** Abort signal — used to cancel in-flight requests and polling on dispose */
|
|
41
|
+
abortSignal?: AbortSignal;
|
|
42
|
+
/**
|
|
43
|
+
* Per-instance in-flight request map for request deduplication. Each card
|
|
44
|
+
* instance should supply its own map so two cards using the same default
|
|
45
|
+
* `responseKey` ("_response") don't dedupe against each other. When omitted,
|
|
46
|
+
* a shared module-level map is used (legacy fallback).
|
|
47
|
+
*/
|
|
48
|
+
inflightRequests?: Map<string, Promise<void>>;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Register a custom action step handler for a specific bot.
|
|
52
|
+
*/
|
|
53
|
+
declare function registerActionHandler(botId: string, type: string, handler: ActionStepHandler): void;
|
|
54
|
+
/**
|
|
55
|
+
* Execute a single ActionStep.
|
|
56
|
+
*/
|
|
57
|
+
declare function runActionStep(step: ActionStep, context?: ActionRunnerContext): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Execute a chain of ActionSteps sequentially.
|
|
60
|
+
*/
|
|
61
|
+
declare function runActionSteps(steps: ActionStep[], context?: ActionRunnerContext): Promise<void>;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Legacy Schema Compatibility — converts old-format card JSON into
|
|
65
|
+
* the current CardSchema format so it can be rendered by the SDK.
|
|
66
|
+
*
|
|
67
|
+
* Old format:
|
|
68
|
+
* ```json
|
|
69
|
+
* {
|
|
70
|
+
* "cardType": "common",
|
|
71
|
+
* "cardContents": [{ "type": "text", "content": { "text": "hello" } }],
|
|
72
|
+
* "text": "card text",
|
|
73
|
+
* "extInfo": { "language": "zh-CN" }
|
|
74
|
+
* }
|
|
75
|
+
* ```
|
|
76
|
+
*
|
|
77
|
+
* Usage:
|
|
78
|
+
* ```ts
|
|
79
|
+
* import { convertLegacySchema } from '@antglobal/copilot-cards-core';
|
|
80
|
+
* const schema = convertLegacySchema(oldJson);
|
|
81
|
+
* renderCard(container, schema);
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
|
|
85
|
+
type LegacyTrackingType = 'click' | 'expo';
|
|
86
|
+
interface LegacyTracking {
|
|
87
|
+
spm: string;
|
|
88
|
+
type: LegacyTrackingType;
|
|
89
|
+
}
|
|
90
|
+
interface LegacyCardSchema {
|
|
91
|
+
cardType: 'common' | 'special' | string;
|
|
92
|
+
cardName?: string;
|
|
93
|
+
cardContents: LegacyCardContentItem[] | LegacyCardContentItem;
|
|
94
|
+
text: string;
|
|
95
|
+
extInfo: {
|
|
96
|
+
language: string;
|
|
97
|
+
[key: string]: any;
|
|
98
|
+
};
|
|
99
|
+
description?: string;
|
|
100
|
+
tracking?: LegacyTracking;
|
|
101
|
+
}
|
|
102
|
+
interface LegacyCardContentItem {
|
|
103
|
+
type: 'timeline' | 'button' | 'image' | 'form' | 'group' | 'text' | 'custom' | string;
|
|
104
|
+
groupInfo?: Record<string, any>;
|
|
105
|
+
content: Record<string, any>;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Detect whether an unknown input is a legacy card schema.
|
|
109
|
+
*
|
|
110
|
+
* Checks for the presence of legacy-specific fields (`cardContents`, `cardType`)
|
|
111
|
+
* and the absence of new-format fields (`rootID`, `elements`).
|
|
112
|
+
*/
|
|
113
|
+
declare function isLegacySchema(input: unknown): input is LegacyCardSchema;
|
|
114
|
+
/** Reset counter (useful for deterministic tests). */
|
|
115
|
+
declare function resetIdCounter(): void;
|
|
116
|
+
/**
|
|
117
|
+
* Convert a legacy CardSchema to the current CardSchema format.
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* ```ts
|
|
121
|
+
* const newSchema = convertLegacySchema(oldJson);
|
|
122
|
+
* renderCard(container, newSchema);
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
declare function convertLegacySchema(legacy: LegacyCardSchema): CardSchema;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Schema Parser — resolves a CardSchema into a renderable tree.
|
|
129
|
+
*
|
|
130
|
+
* A CardSchema contains a flat `elements` map plus a `rootID` entry point,
|
|
131
|
+
* global `variables`, and optional global `actions`.
|
|
132
|
+
*/
|
|
133
|
+
|
|
134
|
+
/** Value that can be a static literal or a `${...}` expression. */
|
|
135
|
+
interface ExpressionValue {
|
|
136
|
+
type: 'static' | 'expression';
|
|
137
|
+
value: string;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Slot layout mode — the key name in `props.slots` determines the layout:
|
|
141
|
+
* - default: no special layout, children appended directly
|
|
142
|
+
* - columns: multi-column layout using groups (each group = one column)
|
|
143
|
+
* - grid: CSS grid with configurable column count and gap
|
|
144
|
+
* - horizontalScroll: horizontal scrollable container with drag and snap
|
|
145
|
+
* - carousel: center-focused carousel with scale/opacity effects
|
|
146
|
+
* - list: vertical list with optional ordered/unordered markers
|
|
147
|
+
* - float: relative container with absolutely positioned overlays
|
|
148
|
+
*/
|
|
149
|
+
type SlotLayout = 'default' | 'columns' | 'grid' | 'horizontalScroll' | 'carousel' | 'list' | 'float' | 'keyValue' | 'table' | 'chart';
|
|
150
|
+
/**
|
|
151
|
+
* Content of a named slot.
|
|
152
|
+
*
|
|
153
|
+
* - `children` — flat list of child element IDs (default, grid, horizontalScroll, carousel, list)
|
|
154
|
+
* - `groups` — grouped child IDs, each sub-array is one region (columns)
|
|
155
|
+
* - `config` — layout-specific parameters (grid columns, list style, float overlays, etc.)
|
|
156
|
+
*
|
|
157
|
+
* Grid `config` fields:
|
|
158
|
+
* - `columns` — column count (default 2)
|
|
159
|
+
* - `gap` — track gap (default '8px')
|
|
160
|
+
* - `rows` — opt-in: explicit row count. When set, empty cells keep their
|
|
161
|
+
* height and children can be pinned to a cell via their own
|
|
162
|
+
* `style.gridColumn` / `style.gridRow`. Omit for auto-flow.
|
|
163
|
+
* - `rowHeight` — min height per row when `rows` is set (default '48px')
|
|
164
|
+
*/
|
|
165
|
+
interface SlotContent {
|
|
166
|
+
children?: string[];
|
|
167
|
+
groups?: string[][];
|
|
168
|
+
config?: Record<string, any>;
|
|
169
|
+
}
|
|
170
|
+
/** A single element inside the card. */
|
|
171
|
+
interface ElementNode {
|
|
172
|
+
id: string;
|
|
173
|
+
type: string;
|
|
174
|
+
props: {
|
|
175
|
+
style?: Record<string, any>;
|
|
176
|
+
content?: ExpressionValue | string;
|
|
177
|
+
slots?: Partial<Record<SlotLayout, SlotContent>>;
|
|
178
|
+
[key: string]: any;
|
|
179
|
+
};
|
|
180
|
+
/** Lifecycle hooks — each is a chain of ActionSteps. */
|
|
181
|
+
lifecycle?: {
|
|
182
|
+
onMount?: ActionStep[];
|
|
183
|
+
onExposed?: ActionStep[];
|
|
184
|
+
onDestroy?: ActionStep[];
|
|
185
|
+
};
|
|
186
|
+
/** Interaction events — each is a chain of ActionSteps or a string reference to schema.actions. */
|
|
187
|
+
events?: {
|
|
188
|
+
onClick?: ActionStep[] | string;
|
|
189
|
+
onChange?: ActionStep[] | string;
|
|
190
|
+
onSubmit?: ActionStep[] | string;
|
|
191
|
+
[key: string]: ActionStep[] | string | undefined;
|
|
192
|
+
};
|
|
193
|
+
/** Directives that control visibility, disabled state, loops, etc. */
|
|
194
|
+
directives?: {
|
|
195
|
+
visible?: string;
|
|
196
|
+
/** Expression that resolves to truthy → element is disabled (greyed out, no events). */
|
|
197
|
+
disabled?: string;
|
|
198
|
+
[key: string]: any;
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
/** Top-level card schema handed to the SDK. */
|
|
202
|
+
interface CardSchema {
|
|
203
|
+
version: string;
|
|
204
|
+
rootID: string;
|
|
205
|
+
elements: Record<string, ElementNode>;
|
|
206
|
+
variables: Record<string, any>;
|
|
207
|
+
/** Global action definitions reusable across elements. */
|
|
208
|
+
actions?: Record<string, ActionStep[]>;
|
|
209
|
+
}
|
|
210
|
+
/** Accepted input — either the current CardSchema or a legacy format. */
|
|
211
|
+
type CardSchemaInput = CardSchema | LegacyCardSchema;
|
|
212
|
+
/**
|
|
213
|
+
* Normalize any supported schema input into the current CardSchema.
|
|
214
|
+
*
|
|
215
|
+
* If the input is already a CardSchema, it is returned as-is.
|
|
216
|
+
* If it is a legacy format, it is automatically converted.
|
|
217
|
+
*/
|
|
218
|
+
declare function normalizeSchema(input: CardSchemaInput): CardSchema;
|
|
219
|
+
/** Nested tree node produced by the parser — ready for a renderer to consume. */
|
|
220
|
+
interface RenderTreeNode {
|
|
221
|
+
id: string;
|
|
222
|
+
type: string;
|
|
223
|
+
props: Record<string, any>;
|
|
224
|
+
children: RenderTreeNode[];
|
|
225
|
+
lifecycle: ElementNode['lifecycle'];
|
|
226
|
+
events: ElementNode['events'];
|
|
227
|
+
directives: ElementNode['directives'];
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Parse a CardSchema into a nested RenderTreeNode starting from `rootID`.
|
|
231
|
+
*
|
|
232
|
+
* Children are resolved through `props.slots` — each slot's `children` array
|
|
233
|
+
* lists element IDs that become nested RenderTreeNodes.
|
|
234
|
+
*/
|
|
235
|
+
declare function parseSchema(input: CardSchemaInput): RenderTreeNode;
|
|
236
|
+
/**
|
|
237
|
+
* Validate a CardSchema and return any error messages.
|
|
238
|
+
*/
|
|
239
|
+
declare function validateSchema(input: CardSchemaInput): string[];
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Expression Engine — resolves template variables in card schemas.
|
|
243
|
+
*
|
|
244
|
+
* Supports expressions like `${user.name}`, `${items.length > 0}`, etc.
|
|
245
|
+
* Variables are resolved against a provided data context.
|
|
246
|
+
*
|
|
247
|
+
* Also handles the ExpressionValue type from CardSchema:
|
|
248
|
+
* { type: 'static', value: 'hello' } → 'hello'
|
|
249
|
+
* { type: 'expression', value: '${user.name}' } → resolved value
|
|
250
|
+
*/
|
|
251
|
+
|
|
252
|
+
interface ExpressionContext {
|
|
253
|
+
/** Variable data source for expression resolution */
|
|
254
|
+
[key: string]: unknown;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Resolve an ExpressionValue against a variables context.
|
|
258
|
+
*
|
|
259
|
+
* @example
|
|
260
|
+
* ```ts
|
|
261
|
+
* resolveExpressionValue({ type: 'expression', value: '${user.name}' }, { user: { name: 'Alice' } })
|
|
262
|
+
* // => 'Alice'
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
declare function resolveExpressionValue(expr: ExpressionValue, context: ExpressionContext): unknown;
|
|
266
|
+
/**
|
|
267
|
+
* Resolve a single expression string against a context.
|
|
268
|
+
*
|
|
269
|
+
* @example
|
|
270
|
+
* ```ts
|
|
271
|
+
* resolveExpression('${user.name}', { user: { name: 'Alice' } })
|
|
272
|
+
* // => 'Alice'
|
|
273
|
+
* ```
|
|
274
|
+
*/
|
|
275
|
+
declare function resolveExpression(expression: string, context: ExpressionContext): unknown;
|
|
276
|
+
/**
|
|
277
|
+
* Resolve all expressions within a template string, returning a string result.
|
|
278
|
+
*
|
|
279
|
+
* @example
|
|
280
|
+
* ```ts
|
|
281
|
+
* interpolate('Hello, ${user.name}! You have ${count} items.', { user: { name: 'Bob' }, count: 5 })
|
|
282
|
+
* // => 'Hello, Bob! You have 5 items.'
|
|
283
|
+
* ```
|
|
284
|
+
*/
|
|
285
|
+
declare function interpolate(template: string, context: ExpressionContext): string;
|
|
286
|
+
/**
|
|
287
|
+
* Safely access a nested value by dot-path (e.g. `user.address.city`).
|
|
288
|
+
*/
|
|
289
|
+
declare function getByPath(obj: unknown, path: string): unknown;
|
|
290
|
+
/**
|
|
291
|
+
* Checks whether a string contains any expression templates.
|
|
292
|
+
*/
|
|
293
|
+
declare function hasExpression(value: string): boolean;
|
|
294
|
+
/**
|
|
295
|
+
* Recursively resolve all expressions in a data structure (object / array / string).
|
|
296
|
+
*/
|
|
297
|
+
declare function resolveDeep(data: unknown, context: ExpressionContext): unknown;
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* ActionRegistry — a simplified, business-friendly API for registering
|
|
301
|
+
* custom action handlers, scoped by botId.
|
|
302
|
+
*
|
|
303
|
+
* Usage:
|
|
304
|
+
* ```ts
|
|
305
|
+
* import { registry } from '@antglobal/copilot-cards-core';
|
|
306
|
+
*
|
|
307
|
+
* // Register a handler for a specific bot
|
|
308
|
+
* registry.register('10001', 'bizRequest', async (params, ctx) => {
|
|
309
|
+
* const res = await fetch(params.api, { method: params.method, body: JSON.stringify(params.data) });
|
|
310
|
+
* if (!res.ok) throw new Error('Business request failed');
|
|
311
|
+
* return res.json();
|
|
312
|
+
* });
|
|
313
|
+
* ```
|
|
314
|
+
*
|
|
315
|
+
* When a handler throws, the action chain is interrupted and `onFail` branch
|
|
316
|
+
* (if defined on the ActionStep) is executed instead.
|
|
317
|
+
*/
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* A simplified action handler that receives `params` directly
|
|
321
|
+
* instead of the full ActionStep object.
|
|
322
|
+
*
|
|
323
|
+
* - Return value is ignored (treated as success).
|
|
324
|
+
* - Throw to signal failure and interrupt the action chain.
|
|
325
|
+
*/
|
|
326
|
+
type SimpleActionHandler = (params: Record<string, any>, context: ActionRunnerContext) => Promise<any> | any;
|
|
327
|
+
declare class ActionRegistry {
|
|
328
|
+
/** botId → Map<type, handler> */
|
|
329
|
+
private _handlers;
|
|
330
|
+
/**
|
|
331
|
+
* Register a custom action handler for a specific bot.
|
|
332
|
+
*
|
|
333
|
+
* The handler receives `step.params` directly. If it throws,
|
|
334
|
+
* the SDK will execute the step's `onFail` branch (if any)
|
|
335
|
+
* and stop the remaining action chain.
|
|
336
|
+
*
|
|
337
|
+
* @param botId The bot this handler belongs to
|
|
338
|
+
* @param type Action type name (e.g. 'bizRequest')
|
|
339
|
+
* @param handler The handler function
|
|
340
|
+
*/
|
|
341
|
+
register(botId: string, type: string, handler: SimpleActionHandler): void;
|
|
342
|
+
/**
|
|
343
|
+
* Remove a previously registered handler for a specific bot.
|
|
344
|
+
*/
|
|
345
|
+
unregister(botId: string, type: string): void;
|
|
346
|
+
/**
|
|
347
|
+
* Check whether a handler is registered for the given bot and type.
|
|
348
|
+
*/
|
|
349
|
+
has(botId: string, type: string): boolean;
|
|
350
|
+
/**
|
|
351
|
+
* Get the list of all registered custom action types for a bot.
|
|
352
|
+
*/
|
|
353
|
+
getRegisteredTypes(botId: string): string[];
|
|
354
|
+
}
|
|
355
|
+
/** Global action registry singleton. */
|
|
356
|
+
declare const registry: ActionRegistry;
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Action Config Provider — abstracts where action chain configurations
|
|
360
|
+
* come from (local mock, remote API, etc.).
|
|
361
|
+
*
|
|
362
|
+
* Action chains are declarative ActionStep[] JSON stored per botId
|
|
363
|
+
* in the server database. The provider fetches them so the SDK can
|
|
364
|
+
* register and execute them on the frontend.
|
|
365
|
+
*
|
|
366
|
+
* Usage:
|
|
367
|
+
* ```ts
|
|
368
|
+
* const provider: ActionConfigProvider = new RemoteActionConfigProvider('https://api.example.com');
|
|
369
|
+
* const configs = await provider.fetchActions('10001');
|
|
370
|
+
* // configs = [{ name: 'confirmOrder', steps: [...] }]
|
|
371
|
+
* ```
|
|
372
|
+
*/
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* A single action chain configuration — corresponds to one row
|
|
376
|
+
* in the server-side database.
|
|
377
|
+
*
|
|
378
|
+
* The `name` is used as a key to reference this action chain
|
|
379
|
+
* from schema events (e.g. `onClick: "confirmOrder"`).
|
|
380
|
+
*/
|
|
381
|
+
interface ActionChainConfig {
|
|
382
|
+
/** Action chain name, e.g. 'confirmOrder', 'submitFeedback' */
|
|
383
|
+
name: string;
|
|
384
|
+
/** Declarative action chain — an ordered list of ActionSteps */
|
|
385
|
+
steps: ActionStep[];
|
|
386
|
+
/** Optional human-readable description */
|
|
387
|
+
description?: string;
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Action configuration provider interface.
|
|
391
|
+
*
|
|
392
|
+
* Implementations fetch action chain configs for a given botId.
|
|
393
|
+
* - `LocalActionConfigProvider` — for demo / dev (in-memory data)
|
|
394
|
+
* - `RemoteActionConfigProvider` — for production (server API)
|
|
395
|
+
*/
|
|
396
|
+
interface ActionConfigProvider {
|
|
397
|
+
/** Fetch all action chain configs for the given botId */
|
|
398
|
+
fetchActions(botId: string): Promise<ActionChainConfig[]>;
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Resolve action references in event bindings.
|
|
402
|
+
*
|
|
403
|
+
* If an event value is a string (e.g. `"confirmOrder"`), look it up
|
|
404
|
+
* in the `actionsMap` and return the corresponding ActionStep[].
|
|
405
|
+
* If it's already an ActionStep[], return as-is.
|
|
406
|
+
*/
|
|
407
|
+
declare function resolveActionRef(eventValue: ActionStep[] | string | undefined, actionsMap: Record<string, ActionStep[]>): ActionStep[] | undefined;
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Lifecycle Manager — manages mount / exposed / destroy hooks for card elements.
|
|
411
|
+
*
|
|
412
|
+
* Each element can declare lifecycle ActionStep chains (onMount, onExposed, onDestroy).
|
|
413
|
+
* The manager tracks mounted/exposed state and runs the corresponding chains via
|
|
414
|
+
* the ActionRunner.
|
|
415
|
+
*/
|
|
416
|
+
|
|
417
|
+
interface ElementLifecycle {
|
|
418
|
+
onMount?: ActionStep[];
|
|
419
|
+
onExposed?: ActionStep[];
|
|
420
|
+
onDestroy?: ActionStep[];
|
|
421
|
+
}
|
|
422
|
+
type CleanupFn = () => void;
|
|
423
|
+
declare class LifecycleManager {
|
|
424
|
+
private lifecycles;
|
|
425
|
+
private mounted;
|
|
426
|
+
private exposed;
|
|
427
|
+
/**
|
|
428
|
+
* Register lifecycle hooks for an element identified by `id`.
|
|
429
|
+
*/
|
|
430
|
+
register(id: string, lifecycle: ElementLifecycle | undefined): CleanupFn;
|
|
431
|
+
/**
|
|
432
|
+
* Remove lifecycle hooks for an element.
|
|
433
|
+
*/
|
|
434
|
+
unregister(id: string): void;
|
|
435
|
+
/**
|
|
436
|
+
* Trigger onMount for an element.
|
|
437
|
+
*/
|
|
438
|
+
mount(id: string, ctx?: ActionRunnerContext): Promise<void>;
|
|
439
|
+
/**
|
|
440
|
+
* Trigger onExposed for an element (entered viewport).
|
|
441
|
+
*/
|
|
442
|
+
markExposed(id: string, ctx?: ActionRunnerContext): Promise<void>;
|
|
443
|
+
/**
|
|
444
|
+
* Trigger onDestroy for an element.
|
|
445
|
+
*/
|
|
446
|
+
destroy(id: string, ctx?: ActionRunnerContext): Promise<void>;
|
|
447
|
+
/**
|
|
448
|
+
* Check if an element is currently mounted.
|
|
449
|
+
*/
|
|
450
|
+
isMounted(id: string): boolean;
|
|
451
|
+
/**
|
|
452
|
+
* Destroy all elements and clear all hooks.
|
|
453
|
+
*/
|
|
454
|
+
dispose(ctx?: ActionRunnerContext): Promise<void>;
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Create a new LifecycleManager instance.
|
|
458
|
+
*/
|
|
459
|
+
declare function createLifecycleManager(): LifecycleManager;
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Streaming Protocol Types — defines the message format for streaming card rendering.
|
|
463
|
+
*
|
|
464
|
+
* Inspired by A2UI (Agent-to-User Interface) protocol, adapted for copilot-cards SDK.
|
|
465
|
+
* Messages are sent as a sequence of JSON commands from Agent to Client,
|
|
466
|
+
* enabling progressive card construction and incremental updates.
|
|
467
|
+
*/
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Union of all streaming command types.
|
|
471
|
+
* Each command represents a discrete UI mutation operation.
|
|
472
|
+
*/
|
|
473
|
+
type StreamingCommand = CreateSurfaceCommand | UpdateComponentsCommand | UpdateDataModelCommand | DeleteSurfaceCommand | AppendContentCommand;
|
|
474
|
+
/**
|
|
475
|
+
* Create a rendering surface (card container).
|
|
476
|
+
*
|
|
477
|
+
* This is typically the first command sent by the Agent.
|
|
478
|
+
* The schema can be provided upfront for a complete initial render,
|
|
479
|
+
* or omitted to create an empty placeholder that's filled incrementally.
|
|
480
|
+
*/
|
|
481
|
+
interface CreateSurfaceCommand {
|
|
482
|
+
type: 'createSurface';
|
|
483
|
+
/** Unique identifier for this surface */
|
|
484
|
+
surfaceId: string;
|
|
485
|
+
/** Initial card schema (optional — allows empty container creation) */
|
|
486
|
+
schema?: CardSchemaInput;
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Incrementally update the component tree.
|
|
490
|
+
*
|
|
491
|
+
* Uses merge semantics: new elements are added, existing elements
|
|
492
|
+
* with the same ID are replaced. Elements not mentioned are preserved.
|
|
493
|
+
*/
|
|
494
|
+
interface UpdateComponentsCommand {
|
|
495
|
+
type: 'updateComponents';
|
|
496
|
+
surfaceId: string;
|
|
497
|
+
/** Elements to add or update (merged into existing schema.elements) */
|
|
498
|
+
elements?: Record<string, ElementNode>;
|
|
499
|
+
/** Update rootID (for initial setup or root node switching) */
|
|
500
|
+
rootID?: string;
|
|
501
|
+
/** Element IDs to remove from the component tree */
|
|
502
|
+
removeElements?: string[];
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Update the data model (variables).
|
|
506
|
+
*
|
|
507
|
+
* Uses JSON Pointer-like path to target a specific location in the
|
|
508
|
+
* variables tree. Supports both leaf value updates and subtree replacement.
|
|
509
|
+
*/
|
|
510
|
+
interface UpdateDataModelCommand {
|
|
511
|
+
type: 'updateDataModel';
|
|
512
|
+
surfaceId: string;
|
|
513
|
+
/** JSON Pointer path, e.g. '/user/name' or '/orderStatus' */
|
|
514
|
+
path: string;
|
|
515
|
+
/** The new value to set at the path */
|
|
516
|
+
value: any;
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Delete (destroy) a surface and clean up all resources.
|
|
520
|
+
*/
|
|
521
|
+
interface DeleteSurfaceCommand {
|
|
522
|
+
type: 'deleteSurface';
|
|
523
|
+
surfaceId: string;
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Append text content to a specific element (optimized for streaming text).
|
|
527
|
+
*
|
|
528
|
+
* This avoids sending the full content on every update — only the delta
|
|
529
|
+
* is transmitted, significantly reducing bandwidth for long streaming text.
|
|
530
|
+
*/
|
|
531
|
+
interface AppendContentCommand {
|
|
532
|
+
type: 'appendContent';
|
|
533
|
+
surfaceId: string;
|
|
534
|
+
/** Target element ID (must be a Text-type component) */
|
|
535
|
+
elementId: string;
|
|
536
|
+
/** Text fragment to append to the element's current content */
|
|
537
|
+
content: string;
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Events emitted by the StreamingEngine to notify the rendering layer
|
|
541
|
+
* about state changes that need to be reflected in the UI.
|
|
542
|
+
*/
|
|
543
|
+
interface StreamingEngineEvents {
|
|
544
|
+
/** Surface created — renderer should initialize the container */
|
|
545
|
+
onSurfaceCreated: (surfaceId: string, schema: CardSchemaInput | null) => void;
|
|
546
|
+
/** Components updated — renderer should perform incremental DOM updates */
|
|
547
|
+
onComponentsUpdated: (surfaceId: string, changes: ComponentChange[]) => void;
|
|
548
|
+
/** Data model updated — renderer should re-resolve affected expressions */
|
|
549
|
+
onDataModelUpdated: (surfaceId: string, path: string, value: any) => void;
|
|
550
|
+
/** Content appended — renderer should append text to the target element */
|
|
551
|
+
onContentAppended: (surfaceId: string, elementId: string, content: string) => void;
|
|
552
|
+
/** Surface deleted — renderer should destroy the container */
|
|
553
|
+
onSurfaceDeleted: (surfaceId: string) => void;
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Describes a single component tree change for incremental rendering.
|
|
557
|
+
*/
|
|
558
|
+
interface ComponentChange {
|
|
559
|
+
/** Type of change */
|
|
560
|
+
changeType: 'add' | 'update' | 'remove';
|
|
561
|
+
/** The affected element ID */
|
|
562
|
+
elementId: string;
|
|
563
|
+
/** The element definition (present for add/update, absent for remove) */
|
|
564
|
+
element?: ElementNode;
|
|
565
|
+
/** Parent element ID (used to determine DOM insertion position) */
|
|
566
|
+
parentId?: string;
|
|
567
|
+
/** Index within parent's children (for insertion ordering) */
|
|
568
|
+
index?: number;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* Streaming Message Parser — converts raw byte streams into typed command objects.
|
|
573
|
+
*
|
|
574
|
+
* Supports multiple transport formats:
|
|
575
|
+
* - NDJSON (Newline-Delimited JSON): one JSON object per line
|
|
576
|
+
* - SSE (Server-Sent Events): `data: {...}` format
|
|
577
|
+
* - Chunked JSON: handles incomplete JSON that spans multiple chunks
|
|
578
|
+
*
|
|
579
|
+
* The parser maintains an internal buffer for handling partial messages
|
|
580
|
+
* that arrive across chunk boundaries.
|
|
581
|
+
*/
|
|
582
|
+
|
|
583
|
+
interface StreamingParserOptions {
|
|
584
|
+
/** Message delimiter, defaults to '\n' (NDJSON) */
|
|
585
|
+
delimiter?: string;
|
|
586
|
+
/** Called when a malformed message is encountered */
|
|
587
|
+
onParseError?: (raw: string, error: Error) => void;
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Streaming message parser that converts raw text chunks from
|
|
591
|
+
* SSE/WebSocket/fetch streaming into typed StreamingCommand objects.
|
|
592
|
+
*
|
|
593
|
+
* @example
|
|
594
|
+
* ```ts
|
|
595
|
+
* const parser = new StreamingParser();
|
|
596
|
+
*
|
|
597
|
+
* // Feed chunks as they arrive from the transport
|
|
598
|
+
* socket.onmessage = (e) => {
|
|
599
|
+
* const commands = parser.parse(e.data);
|
|
600
|
+
* commands.forEach(cmd => engine.apply(cmd));
|
|
601
|
+
* };
|
|
602
|
+
*
|
|
603
|
+
* // Flush remaining buffer when stream ends
|
|
604
|
+
* socket.onclose = () => {
|
|
605
|
+
* const remaining = parser.flush();
|
|
606
|
+
* remaining.forEach(cmd => engine.apply(cmd));
|
|
607
|
+
* };
|
|
608
|
+
* ```
|
|
609
|
+
*/
|
|
610
|
+
declare class StreamingParser {
|
|
611
|
+
private buffer;
|
|
612
|
+
private delimiter;
|
|
613
|
+
private onParseError?;
|
|
614
|
+
constructor(options?: StreamingParserOptions);
|
|
615
|
+
/**
|
|
616
|
+
* Parse a raw text chunk into an array of streaming commands.
|
|
617
|
+
*
|
|
618
|
+
* Incomplete lines are buffered internally and will be completed
|
|
619
|
+
* when the next chunk arrives (or when flush() is called).
|
|
620
|
+
*
|
|
621
|
+
* @param chunk - Raw text chunk from the transport layer
|
|
622
|
+
* @returns Array of successfully parsed commands (may be empty)
|
|
623
|
+
*/
|
|
624
|
+
parse(chunk: string): StreamingCommand[];
|
|
625
|
+
/**
|
|
626
|
+
* Flush the internal buffer and attempt to parse any remaining content.
|
|
627
|
+
*
|
|
628
|
+
* Call this when the stream ends to ensure no messages are lost.
|
|
629
|
+
*
|
|
630
|
+
* @returns Array of commands parsed from the remaining buffer
|
|
631
|
+
*/
|
|
632
|
+
flush(): StreamingCommand[];
|
|
633
|
+
/**
|
|
634
|
+
* Reset the parser state, clearing any buffered content.
|
|
635
|
+
*/
|
|
636
|
+
reset(): void;
|
|
637
|
+
/**
|
|
638
|
+
* Parse a single line into a StreamingCommand, handling SSE format.
|
|
639
|
+
*/
|
|
640
|
+
private parseLine;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
/**
|
|
644
|
+
* Streaming Engine — maintains card state and computes incremental changes.
|
|
645
|
+
*
|
|
646
|
+
* The engine acts as the central coordinator between the message parser
|
|
647
|
+
* and the rendering layer. It maintains a mutable CardSchema state for
|
|
648
|
+
* each surface and emits fine-grained change events that renderers can
|
|
649
|
+
* use for efficient incremental updates.
|
|
650
|
+
*
|
|
651
|
+
* Key responsibilities:
|
|
652
|
+
* - Manage multiple concurrent surfaces
|
|
653
|
+
* - Apply streaming commands to internal state
|
|
654
|
+
* - Compute parent-child relationships for component changes
|
|
655
|
+
* - Emit typed events for the rendering layer
|
|
656
|
+
*/
|
|
657
|
+
|
|
658
|
+
declare class StreamingEngine {
|
|
659
|
+
/** Active surfaces keyed by surfaceId */
|
|
660
|
+
private surfaces;
|
|
661
|
+
/** Event listeners provided by the rendering layer */
|
|
662
|
+
private listeners;
|
|
663
|
+
constructor(listeners: StreamingEngineEvents);
|
|
664
|
+
/**
|
|
665
|
+
* Apply a single streaming command to the engine state.
|
|
666
|
+
* This triggers the appropriate event listener after state mutation.
|
|
667
|
+
*/
|
|
668
|
+
apply(command: StreamingCommand): void;
|
|
669
|
+
/**
|
|
670
|
+
* Apply multiple streaming commands in order.
|
|
671
|
+
*/
|
|
672
|
+
applyBatch(commands: StreamingCommand[]): void;
|
|
673
|
+
/**
|
|
674
|
+
* Get the current schema for a surface (read-only snapshot).
|
|
675
|
+
*/
|
|
676
|
+
getSchema(surfaceId: string): CardSchema | undefined;
|
|
677
|
+
/**
|
|
678
|
+
* Get the current variables for a surface.
|
|
679
|
+
*/
|
|
680
|
+
getVariables(surfaceId: string): Record<string, any> | undefined;
|
|
681
|
+
/**
|
|
682
|
+
* Check if a surface exists.
|
|
683
|
+
*/
|
|
684
|
+
hasSurface(surfaceId: string): boolean;
|
|
685
|
+
/**
|
|
686
|
+
* Dispose all surfaces and reset state.
|
|
687
|
+
*/
|
|
688
|
+
dispose(): void;
|
|
689
|
+
private handleCreateSurface;
|
|
690
|
+
private handleUpdateComponents;
|
|
691
|
+
private handleUpdateDataModel;
|
|
692
|
+
private handleAppendContent;
|
|
693
|
+
private handleDeleteSurface;
|
|
694
|
+
/**
|
|
695
|
+
* Find the parent element ID for a given element by scanning all slots.
|
|
696
|
+
*/
|
|
697
|
+
private findParentId;
|
|
698
|
+
/**
|
|
699
|
+
* Find the index of a child element within its parent's slot children.
|
|
700
|
+
*/
|
|
701
|
+
private findChildIndex;
|
|
702
|
+
/**
|
|
703
|
+
* Check if an element references the given child ID in any of its slots.
|
|
704
|
+
*/
|
|
705
|
+
private elementContainsChild;
|
|
706
|
+
/**
|
|
707
|
+
* Remove a child ID from all parent element slot references.
|
|
708
|
+
*/
|
|
709
|
+
private removeFromParentSlots;
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* Set a value at a JSON Pointer-like path in an object.
|
|
713
|
+
*
|
|
714
|
+
* Path format: '/key1/key2/key3' → obj.key1.key2.key3 = value
|
|
715
|
+
* Root path '/' sets the entire object.
|
|
716
|
+
*
|
|
717
|
+
* Prototype-polluting segments (`__proto__` / `constructor` / `prototype`) are
|
|
718
|
+
* rejected — the whole write is dropped rather than silently retargeted.
|
|
719
|
+
*
|
|
720
|
+
* @example
|
|
721
|
+
* ```ts
|
|
722
|
+
* const obj = { user: { name: 'Alice' } };
|
|
723
|
+
* setByPath(obj, '/user/name', 'Bob');
|
|
724
|
+
* // obj.user.name === 'Bob'
|
|
725
|
+
* ```
|
|
726
|
+
*/
|
|
727
|
+
declare function setByPath(obj: Record<string, any>, path: string, value: any): void;
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* A2UI v0.9 Envelope Adapter — converts Google A2UI protocol messages
|
|
731
|
+
* into internal StreamingCommand objects.
|
|
732
|
+
*
|
|
733
|
+
* A2UI (https://a2ui.org) wraps each message as
|
|
734
|
+
* `{ "version": "v0.9", "<messageType>": { ... } }`, while the internal
|
|
735
|
+
* command stream uses a `type` discriminator field. The command names are
|
|
736
|
+
* already aligned (createSurface / updateComponents / updateDataModel /
|
|
737
|
+
* deleteSurface), so conversion is mostly an envelope unwrap plus a
|
|
738
|
+
* component-shape mapping:
|
|
739
|
+
*
|
|
740
|
+
* - A2UI components are a flat array with a `component` discriminator and
|
|
741
|
+
* top-level props (`{"id":"t1","component":"Text","content":...,"children":[...]}`)
|
|
742
|
+
* - Internal elements are a map with `type` + nested `props`
|
|
743
|
+
* (`{"t1":{"id":"t1","type":"Text","props":{content,slots:{default:{children}}}}}`)
|
|
744
|
+
*
|
|
745
|
+
* `appendContent` is accepted in envelope form as a non-standard extension
|
|
746
|
+
* (A2UI has no token-append primitive; we keep ours for typewriter streaming).
|
|
747
|
+
*/
|
|
748
|
+
|
|
749
|
+
/** A single A2UI v0.9 component (flat form, `component` discriminator). */
|
|
750
|
+
interface A2UIComponent {
|
|
751
|
+
id: string;
|
|
752
|
+
component: string;
|
|
753
|
+
/** Child component IDs — mapped to `props.slots.default.children`. */
|
|
754
|
+
children?: string[];
|
|
755
|
+
/** Layout slots pass-through (grid/chart/... — catalog extension). */
|
|
756
|
+
slots?: Record<string, any>;
|
|
757
|
+
/** visible/disabled directives (top-level, same as internal schema). */
|
|
758
|
+
directives?: Record<string, any>;
|
|
759
|
+
/** Event bindings pass-through (onClick etc.). */
|
|
760
|
+
events?: Record<string, any>;
|
|
761
|
+
/** Any other field is treated as a component prop. */
|
|
762
|
+
[prop: string]: any;
|
|
763
|
+
}
|
|
764
|
+
/** A2UI v0.9 message envelope: exactly one action key per message. */
|
|
765
|
+
interface A2UIEnvelope {
|
|
766
|
+
version?: string;
|
|
767
|
+
createSurface?: {
|
|
768
|
+
surfaceId: string;
|
|
769
|
+
catalogId?: string;
|
|
770
|
+
theme?: any;
|
|
771
|
+
sendDataModel?: boolean;
|
|
772
|
+
schema?: any;
|
|
773
|
+
};
|
|
774
|
+
updateComponents?: {
|
|
775
|
+
surfaceId: string;
|
|
776
|
+
components?: A2UIComponent[];
|
|
777
|
+
};
|
|
778
|
+
updateDataModel?: {
|
|
779
|
+
surfaceId: string;
|
|
780
|
+
path?: string;
|
|
781
|
+
value?: any;
|
|
782
|
+
};
|
|
783
|
+
deleteSurface?: {
|
|
784
|
+
surfaceId: string;
|
|
785
|
+
};
|
|
786
|
+
/** Non-standard extension: token-level text append (see module doc). */
|
|
787
|
+
appendContent?: {
|
|
788
|
+
surfaceId: string;
|
|
789
|
+
elementId: string;
|
|
790
|
+
content: string;
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* Check whether a parsed JSON object looks like an A2UI envelope
|
|
795
|
+
* (no internal `type` discriminator, but exactly an A2UI action key).
|
|
796
|
+
*/
|
|
797
|
+
declare function isA2UIEnvelope(msg: unknown): msg is A2UIEnvelope;
|
|
798
|
+
/**
|
|
799
|
+
* Convert a flat A2UI component into an internal ElementNode.
|
|
800
|
+
*/
|
|
801
|
+
declare function a2uiComponentToElement(comp: A2UIComponent): ElementNode;
|
|
802
|
+
/**
|
|
803
|
+
* Convert an A2UI v0.9 envelope message into an internal StreamingCommand.
|
|
804
|
+
* Returns null for unrecognized messages.
|
|
805
|
+
*
|
|
806
|
+
* Notes:
|
|
807
|
+
* - `catalogId` / `theme` / `sendDataModel` are accepted but currently ignored
|
|
808
|
+
* (component catalog is built into the renderer).
|
|
809
|
+
* - `createSurface.schema` is a convenience extension: a full internal schema
|
|
810
|
+
* may be attached for template-style cards.
|
|
811
|
+
* - A component with `id: "root"` sets the surface rootID (A2UI convention).
|
|
812
|
+
*/
|
|
813
|
+
declare function a2uiToCommand(msg: A2UIEnvelope): StreamingCommand | null;
|
|
814
|
+
|
|
815
|
+
/**
|
|
816
|
+
* Partial Schema Extractor — pulls completed pieces out of an INCOMPLETE
|
|
817
|
+
* card-schema JSON text while it is still streaming in from an LLM.
|
|
818
|
+
*
|
|
819
|
+
* A card schema is `{"version","rootID","variables","elements":{id:{...},...}}`.
|
|
820
|
+
* The `elements` map is flat, so every element value becomes parseable the
|
|
821
|
+
* moment its own braces balance — long before the whole document closes.
|
|
822
|
+
* This enables progressive rendering: blocks mount as they complete instead
|
|
823
|
+
* of waiting out the entire (possibly multi-thousand-token) JSON.
|
|
824
|
+
*
|
|
825
|
+
* Design constraints:
|
|
826
|
+
* - Pure & stateless: call it every frame with the ACCUMULATED text
|
|
827
|
+
* (append-only); identical input → identical output.
|
|
828
|
+
* - String-aware scanning: braces/quotes inside JSON string values
|
|
829
|
+
* (including escapes) never confuse the balancer.
|
|
830
|
+
* - Malformed single elements are skipped (the final full-document parse
|
|
831
|
+
* in the caller is the correctness backstop).
|
|
832
|
+
*/
|
|
833
|
+
|
|
834
|
+
interface PartialSchemaResult {
|
|
835
|
+
version?: string;
|
|
836
|
+
rootID?: string;
|
|
837
|
+
variables?: Record<string, any>;
|
|
838
|
+
/** Elements whose own JSON closed and parsed successfully. */
|
|
839
|
+
elements: Record<string, ElementNode>;
|
|
840
|
+
/** True when the WHOLE document parsed — result is then the full schema. */
|
|
841
|
+
complete: boolean;
|
|
842
|
+
}
|
|
843
|
+
/**
|
|
844
|
+
* Extract completed pieces from (possibly incomplete) schema JSON text.
|
|
845
|
+
*
|
|
846
|
+
* @param text - Accumulated schema JSON text, starting at the document `{`.
|
|
847
|
+
* Surrounding whitespace is tolerated; surrounding prose is not
|
|
848
|
+
* (strip markers like `<card>` before calling).
|
|
849
|
+
*/
|
|
850
|
+
declare function extractPartialSchema(text: string): PartialSchemaResult;
|
|
851
|
+
|
|
852
|
+
export { ActionRegistry, LifecycleManager, StreamingEngine, StreamingParser, a2uiComponentToElement, a2uiToCommand, convertLegacySchema, createLifecycleManager, extractPartialSchema, getByPath, hasExpression, interpolate, isA2UIEnvelope, isLegacySchema, normalizeSchema, parseSchema, registerActionHandler, registry, resetIdCounter, resolveActionRef, resolveDeep, resolveExpression, resolveExpressionValue, runActionStep, runActionSteps, setByPath, validateSchema };
|
|
853
|
+
export type { A2UIComponent, A2UIEnvelope, ActionChainConfig, ActionConfigProvider, ActionRunnerContext, ActionStep, ActionStepHandler, AppendContentCommand, CardSchema, CardSchemaInput, CleanupFn, ComponentChange, CreateSurfaceCommand, DeleteSurfaceCommand, ElementLifecycle, ElementNode, ExpressionContext, ExpressionValue, LegacyCardContentItem, LegacyCardSchema, LegacyTracking, LegacyTrackingType, PartialSchemaResult, RenderTreeNode, SimpleActionHandler, SlotContent, SlotLayout, StreamingCommand, StreamingEngineEvents, StreamingParserOptions, UpdateComponentsCommand, UpdateDataModelCommand };
|