@decocms/bindings 1.4.7 → 1.4.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decocms/bindings",
3
- "version": "1.4.7",
3
+ "version": "1.4.9",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "check": "tsc --noEmit",
@@ -28,7 +28,6 @@
28
28
  "./mcp": "./src/well-known/mcp.ts",
29
29
  "./assistant": "./src/well-known/assistant.ts",
30
30
  "./prompt": "./src/well-known/prompt.ts",
31
- "./workflow": "./src/well-known/workflow.ts",
32
31
  "./plugins": "./src/core/plugins.ts",
33
32
  "./plugin-router": "./src/core/plugin-router.tsx",
34
33
  "./ai-gateway": "./src/well-known/ai-gateway.ts",
package/src/index.ts CHANGED
@@ -49,41 +49,6 @@ export {
49
49
  type EventSubscriberBindingClient,
50
50
  } from "./well-known/event-subscriber";
51
51
 
52
- // Re-export event bus binding types (for interacting with an event bus)
53
- export {
54
- EventPublishInputSchema,
55
- type EventPublishInput,
56
- EventPublishOutputSchema,
57
- type EventPublishOutput,
58
- EventSubscribeInputSchema,
59
- type EventSubscribeInput,
60
- EventSubscribeOutputSchema,
61
- type EventSubscribeOutput,
62
- EventUnsubscribeInputSchema,
63
- type EventUnsubscribeInput,
64
- EventUnsubscribeOutputSchema,
65
- type EventUnsubscribeOutput,
66
- EventCancelInputSchema,
67
- type EventCancelInput,
68
- EventCancelOutputSchema,
69
- type EventCancelOutput,
70
- EventAckInputSchema,
71
- type EventAckInput,
72
- EventAckOutputSchema,
73
- type EventAckOutput,
74
- SubscriptionItemSchema,
75
- type SubscriptionItem,
76
- SubscriptionDetailSchema,
77
- type SubscriptionDetail,
78
- EventSyncSubscriptionsInputSchema,
79
- type EventSyncSubscriptionsInput,
80
- EventSyncSubscriptionsOutputSchema,
81
- type EventSyncSubscriptionsOutput,
82
- EVENT_BUS_BINDING,
83
- EventBusBinding,
84
- type EventBusBindingClient,
85
- } from "./well-known/event-bus";
86
-
87
52
  // Re-export trigger binding types (for connections that can emit triggers)
88
53
  export {
89
54
  TriggerParamSchema,
@@ -121,9 +86,6 @@ export {
121
86
  type DeleteObjectsOutput,
122
87
  } from "./well-known/object-storage";
123
88
 
124
- // Re-export workflow binding types
125
- export { WORKFLOWS_COLLECTION_BINDING } from "./well-known/workflow";
126
-
127
89
  // Re-export brand binding types (for reading org brand context)
128
90
  export {
129
91
  BrandColorsSchema,
@@ -1,454 +0,0 @@
1
- /**
2
- * Event Bus Well-Known Binding
3
- *
4
- * Defines the interface for interacting with an event bus via MCP.
5
- * Any MCP that implements this binding can publish events and manage subscriptions.
6
- *
7
- * This binding includes:
8
- * - EVENT_PUBLISH: Publish an event to the bus
9
- * - EVENT_SUBSCRIBE: Subscribe to events of a specific type
10
- * - EVENT_UNSUBSCRIBE: Remove a subscription
11
- *
12
- * Events follow the CloudEvents v1.0 specification.
13
- * @see https://cloudevents.io/
14
- */
15
-
16
- import { z } from "zod";
17
- import { bindingClient, type ToolBinder } from "../core/binder";
18
-
19
- // ============================================================================
20
- // Publish Schemas
21
- // ============================================================================
22
-
23
- /**
24
- * EVENT_PUBLISH Input Schema
25
- *
26
- * Input for publishing an event.
27
- * Note: `source` is automatically set by the event bus from the caller's connection ID.
28
- */
29
- export const EventPublishInputSchema = z.object({
30
- /** Event type (e.g., "order.created", "user.signup") */
31
- type: z.string().min(1).max(255).describe("Event type identifier"),
32
-
33
- /** Optional subject/resource identifier */
34
- subject: z
35
- .string()
36
- .max(255)
37
- .optional()
38
- .describe("Subject/resource identifier (e.g., order ID)"),
39
-
40
- /** Event payload (any JSON value) */
41
- data: z.unknown().optional().describe("Event payload"),
42
-
43
- /**
44
- * Optional scheduled delivery time (ISO 8601 timestamp).
45
- * If provided, the event will not be delivered until this time.
46
- * If omitted, the event is delivered immediately.
47
- * Cannot be used together with `cron`.
48
- */
49
- deliverAt: z
50
- .string()
51
- .datetime()
52
- .optional()
53
- .describe(
54
- "Scheduled delivery time (ISO 8601). Omit for immediate delivery.",
55
- ),
56
-
57
- /**
58
- * Optional cron expression for recurring events.
59
- * If provided, the event will be delivered repeatedly according to the schedule.
60
- * Uses standard cron syntax (5 or 6 fields).
61
- * Cannot be used together with `deliverAt`.
62
- *
63
- * Examples:
64
- * - "0 9 * * 1" - Every Monday at 9:00 AM
65
- * - "0 0 1 * *" - First day of every month at midnight
66
- * - "0/15 * * * *" - Every 15 minutes
67
- */
68
- cron: z
69
- .string()
70
- .max(100)
71
- .optional()
72
- .describe(
73
- "Cron expression for recurring delivery. Use EVENT_CANCEL to stop.",
74
- ),
75
- });
76
-
77
- export type EventPublishInput = z.infer<typeof EventPublishInputSchema>;
78
-
79
- /**
80
- * EVENT_PUBLISH Output Schema
81
- */
82
- export const EventPublishOutputSchema = z.object({
83
- /** Created event ID */
84
- id: z.string().describe("Unique event ID"),
85
-
86
- /** Event type */
87
- type: z.string().describe("Event type"),
88
-
89
- /** Source connection ID */
90
- source: z.string().describe("Source connection ID"),
91
-
92
- /** Event timestamp (ISO 8601) */
93
- time: z.string().describe("Event timestamp"),
94
- });
95
-
96
- export type EventPublishOutput = z.infer<typeof EventPublishOutputSchema>;
97
-
98
- // ============================================================================
99
- // Subscribe Schemas
100
- // ============================================================================
101
-
102
- /**
103
- * EVENT_SUBSCRIBE Input Schema
104
- *
105
- * Input for subscribing to events.
106
- * The subscriber connection ID is automatically set from the caller's token.
107
- */
108
- export const EventSubscribeInputSchema = z.object({
109
- /** Event type pattern to match */
110
- eventType: z.string().min(1).max(255).describe("Event type to subscribe to"),
111
-
112
- /** Optional: Only receive events from this publisher connection */
113
- publisher: z
114
- .string()
115
- .optional()
116
- .describe("Filter events by publisher connection ID"),
117
-
118
- /** Optional: JSONPath filter expression on event data */
119
- filter: z
120
- .string()
121
- .max(1000)
122
- .optional()
123
- .describe("JSONPath filter expression on event data"),
124
- });
125
-
126
- export type EventSubscribeInput = z.infer<typeof EventSubscribeInputSchema>;
127
-
128
- /**
129
- * EVENT_SUBSCRIBE Output Schema
130
- */
131
- export const EventSubscribeOutputSchema = z.object({
132
- /** Created subscription */
133
- subscription: z.object({
134
- /** Subscription ID */
135
- id: z.string().describe("Subscription ID"),
136
-
137
- /** Subscriber connection ID */
138
- connectionId: z.string().describe("Subscriber connection ID"),
139
-
140
- /** Event type pattern */
141
- eventType: z.string().describe("Event type pattern"),
142
-
143
- /** Publisher connection filter */
144
- publisher: z.string().nullable().describe("Publisher connection filter"),
145
-
146
- /** JSONPath filter */
147
- filter: z.string().nullable().describe("JSONPath filter expression"),
148
-
149
- /** Whether subscription is enabled */
150
- enabled: z.boolean().describe("Whether subscription is enabled"),
151
-
152
- /** Created timestamp */
153
- createdAt: z.string().datetime().describe("Created timestamp (ISO 8601)"),
154
-
155
- /** Updated timestamp */
156
- updatedAt: z.string().datetime().describe("Updated timestamp (ISO 8601)"),
157
- }),
158
- });
159
-
160
- export type EventSubscribeOutput = z.infer<typeof EventSubscribeOutputSchema>;
161
-
162
- // ============================================================================
163
- // Sync Subscriptions Schemas
164
- // ============================================================================
165
-
166
- /**
167
- * Subscription item schema for sync operations
168
- */
169
- export const SubscriptionItemSchema = z.object({
170
- /** Event type pattern to match */
171
- eventType: z.string().min(1).max(255).describe("Event type to subscribe to"),
172
-
173
- /** Optional: Only receive events from this publisher connection */
174
- publisher: z
175
- .string()
176
- .optional()
177
- .describe("Filter events by publisher connection ID"),
178
-
179
- /** Optional: JSONPath filter expression on event data */
180
- filter: z
181
- .string()
182
- .max(1000)
183
- .optional()
184
- .describe("JSONPath filter expression on event data"),
185
- });
186
-
187
- export type SubscriptionItem = z.infer<typeof SubscriptionItemSchema>;
188
-
189
- /**
190
- * Subscription detail schema (returned in responses)
191
- */
192
- export const SubscriptionDetailSchema = z.object({
193
- /** Subscription ID */
194
- id: z.string().describe("Subscription ID"),
195
-
196
- /** Subscriber connection ID */
197
- connectionId: z.string().describe("Subscriber connection ID"),
198
-
199
- /** Event type pattern */
200
- eventType: z.string().describe("Event type pattern"),
201
-
202
- /** Publisher connection filter */
203
- publisher: z.string().nullable().describe("Publisher connection filter"),
204
-
205
- /** JSONPath filter */
206
- filter: z.string().nullable().describe("JSONPath filter expression"),
207
-
208
- /** Whether subscription is enabled */
209
- enabled: z.boolean().describe("Whether subscription is enabled"),
210
-
211
- /** Created timestamp */
212
- createdAt: z.string().datetime().describe("Created timestamp (ISO 8601)"),
213
-
214
- /** Updated timestamp */
215
- updatedAt: z.string().datetime().describe("Updated timestamp (ISO 8601)"),
216
- });
217
-
218
- export type SubscriptionDetail = z.infer<typeof SubscriptionDetailSchema>;
219
-
220
- /**
221
- * EVENT_SYNC_SUBSCRIPTIONS Input Schema
222
- *
223
- * Input for syncing subscriptions to a desired state.
224
- * The system will create new, delete removed, and update changed subscriptions.
225
- * Subscriptions are identified by (eventType, publisher) - only one subscription
226
- * per combination is allowed.
227
- */
228
- export const EventSyncSubscriptionsInputSchema = z.object({
229
- /** Desired subscriptions - system will create/update/delete to match */
230
- subscriptions: z
231
- .array(SubscriptionItemSchema)
232
- .describe(
233
- "Desired subscriptions - system will create/update/delete to match",
234
- ),
235
- });
236
-
237
- export type EventSyncSubscriptionsInput = z.infer<
238
- typeof EventSyncSubscriptionsInputSchema
239
- >;
240
-
241
- /**
242
- * EVENT_SYNC_SUBSCRIPTIONS Output Schema
243
- */
244
- export const EventSyncSubscriptionsOutputSchema = z.object({
245
- /** Number of new subscriptions created */
246
- created: z.number().int().min(0).describe("Number of subscriptions created"),
247
-
248
- /** Number of subscriptions with filter updated */
249
- updated: z
250
- .number()
251
- .int()
252
- .min(0)
253
- .describe("Number of subscriptions with filter updated"),
254
-
255
- /** Number of old subscriptions removed */
256
- deleted: z.number().int().min(0).describe("Number of subscriptions removed"),
257
-
258
- /** Number of subscriptions unchanged */
259
- unchanged: z
260
- .number()
261
- .int()
262
- .min(0)
263
- .describe("Number of subscriptions unchanged"),
264
-
265
- /** Current subscriptions after sync */
266
- subscriptions: z
267
- .array(SubscriptionDetailSchema)
268
- .describe("Current subscriptions after sync"),
269
- });
270
-
271
- export type EventSyncSubscriptionsOutput = z.infer<
272
- typeof EventSyncSubscriptionsOutputSchema
273
- >;
274
-
275
- // ============================================================================
276
- // Unsubscribe Schemas
277
- // ============================================================================
278
-
279
- /**
280
- * EVENT_UNSUBSCRIBE Input Schema
281
- */
282
- export const EventUnsubscribeInputSchema = z.object({
283
- /** Subscription ID to remove */
284
- subscriptionId: z.string().describe("Subscription ID to remove"),
285
- });
286
-
287
- export type EventUnsubscribeInput = z.infer<typeof EventUnsubscribeInputSchema>;
288
-
289
- /**
290
- * EVENT_UNSUBSCRIBE Output Schema
291
- */
292
- export const EventUnsubscribeOutputSchema = z.object({
293
- /** Success status */
294
- success: z.boolean().describe("Whether unsubscribe was successful"),
295
-
296
- /** Subscription ID that was removed */
297
- subscriptionId: z.string().describe("Subscription ID that was removed"),
298
- });
299
-
300
- export type EventUnsubscribeOutput = z.infer<
301
- typeof EventUnsubscribeOutputSchema
302
- >;
303
-
304
- // ============================================================================
305
- // Cancel Schemas (for stopping recurring events)
306
- // ============================================================================
307
-
308
- /**
309
- * EVENT_CANCEL Input Schema
310
- *
311
- * Input for cancelling a recurring event.
312
- * Only the publisher connection can cancel its own events.
313
- */
314
- export const EventCancelInputSchema = z.object({
315
- /** Event ID to cancel */
316
- eventId: z.string().describe("Event ID to cancel"),
317
- });
318
-
319
- export type EventCancelInput = z.infer<typeof EventCancelInputSchema>;
320
-
321
- /**
322
- * EVENT_CANCEL Output Schema
323
- */
324
- export const EventCancelOutputSchema = z.object({
325
- /** Success status */
326
- success: z.boolean().describe("Whether cancellation was successful"),
327
-
328
- /** Event ID that was cancelled */
329
- eventId: z.string().describe("Event ID that was cancelled"),
330
- });
331
-
332
- export type EventCancelOutput = z.infer<typeof EventCancelOutputSchema>;
333
-
334
- // ============================================================================
335
- // Ack Schemas (for acknowledging async event processing)
336
- // ============================================================================
337
-
338
- /**
339
- * EVENT_ACK Input Schema
340
- *
341
- * Input for acknowledging an event delivery.
342
- * Used when ON_EVENTS returns retryAfter - the subscriber must call EVENT_ACK
343
- * to confirm successful processing, otherwise the event will be re-delivered.
344
- *
345
- * The subscriber connection ID is determined from the caller's token.
346
- */
347
- export const EventAckInputSchema = z.object({
348
- /** Event ID to acknowledge */
349
- eventId: z.string().describe("Event ID to acknowledge"),
350
- });
351
-
352
- export type EventAckInput = z.infer<typeof EventAckInputSchema>;
353
-
354
- /**
355
- * EVENT_ACK Output Schema
356
- */
357
- export const EventAckOutputSchema = z.object({
358
- /** Success status */
359
- success: z.boolean().describe("Whether ACK was successful"),
360
-
361
- /** Event ID that was acknowledged */
362
- eventId: z.string().describe("Event ID that was acknowledged"),
363
- });
364
-
365
- export type EventAckOutput = z.infer<typeof EventAckOutputSchema>;
366
-
367
- // ============================================================================
368
- // Event Bus Binding
369
- // ============================================================================
370
-
371
- /**
372
- * Event Bus Binding
373
- *
374
- * Defines the interface for interacting with an event bus.
375
- * Implementations must provide PUBLISH, SUBSCRIBE, UNSUBSCRIBE, CANCEL, ACK, and SYNC_SUBSCRIPTIONS tools.
376
- *
377
- * Required tools:
378
- * - EVENT_PUBLISH: Publish an event (supports one-time, scheduled, and recurring via cron)
379
- * - EVENT_SUBSCRIBE: Subscribe to events
380
- * - EVENT_UNSUBSCRIBE: Remove a subscription
381
- * - EVENT_CANCEL: Cancel a recurring event (stops future deliveries)
382
- * - EVENT_ACK: Acknowledge event delivery (for async processing with retryAfter)
383
- * - EVENT_SYNC_SUBSCRIPTIONS: Sync subscriptions to desired state
384
- */
385
- export const EVENT_BUS_BINDING = [
386
- {
387
- name: "EVENT_PUBLISH" as const,
388
- inputSchema: EventPublishInputSchema,
389
- outputSchema: EventPublishOutputSchema,
390
- },
391
- {
392
- name: "EVENT_SUBSCRIBE" as const,
393
- inputSchema: EventSubscribeInputSchema,
394
- outputSchema: EventSubscribeOutputSchema,
395
- },
396
- {
397
- name: "EVENT_UNSUBSCRIBE" as const,
398
- inputSchema: EventUnsubscribeInputSchema,
399
- outputSchema: EventUnsubscribeOutputSchema,
400
- },
401
- {
402
- name: "EVENT_CANCEL" as const,
403
- inputSchema: EventCancelInputSchema,
404
- outputSchema: EventCancelOutputSchema,
405
- },
406
- {
407
- name: "EVENT_ACK" as const,
408
- inputSchema: EventAckInputSchema,
409
- outputSchema: EventAckOutputSchema,
410
- },
411
- {
412
- name: "EVENT_SYNC_SUBSCRIPTIONS" as const,
413
- inputSchema: EventSyncSubscriptionsInputSchema,
414
- outputSchema: EventSyncSubscriptionsOutputSchema,
415
- },
416
- ] satisfies ToolBinder[];
417
-
418
- /**
419
- * Event Bus Binding Client
420
- *
421
- * Use this to create a client for interacting with an event bus.
422
- *
423
- * @example
424
- * ```typescript
425
- * import { EventBusBinding } from "@decocms/bindings/event-bus";
426
- *
427
- * // For a connection
428
- * const client = EventBusBinding.forConnection(connection);
429
- *
430
- * // Publish an event
431
- * const event = await client.EVENT_PUBLISH({
432
- * type: "order.created",
433
- * data: { orderId: "123" }
434
- * });
435
- *
436
- * // Subscribe to events
437
- * const sub = await client.EVENT_SUBSCRIBE({
438
- * eventType: "order.created"
439
- * });
440
- *
441
- * // Unsubscribe
442
- * await client.EVENT_UNSUBSCRIBE({
443
- * subscriptionId: sub.subscription.id
444
- * });
445
- * ```
446
- */
447
- export const EventBusBinding = bindingClient(EVENT_BUS_BINDING);
448
-
449
- /**
450
- * Type helper for the Event Bus binding client
451
- */
452
- export type EventBusBindingClient = ReturnType<
453
- typeof EventBusBinding.forConnection
454
- >;
@@ -1,744 +0,0 @@
1
- /**
2
- * Workflows Well-Known Binding
3
- *
4
- * Defines the interface for workflow providers.
5
- * Any MCP that implements this binding can expose configurable workflows,
6
- * executions, step results, and events via collection bindings.
7
- *
8
- * This binding uses collection bindings for LIST and GET operations (read-only).
9
- */
10
-
11
- import { z } from "zod";
12
- import { type Binder, bindingClient, type ToolBinder } from "../core/binder";
13
- import {
14
- BaseCollectionEntitySchema,
15
- createCollectionBindings,
16
- } from "./collections";
17
- export const ToolCallActionSchema = z.object({
18
- toolName: z
19
- .string()
20
- .describe("Name of the tool to invoke on that connection"),
21
- transformCode: z
22
- .string()
23
- .optional()
24
- .describe(`Pure TypeScript function for data transformation of the tool call result. Must be a TypeScript file that declares the Output interface and exports a default function: \`interface Output { ... } export default async function(stepInput): Output { ... }\`
25
- The stepInput will match with the tool call outputSchema. If transformCode is not provided, the tool call result will be used as the step output.
26
- Providing an transformCode is recommended because it both allows you to transform the data and validate it against a JSON Schema - tools are ephemeral and may return unexpected data.`),
27
- });
28
- export type ToolCallAction = z.infer<typeof ToolCallActionSchema>;
29
-
30
- export const CodeActionSchema = z.object({
31
- code: z.string().describe(
32
- `Pure TypeScript function for data transformation. Useful to merge data from multiple steps and transform it. Must be a TypeScript file that declares the Output interface and exports a default function: \`interface Output { ... } export default async function(stepInput): Output { ... }\`
33
- The stepInput is the resolved value of the references in the input field. Example:
34
- {
35
- "input": {
36
- "name": "@Step_1.name",
37
- "age": "@Step_2.age"
38
- },
39
- "code": "export default function(stepInput): Output { return { result: \`\${stepInput.name} is \${stepInput.age} years old.\` } }"
40
- }
41
- `,
42
- ),
43
- });
44
- export type CodeAction = z.infer<typeof CodeActionSchema>;
45
-
46
- export const WaitForSignalActionSchema = z.object({
47
- signalName: z
48
- .string()
49
- .describe(
50
- "Signal name to wait for (e.g., 'approval'). Execution pauses until SEND_SIGNAL is called with this name.",
51
- ),
52
- });
53
- export type WaitForSignalAction = z.infer<typeof WaitForSignalActionSchema>;
54
-
55
- export const StepActionSchema = z.union([
56
- ToolCallActionSchema.describe("Call an external tool via MCP connection. "),
57
- CodeActionSchema.describe(
58
- "Run pure TypeScript code for data transformation. Useful to merge data from multiple steps and transform it.",
59
- ),
60
- ]);
61
- export type StepAction = z.infer<typeof StepActionSchema>;
62
-
63
- /**
64
- * Step Config Schema - Optional configuration for retry, timeout, and looping
65
- */
66
- /**
67
- * Step Condition Schema - Structured condition for bail and future `when` support.
68
- * Evaluates a resolved @ref against an optional operator.
69
- * If no operator is specified, checks for truthiness.
70
- */
71
- export const StepConditionSchema = z.object({
72
- ref: z
73
- .string()
74
- .describe(
75
- "@ref to resolve and evaluate, e.g. '@validate.shouldStop' or '@input.skipProcessing'",
76
- ),
77
- eq: z
78
- .unknown()
79
- .optional()
80
- .describe("Step bails if resolved value equals this"),
81
- neq: z
82
- .unknown()
83
- .optional()
84
- .describe("Step bails if resolved value does NOT equal this"),
85
- gt: z
86
- .number()
87
- .optional()
88
- .describe("Step bails if resolved value is greater than this"),
89
- lt: z
90
- .number()
91
- .optional()
92
- .describe("Step bails if resolved value is less than this"),
93
- });
94
- export type StepCondition = z.infer<typeof StepConditionSchema>;
95
-
96
- export const StepConfigSchema = z.object({
97
- maxAttempts: z
98
- .number()
99
- .int()
100
- .min(1)
101
- .max(10)
102
- .optional()
103
- .describe("Max retry attempts on failure (default: 1, no retries)"),
104
- backoffMs: z
105
- .number()
106
- .int()
107
- .min(100)
108
- .max(60_000)
109
- .optional()
110
- .describe("Initial delay between retries in ms (doubles each attempt)"),
111
- timeoutMs: z
112
- .number()
113
- .int()
114
- .min(100)
115
- .max(86_400_000)
116
- .optional()
117
- .describe(
118
- "Max execution time in ms before step fails (default: 30000). Upper bound 24h.",
119
- ),
120
- onError: z
121
- .enum(["fail", "continue"])
122
- .optional()
123
- .describe(
124
- "What to do when this step fails: 'fail' aborts the workflow, 'continue' skips the error and proceeds",
125
- ),
126
- });
127
- export type StepConfig = z.infer<typeof StepConfigSchema>;
128
-
129
- /**
130
- * Step Schema - A single unit of work in a workflow
131
- *
132
- * Action types:
133
- * - Tool call: Invoke an external tool via MCP connection
134
- * - Code: Run pure TypeScript for data transformation
135
- * - Wait for signal: Pause until external input (human-in-the-loop)
136
- *
137
- * Data flow uses @ref syntax:
138
- * - @input.field → workflow input
139
- * - @stepName.field → output from a previous step
140
- * - @ctx.execution_id → current workflow execution ID
141
- */
142
-
143
- export type JsonSchema = {
144
- type?: string;
145
- properties?: Record<string, JsonSchema>;
146
- required?: string[];
147
- description?: string;
148
- additionalProperties?: boolean | Record<string, unknown>;
149
- additionalItems?: boolean | Record<string, unknown>;
150
- items?: JsonSchema;
151
- format?: string;
152
- enum?: string[];
153
- maxLength?: number;
154
- anyOf?: JsonSchema[];
155
- [key: string]: unknown;
156
- };
157
- export const JsonSchemaSchema: z.ZodType<JsonSchema> = z.lazy(() =>
158
- z
159
- .object({
160
- type: z.string().optional(),
161
- properties: z.record(z.string(), z.unknown()).optional(),
162
- required: z.array(z.string()).optional(),
163
- description: z.string().optional(),
164
- additionalProperties: z
165
- .union([z.boolean(), z.record(z.string(), z.unknown())])
166
- .optional(),
167
- additionalItems: z
168
- .union([z.boolean(), z.record(z.string(), z.unknown())])
169
- .optional(),
170
- items: JsonSchemaSchema.optional(),
171
- })
172
- .passthrough(),
173
- ) as unknown as z.ZodType<JsonSchema>;
174
-
175
- /**
176
- * Step names that are reserved by the @ref system and cannot be used as step names.
177
- * These are intercepted before step lookup in the ref resolver.
178
- */
179
- export const RESERVED_STEP_NAMES = ["input", "item", "index", "ctx"] as const;
180
-
181
- export const StepSchema = z.object({
182
- name: z
183
- .string()
184
- .min(1)
185
- .refine(
186
- (name) => !(RESERVED_STEP_NAMES as readonly string[]).includes(name),
187
- {
188
- message: `Step name is reserved. Reserved names: ${RESERVED_STEP_NAMES.join(", ")}`,
189
- },
190
- )
191
- .describe(
192
- "Unique identifier for this step. Other steps reference its output as @name.field",
193
- ),
194
- description: z.string().optional().describe("What this step does"),
195
- action: StepActionSchema,
196
- input: z
197
- .record(z.string(), z.unknown())
198
- .optional()
199
- .describe(
200
- "Data passed to the action. Use @ref for dynamic values: @input.field (workflow input), @stepName.field (previous step output), @item/@index (loop context), @ctx.execution_id (current execution ID). Example: { 'userId': '@input.user_id', 'data': '@fetch.result', 'executionId': '@ctx.execution_id' }",
201
- ),
202
- outputSchema: JsonSchemaSchema.optional().describe(
203
- "Optional JSON Schema describing the expected output of the step.",
204
- ),
205
- config: StepConfigSchema.optional().describe("Retry and timeout settings"),
206
- forEach: z
207
- .object({
208
- ref: z.string().describe("@ ref to the step to iterate over"),
209
- concurrency: z
210
- .number()
211
- .optional()
212
- .default(1)
213
- .describe("max parallel iterations. default is 1 (sequential)"),
214
- })
215
- .optional(),
216
- bail: z
217
- .union([z.literal(true), StepConditionSchema])
218
- .optional()
219
- .describe(
220
- "Early return: if true, the workflow exits with this step's output on successful completion. If a condition object, the workflow exits only when the condition is met. Bail is ignored on step error.",
221
- ),
222
- });
223
-
224
- export type Step = z.infer<typeof StepSchema>;
225
-
226
- /**
227
- * Workflow Execution Status
228
- *
229
- * States:
230
- * - pending: Created but not started
231
- * - running: Currently executing
232
- * - completed: Successfully finished
233
- * - cancelled: Manually cancelled
234
- */
235
-
236
- const WorkflowExecutionStatusEnum = z
237
- .enum(["enqueued", "running", "success", "error", "failed", "cancelled"])
238
- .default("enqueued");
239
- export type WorkflowExecutionStatus = z.infer<
240
- typeof WorkflowExecutionStatusEnum
241
- >;
242
-
243
- /**
244
- * Workflow Execution Schema
245
- *
246
- * Includes lock columns and retry tracking.
247
- */
248
- export const WorkflowExecutionSchema = BaseCollectionEntitySchema.extend({
249
- virtual_mcp_id: z
250
- .string()
251
- .describe(
252
- "ID of the virtual MCP (agent) that will be used to execute the workflow",
253
- ),
254
- status: WorkflowExecutionStatusEnum.describe(
255
- "Current status of the workflow execution",
256
- ),
257
- input: z
258
- .record(z.string(), z.unknown())
259
- .optional()
260
- .describe("Input data for the workflow execution"),
261
- output: z
262
- .unknown()
263
- .optional()
264
- .describe("Output data for the workflow execution"),
265
- completed_at_epoch_ms: z
266
- .number()
267
- .nullish()
268
- .describe("Timestamp of when the workflow execution completed"),
269
- start_at_epoch_ms: z
270
- .number()
271
- .nullish()
272
- .describe("Timestamp of when the workflow execution started or will start"),
273
- timeout_ms: z
274
- .number()
275
- .nullish()
276
- .describe("Timeout in milliseconds for the workflow execution"),
277
- deadline_at_epoch_ms: z
278
- .number()
279
- .nullish()
280
- .describe(
281
- "Deadline for the workflow execution - when the workflow execution will be cancelled if it is not completed. This is read-only and is set by the workflow engine when an execution is created.",
282
- ),
283
- error: z
284
- .unknown()
285
- .nullish()
286
- .describe("Error that occurred during the workflow execution"),
287
- completed_steps: z
288
- .object({
289
- success: z
290
- .array(
291
- z.object({
292
- name: z.string(),
293
- completed_at_epoch_ms: z.number(),
294
- }),
295
- )
296
- .describe("Names of the steps that were completed successfully"),
297
- error: z.array(z.string()).describe("Names of the steps that errored"),
298
- })
299
- .optional()
300
- .describe("Names of the steps that were completed and their status"),
301
- running_steps: z
302
- .array(z.string())
303
- .optional()
304
- .describe("Names of the steps that are currently running"),
305
- });
306
- export type WorkflowExecution = z.infer<typeof WorkflowExecutionSchema>;
307
-
308
- /**
309
- * Event Type Enum
310
- *
311
- * Event types for the unified events table:
312
- * - signal: External signal (human-in-the-loop)
313
- * - timer: Durable sleep wake-up
314
- * - message: Inter-workflow communication (send/recv)
315
- * - output: Published value (setEvent/getEvent)
316
- * - step_started: Observability - step began
317
- * - step_completed: Observability - step finished
318
- * - workflow_started: Workflow began execution
319
- * - workflow_completed: Workflow finished
320
- */
321
- export const EventTypeEnum = z.enum([
322
- "signal",
323
- "timer",
324
- "message",
325
- "output",
326
- "step_started",
327
- "step_completed",
328
- "workflow_started",
329
- "workflow_completed",
330
- ]);
331
-
332
- export type EventType = z.infer<typeof EventTypeEnum>;
333
-
334
- /**
335
- * Workflow Event Schema
336
- *
337
- * Unified events table for signals, timers, messages, and observability.
338
- */
339
- export const WorkflowEventSchema = BaseCollectionEntitySchema.extend({
340
- execution_id: z.string(),
341
- type: EventTypeEnum,
342
- name: z.string().nullish(),
343
- payload: z.unknown().optional(),
344
- visible_at: z.number().nullish(),
345
- consumed_at: z.number().nullish(),
346
- source_execution_id: z.string().nullish(),
347
- });
348
-
349
- export type WorkflowEvent = z.infer<typeof WorkflowEventSchema>;
350
-
351
- /**
352
- * Workflow Schema - A sequence of steps that execute with data flowing between them
353
- *
354
- * Key concepts:
355
- * - Steps run in parallel unless they reference each other via @ref
356
- * - Use @ref to wire data: @input.field, @stepName.field, @item (in loops)
357
- * - Execution order is auto-determined from @ref dependencies
358
- *
359
- * Example: 2 parallel fetches + 1 merge step
360
- * {
361
- * "title": "Fetch and Merge",
362
- * "steps": [
363
- * { "name": "fetch_users", "action": { "connectionId": "api", "toolName": "getUsers" } },
364
- * { "name": "fetch_orders", "action": { "connectionId": "api", "toolName": "getOrders" } },
365
- * { "name": "merge", "action": { "code": "..." }, "input": { "users": "@fetch_users.data", "orders": "@fetch_orders.data" } }
366
- * ]
367
- * }
368
- * → fetch_users and fetch_orders run in parallel; merge waits for both
369
- */
370
- export const WorkflowSchema = BaseCollectionEntitySchema.extend({
371
- description: z
372
- .string()
373
- .optional()
374
- .describe("Human-readable summary of what this workflow does"),
375
-
376
- input_schema: JsonSchemaSchema.optional().describe(
377
- "Optional JSON Schema describing the expected input for this workflow.",
378
- ),
379
-
380
- steps: z
381
- .array(StepSchema)
382
- .describe(
383
- "Ordered list of steps. Execution order is auto-determined by @ref dependencies: steps with no @ref dependencies run in parallel; steps referencing @stepName wait for that step to complete.",
384
- ),
385
- });
386
-
387
- export type Workflow = z.infer<typeof WorkflowSchema>;
388
-
389
- /**
390
- * WORKFLOW Collection Binding
391
- *
392
- * Collection bindings for workflows (read-only).
393
- * Provides LIST and GET operations for workflows.
394
- */
395
- export const WORKFLOWS_COLLECTION_BINDING = createCollectionBindings(
396
- "workflow",
397
- WorkflowSchema,
398
- );
399
-
400
- const DEFAULT_STEP_CONFIG: StepConfig = {
401
- maxAttempts: 1,
402
- timeoutMs: 30000,
403
- };
404
-
405
- // export const DEFAULT_WAIT_FOR_SIGNAL_STEP: Omit<Step, "name"> = {
406
- // action: {
407
- // signalName: "approve_output",
408
- // },
409
- // outputSchema: {
410
- // type: "object",
411
- // properties: {
412
- // approved: {
413
- // type: "boolean",
414
- // description: "Whether the output was approved",
415
- // },
416
- // },
417
- // },
418
- // };
419
- export const DEFAULT_TOOL_STEP: Omit<Step, "name"> = {
420
- action: {
421
- toolName: "LLM_DO_GENERATE",
422
- transformCode: `
423
- interface Input {
424
-
425
- }
426
- export default function(stepInput) { return stepInput.result }`,
427
- },
428
- input: {
429
- modelId: "anthropic/claude-4.5-haiku",
430
- prompt: "Write a haiku about the weather.",
431
- },
432
-
433
- config: DEFAULT_STEP_CONFIG,
434
- outputSchema: {
435
- type: "object",
436
- properties: {
437
- result: {
438
- type: "string",
439
- description: "The result of the step",
440
- },
441
- },
442
- },
443
- };
444
- export const DEFAULT_CODE_STEP: Step = {
445
- name: "Initial Step",
446
- action: {
447
- code: `
448
- interface Input {
449
- example: string;
450
- }
451
-
452
- interface Output {
453
- result: unknown;
454
- }
455
-
456
- export default async function(stepInput: Input): Promise<Output> {
457
- return {
458
- result: stepInput.example
459
- }
460
- }`,
461
- },
462
- config: DEFAULT_STEP_CONFIG,
463
- outputSchema: {
464
- type: "object",
465
- properties: {
466
- result: {
467
- type: "string",
468
- description: "The result of the step",
469
- },
470
- },
471
- required: ["result"],
472
- description:
473
- "The output of the step. This is a JSON Schema describing the expected output of the step.",
474
- },
475
- };
476
-
477
- export const createDefaultWorkflow = (id?: string): Workflow => ({
478
- id: id || crypto.randomUUID(),
479
- title: "Default Workflow",
480
- description: "The default workflow for the toolkit",
481
- steps: [DEFAULT_CODE_STEP],
482
- created_at: new Date().toISOString(),
483
- updated_at: new Date().toISOString(),
484
- });
485
-
486
- export const WORKFLOW_EXECUTIONS_COLLECTION_BINDING = createCollectionBindings(
487
- "workflow_execution",
488
- WorkflowExecutionSchema,
489
- );
490
-
491
- /**
492
- * WORKFLOWS Binding
493
- *
494
- * Defines the interface for workflow providers.
495
- * Any MCP that implements this binding can provide configurable workflows.
496
- *
497
- * Required tools:
498
- * - COLLECTION_WORKFLOW_LIST: List available workflows with their configurations
499
- * - COLLECTION_WORKFLOW_GET: Get a single workflow by ID (includes steps and triggers)
500
- */
501
- export const WORKFLOW_COLLECTIONS_BINDINGS = [
502
- ...WORKFLOWS_COLLECTION_BINDING,
503
- ...WORKFLOW_EXECUTIONS_COLLECTION_BINDING,
504
- ] as const satisfies Binder;
505
-
506
- export const WORKFLOW_BINDING = [
507
- ...WORKFLOW_COLLECTIONS_BINDINGS,
508
- ] satisfies ToolBinder[];
509
-
510
- export const WorkflowBinding = bindingClient(WORKFLOW_BINDING);
511
-
512
- export const WORKFLOW_EXECUTION_BINDING = createCollectionBindings(
513
- "workflow_execution",
514
- WorkflowExecutionSchema,
515
- );
516
-
517
- /**
518
- * DAG (Directed Acyclic Graph) utilities for workflow step execution
519
- *
520
- * Pure TypeScript functions for analyzing step dependencies and grouping
521
- * steps into execution levels for parallel execution.
522
- *
523
- * Can be used in both frontend (visualization) and backend (execution).
524
- */
525
-
526
- /**
527
- * Minimal step interface for DAG computation.
528
- * This allows the DAG utilities to work with any step-like object.
529
- */
530
- export interface DAGStep {
531
- name: string;
532
- input?: unknown;
533
- bail?: true | StepCondition;
534
- }
535
-
536
- /**
537
- * Extract all @ref references from a value recursively.
538
- * Finds patterns like @stepName or @stepName.field
539
- *
540
- * @param input - Any value that might contain @ref strings
541
- * @returns Array of unique reference names (without @ prefix)
542
- */
543
- export function getAllRefs(input: unknown): string[] {
544
- const refs: string[] = [];
545
-
546
- function traverse(value: unknown) {
547
- if (typeof value === "string") {
548
- const matches = value.match(/@([a-zA-Z_][a-zA-Z0-9_-]*)/g);
549
- if (matches) {
550
- refs.push(...matches.map((m) => m.substring(1))); // Remove @ prefix
551
- }
552
- } else if (Array.isArray(value)) {
553
- value.forEach(traverse);
554
- } else if (typeof value === "object" && value !== null) {
555
- Object.values(value).forEach(traverse);
556
- }
557
- }
558
-
559
- traverse(input);
560
- return [...new Set(refs)].sort(); // Dedupe and sort for consistent results
561
- }
562
-
563
- /**
564
- * Get the dependencies of a step (other steps it references).
565
- * Only returns dependencies that are actual step names (filters out built-ins like "item", "index", "input").
566
- *
567
- * @param step - The step to analyze
568
- * @param allStepNames - Set of all step names in the workflow
569
- * @returns Array of step names this step depends on
570
- */
571
- export function getStepDependencies(
572
- step: DAGStep,
573
- allStepNames: Set<string>,
574
- ): string[] {
575
- const deps: string[] = [];
576
-
577
- function traverse(value: unknown) {
578
- if (typeof value === "string") {
579
- // Match @stepName or @stepName.something patterns
580
- const matches = value.match(/@([a-zA-Z_][a-zA-Z0-9_-]*)/g);
581
- if (matches) {
582
- for (const match of matches) {
583
- const refName = match.substring(1); // Remove @
584
- // Only count as dependency if it references another step
585
- // (not "item", "index", "input" from forEach or workflow input)
586
- if (allStepNames.has(refName) && refName !== step.name) {
587
- deps.push(refName);
588
- }
589
- }
590
- }
591
- } else if (Array.isArray(value)) {
592
- value.forEach(traverse);
593
- } else if (typeof value === "object" && value !== null) {
594
- Object.values(value).forEach(traverse);
595
- }
596
- }
597
-
598
- traverse(step.input);
599
-
600
- // Extract dependency from bail condition ref
601
- if (step.bail && step.bail !== true) {
602
- traverse(step.bail.ref);
603
- }
604
-
605
- return [...new Set(deps)];
606
- }
607
-
608
- /**
609
- * Build edges for the DAG: [fromStep, toStep][]
610
- */
611
- export function buildDagEdges(steps: Step[]): [string, string][] {
612
- const stepNames = new Set(steps.map((s) => s.name));
613
- const edges: [string, string][] = [];
614
-
615
- for (const step of steps) {
616
- const deps = getStepDependencies(step, stepNames);
617
- for (const dep of deps) {
618
- edges.push([dep, step.name]);
619
- }
620
- }
621
-
622
- return edges;
623
- }
624
-
625
- /**
626
- * Compute topological levels for all steps.
627
- * Level 0 = no dependencies on other steps
628
- * Level N = depends on at least one step at level N-1
629
- *
630
- * @param steps - Array of steps to analyze
631
- * @returns Map from step name to level number
632
- */
633
- export function computeStepLevels<T extends DAGStep>(
634
- steps: T[],
635
- ): Map<string, number> {
636
- const stepNames = new Set(steps.map((s) => s.name));
637
- const levels = new Map<string, number>();
638
-
639
- // Build dependency map
640
- const depsMap = new Map<string, string[]>();
641
- for (const step of steps) {
642
- depsMap.set(step.name, getStepDependencies(step, stepNames));
643
- }
644
-
645
- // Compute level for each step (with memoization)
646
- function getLevel(stepName: string, visited: Set<string>): number {
647
- if (levels.has(stepName)) return levels.get(stepName)!;
648
- if (visited.has(stepName)) return 0; // Cycle detection
649
-
650
- visited.add(stepName);
651
- const deps = depsMap.get(stepName) || [];
652
-
653
- if (deps.length === 0) {
654
- levels.set(stepName, 0);
655
- return 0;
656
- }
657
-
658
- const maxDepLevel = Math.max(...deps.map((d) => getLevel(d, visited)));
659
- const level = maxDepLevel + 1;
660
- levels.set(stepName, level);
661
- return level;
662
- }
663
-
664
- for (const step of steps) {
665
- getLevel(step.name, new Set());
666
- }
667
-
668
- return levels;
669
- }
670
-
671
- /**
672
- * Group steps by their execution level.
673
- * Steps at the same level have no dependencies on each other and can run in parallel.
674
- *
675
- * @param steps - Array of steps to group
676
- * @returns Array of step arrays, where index is the level
677
- */
678
- export function groupStepsByLevel<T extends DAGStep>(steps: T[]): T[][] {
679
- const levels = computeStepLevels(steps);
680
- const maxLevel = Math.max(...Array.from(levels.values()), -1);
681
-
682
- const grouped: T[][] = [];
683
- for (let level = 0; level <= maxLevel; level++) {
684
- const stepsAtLevel = steps.filter((s) => levels.get(s.name) === level);
685
- if (stepsAtLevel.length > 0) {
686
- grouped.push(stepsAtLevel);
687
- }
688
- }
689
-
690
- return grouped;
691
- }
692
-
693
- /**
694
- * Validate that there are no cycles in the step dependencies.
695
- *
696
- * @param steps - Array of steps to validate
697
- * @returns Object with isValid and optional error message
698
- */
699
- export function validateNoCycles<T extends DAGStep>(
700
- steps: T[],
701
- ): { isValid: boolean; error?: string } {
702
- const stepNames = new Set(steps.map((s) => s.name));
703
- const depsMap = new Map<string, string[]>();
704
-
705
- for (const step of steps) {
706
- depsMap.set(step.name, getStepDependencies(step, stepNames));
707
- }
708
-
709
- const visited = new Set<string>();
710
- const recursionStack = new Set<string>();
711
-
712
- function hasCycle(stepName: string, path: string[]): string[] | null {
713
- if (recursionStack.has(stepName)) {
714
- return [...path, stepName];
715
- }
716
- if (visited.has(stepName)) {
717
- return null;
718
- }
719
-
720
- visited.add(stepName);
721
- recursionStack.add(stepName);
722
-
723
- const deps = depsMap.get(stepName) || [];
724
- for (const dep of deps) {
725
- const cycle = hasCycle(dep, [...path, stepName]);
726
- if (cycle) return cycle;
727
- }
728
-
729
- recursionStack.delete(stepName);
730
- return null;
731
- }
732
-
733
- for (const step of steps) {
734
- const cycle = hasCycle(step.name, []);
735
- if (cycle) {
736
- return {
737
- isValid: false,
738
- error: `Circular dependency detected: ${cycle.join(" -> ")}`,
739
- };
740
- }
741
- }
742
-
743
- return { isValid: true };
744
- }