@kadoa/mcp 0.7.0 → 0.7.2
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 -1
- package/dist/index.js +659 -413
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -17,7 +17,7 @@ var package_default;
|
|
|
17
17
|
var init_package = __esm(() => {
|
|
18
18
|
package_default = {
|
|
19
19
|
name: "@kadoa/mcp",
|
|
20
|
-
version: "0.7.
|
|
20
|
+
version: "0.7.2",
|
|
21
21
|
description: "Kadoa MCP Server — manage workflows from Claude Desktop, Cursor, and other MCP clients",
|
|
22
22
|
type: "module",
|
|
23
23
|
main: "dist/index.js",
|
|
@@ -44,7 +44,7 @@ var init_package = __esm(() => {
|
|
|
44
44
|
prepublishOnly: "bun run check-types && bun run test:unit && bun run build"
|
|
45
45
|
},
|
|
46
46
|
dependencies: {
|
|
47
|
-
"@kadoa/node-sdk": "^0.
|
|
47
|
+
"@kadoa/node-sdk": "^0.48.0",
|
|
48
48
|
"@modelcontextprotocol/client": "2.1.0",
|
|
49
49
|
"@modelcontextprotocol/express": "2.0.1",
|
|
50
50
|
"@modelcontextprotocol/node": "2.1.0",
|
|
@@ -212,252 +212,187 @@ function withVariableDashboardUrl(variable) {
|
|
|
212
212
|
return { ...variable, dashboardUrl: variableSettingsDashboardUrl() };
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
-
// src/tools/shared/
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
id: apiField,
|
|
234
|
-
name: apiField,
|
|
235
|
-
dashboardUrl: z.string(),
|
|
236
|
-
status: z.string(),
|
|
237
|
-
state: nullableString,
|
|
238
|
-
displayState: nullableString,
|
|
239
|
-
runState: nullableString,
|
|
240
|
-
urls: apiField,
|
|
241
|
-
totalRecords: apiField,
|
|
242
|
-
startedAt: apiField,
|
|
243
|
-
finishedAt: apiField,
|
|
244
|
-
createdAt: apiField,
|
|
245
|
-
assistant: z.object({
|
|
246
|
-
eligible: z.boolean(),
|
|
247
|
-
dataStale: z.boolean(),
|
|
248
|
-
session: z.object({ id: z.string(), threadId: apiField, status: apiField }).loose().nullable()
|
|
249
|
-
}).loose(),
|
|
250
|
-
template: z.object({ id: z.string(), pinnedVersion: apiField, latestVersion: apiField, isOutdated: z.boolean() }).loose().nullable(),
|
|
251
|
-
realtime: z.object({ enabled: z.boolean(), lastDataChangedAt: apiField, health: apiField }).loose(),
|
|
252
|
-
awaitingUserInput: apiField
|
|
253
|
-
}).loose();
|
|
254
|
-
ListWorkflowsOutput = z.object({ workflows: z.array(WorkflowRowOutput), pagination: PaginationOutput }).loose();
|
|
255
|
-
GetWorkflowOutput = z.object({
|
|
256
|
-
id: z.string(),
|
|
257
|
-
name: apiField,
|
|
258
|
-
dashboardUrl: z.string(),
|
|
259
|
-
description: apiField,
|
|
260
|
-
status: z.string(),
|
|
261
|
-
state: nullableString,
|
|
262
|
-
displayState: nullableString,
|
|
263
|
-
runState: nullableString,
|
|
264
|
-
urls: apiField,
|
|
265
|
-
prompt: nullableString,
|
|
266
|
-
intentSource: z.enum(["extraction_spec", "legacy_prompt"]).nullable(),
|
|
267
|
-
extractionSpec: apiField,
|
|
268
|
-
entity: apiField,
|
|
269
|
-
schema: apiField,
|
|
270
|
-
navigationMode: apiField,
|
|
271
|
-
dataStale: apiField,
|
|
272
|
-
assistantEligible: z.boolean(),
|
|
273
|
-
assistantSessionId: apiField,
|
|
274
|
-
assistantThreadId: apiField,
|
|
275
|
-
assistantSessionStatus: apiField,
|
|
276
|
-
assistant: z.object({ eligible: z.boolean(), sessionId: apiField, threadId: apiField, sessionStatus: apiField }).loose(),
|
|
277
|
-
template: z.object({
|
|
278
|
-
id: apiField,
|
|
279
|
-
name: apiField,
|
|
280
|
-
version: apiField,
|
|
281
|
-
controlledParts: z.array(z.string()),
|
|
282
|
-
hasSchemaValidationRules: apiField
|
|
283
|
-
}).loose().nullable(),
|
|
284
|
-
updateInterval: apiField,
|
|
285
|
-
schedules: apiField,
|
|
286
|
-
scheduleTimezone: apiField,
|
|
287
|
-
nextInvocation: apiField,
|
|
288
|
-
location: apiField,
|
|
289
|
-
limit: apiField,
|
|
290
|
-
tags: apiField,
|
|
291
|
-
totalRecords: apiField,
|
|
292
|
-
job: z.object({ id: apiField }).loose().nullable(),
|
|
293
|
-
startedAt: apiField,
|
|
294
|
-
finishedAt: apiField,
|
|
295
|
-
estimatedFinishedAtUtc: apiField,
|
|
296
|
-
errors: apiField,
|
|
297
|
-
createdAt: apiField,
|
|
298
|
-
notificationConfig: apiField,
|
|
299
|
-
channelCounts: apiField,
|
|
300
|
-
dataValidation: apiField,
|
|
301
|
-
validationStatistics: apiField,
|
|
302
|
-
realtime: z.object({ lastDataChangedAt: apiField, observerHealth: apiField }).loose().nullable()
|
|
303
|
-
}).loose();
|
|
304
|
-
FetchDataOutput = z.object({
|
|
305
|
-
data: z.array(z.record(z.string(), z.unknown())),
|
|
306
|
-
pagination: PaginationOutput,
|
|
307
|
-
totalCount: z.number().optional(),
|
|
308
|
-
hint: z.string().optional()
|
|
309
|
-
}).loose();
|
|
310
|
-
ASSISTANT_ACTIVITIES = [
|
|
311
|
-
"unsupported",
|
|
312
|
-
"not_started",
|
|
313
|
-
"working",
|
|
314
|
-
"waiting_for_input",
|
|
315
|
-
"idle",
|
|
316
|
-
"interrupted",
|
|
317
|
-
"failed",
|
|
318
|
-
"closed",
|
|
319
|
-
"unknown"
|
|
320
|
-
];
|
|
321
|
-
WorkflowAssistantOutput = z.object({
|
|
322
|
-
workflowId: z.string(),
|
|
323
|
-
assistantEligible: z.boolean(),
|
|
324
|
-
sessionId: nullableString,
|
|
325
|
-
threadId: apiField,
|
|
326
|
-
sessionStatus: apiField,
|
|
327
|
-
activity: z.enum(ASSISTANT_ACTIVITIES),
|
|
328
|
-
canAcceptUpdate: z.boolean(),
|
|
329
|
-
isTerminal: z.boolean(),
|
|
330
|
-
paused: apiField,
|
|
331
|
-
pause: apiField,
|
|
332
|
-
pendingQuestion: apiField
|
|
333
|
-
}).loose();
|
|
334
|
-
AssistantTimelineOutput = z.object({
|
|
335
|
-
workflowId: z.string(),
|
|
336
|
-
sessionId: apiField,
|
|
337
|
-
items: z.array(z.unknown()),
|
|
338
|
-
pagination: PaginationOutput,
|
|
339
|
-
dashboardUrl: z.string()
|
|
340
|
-
}).loose();
|
|
341
|
-
AssistantAnswerOutput = z.object({
|
|
342
|
-
success: z.literal(true),
|
|
343
|
-
workflowId: z.string(),
|
|
344
|
-
sessionId: z.string(),
|
|
345
|
-
threadId: apiField,
|
|
346
|
-
questionId: z.string(),
|
|
347
|
-
message: z.string()
|
|
348
|
-
}).loose();
|
|
349
|
-
AssistantControlOutput = z.object({
|
|
350
|
-
success: z.boolean(),
|
|
351
|
-
status: z.literal("accepted"),
|
|
352
|
-
action: z.enum(["interrupt", "resume", "stop"]),
|
|
353
|
-
workflowId: z.string(),
|
|
354
|
-
sessionId: z.string(),
|
|
355
|
-
delivery: apiField,
|
|
356
|
-
jobId: apiField,
|
|
357
|
-
message: z.string()
|
|
358
|
-
}).loose();
|
|
359
|
-
WorkflowStrategyOutput = z.object({
|
|
360
|
-
workflowId: z.string(),
|
|
361
|
-
sessionId: nullableString,
|
|
362
|
-
source: z.enum(["assistant_build", "workflow_metadata"]).nullable(),
|
|
363
|
-
status: z.enum(["ready", "building", "unavailable"]),
|
|
364
|
-
strategy: apiField,
|
|
365
|
-
message: z.string().optional()
|
|
366
|
-
}).loose();
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
// src/tools/shared/pagination.ts
|
|
370
|
-
import { z as z2 } from "zod";
|
|
371
|
-
function limitInput(options) {
|
|
372
|
-
const range = `limit must be a whole number from 1 to ${options.max}`;
|
|
373
|
-
const overMax = `${range}. ${options.overMaxHint ?? "Page with pagination.next."}`;
|
|
374
|
-
return z2.preprocess(coerceNumber(), z2.number().int(range).min(1, range).max(options.max, overMax)).optional().describe(`${options.noun} per page, 1-${options.max} (default ${options.defaultLimit})`);
|
|
375
|
-
}
|
|
376
|
-
function offsetInput(param, noun) {
|
|
377
|
-
const range = `${param} must be a whole number from 0 to ${MAX_OFFSET}`;
|
|
378
|
-
return z2.preprocess(coerceNumber(), z2.number().int(range).min(0, range).max(MAX_OFFSET, range)).optional().describe(`Number of ${noun} to skip (default 0)`);
|
|
379
|
-
}
|
|
380
|
-
function pageInput() {
|
|
381
|
-
const range = `page must be a whole number from 1 to ${MAX_PAGE}`;
|
|
382
|
-
return z2.preprocess(coerceNumber(), z2.number().int(range).min(1, range).max(MAX_PAGE, range)).optional().describe("Page number, 1-based (default 1)");
|
|
383
|
-
}
|
|
384
|
-
function nextArgs(args, patch) {
|
|
385
|
-
const next = {};
|
|
386
|
-
for (const [key, value] of Object.entries({ ...args, ...patch })) {
|
|
387
|
-
if (value !== undefined)
|
|
388
|
-
next[key] = value;
|
|
215
|
+
// src/tools/shared/portable-schema.ts
|
|
216
|
+
function lookupRef(ref, defs) {
|
|
217
|
+
if (typeof ref !== "string")
|
|
218
|
+
return;
|
|
219
|
+
const match = /^#\/(?:\$defs|definitions)\/(.+)$/.exec(ref);
|
|
220
|
+
if (!match)
|
|
221
|
+
return;
|
|
222
|
+
const name = match[1].replace(/~1/g, "/").replace(/~0/g, "~");
|
|
223
|
+
const def = defs[name];
|
|
224
|
+
return isObject(def) ? { name, def } : undefined;
|
|
225
|
+
}
|
|
226
|
+
function mergeSchemas(base, extra) {
|
|
227
|
+
const merged = { ...extra, ...base };
|
|
228
|
+
if (isObject(base.properties) || isObject(extra.properties)) {
|
|
229
|
+
merged.properties = {
|
|
230
|
+
...isObject(extra.properties) ? extra.properties : {},
|
|
231
|
+
...isObject(base.properties) ? base.properties : {}
|
|
232
|
+
};
|
|
389
233
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
234
|
+
if (Array.isArray(base.required) || Array.isArray(extra.required)) {
|
|
235
|
+
const required = [
|
|
236
|
+
...Array.isArray(extra.required) ? extra.required : [],
|
|
237
|
+
...Array.isArray(base.required) ? base.required : []
|
|
238
|
+
];
|
|
239
|
+
merged.required = [...new Set(required)];
|
|
240
|
+
}
|
|
241
|
+
return merged;
|
|
242
|
+
}
|
|
243
|
+
function walk(node, defs, refCounts, io) {
|
|
244
|
+
if (!isObject(node))
|
|
245
|
+
return node;
|
|
246
|
+
let current = { ...node };
|
|
247
|
+
let counts = refCounts;
|
|
248
|
+
while ("$ref" in current) {
|
|
249
|
+
const { $ref, ...site } = current;
|
|
250
|
+
const target = lookupRef($ref, defs);
|
|
251
|
+
if (!target) {
|
|
252
|
+
current = site;
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
255
|
+
const seen = counts.get(target.name) ?? 0;
|
|
256
|
+
counts = new Map(counts).set(target.name, seen + 1);
|
|
257
|
+
if (seen >= MAX_REF_DEPTH) {
|
|
258
|
+
const description = typeof target.def.description === "string" ? target.def.description : STUB_DESCRIPTION;
|
|
259
|
+
const type = typeof target.def.type === "string" ? target.def.type : io === "input" ? "object" : undefined;
|
|
260
|
+
current = { ...type ? { type } : {}, description, ...site };
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
current = { ...target.def, ...site };
|
|
264
|
+
}
|
|
265
|
+
for (const keyword of DROPPED_KEYWORDS)
|
|
266
|
+
delete current[keyword];
|
|
267
|
+
if ("const" in current) {
|
|
268
|
+
const { const: value, ...rest } = current;
|
|
269
|
+
current = typeof value === "string" ? { ...rest, enum: [value] } : rest;
|
|
270
|
+
if (current.type === undefined && ["string", "number", "boolean"].includes(typeof value)) {
|
|
271
|
+
current.type = typeof value;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (typeof current.exclusiveMinimum === "number") {
|
|
275
|
+
const bound = current.exclusiveMinimum;
|
|
276
|
+
current.minimum = typeof current.minimum === "number" ? Math.max(current.minimum, bound) : bound;
|
|
277
|
+
}
|
|
278
|
+
if (typeof current.exclusiveMaximum === "number") {
|
|
279
|
+
const bound = current.exclusiveMaximum;
|
|
280
|
+
current.maximum = typeof current.maximum === "number" ? Math.min(current.maximum, bound) : bound;
|
|
281
|
+
}
|
|
282
|
+
delete current.exclusiveMinimum;
|
|
283
|
+
delete current.exclusiveMaximum;
|
|
284
|
+
if (io === "input" && Array.isArray(current.enum) && Array.isArray(current.type) && current.type.includes("null")) {
|
|
285
|
+
current.enum = current.enum.filter((member) => member !== null);
|
|
286
|
+
}
|
|
287
|
+
if (Array.isArray(current.enum) && current.enum.some((member) => typeof member !== "string")) {
|
|
288
|
+
delete current.enum;
|
|
289
|
+
}
|
|
290
|
+
if (Array.isArray(current.prefixItems)) {
|
|
291
|
+
const { prefixItems, ...rest } = current;
|
|
292
|
+
current = rest;
|
|
293
|
+
if (current.items === undefined && prefixItems.length > 0) {
|
|
294
|
+
current.items = prefixItems.length === 1 ? prefixItems[0] : { anyOf: prefixItems };
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
if (isObject(current.properties)) {
|
|
298
|
+
current.properties = Object.fromEntries(Object.entries(current.properties).map(([name, value]) => [name, walk(value, defs, counts, io)]));
|
|
299
|
+
}
|
|
300
|
+
if (isObject(current.items))
|
|
301
|
+
current.items = walk(current.items, defs, counts, io);
|
|
302
|
+
if (Array.isArray(current.oneOf)) {
|
|
303
|
+
current.anyOf = [...Array.isArray(current.anyOf) ? current.anyOf : [], ...current.oneOf];
|
|
304
|
+
delete current.oneOf;
|
|
305
|
+
}
|
|
306
|
+
if (Array.isArray(current.anyOf))
|
|
307
|
+
current.anyOf = current.anyOf.map((member) => walk(member, defs, counts, io));
|
|
308
|
+
if (Array.isArray(current.allOf)) {
|
|
309
|
+
const { allOf, ...rest } = current;
|
|
310
|
+
current = rest;
|
|
311
|
+
for (const member of allOf) {
|
|
312
|
+
const walked = walk(member, defs, counts, io);
|
|
313
|
+
if (isObject(walked))
|
|
314
|
+
current = mergeSchemas(current, walked);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
if (Array.isArray(current.type)) {
|
|
318
|
+
const types = current.type.filter((entry) => entry !== "null");
|
|
319
|
+
const nullable = types.length !== current.type.length;
|
|
320
|
+
if (types.length === 0) {
|
|
321
|
+
current.type = "null";
|
|
322
|
+
} else if (types.length === 1 && !(nullable && io === "output")) {
|
|
323
|
+
current.type = types[0];
|
|
324
|
+
if (nullable)
|
|
325
|
+
current.nullable = true;
|
|
326
|
+
} else {
|
|
327
|
+
const members = current.type.map((type) => ({ type }));
|
|
328
|
+
delete current.type;
|
|
329
|
+
current.anyOf = members;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if (Array.isArray(current.anyOf) && current.anyOf.some(isNullSchema)) {
|
|
333
|
+
const members = current.anyOf.filter((member) => !isNullSchema(member));
|
|
334
|
+
const { anyOf: _anyOf, ...rest } = current;
|
|
335
|
+
if (members.length === 0) {
|
|
336
|
+
current = { ...rest, type: "null" };
|
|
337
|
+
} else if (io === "input" && members.length === 1 && isObject(members[0])) {
|
|
338
|
+
const merged = mergeSchemas(rest, members[0]);
|
|
339
|
+
if (typeof merged.type === "string")
|
|
340
|
+
current = { ...merged, nullable: true };
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return current;
|
|
448
344
|
}
|
|
449
|
-
function
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
345
|
+
function toPortableJsonSchema(schema, options = {}) {
|
|
346
|
+
const io = options.io ?? "input";
|
|
347
|
+
const defs = {
|
|
348
|
+
...isObject(schema.definitions) ? schema.definitions : {},
|
|
349
|
+
...isObject(schema.$defs) ? schema.$defs : {}
|
|
453
350
|
};
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
351
|
+
return walk(schema, defs, new Map, io);
|
|
352
|
+
}
|
|
353
|
+
var NON_PORTABLE_KEYWORDS, DROPPED_KEYWORDS, MAX_REF_DEPTH = 3, STUB_DESCRIPTION = "Nested value of the same shape", isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value), isNullSchema = (schema) => isObject(schema) && schema.type === "null";
|
|
354
|
+
var init_portable_schema = __esm(() => {
|
|
355
|
+
NON_PORTABLE_KEYWORDS = new Set([
|
|
356
|
+
"$ref",
|
|
357
|
+
"$defs",
|
|
358
|
+
"definitions",
|
|
359
|
+
"const",
|
|
360
|
+
"oneOf",
|
|
361
|
+
"allOf",
|
|
362
|
+
"exclusiveMinimum",
|
|
363
|
+
"exclusiveMaximum",
|
|
364
|
+
"prefixItems",
|
|
365
|
+
"$schema",
|
|
366
|
+
"additionalProperties",
|
|
367
|
+
"propertyNames",
|
|
368
|
+
"multipleOf",
|
|
369
|
+
"uniqueItems",
|
|
370
|
+
"id",
|
|
371
|
+
"$id",
|
|
372
|
+
"contentEncoding",
|
|
373
|
+
"contentMediaType",
|
|
374
|
+
"dependentRequired",
|
|
375
|
+
"patternProperties"
|
|
376
|
+
]);
|
|
377
|
+
DROPPED_KEYWORDS = [
|
|
378
|
+
"$schema",
|
|
379
|
+
"$id",
|
|
380
|
+
"id",
|
|
381
|
+
"$defs",
|
|
382
|
+
"definitions",
|
|
383
|
+
"additionalProperties",
|
|
384
|
+
"propertyNames",
|
|
385
|
+
"patternProperties",
|
|
386
|
+
"multipleOf",
|
|
387
|
+
"uniqueItems",
|
|
388
|
+
"contentEncoding",
|
|
389
|
+
"contentMediaType",
|
|
390
|
+
"dependentRequired"
|
|
391
|
+
];
|
|
392
|
+
});
|
|
458
393
|
|
|
459
394
|
// src/tools/shared/schemas.ts
|
|
460
|
-
import { z
|
|
395
|
+
import { z } from "zod";
|
|
461
396
|
function isPlainObject(value) {
|
|
462
397
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
463
398
|
}
|
|
@@ -519,18 +454,14 @@ function stampSchemaValidationRules(rules, attribution, previous) {
|
|
|
519
454
|
return Object.fromEntries(Object.entries(rules).map(([name, field]) => [name, stampField(field, previousFields[name])]));
|
|
520
455
|
}
|
|
521
456
|
function strictSchema(shape) {
|
|
522
|
-
return
|
|
457
|
+
return portable(z.object(shape).strict());
|
|
523
458
|
}
|
|
524
|
-
function
|
|
459
|
+
function portable(schema) {
|
|
525
460
|
const jsonSchema = schema["~standard"].jsonSchema;
|
|
526
461
|
const emitInput = jsonSchema.input;
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
for (const def of Object.values(defs ?? {}))
|
|
531
|
-
delete def.id;
|
|
532
|
-
return result;
|
|
533
|
-
};
|
|
462
|
+
const emitOutput = jsonSchema.output;
|
|
463
|
+
jsonSchema.input = (params) => toPortableJsonSchema(emitInput(params), { io: "input" });
|
|
464
|
+
jsonSchema.output = (params) => toPortableJsonSchema(emitOutput(params), { io: "output" });
|
|
534
465
|
return schema;
|
|
535
466
|
}
|
|
536
467
|
function normalizeDataFilters(val, ctx) {
|
|
@@ -555,7 +486,7 @@ function normalizeDataFilters(val, ctx) {
|
|
|
555
486
|
code: "custom",
|
|
556
487
|
message: "Grouped filters ({logicalOperator, conditions}) are not supported. Send a flat array of " + "{field, operator, value} filters instead; multiple filters are combined with OR."
|
|
557
488
|
});
|
|
558
|
-
return
|
|
489
|
+
return z.NEVER;
|
|
559
490
|
}
|
|
560
491
|
return [v];
|
|
561
492
|
}
|
|
@@ -563,7 +494,8 @@ function normalizeDataFilters(val, ctx) {
|
|
|
563
494
|
}
|
|
564
495
|
var UpdateIntervalSchema, NOTIFICATION_EVENT_TYPES, NotificationEventTypeSchema, SchemaFieldDataTypeSchema, SchemaFieldShape, SchemaValidationPresenceRule, SchemaValidationUniquenessRule, SchemaValidationBoundRule, SchemaValidationStringFormatRule, SchemaValidationFieldRulesSchema, SchemaValidationRulesSchema, SCHEMA_VALIDATION_ATTRIBUTION_KEYS, LocationSchema, MonitoringValueOperators, MonitoringValuelessOperators, MonitoringConditionOperatorSchema, MonitoringConditionSchema, MonitoringSchema, urlInputShape, extractionPromptInput, extractionInputShape, webhookAuthShape, notificationChannelsSchema, notificationsInputShape, requiredNotificationsInputShape, DATA_FILTER_OPERATORS, DATA_FILTER_VALUELESS_OPERATORS, DATA_FILTER_LIST_OPERATORS, DataFilterSchema, dataFiltersInput;
|
|
565
496
|
var init_schemas = __esm(() => {
|
|
566
|
-
|
|
497
|
+
init_portable_schema();
|
|
498
|
+
UpdateIntervalSchema = z.enum([
|
|
567
499
|
"ONLY_ONCE",
|
|
568
500
|
"EVERY_10_MINUTES",
|
|
569
501
|
"HALF_HOURLY",
|
|
@@ -597,8 +529,8 @@ var init_schemas = __esm(() => {
|
|
|
597
529
|
"credits_low",
|
|
598
530
|
"free_trial_ending"
|
|
599
531
|
];
|
|
600
|
-
NotificationEventTypeSchema =
|
|
601
|
-
SchemaFieldDataTypeSchema =
|
|
532
|
+
NotificationEventTypeSchema = z.enum(NOTIFICATION_EVENT_TYPES);
|
|
533
|
+
SchemaFieldDataTypeSchema = z.enum([
|
|
602
534
|
"STRING",
|
|
603
535
|
"NUMBER",
|
|
604
536
|
"BOOLEAN",
|
|
@@ -611,61 +543,61 @@ var init_schemas = __esm(() => {
|
|
|
611
543
|
"ARRAY"
|
|
612
544
|
]);
|
|
613
545
|
SchemaFieldShape = {
|
|
614
|
-
name:
|
|
615
|
-
description:
|
|
616
|
-
example:
|
|
546
|
+
name: z.string().describe("Field name"),
|
|
547
|
+
description: z.string().optional().describe("What this field contains"),
|
|
548
|
+
example: z.string().describe("Example value"),
|
|
617
549
|
dataType: SchemaFieldDataTypeSchema.optional().describe("Data type for the field"),
|
|
618
|
-
isKey:
|
|
550
|
+
isKey: z.preprocess(coerceBoolean(), z.boolean()).optional().describe("Marks this field as a key field - the stable identity used to match records across runs. Change detection diffs records by their key fields, so a monitored or real-time workflow without one cannot tell an updated record from a new one. Set it on whatever uniquely identifies a row (a detail URL, an ID, a ticker).")
|
|
619
551
|
};
|
|
620
|
-
SchemaValidationPresenceRule =
|
|
621
|
-
target:
|
|
552
|
+
SchemaValidationPresenceRule = z.object({
|
|
553
|
+
target: z.number().int().min(0).max(100).step(20).describe("Expected percentage of rows with a value")
|
|
622
554
|
}).strict().meta({ id: "PresenceRule" });
|
|
623
|
-
SchemaValidationUniquenessRule =
|
|
624
|
-
target:
|
|
555
|
+
SchemaValidationUniquenessRule = z.object({
|
|
556
|
+
target: z.number().int().min(0).max(100).step(20).describe("Expected percentage of rows with a unique value")
|
|
625
557
|
}).strict().meta({ id: "UniquenessRule" });
|
|
626
|
-
SchemaValidationBoundRule =
|
|
627
|
-
value:
|
|
558
|
+
SchemaValidationBoundRule = z.object({
|
|
559
|
+
value: z.number().int().min(0).describe("Length or count bound")
|
|
628
560
|
}).strict().meta({ id: "BoundRule" });
|
|
629
|
-
SchemaValidationStringFormatRule =
|
|
630
|
-
|
|
631
|
-
kind:
|
|
632
|
-
charset:
|
|
633
|
-
|
|
634
|
-
kind:
|
|
635
|
-
preset:
|
|
561
|
+
SchemaValidationStringFormatRule = z.discriminatedUnion("kind", [
|
|
562
|
+
z.object({
|
|
563
|
+
kind: z.literal("FREE_TEXT"),
|
|
564
|
+
charset: z.discriminatedUnion("kind", [
|
|
565
|
+
z.object({
|
|
566
|
+
kind: z.literal("PRESET"),
|
|
567
|
+
preset: z.enum(["natural_language", "alphanumeric", "alpha"])
|
|
636
568
|
}).strict()
|
|
637
569
|
])
|
|
638
570
|
}).strict(),
|
|
639
|
-
|
|
640
|
-
kind:
|
|
641
|
-
source:
|
|
642
|
-
|
|
643
|
-
kind:
|
|
644
|
-
preset:
|
|
571
|
+
z.object({
|
|
572
|
+
kind: z.literal("FORMAT"),
|
|
573
|
+
source: z.discriminatedUnion("kind", [
|
|
574
|
+
z.object({
|
|
575
|
+
kind: z.literal("PRESET"),
|
|
576
|
+
preset: z.enum(["url", "email", "phone", "date", "datetime", "time", "uuid", "slug"])
|
|
645
577
|
}).strict(),
|
|
646
|
-
|
|
647
|
-
kind:
|
|
648
|
-
pattern:
|
|
578
|
+
z.object({
|
|
579
|
+
kind: z.literal("CUSTOM"),
|
|
580
|
+
pattern: z.string().min(1).describe("Regular expression pattern")
|
|
649
581
|
}).strict()
|
|
650
582
|
])
|
|
651
583
|
}).strict(),
|
|
652
|
-
|
|
653
|
-
kind:
|
|
654
|
-
source:
|
|
655
|
-
|
|
656
|
-
kind:
|
|
657
|
-
preset:
|
|
584
|
+
z.object({
|
|
585
|
+
kind: z.literal("LIST"),
|
|
586
|
+
source: z.discriminatedUnion("kind", [
|
|
587
|
+
z.object({
|
|
588
|
+
kind: z.literal("PRESET"),
|
|
589
|
+
preset: z.enum(["language2", "country2", "country3", "currency3", "month3", "usState2"])
|
|
658
590
|
}).strict(),
|
|
659
|
-
|
|
660
|
-
kind:
|
|
661
|
-
values:
|
|
591
|
+
z.object({
|
|
592
|
+
kind: z.literal("CUSTOM"),
|
|
593
|
+
values: z.array(z.string().min(1)).min(1).describe("Allowed string values")
|
|
662
594
|
}).strict()
|
|
663
595
|
])
|
|
664
596
|
}).strict()
|
|
665
597
|
]);
|
|
666
|
-
SchemaValidationFieldRulesSchema =
|
|
667
|
-
|
|
668
|
-
kind:
|
|
598
|
+
SchemaValidationFieldRulesSchema = z.lazy(() => z.discriminatedUnion("kind", [
|
|
599
|
+
z.object({
|
|
600
|
+
kind: z.literal("STRING"),
|
|
669
601
|
presence: SchemaValidationPresenceRule.optional(),
|
|
670
602
|
uniqueness: SchemaValidationUniquenessRule.optional(),
|
|
671
603
|
minLength: SchemaValidationBoundRule.optional(),
|
|
@@ -674,27 +606,27 @@ var init_schemas = __esm(() => {
|
|
|
674
606
|
maxHtmlElements: SchemaValidationBoundRule.optional(),
|
|
675
607
|
format: SchemaValidationStringFormatRule.optional()
|
|
676
608
|
}).strict(),
|
|
677
|
-
|
|
678
|
-
kind:
|
|
609
|
+
z.object({
|
|
610
|
+
kind: z.literal("NUMBER"),
|
|
679
611
|
presence: SchemaValidationPresenceRule.optional(),
|
|
680
612
|
uniqueness: SchemaValidationUniquenessRule.optional(),
|
|
681
|
-
minimum:
|
|
682
|
-
maximum:
|
|
683
|
-
maxDecimalPlaces:
|
|
613
|
+
minimum: z.object({ value: z.number().describe("Minimum numeric value") }).strict().optional(),
|
|
614
|
+
maximum: z.object({ value: z.number().describe("Maximum numeric value") }).strict().optional(),
|
|
615
|
+
maxDecimalPlaces: z.object({ value: z.number().int().min(0).max(16) }).strict().optional()
|
|
684
616
|
}).strict(),
|
|
685
|
-
|
|
686
|
-
kind:
|
|
617
|
+
z.object({
|
|
618
|
+
kind: z.literal("OTHER"),
|
|
687
619
|
presence: SchemaValidationPresenceRule.optional(),
|
|
688
620
|
uniqueness: SchemaValidationUniquenessRule.optional()
|
|
689
621
|
}).strict(),
|
|
690
|
-
|
|
691
|
-
kind:
|
|
622
|
+
z.object({
|
|
623
|
+
kind: z.literal("OBJECT"),
|
|
692
624
|
presence: SchemaValidationPresenceRule.optional(),
|
|
693
625
|
uniqueness: SchemaValidationUniquenessRule.optional(),
|
|
694
|
-
properties:
|
|
626
|
+
properties: z.record(z.string(), SchemaValidationFieldRulesSchema)
|
|
695
627
|
}).strict(),
|
|
696
|
-
|
|
697
|
-
kind:
|
|
628
|
+
z.object({
|
|
629
|
+
kind: z.literal("ARRAY"),
|
|
698
630
|
presence: SchemaValidationPresenceRule.optional(),
|
|
699
631
|
uniqueness: SchemaValidationUniquenessRule.optional(),
|
|
700
632
|
minItems: SchemaValidationBoundRule.optional(),
|
|
@@ -702,21 +634,21 @@ var init_schemas = __esm(() => {
|
|
|
702
634
|
items: SchemaValidationFieldRulesSchema.optional()
|
|
703
635
|
}).strict()
|
|
704
636
|
]));
|
|
705
|
-
SchemaValidationRulesSchema =
|
|
637
|
+
SchemaValidationRulesSchema = z.record(z.string(), SchemaValidationFieldRulesSchema);
|
|
706
638
|
SCHEMA_VALIDATION_ATTRIBUTION_KEYS = new Set(["editedBy", "editedByLabel", "editedAt"]);
|
|
707
|
-
LocationSchema =
|
|
708
|
-
type:
|
|
709
|
-
isoCode:
|
|
639
|
+
LocationSchema = z.object({
|
|
640
|
+
type: z.enum(["auto", "manual"]),
|
|
641
|
+
isoCode: z.string().trim().min(2).optional()
|
|
710
642
|
}).strict().superRefine((location, ctx) => {
|
|
711
643
|
if (location.type === "manual" && !location.isoCode) {
|
|
712
644
|
ctx.addIssue({
|
|
713
|
-
code:
|
|
645
|
+
code: z.ZodIssueCode.custom,
|
|
714
646
|
path: ["isoCode"],
|
|
715
647
|
message: "isoCode is required for manual location"
|
|
716
648
|
});
|
|
717
649
|
}
|
|
718
650
|
if (location.type === "auto" && location.isoCode) {
|
|
719
|
-
ctx.addIssue({ code:
|
|
651
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["isoCode"], message: "omit isoCode for auto location" });
|
|
720
652
|
}
|
|
721
653
|
});
|
|
722
654
|
MonitoringValueOperators = [
|
|
@@ -738,81 +670,81 @@ var init_schemas = __esm(() => {
|
|
|
738
670
|
"WITHIN_LAST_DAYS"
|
|
739
671
|
];
|
|
740
672
|
MonitoringValuelessOperators = ["IS_NULL", "IS_NOT_NULL", "IS_EMPTY", "IS_NOT_EMPTY"];
|
|
741
|
-
MonitoringConditionOperatorSchema =
|
|
742
|
-
MonitoringConditionSchema =
|
|
743
|
-
type:
|
|
744
|
-
field:
|
|
673
|
+
MonitoringConditionOperatorSchema = z.enum([...MonitoringValueOperators, ...MonitoringValuelessOperators]);
|
|
674
|
+
MonitoringConditionSchema = z.object({
|
|
675
|
+
type: z.enum(["SINGLE", "ANY"]),
|
|
676
|
+
field: z.string().trim().min(1).optional(),
|
|
745
677
|
operator: MonitoringConditionOperatorSchema,
|
|
746
|
-
value:
|
|
678
|
+
value: z.union([z.string(), z.number(), z.boolean()]).optional()
|
|
747
679
|
}).strict().superRefine((condition, ctx) => {
|
|
748
680
|
if (condition.type === "SINGLE" && !condition.field) {
|
|
749
681
|
ctx.addIssue({
|
|
750
|
-
code:
|
|
682
|
+
code: z.ZodIssueCode.custom,
|
|
751
683
|
path: ["field"],
|
|
752
684
|
message: "field is required for SINGLE conditions"
|
|
753
685
|
});
|
|
754
686
|
}
|
|
755
687
|
const needsValue = MonitoringValueOperators.includes(condition.operator);
|
|
756
688
|
if (needsValue && condition.value === undefined) {
|
|
757
|
-
ctx.addIssue({ code:
|
|
689
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["value"], message: `${condition.operator} requires a value` });
|
|
758
690
|
}
|
|
759
691
|
if (!needsValue && condition.value !== undefined) {
|
|
760
692
|
ctx.addIssue({
|
|
761
|
-
code:
|
|
693
|
+
code: z.ZodIssueCode.custom,
|
|
762
694
|
path: ["value"],
|
|
763
695
|
message: `${condition.operator} does not accept a value`
|
|
764
696
|
});
|
|
765
697
|
}
|
|
766
698
|
});
|
|
767
|
-
MonitoringSchema =
|
|
768
|
-
fields:
|
|
769
|
-
fieldName:
|
|
770
|
-
operator:
|
|
771
|
-
isKeyField:
|
|
699
|
+
MonitoringSchema = z.object({
|
|
700
|
+
fields: z.preprocess(coerceArray(), z.array(z.object({
|
|
701
|
+
fieldName: z.string().trim().min(1),
|
|
702
|
+
operator: z.enum(["changed", "added", "removed"]),
|
|
703
|
+
isKeyField: z.preprocess(coerceBoolean(), z.boolean()).optional()
|
|
772
704
|
}).strict()).min(1)),
|
|
773
|
-
conditions:
|
|
774
|
-
logicalOperator:
|
|
775
|
-
conditions:
|
|
705
|
+
conditions: z.object({
|
|
706
|
+
logicalOperator: z.enum(["AND", "OR"]),
|
|
707
|
+
conditions: z.preprocess(coerceArray(), z.array(MonitoringConditionSchema).min(1))
|
|
776
708
|
}).strict().optional()
|
|
777
709
|
}).strict();
|
|
778
710
|
urlInputShape = {
|
|
779
|
-
url:
|
|
780
|
-
urls:
|
|
711
|
+
url: z.string().optional().describe("Single URL - prefer using 'urls' instead. If both are provided, 'urls' takes precedence."),
|
|
712
|
+
urls: z.preprocess(coerceArray(true), z.array(z.string()).min(1)).optional().describe("Starting URLs for the workflow (array of strings). Also accepts a single URL string.")
|
|
781
713
|
};
|
|
782
|
-
extractionPromptInput =
|
|
714
|
+
extractionPromptInput = z.string().optional();
|
|
783
715
|
extractionInputShape = {
|
|
784
|
-
name:
|
|
785
|
-
entity:
|
|
786
|
-
schema:
|
|
716
|
+
name: z.string().optional().describe("Optional name for the workflow"),
|
|
717
|
+
entity: z.string().optional().describe("Entity name for extraction (e.g., 'Product', 'Job Posting')"),
|
|
718
|
+
schema: z.preprocess(coerceArray(), z.array(z.object(SchemaFieldShape).strict())).optional().describe("Extraction schema fields. If omitted, the AI agent auto-detects the schema. When you do supply fields, mark the one that identifies a record with isKey - change detection needs it.")
|
|
787
719
|
};
|
|
788
|
-
webhookAuthShape =
|
|
789
|
-
type:
|
|
790
|
-
token:
|
|
791
|
-
username:
|
|
792
|
-
password:
|
|
793
|
-
headers:
|
|
720
|
+
webhookAuthShape = z.object({
|
|
721
|
+
type: z.enum(["bearer", "basic", "header"]).describe("Authentication type"),
|
|
722
|
+
token: z.string().optional().describe("Bearer token (required for 'bearer' type)"),
|
|
723
|
+
username: z.string().optional().describe("Username (required for 'basic' type)"),
|
|
724
|
+
password: z.string().optional().describe("Password (required for 'basic' type)"),
|
|
725
|
+
headers: z.object({}).passthrough().optional().describe("Custom headers as key-value pairs (required for 'header' type)")
|
|
794
726
|
}).optional().describe("Authentication configuration for the webhook endpoint");
|
|
795
|
-
notificationChannelsSchema =
|
|
796
|
-
email:
|
|
797
|
-
recipients:
|
|
727
|
+
notificationChannelsSchema = z.object({
|
|
728
|
+
email: z.object({
|
|
729
|
+
recipients: z.array(z.string().email()).optional().describe("Email addresses to notify. Omit to use account default email.")
|
|
798
730
|
}).optional().describe("Send email notifications. Pass {} for account default email, or {recipients: [...]} for custom addresses."),
|
|
799
|
-
webhook:
|
|
800
|
-
url:
|
|
801
|
-
httpMethod:
|
|
731
|
+
webhook: z.object({
|
|
732
|
+
url: z.string().url().describe("Webhook endpoint URL"),
|
|
733
|
+
httpMethod: z.enum(["POST", "GET", "PUT", "PATCH"]).optional().describe("HTTP method (defaults to POST)"),
|
|
802
734
|
auth: webhookAuthShape
|
|
803
735
|
}).optional().describe("Send notifications via HTTP webhook"),
|
|
804
|
-
slack:
|
|
805
|
-
webhookUrl:
|
|
806
|
-
slackChannelId:
|
|
807
|
-
slackChannelName:
|
|
736
|
+
slack: z.object({
|
|
737
|
+
webhookUrl: z.string().url().optional().describe("Slack incoming webhook URL (legacy)"),
|
|
738
|
+
slackChannelId: z.string().optional().describe("Slack channel ID from OAuth integration (e.g. C01ABCDEF)"),
|
|
739
|
+
slackChannelName: z.string().optional().describe("Slack channel name (e.g. #alerts)")
|
|
808
740
|
}).optional().describe("Send notifications to a Slack channel. Use slackChannelId/slackChannelName for OAuth integration, or webhookUrl for legacy incoming webhooks."),
|
|
809
|
-
websocket:
|
|
741
|
+
websocket: z.boolean().optional().describe("Enable WebSocket notifications for programmatic real-time consumption")
|
|
810
742
|
});
|
|
811
743
|
notificationsInputShape = {
|
|
812
|
-
notifications:
|
|
744
|
+
notifications: z.preprocess(coerceJson(), notificationChannelsSchema.optional()).optional().describe("Optional notification channels to alert when data changes.")
|
|
813
745
|
};
|
|
814
746
|
requiredNotificationsInputShape = {
|
|
815
|
-
notifications:
|
|
747
|
+
notifications: z.preprocess(coerceJson(), notificationChannelsSchema).describe("Notification channels to alert when data changes.")
|
|
816
748
|
};
|
|
817
749
|
DATA_FILTER_OPERATORS = [
|
|
818
750
|
"EQUALS",
|
|
@@ -837,10 +769,10 @@ var init_schemas = __esm(() => {
|
|
|
837
769
|
];
|
|
838
770
|
DATA_FILTER_VALUELESS_OPERATORS = new Set(["IS_NULL", "IS_NOT_NULL", "IS_EMPTY", "IS_NOT_EMPTY"]);
|
|
839
771
|
DATA_FILTER_LIST_OPERATORS = new Set(["IN", "NOT_IN"]);
|
|
840
|
-
DataFilterSchema =
|
|
841
|
-
field:
|
|
842
|
-
operator:
|
|
843
|
-
value:
|
|
772
|
+
DataFilterSchema = z.object({
|
|
773
|
+
field: z.string().trim().min(1).describe("Field name from the workflow schema; one nested level is allowed, e.g. classification.match"),
|
|
774
|
+
operator: z.preprocess((val) => typeof val === "string" ? val.trim().toUpperCase() : val, z.enum(DATA_FILTER_OPERATORS)),
|
|
775
|
+
value: z.union([z.string(), z.number(), z.boolean(), z.array(z.union([z.string(), z.number()]))]).optional().describe("Omit for IS_NULL, IS_NOT_NULL, IS_EMPTY, IS_NOT_EMPTY; an array for IN/NOT_IN; days for WITHIN_LAST_DAYS")
|
|
844
776
|
}).strict().superRefine((filter, ctx) => {
|
|
845
777
|
if (DATA_FILTER_VALUELESS_OPERATORS.has(filter.operator)) {
|
|
846
778
|
if (filter.value !== undefined) {
|
|
@@ -865,9 +797,259 @@ var init_schemas = __esm(() => {
|
|
|
865
797
|
});
|
|
866
798
|
}
|
|
867
799
|
});
|
|
868
|
-
dataFiltersInput =
|
|
800
|
+
dataFiltersInput = z.preprocess(normalizeDataFilters, z.array(DataFilterSchema).optional()).optional().describe('Row filters, e.g. [{"field":"title","operator":"CONTAINS","value":"Manager"}]. Multiple filters are combined with OR. BEFORE, AFTER and WITHIN_LAST_DAYS only apply to DATE/DATETIME fields.');
|
|
869
801
|
});
|
|
870
802
|
|
|
803
|
+
// src/tools/shared/output-schemas.ts
|
|
804
|
+
import { z as z2 } from "zod";
|
|
805
|
+
var apiField, nullableString, PaginationOutput, workflowStatusFields, WorkflowRowOutput, ListWorkflowsOutput, GetWorkflowOutput, FetchDataOutput, ASSISTANT_ACTIVITIES, WorkflowAssistantOutput, AssistantTimelineOutput, AssistantAnswerOutput, AssistantControlOutput, WorkflowStrategyOutput;
|
|
806
|
+
var init_output_schemas = __esm(() => {
|
|
807
|
+
init_schemas();
|
|
808
|
+
apiField = z2.unknown().optional();
|
|
809
|
+
nullableString = z2.string().nullable();
|
|
810
|
+
PaginationOutput = portable(z2.object({
|
|
811
|
+
totalCount: z2.number().optional(),
|
|
812
|
+
hasMore: z2.boolean(),
|
|
813
|
+
limit: z2.number().optional(),
|
|
814
|
+
skip: z2.number().optional(),
|
|
815
|
+
offset: z2.number().optional(),
|
|
816
|
+
page: z2.number().optional(),
|
|
817
|
+
totalPages: z2.number().optional(),
|
|
818
|
+
nextCursor: nullableString.optional(),
|
|
819
|
+
next: z2.record(z2.string(), z2.unknown()).optional()
|
|
820
|
+
}).loose());
|
|
821
|
+
workflowStatusFields = {
|
|
822
|
+
status: z2.string(),
|
|
823
|
+
badges: z2.array(z2.object({ kind: z2.string(), status: z2.string(), label: z2.string() }).loose()),
|
|
824
|
+
group: nullableString,
|
|
825
|
+
reviewReason: z2.object({ code: z2.string(), explanation: z2.string() }).loose().nullable(),
|
|
826
|
+
execution: apiField,
|
|
827
|
+
assistantState: apiField,
|
|
828
|
+
workspaceActivity: apiField
|
|
829
|
+
};
|
|
830
|
+
WorkflowRowOutput = z2.object({
|
|
831
|
+
id: apiField,
|
|
832
|
+
name: apiField,
|
|
833
|
+
dashboardUrl: z2.string(),
|
|
834
|
+
...workflowStatusFields,
|
|
835
|
+
state: nullableString,
|
|
836
|
+
displayState: nullableString,
|
|
837
|
+
runState: nullableString,
|
|
838
|
+
urls: apiField,
|
|
839
|
+
totalRecords: apiField,
|
|
840
|
+
startedAt: apiField,
|
|
841
|
+
finishedAt: apiField,
|
|
842
|
+
createdAt: apiField,
|
|
843
|
+
assistant: z2.object({
|
|
844
|
+
eligible: z2.boolean(),
|
|
845
|
+
session: z2.object({ id: z2.string(), threadId: apiField, status: apiField }).loose().nullable()
|
|
846
|
+
}).loose(),
|
|
847
|
+
template: z2.object({ id: z2.string(), pinnedVersion: apiField, latestVersion: apiField, isOutdated: z2.boolean() }).loose().nullable(),
|
|
848
|
+
realtime: z2.object({ enabled: z2.boolean(), lastDataChangedAt: apiField, health: apiField }).loose(),
|
|
849
|
+
awaitingUserInput: apiField
|
|
850
|
+
}).loose();
|
|
851
|
+
ListWorkflowsOutput = portable(z2.object({ workflows: z2.array(WorkflowRowOutput), pagination: PaginationOutput }).loose());
|
|
852
|
+
GetWorkflowOutput = portable(z2.object({
|
|
853
|
+
id: z2.string(),
|
|
854
|
+
name: apiField,
|
|
855
|
+
dashboardUrl: z2.string(),
|
|
856
|
+
description: apiField,
|
|
857
|
+
...workflowStatusFields,
|
|
858
|
+
state: nullableString,
|
|
859
|
+
displayState: nullableString,
|
|
860
|
+
runState: nullableString,
|
|
861
|
+
urls: apiField,
|
|
862
|
+
prompt: nullableString,
|
|
863
|
+
intentSource: z2.enum(["extraction_spec", "legacy_prompt"]).nullable(),
|
|
864
|
+
extractionSpec: apiField,
|
|
865
|
+
entity: apiField,
|
|
866
|
+
schema: apiField,
|
|
867
|
+
navigationMode: apiField,
|
|
868
|
+
assistantEligible: z2.boolean(),
|
|
869
|
+
assistantSessionId: apiField,
|
|
870
|
+
assistantThreadId: apiField,
|
|
871
|
+
assistantSessionStatus: apiField,
|
|
872
|
+
assistant: z2.object({ eligible: z2.boolean(), sessionId: apiField, threadId: apiField, sessionStatus: apiField }).loose(),
|
|
873
|
+
template: z2.object({
|
|
874
|
+
id: apiField,
|
|
875
|
+
name: apiField,
|
|
876
|
+
version: apiField,
|
|
877
|
+
controlledParts: z2.array(z2.string()),
|
|
878
|
+
hasSchemaValidationRules: apiField
|
|
879
|
+
}).loose().nullable(),
|
|
880
|
+
updateInterval: apiField,
|
|
881
|
+
schedules: apiField,
|
|
882
|
+
scheduleTimezone: apiField,
|
|
883
|
+
nextInvocation: apiField,
|
|
884
|
+
location: apiField,
|
|
885
|
+
limit: apiField,
|
|
886
|
+
tags: apiField,
|
|
887
|
+
totalRecords: apiField,
|
|
888
|
+
job: z2.object({ id: apiField }).loose().nullable(),
|
|
889
|
+
startedAt: apiField,
|
|
890
|
+
finishedAt: apiField,
|
|
891
|
+
estimatedFinishedAtUtc: apiField,
|
|
892
|
+
errors: apiField,
|
|
893
|
+
createdAt: apiField,
|
|
894
|
+
notificationConfig: apiField,
|
|
895
|
+
channelCounts: apiField,
|
|
896
|
+
realtime: z2.object({ lastDataChangedAt: apiField, observerHealth: apiField }).loose().nullable()
|
|
897
|
+
}).loose());
|
|
898
|
+
FetchDataOutput = portable(z2.object({
|
|
899
|
+
data: z2.array(z2.record(z2.string(), z2.unknown())),
|
|
900
|
+
pagination: PaginationOutput,
|
|
901
|
+
totalCount: z2.number().optional(),
|
|
902
|
+
hint: z2.string().optional()
|
|
903
|
+
}).loose());
|
|
904
|
+
ASSISTANT_ACTIVITIES = [
|
|
905
|
+
"unsupported",
|
|
906
|
+
"not_started",
|
|
907
|
+
"working",
|
|
908
|
+
"waiting_for_input",
|
|
909
|
+
"idle",
|
|
910
|
+
"interrupted",
|
|
911
|
+
"failed",
|
|
912
|
+
"closed",
|
|
913
|
+
"unknown"
|
|
914
|
+
];
|
|
915
|
+
WorkflowAssistantOutput = portable(z2.object({
|
|
916
|
+
workflowId: z2.string(),
|
|
917
|
+
assistantEligible: z2.boolean(),
|
|
918
|
+
sessionId: nullableString,
|
|
919
|
+
threadId: apiField,
|
|
920
|
+
sessionStatus: apiField,
|
|
921
|
+
activity: z2.enum(ASSISTANT_ACTIVITIES),
|
|
922
|
+
canAcceptUpdate: z2.boolean(),
|
|
923
|
+
isTerminal: z2.boolean(),
|
|
924
|
+
paused: apiField,
|
|
925
|
+
pause: apiField,
|
|
926
|
+
pendingQuestion: apiField
|
|
927
|
+
}).loose());
|
|
928
|
+
AssistantTimelineOutput = portable(z2.object({
|
|
929
|
+
workflowId: z2.string(),
|
|
930
|
+
sessionId: apiField,
|
|
931
|
+
items: z2.array(z2.unknown()),
|
|
932
|
+
pagination: PaginationOutput,
|
|
933
|
+
dashboardUrl: z2.string()
|
|
934
|
+
}).loose());
|
|
935
|
+
AssistantAnswerOutput = portable(z2.object({
|
|
936
|
+
success: z2.literal(true),
|
|
937
|
+
workflowId: z2.string(),
|
|
938
|
+
sessionId: z2.string(),
|
|
939
|
+
threadId: apiField,
|
|
940
|
+
questionId: z2.string(),
|
|
941
|
+
message: z2.string()
|
|
942
|
+
}).loose());
|
|
943
|
+
AssistantControlOutput = portable(z2.object({
|
|
944
|
+
success: z2.boolean(),
|
|
945
|
+
status: z2.literal("accepted"),
|
|
946
|
+
action: z2.enum(["interrupt", "resume", "stop"]),
|
|
947
|
+
workflowId: z2.string(),
|
|
948
|
+
sessionId: z2.string(),
|
|
949
|
+
delivery: apiField,
|
|
950
|
+
jobId: apiField,
|
|
951
|
+
message: z2.string()
|
|
952
|
+
}).loose());
|
|
953
|
+
WorkflowStrategyOutput = portable(z2.object({
|
|
954
|
+
workflowId: z2.string(),
|
|
955
|
+
sessionId: nullableString,
|
|
956
|
+
source: z2.enum(["assistant_build", "workflow_metadata"]).nullable(),
|
|
957
|
+
status: z2.enum(["ready", "building", "unavailable"]),
|
|
958
|
+
strategy: apiField,
|
|
959
|
+
message: z2.string().optional()
|
|
960
|
+
}).loose());
|
|
961
|
+
});
|
|
962
|
+
|
|
963
|
+
// src/tools/shared/pagination.ts
|
|
964
|
+
import { z as z3 } from "zod";
|
|
965
|
+
function limitInput(options) {
|
|
966
|
+
const range = `limit must be a whole number from 1 to ${options.max}`;
|
|
967
|
+
const overMax = `${range}. ${options.overMaxHint ?? "Page with pagination.next."}`;
|
|
968
|
+
return z3.preprocess(coerceNumber(), z3.number().int(range).min(1, range).max(options.max, overMax)).optional().describe(`${options.noun} per page, 1-${options.max} (default ${options.defaultLimit})`);
|
|
969
|
+
}
|
|
970
|
+
function offsetInput(param, noun) {
|
|
971
|
+
const range = `${param} must be a whole number from 0 to ${MAX_OFFSET}`;
|
|
972
|
+
return z3.preprocess(coerceNumber(), z3.number().int(range).min(0, range).max(MAX_OFFSET, range)).optional().describe(`Number of ${noun} to skip (default 0)`);
|
|
973
|
+
}
|
|
974
|
+
function pageInput() {
|
|
975
|
+
const range = `page must be a whole number from 1 to ${MAX_PAGE}`;
|
|
976
|
+
return z3.preprocess(coerceNumber(), z3.number().int(range).min(1, range).max(MAX_PAGE, range)).optional().describe("Page number, 1-based (default 1)");
|
|
977
|
+
}
|
|
978
|
+
function nextArgs(args, patch) {
|
|
979
|
+
const next = {};
|
|
980
|
+
for (const [key, value] of Object.entries({ ...args, ...patch })) {
|
|
981
|
+
if (value !== undefined)
|
|
982
|
+
next[key] = value;
|
|
983
|
+
}
|
|
984
|
+
return next;
|
|
985
|
+
}
|
|
986
|
+
function withTotal(totalCount, hasMore) {
|
|
987
|
+
return totalCount !== undefined ? { totalCount, hasMore } : { hasMore };
|
|
988
|
+
}
|
|
989
|
+
function offsetPage(options) {
|
|
990
|
+
const nextOffset = options.offset + options.returned;
|
|
991
|
+
const more = options.hasMore ?? (options.totalCount !== undefined ? nextOffset < options.totalCount : options.returned >= options.limit);
|
|
992
|
+
const hasMore = options.returned > 0 && more;
|
|
993
|
+
const meta = withTotal(options.totalCount, hasMore);
|
|
994
|
+
meta.limit = options.limit;
|
|
995
|
+
meta[options.param] = options.offset;
|
|
996
|
+
if (hasMore)
|
|
997
|
+
meta.next = nextArgs(options.args, { [options.param]: nextOffset, limit: options.limit });
|
|
998
|
+
return meta;
|
|
999
|
+
}
|
|
1000
|
+
function numberedPage(options) {
|
|
1001
|
+
const more = options.totalPages !== undefined ? options.page < options.totalPages : options.totalCount !== undefined ? options.page * options.limit < options.totalCount : options.returned >= options.limit;
|
|
1002
|
+
const hasMore = options.returned > 0 && more;
|
|
1003
|
+
const meta = withTotal(options.totalCount, hasMore);
|
|
1004
|
+
meta.limit = options.limit;
|
|
1005
|
+
meta.page = options.page;
|
|
1006
|
+
if (options.totalPages !== undefined)
|
|
1007
|
+
meta.totalPages = options.totalPages;
|
|
1008
|
+
if (hasMore)
|
|
1009
|
+
meta.next = nextArgs(options.args, { page: options.page + 1, limit: options.limit });
|
|
1010
|
+
return meta;
|
|
1011
|
+
}
|
|
1012
|
+
function cursorPage(options) {
|
|
1013
|
+
const cursor = typeof options.nextCursor === "string" && options.nextCursor.length > 0 ? options.nextCursor : null;
|
|
1014
|
+
const meta = { hasMore: options.hasMore && cursor !== null, nextCursor: cursor };
|
|
1015
|
+
if (meta.hasMore)
|
|
1016
|
+
meta.next = nextArgs(options.args, { cursor });
|
|
1017
|
+
return meta;
|
|
1018
|
+
}
|
|
1019
|
+
function completeList(totalCount) {
|
|
1020
|
+
return { totalCount, hasMore: false };
|
|
1021
|
+
}
|
|
1022
|
+
var MAX_OFFSET = 1e6, MAX_PAGE = 1e5;
|
|
1023
|
+
var init_pagination = () => {};
|
|
1024
|
+
|
|
1025
|
+
// src/tools/shared/results.ts
|
|
1026
|
+
function asStructuredObject(data) {
|
|
1027
|
+
return data !== null && !Array.isArray(data) ? data : { result: data };
|
|
1028
|
+
}
|
|
1029
|
+
function structuredResult(data, extra = []) {
|
|
1030
|
+
const structuredContent = asStructuredObject(data);
|
|
1031
|
+
return {
|
|
1032
|
+
structuredContent,
|
|
1033
|
+
content: [{ type: "text", text: JSON.stringify(structuredContent) }, ...extra]
|
|
1034
|
+
};
|
|
1035
|
+
}
|
|
1036
|
+
function jsonResult(data) {
|
|
1037
|
+
return structuredResult(data);
|
|
1038
|
+
}
|
|
1039
|
+
function textOnlyResult(data, extra = []) {
|
|
1040
|
+
const structuredContent = asStructuredObject(data);
|
|
1041
|
+
return { content: [{ type: "text", text: JSON.stringify(structuredContent) }, ...extra] };
|
|
1042
|
+
}
|
|
1043
|
+
function errorResult(message, details = {}) {
|
|
1044
|
+
return {
|
|
1045
|
+
content: [{ type: "text", text: JSON.stringify({ error: message, ...details }) }],
|
|
1046
|
+
isError: true
|
|
1047
|
+
};
|
|
1048
|
+
}
|
|
1049
|
+
function withExtraContent(result, ...blocks) {
|
|
1050
|
+
return { ...result, content: [...result.content, ...blocks] };
|
|
1051
|
+
}
|
|
1052
|
+
|
|
871
1053
|
// src/tools/assistant.ts
|
|
872
1054
|
import { FREEFORM_ASSISTANT_ANSWER_KEY } from "@kadoa/node-sdk";
|
|
873
1055
|
import { z as z4 } from "zod";
|
|
@@ -2390,13 +2572,13 @@ function registerTemplateTools(server, ctx, helpers) {
|
|
|
2390
2572
|
description: `Publish a new version of a template. Versions capture the full workflow config: prompt, schema, and notifications. All fields are optional - include only what this version should set.
|
|
2391
2573
|
|
|
2392
2574
|
` + "THIS IS HOW YOU EDIT A TEMPLATE'S PROMPT OR SCHEMA. Published versions are immutable, so 'changing the template prompt' means publishing a new version here and then calling `apply_template_version` to push it onto linked workflows. " + "`update_template` only renames a template; it cannot touch prompt or schema.",
|
|
2393
|
-
inputSchema:
|
|
2575
|
+
inputSchema: strictSchema({
|
|
2394
2576
|
templateId: z10.string().min(1).describe("The template ID to publish a version for"),
|
|
2395
2577
|
prompt: z10.string().optional().describe("User prompt to copy into workflow config"),
|
|
2396
2578
|
schemaId: z10.string().optional().describe("Existing schema ID to reference (mutually exclusive with schemaFields)"),
|
|
2397
2579
|
schemaFields: z10.preprocess(coerceArray(), z10.array(z10.preprocess(normalizeTemplateSchemaField, z10.object(TemplateSchemaFieldShape).strict()))).optional().describe("Inline schema fields to create a new schema (mutually exclusive with schemaId)"),
|
|
2398
2580
|
schemaEntity: z10.string().optional().describe("Entity name for the inline schema"),
|
|
2399
|
-
schemaValidationRules: z10.preprocess((val) => stripSchemaValidationAttribution(coerceJson()(val)), SchemaValidationRulesSchema).optional().describe(
|
|
2581
|
+
schemaValidationRules: z10.preprocess((val) => stripSchemaValidationAttribution(coerceJson()(val)), SchemaValidationRulesSchema).optional().describe("Per-field schema validation rules keyed by field name. Each value is an object with a required `kind` and optional rules for that kind; unknown keys are rejected. " + "Every kind accepts presence and uniqueness, each { target: integer 0-100 in steps of 20 } (expected percentage of rows with a value / a unique value). " + "STRING: minLength, maxLength, minHtmlElements, maxHtmlElements, each { value: integer >= 0 }; format, one of " + '{ kind: "FORMAT", source: { kind: "PRESET", preset: url | email | phone | date | datetime | time | uuid | slug } or { kind: "CUSTOM", pattern: regex string } }, ' + '{ kind: "FREE_TEXT", charset: { kind: "PRESET", preset: natural_language | alphanumeric | alpha } }, or ' + '{ kind: "LIST", source: { kind: "PRESET", preset: language2 | country2 | country3 | currency3 | month3 | usState2 } or { kind: "CUSTOM", values: [non-empty strings] } }. ' + "NUMBER: minimum and maximum, each { value: number }; maxDecimalPlaces { value: integer 0-16 }. " + "OTHER: only presence and uniqueness. " + "OBJECT: properties (required), a map of nested field name to rule. " + "ARRAY: minItems, maxItems, each { value: integer >= 0 }; items, the rule for each element. " + 'Examples: { "price": { "kind": "NUMBER", "minimum": { "value": 0 } } }; { "email": { "kind": "STRING", "format": { "kind": "FORMAT", "source": { "kind": "PRESET", "preset": "email" } } } }. ' + "Edit attribution is stamped server-side, so send only rule values. Not inherited from prior versions - omitting this on a new version drops any rules the previous version had."),
|
|
2400
2582
|
notifications: z10.preprocess(coerceArray(), z10.array(z10.object({
|
|
2401
2583
|
eventType: z10.string().describe("Notification event type"),
|
|
2402
2584
|
eventConfiguration: z10.preprocess(coerceJson(), z10.record(z10.string(), z10.unknown())).optional(),
|
|
@@ -2412,7 +2594,7 @@ function registerTemplateTools(server, ctx, helpers) {
|
|
|
2412
2594
|
expressions: z10.preprocess(coerceArray(true), z10.array(z10.string())).optional().describe("Cron expressions when type='cron' (e.g. '0 8 * * 2')")
|
|
2413
2595
|
}))).optional().describe("Schedule specs. Required when interval='CUSTOM'.")
|
|
2414
2596
|
})).optional().describe("Template-controlled scheduling for linked workflows ({interval, schedules}). Linked workflows inherit and cannot override this when set.")
|
|
2415
|
-
})
|
|
2597
|
+
}),
|
|
2416
2598
|
annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false }
|
|
2417
2599
|
}, withErrorHandling("create_template_version", async (args) => {
|
|
2418
2600
|
const { templateId, schemaValidationRules, ...rest } = args;
|
|
@@ -2662,49 +2844,109 @@ var init_variables = __esm(() => {
|
|
|
2662
2844
|
init_schemas();
|
|
2663
2845
|
});
|
|
2664
2846
|
|
|
2665
|
-
// src/tools/
|
|
2666
|
-
|
|
2667
|
-
|
|
2847
|
+
// src/tools/shared/workflow-status.ts
|
|
2848
|
+
function reviewReasonExplanation(reason) {
|
|
2849
|
+
return reason && REVIEW_REASON_EXPLANATIONS[reason] || UNKNOWN_REVIEW_REASON;
|
|
2850
|
+
}
|
|
2668
2851
|
function isRealTimeInterval(interval) {
|
|
2669
2852
|
return interval?.toUpperCase().replace("-", "_") === "REAL_TIME";
|
|
2670
2853
|
}
|
|
2671
|
-
function
|
|
2672
|
-
const ds =
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
if (!workflow.updateInterval || workflow.updateInterval === "only once")
|
|
2678
|
-
return "Complete";
|
|
2679
|
-
return "Scheduled";
|
|
2680
|
-
case "RUNNING":
|
|
2681
|
-
return "Running";
|
|
2682
|
-
case "VALIDATING":
|
|
2683
|
-
return "Validating";
|
|
2684
|
-
case "FAILED":
|
|
2685
|
-
return "Failed";
|
|
2686
|
-
case "PAUSED":
|
|
2687
|
-
return "Paused";
|
|
2688
|
-
case "SETUP":
|
|
2689
|
-
return "Sample Processing";
|
|
2690
|
-
case "QUEUED":
|
|
2691
|
-
return "Queued";
|
|
2692
|
-
case "PENDING_START":
|
|
2693
|
-
return "Not Started";
|
|
2694
|
-
case "PREVIEW":
|
|
2695
|
-
return "Pending Review";
|
|
2696
|
-
case "DEGRADED":
|
|
2697
|
-
return "Degraded";
|
|
2698
|
-
case "DELETED":
|
|
2699
|
-
return "Deleted";
|
|
2700
|
-
case "COMPLIANCE_REVIEW":
|
|
2701
|
-
return "Compliance Review";
|
|
2702
|
-
case "COMPLIANCE_REJECTED":
|
|
2703
|
-
return "Rejected";
|
|
2704
|
-
default:
|
|
2705
|
-
return ds ?? "Unknown";
|
|
2854
|
+
function fallbackStatusCode(w) {
|
|
2855
|
+
const ds = w.displayState ?? w.state ?? null;
|
|
2856
|
+
if (ds === "ACTIVE") {
|
|
2857
|
+
if (w.isRealTime === true || isRealTimeInterval(w.updateInterval))
|
|
2858
|
+
return "RUNNING";
|
|
2859
|
+
return w.updateInterval && !["ONLY_ONCE", "only once"].includes(w.updateInterval) ? "SCHEDULED" : "COMPLETE";
|
|
2706
2860
|
}
|
|
2861
|
+
if (ds === "VALIDATING")
|
|
2862
|
+
return "AWAITING_REVIEW";
|
|
2863
|
+
return ds;
|
|
2864
|
+
}
|
|
2865
|
+
function labelFor(code, previewReady) {
|
|
2866
|
+
const label = WORKFLOW_STATUS_LABELS[code === "PREVIEW" && previewReady ? "PREVIEW_READY" : code];
|
|
2867
|
+
if (label)
|
|
2868
|
+
return label;
|
|
2869
|
+
console.error(`[workflow-status] unmapped workflow status, passing through verbatim: ${code}`);
|
|
2870
|
+
return code;
|
|
2871
|
+
}
|
|
2872
|
+
function workflowStatusLabel(w) {
|
|
2873
|
+
const code = w.presentation?.status ?? fallbackStatusCode(w);
|
|
2874
|
+
if (!code)
|
|
2875
|
+
return "Unknown";
|
|
2876
|
+
return labelFor(code, w.availableActions?.approve?.allowed === true);
|
|
2877
|
+
}
|
|
2878
|
+
function workflowStatusView(w) {
|
|
2879
|
+
const previewReady = w.availableActions?.approve?.allowed === true;
|
|
2880
|
+
return {
|
|
2881
|
+
status: workflowStatusLabel(w),
|
|
2882
|
+
badges: (w.presentation?.badges ?? []).map((b) => ({
|
|
2883
|
+
kind: b.kind,
|
|
2884
|
+
status: b.status,
|
|
2885
|
+
label: labelFor(b.status, previewReady)
|
|
2886
|
+
})),
|
|
2887
|
+
group: w.presentation?.group ?? null,
|
|
2888
|
+
reviewReason: w.reviewReason ? { code: w.reviewReason, explanation: reviewReasonExplanation(w.reviewReason) } : null
|
|
2889
|
+
};
|
|
2707
2890
|
}
|
|
2891
|
+
function runStatusLabel(state) {
|
|
2892
|
+
if (!state)
|
|
2893
|
+
return "Unknown";
|
|
2894
|
+
return RUN_STATE_LABELS[state] ?? state;
|
|
2895
|
+
}
|
|
2896
|
+
var REVIEW_STATUS_LABEL = "Verifying data", WORKFLOW_STATUS_LABELS, REVIEW_REASON_EXPLANATIONS, UNKNOWN_REVIEW_REASON = "Kadoa's quality or compliance checks selected this run for review.", RUN_STATE_LABELS;
|
|
2897
|
+
var init_workflow_status = __esm(() => {
|
|
2898
|
+
WORKFLOW_STATUS_LABELS = {
|
|
2899
|
+
ASSISTANT_WORKING: "Agent working",
|
|
2900
|
+
AWAITING_REVIEW: REVIEW_STATUS_LABEL,
|
|
2901
|
+
COMPLETE: "Complete",
|
|
2902
|
+
COMPLIANCE_REJECTED: "Rejected",
|
|
2903
|
+
COMPLIANCE_REVIEW: "Compliance review",
|
|
2904
|
+
DEGRADED: "Degraded",
|
|
2905
|
+
DELETED: "Deleted",
|
|
2906
|
+
DRAFT: "Draft",
|
|
2907
|
+
ERROR: "Failed",
|
|
2908
|
+
FAILED: "Failed",
|
|
2909
|
+
IN_SUPPORT: "In support",
|
|
2910
|
+
NOT_SUPPORTED: "Not supported",
|
|
2911
|
+
PAUSED: "Paused",
|
|
2912
|
+
PENDING_START: "Not started",
|
|
2913
|
+
PREVIEW: "Preview",
|
|
2914
|
+
PREVIEW_READY: "Preview ready",
|
|
2915
|
+
QUEUED: "Queued",
|
|
2916
|
+
REPLY_NEEDED: "Reply needed",
|
|
2917
|
+
RUNNING: "Running",
|
|
2918
|
+
SCHEDULED: "Scheduled",
|
|
2919
|
+
SETUP: "Setup",
|
|
2920
|
+
STOPPED: "Stopped",
|
|
2921
|
+
UPDATE_FAILED: "Update failed",
|
|
2922
|
+
UPDATING: "Updating"
|
|
2923
|
+
};
|
|
2924
|
+
REVIEW_REASON_EXPLANATIONS = {
|
|
2925
|
+
COMPLIANCE_REVIEW: "This run triggered a compliance check before delivery.",
|
|
2926
|
+
HARD_FAIL: "An error stopped this run from completing normally.",
|
|
2927
|
+
TRANSFORMATION_REPAIR_REQUIRED: "A data transformation could not be fixed automatically.",
|
|
2928
|
+
MINIMUM_HUMAN_APPROVALS: "Kadoa's quality checks selected this run for review before delivery.",
|
|
2929
|
+
DATA_QUALITY_SCHEMA_ADHERENCE: "The extracted data did not match this workflow's fields and data types.",
|
|
2930
|
+
DATA_QUALITY_SCHEMA_VALIDATION: "Extracted values did not match this workflow's validation rules.",
|
|
2931
|
+
DATA_QUALITY_COMPLETENESS: "The extracted record count did not match the expected source count.",
|
|
2932
|
+
DATA_QUALITY_SEMANTIC_PLAUSIBILITY: "Some extracted values look unusual for their fields.",
|
|
2933
|
+
ROW_COUNT_CHANGED: "This run's row count changed significantly.",
|
|
2934
|
+
WORKFLOW_REVISION_DRIFT: "This run no longer matches the workflow's current setup.",
|
|
2935
|
+
ZERO_RESULTS: "The source returned no data."
|
|
2936
|
+
};
|
|
2937
|
+
RUN_STATE_LABELS = {
|
|
2938
|
+
FINISHED: "Success",
|
|
2939
|
+
IN_PROGRESS: "Running",
|
|
2940
|
+
VALIDATING: REVIEW_STATUS_LABEL,
|
|
2941
|
+
FAILED: "Failed",
|
|
2942
|
+
FAILED_INSUFFICIENT_FUNDS: "Failed",
|
|
2943
|
+
NOT_SUPPORTED: "Failed"
|
|
2944
|
+
};
|
|
2945
|
+
});
|
|
2946
|
+
|
|
2947
|
+
// src/tools/workflows.ts
|
|
2948
|
+
import { WorkflowStatusFilter } from "@kadoa/node-sdk";
|
|
2949
|
+
import { z as z12 } from "zod";
|
|
2708
2950
|
function mapRunStatus(state) {
|
|
2709
2951
|
switch (state) {
|
|
2710
2952
|
case "FINISHED":
|
|
@@ -3120,7 +3362,7 @@ function registerWorkflowCoreTools(server, ctx, helpers, defaultProductType) {
|
|
|
3120
3362
|
}));
|
|
3121
3363
|
server.registerTool("list_workflows", {
|
|
3122
3364
|
title: "List Workflows",
|
|
3123
|
-
description: `List workflows with customer-visible status, raw workflow and run states, Assistant session availability, template version drift, realtime freshness or health, and awaitingUserInput when the requesting user must act. Uses one paginated list request; use get_workflow only when you need full details for a selected workflow. To answer "what needs my attention", pass statusFilters: ["group:attention"]; this returns exactly the dashboard's "Needs your attention" group. In group:attention, a row with awaitingUserInput.prompt is an Assistant question: use get_workflow_assistant then answer_workflow_assistant_question. A row with awaitingUserInput but no prompt is a setup the user left unfinished: use get_workflow_assistant to see where it stopped, then resume_workflow_assistant or request_workflow_update. A row with
|
|
3365
|
+
description: `List workflows with customer-visible status, raw workflow and run states, Assistant session availability, template version drift, realtime freshness or health, and awaitingUserInput when the requesting user must act. Uses one paginated list request; use get_workflow only when you need full details for a selected workflow. To answer "what needs my attention", pass statusFilters: ["group:attention"]; this returns exactly the dashboard's "Needs your attention" group. In group:attention, a row with awaitingUserInput.prompt is an Assistant question: use get_workflow_assistant then answer_workflow_assistant_question. A row with awaitingUserInput but no prompt is a setup the user left unfinished: use get_workflow_assistant to see where it stopped, then resume_workflow_assistant or request_workflow_update. A row with status "Preview ready" is preview data ready to approve with approve_workflow; "Preview" means preview data exists but approval isn't available yet. Workflows with an open support ticket are excluded from group:attention and appear under group:support. badges[].label is exactly what the Kadoa dashboard displays (lifecycle, activity, and support badges, e.g. "Setup" + "Agent working"): use these labels when describing the workflow to the user. status is the one-line summary in dashboard wording (e.g. "Verifying data" = the latest run is held for quality review before delivery; see reviewReason for why).`,
|
|
3124
3366
|
inputSchema: strictSchema({
|
|
3125
3367
|
limit: limitInput({
|
|
3126
3368
|
noun: "Workflows",
|
|
@@ -3158,10 +3400,13 @@ function registerWorkflowCoreTools(server, ctx, helpers, defaultProductType) {
|
|
|
3158
3400
|
id: w.id,
|
|
3159
3401
|
name: w.name,
|
|
3160
3402
|
dashboardUrl: workflowDashboardUrl(w.id),
|
|
3161
|
-
|
|
3403
|
+
...workflowStatusView(w),
|
|
3162
3404
|
state: w.state ?? null,
|
|
3163
3405
|
displayState: w.displayState ?? null,
|
|
3164
3406
|
runState: w.runState ?? null,
|
|
3407
|
+
execution: w.execution ?? null,
|
|
3408
|
+
assistantState: w.assistantState ?? null,
|
|
3409
|
+
workspaceActivity: w.workspaceActivity ?? null,
|
|
3165
3410
|
urls: w.urls,
|
|
3166
3411
|
totalRecords: w.totalRecords,
|
|
3167
3412
|
startedAt: w.startedAt,
|
|
@@ -3169,7 +3414,6 @@ function registerWorkflowCoreTools(server, ctx, helpers, defaultProductType) {
|
|
|
3169
3414
|
createdAt: w.createdAt,
|
|
3170
3415
|
assistant: {
|
|
3171
3416
|
eligible: w.assistantEligible === true,
|
|
3172
|
-
dataStale: w.dataStale === true,
|
|
3173
3417
|
session: w.assistantSessionId ? {
|
|
3174
3418
|
id: w.assistantSessionId,
|
|
3175
3419
|
threadId: w.assistantThreadId ?? null,
|
|
@@ -3201,7 +3445,7 @@ function registerWorkflowCoreTools(server, ctx, helpers, defaultProductType) {
|
|
|
3201
3445
|
}));
|
|
3202
3446
|
server.registerTool("get_workflow", {
|
|
3203
3447
|
title: "Get Workflow",
|
|
3204
|
-
description:
|
|
3448
|
+
description: 'Get canonical workflow details, including Assistant/session linkage, extraction intent, template-controlled parts, job/run state, schedule timezone, location, monitoring, and realtime health. For Assistant-built workflows, extractionSpec - not the legacy prompt - is authoritative. badges[].label is exactly what the Kadoa dashboard displays (lifecycle, activity, and support badges, e.g. "Setup" + "Agent working"): use these labels when describing the workflow to the user. status is the one-line summary in dashboard wording (e.g. "Verifying data" = the latest run is held for quality review before delivery; see reviewReason for why).',
|
|
3205
3449
|
inputSchema: strictSchema({
|
|
3206
3450
|
workflowId: z12.string().min(1).describe("The workflow ID")
|
|
3207
3451
|
}),
|
|
@@ -3226,10 +3470,13 @@ function registerWorkflowCoreTools(server, ctx, helpers, defaultProductType) {
|
|
|
3226
3470
|
name: workflow.name,
|
|
3227
3471
|
dashboardUrl: workflowDashboardUrl(workflow.id ?? args.workflowId),
|
|
3228
3472
|
description: workflow.description,
|
|
3229
|
-
|
|
3473
|
+
...workflowStatusView(workflow),
|
|
3230
3474
|
state: workflow.state ?? null,
|
|
3231
3475
|
displayState: workflow.displayState ?? null,
|
|
3232
3476
|
runState: workflow.runState ?? null,
|
|
3477
|
+
execution: workflow.execution ?? null,
|
|
3478
|
+
assistantState: workflow.assistantState ?? null,
|
|
3479
|
+
workspaceActivity: workflow.workspaceActivity ?? null,
|
|
3233
3480
|
urls: workflow.urls,
|
|
3234
3481
|
prompt: extractionSpec ? null : legacyPrompt,
|
|
3235
3482
|
intentSource: extractionSpec ? "extraction_spec" : legacyPrompt ? "legacy_prompt" : null,
|
|
@@ -3237,7 +3484,6 @@ function registerWorkflowCoreTools(server, ctx, helpers, defaultProductType) {
|
|
|
3237
3484
|
entity: workflow.entity,
|
|
3238
3485
|
schema: workflow.schema,
|
|
3239
3486
|
navigationMode: workflow.navigationMode,
|
|
3240
|
-
dataStale: workflow.dataStale ?? false,
|
|
3241
3487
|
assistantEligible: workflow.assistantEligible === true,
|
|
3242
3488
|
assistantSessionId: workflow.assistantSessionId ?? null,
|
|
3243
3489
|
assistantThreadId: workflow.assistantThreadId ?? null,
|
|
@@ -3280,8 +3526,6 @@ function registerWorkflowCoreTools(server, ctx, helpers, defaultProductType) {
|
|
|
3280
3526
|
createdAt: workflow.createdAt,
|
|
3281
3527
|
notificationConfig: workflow.notificationConfig,
|
|
3282
3528
|
channelCounts: workflow.channelCounts ?? {},
|
|
3283
|
-
dataValidation: workflow.dataValidation ?? null,
|
|
3284
|
-
validationStatistics: workflow.validationStatistics ?? null,
|
|
3285
3529
|
realtime: isRealtime ? {
|
|
3286
3530
|
lastDataChangedAt: workflow.lastDataChangedAt ?? null,
|
|
3287
3531
|
observerHealth: workflow.observerHealth ?? null
|
|
@@ -3381,7 +3625,7 @@ function registerWorkflowHistoryTools(server, ctx, helpers) {
|
|
|
3381
3625
|
}));
|
|
3382
3626
|
server.registerTool("list_workflow_runs", {
|
|
3383
3627
|
title: "List Workflow Runs",
|
|
3384
|
-
description: "List a workflow's execution run history - each run's status, start/finish time, " + "record count, and errors. Use to answer 'when did this last succeed?' " + "(status='success', limit=1) or 'recent success/failure pattern?'. Distinct from " + "get_workflow_history, which is the config audit log. `status` is normally " + "success | failed | in_progress; a run whose backend state this server does not " + "recognize reports that raw state verbatim, so treat any other value as unknown " + "rather than as a failure.",
|
|
3628
|
+
description: "List a workflow's execution run history - each run's status, start/finish time, " + "record count, and errors. Use to answer 'when did this last succeed?' " + "(status='success', limit=1) or 'recent success/failure pattern?'. Distinct from " + "get_workflow_history, which is the config audit log. `status` is normally " + "success | failed | in_progress; a run whose backend state this server does not " + "recognize reports that raw state verbatim, so treat any other value as unknown " + "rather than as a failure. `label` is the dashboard's wording for the run " + "(Success, Failed, Running, or 'Verifying data' = held for quality review before delivery); " + "prefer it when describing a run to the user.",
|
|
3385
3629
|
inputSchema: strictSchema({
|
|
3386
3630
|
workflowId: z12.string().min(1).describe("The workflow ID"),
|
|
3387
3631
|
status: z12.enum(["success", "failed", "in_progress"]).optional().describe("Filter runs by outcome"),
|
|
@@ -3400,6 +3644,7 @@ function registerWorkflowHistoryTools(server, ctx, helpers) {
|
|
|
3400
3644
|
const runs = (response.workflowRuns ?? []).map((r) => ({
|
|
3401
3645
|
id: r.id,
|
|
3402
3646
|
status: mapRunStatus(r.state),
|
|
3647
|
+
label: runStatusLabel(r.state),
|
|
3403
3648
|
startedAt: r.startedAt,
|
|
3404
3649
|
finishedAt: r.finishedAt,
|
|
3405
3650
|
records: r.records,
|
|
@@ -3449,7 +3694,7 @@ function registerWorkflowRunTools(server, ctx, helpers) {
|
|
|
3449
3694
|
const FETCH_DATA_MAX_LIMIT = 500;
|
|
3450
3695
|
server.registerTool("fetch_data", {
|
|
3451
3696
|
title: "Fetch Workflow Data",
|
|
3452
|
-
description: "Get a PAGE of extracted data from a workflow. Use ONLY for previews, sorted/filtered slices, or explicit 'first N rows' / 'top N' queries (capped at 500 rows per call). Do NOT use this to retrieve a full dataset, 'all rows', or anything the user wants to analyze in Excel / pandas / duckdb - use export_data for those. Data is only available after the workflow run has completed (status is no longer 'Running' or '
|
|
3697
|
+
description: "Get a PAGE of extracted data from a workflow. Use ONLY for previews, sorted/filtered slices, or explicit 'first N rows' / 'top N' queries (capped at 500 rows per call). Do NOT use this to retrieve a full dataset, 'all rows', or anything the user wants to analyze in Excel / pandas / duckdb - use export_data for those. Data is only available after the workflow run has completed (status is no longer 'Running' or 'Verifying data'). Do NOT poll or sleep-wait for completion.",
|
|
3453
3698
|
inputSchema: strictSchema({
|
|
3454
3699
|
workflowId: z12.string().min(1).describe("The workflow ID"),
|
|
3455
3700
|
limit: limitInput({
|
|
@@ -3611,7 +3856,7 @@ function registerWorkflowRunTools(server, ctx, helpers) {
|
|
|
3611
3856
|
try {
|
|
3612
3857
|
const workflow = await ctx.client.workflow.get(args.workflowId);
|
|
3613
3858
|
workflowName = workflow.name || args.workflowId;
|
|
3614
|
-
workflowStatus =
|
|
3859
|
+
workflowStatus = workflowStatusLabel(workflow);
|
|
3615
3860
|
} catch {}
|
|
3616
3861
|
if (!args.confirmed) {
|
|
3617
3862
|
return jsonResult({
|
|
@@ -3679,7 +3924,7 @@ function registerWorkflowRunTools(server, ctx, helpers) {
|
|
|
3679
3924
|
timezone: z12.string().min(1).optional().describe("IANA timezone for cron schedules, for example America/New_York. A timezone-only update preserves existing cron expressions."),
|
|
3680
3925
|
location: z12.preprocess(coerceJson(), LocationSchema).optional().describe("Scraping location: {type:'auto'} or {type:'manual', isoCode:'US'}"),
|
|
3681
3926
|
monitoring: z12.preprocess(coerceJson(), MonitoringSchema).optional().describe("Complete monitoring replacement: fields to monitor and optional AND/OR conditions. Call get_workflow first because this replaces the existing monitoring configuration."),
|
|
3682
|
-
limit: z12.preprocess(coerceNumber(), z12.number().positive().nullable()).optional().describe("Maximum records per run. Pass null to clear the limit and extract all rows.")
|
|
3927
|
+
limit: z12.preprocess(coerceNumber(), z12.number().positive().nullable()).optional().describe("Maximum records per run; must be positive (greater than 0). Pass null to clear the limit and extract all rows.")
|
|
3683
3928
|
}),
|
|
3684
3929
|
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: false }
|
|
3685
3930
|
}, withErrorHandling("update_workflow", async (args) => {
|
|
@@ -3723,6 +3968,7 @@ var init_workflows = __esm(() => {
|
|
|
3723
3968
|
init_output_schemas();
|
|
3724
3969
|
init_pagination();
|
|
3725
3970
|
init_schemas();
|
|
3971
|
+
init_workflow_status();
|
|
3726
3972
|
WORKFLOW_AUDIT_WATCHED_KEYS = [
|
|
3727
3973
|
"name",
|
|
3728
3974
|
"tags",
|
|
@@ -4476,7 +4722,7 @@ async function createServer3(auth, options) {
|
|
|
4476
4722
|
"Use list_changes and get_change to retrieve detected diffs from realtime monitoring workflows.",
|
|
4477
4723
|
"List tools, plus fetch_data, get_workflow_history, and get_workflow_assistant_timeline, all return pagination {totalCount?, hasMore, next?}. When hasMore is true, call the same tool again with pagination.next as its arguments to get the next page. totalCount is present only when the total is known; never treat the page size as the total.",
|
|
4478
4724
|
"Large results (fetch_data, get_workflow_history, get_change) are cut to whole rows or entries under about 80 KB and marked truncated: true with a hint; follow pagination.next, narrow the filters, or use export_data, and never conclude the data ends there.",
|
|
4479
|
-
|
|
4725
|
+
`When the user asks what needs their attention, call list_workflows with statusFilters group:attention. A row with awaitingUserInput.prompt is the Assistant's pending question: answer it with get_workflow_assistant then answer_workflow_assistant_question. A row with awaitingUserInput but no prompt is a setup the user left unfinished: use get_workflow_assistant to see where it stopped, then resume_workflow_assistant or request_workflow_update. A row with status "Preview ready" is preview data ready to approve with approve_workflow; "Preview" means preview data exists but approval isn't available yet. Workflows with an open support ticket are excluded from group:attention and appear under group:support.`,
|
|
4480
4726
|
"",
|
|
4481
4727
|
"Schema tips: Use descriptive field names and examples. Group related data under one entity.",
|
|
4482
4728
|
"The AI agent uses the schema + prompt to understand what to extract - a detailed prompt with a comprehensive schema produces better results than multiple simple workflows.",
|
|
@@ -4954,7 +5200,7 @@ async function createServer(auth, options) {
|
|
|
4954
5200
|
"Use list_changes and get_change to retrieve detected diffs from realtime monitoring workflows.",
|
|
4955
5201
|
"List tools, plus fetch_data, get_workflow_history, and get_workflow_assistant_timeline, all return pagination {totalCount?, hasMore, next?}. When hasMore is true, call the same tool again with pagination.next as its arguments to get the next page. totalCount is present only when the total is known; never treat the page size as the total.",
|
|
4956
5202
|
"Large results (fetch_data, get_workflow_history, get_change) are cut to whole rows or entries under about 80 KB and marked truncated: true with a hint; follow pagination.next, narrow the filters, or use export_data, and never conclude the data ends there.",
|
|
4957
|
-
|
|
5203
|
+
`When the user asks what needs their attention, call list_workflows with statusFilters group:attention. A row with awaitingUserInput.prompt is the Assistant's pending question: answer it with get_workflow_assistant then answer_workflow_assistant_question. A row with awaitingUserInput but no prompt is a setup the user left unfinished: use get_workflow_assistant to see where it stopped, then resume_workflow_assistant or request_workflow_update. A row with status "Preview ready" is preview data ready to approve with approve_workflow; "Preview" means preview data exists but approval isn't available yet. Workflows with an open support ticket are excluded from group:attention and appear under group:support.`,
|
|
4958
5204
|
"",
|
|
4959
5205
|
"Schema tips: Use descriptive field names and examples. Group related data under one entity.",
|
|
4960
5206
|
"The AI agent uses the schema + prompt to understand what to extract - a detailed prompt with a comprehensive schema produces better results than multiple simple workflows.",
|