@doist/automations-cli 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  collectSkillStatus,
6
6
  getInstaller,
7
7
  updateAllInstalledSkills
8
- } from "./chunk-XHAVDKEN.js";
8
+ } from "./chunk-DYIIMUG3.js";
9
9
 
10
10
  // src/index.ts
11
11
  import { Command } from "commander";
@@ -15,7 +15,7 @@ import { fileURLToPath } from "node:url";
15
15
  // package.json
16
16
  var package_default = {
17
17
  name: "@doist/automations-cli",
18
- version: "1.2.0",
18
+ version: "1.3.0",
19
19
  description: "Command-line client for Todoist Automations",
20
20
  type: "module",
21
21
  bin: {
@@ -63,7 +63,7 @@ var package_default = {
63
63
  "@napi-rs/keyring": "1.3.0",
64
64
  ai: "7.0.77",
65
65
  commander: "15.0.0",
66
- oauth4webapi: "3.8.7",
66
+ oauth4webapi: "3.8.8",
67
67
  open: "11.0.2",
68
68
  "p-limit": "3.1.0"
69
69
  },
@@ -19109,1820 +19109,1882 @@ var sendAdminTestEmailResponseSchema = external_exports.object({
19109
19109
  messageId: external_exports.string()
19110
19110
  });
19111
19111
 
19112
- // ../../packages/core/dist/esm/schemas/connector.schema.js
19113
- var TriggerSupportedEventSchema = external_exports.object({
19114
- id: external_exports.string(),
19115
- name: external_exports.string(),
19116
- description: external_exports.string()
19112
+ // ../../packages/core/dist/esm/types/failure-category.types.js
19113
+ var FAILURE_CATEGORIES = [
19114
+ "mcp_upstream_unavailable",
19115
+ "mcp_client_missing",
19116
+ "mcp_tool_missing",
19117
+ "mcp_tool_call_failed",
19118
+ "llm_schema_validation",
19119
+ "llm_empty_response",
19120
+ "json_parse_error",
19121
+ "code_execution_error",
19122
+ "code_generation_error",
19123
+ "step_execution_error",
19124
+ "step_failure_pattern",
19125
+ "schema_validation",
19126
+ "template_variable_error",
19127
+ "invalid_configuration",
19128
+ "invalid_manifest",
19129
+ "automation_not_found",
19130
+ "timeout",
19131
+ "runtime_error",
19132
+ "insufficient_credit",
19133
+ "billing_unavailable",
19134
+ "unknown"
19135
+ ];
19136
+
19137
+ // ../../packages/core/dist/esm/schemas/query-param.schema.js
19138
+ var isoDateTimeOrDateQueryParamSchema = external_exports.union([
19139
+ external_exports.iso.datetime({ offset: true }),
19140
+ external_exports.iso.date()
19141
+ ]);
19142
+ var booleanQueryParamSchema = external_exports.preprocess((value) => value === "true" ? true : value === "false" ? false : value, external_exports.boolean());
19143
+ var booleanQueryFlagSchema = booleanQueryParamSchema.optional().transform((value) => value === true);
19144
+
19145
+ // ../../packages/core/dist/esm/schemas/automation-run.schema.js
19146
+ var AUTOMATION_RUN_RESULT_TEXT_MAX_LENGTH = 4e3;
19147
+ var DATE_ONLY_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
19148
+ var MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1e3;
19149
+ function resolveRunDateRange({ since, until }) {
19150
+ const untilExclusive = until !== void 0 && DATE_ONLY_PATTERN.test(until);
19151
+ return {
19152
+ since: since ? new Date(DATE_ONLY_PATTERN.test(since) ? `${since}T00:00:00.000Z` : since) : void 0,
19153
+ until: until ? new Date(untilExclusive ? (/* @__PURE__ */ new Date(`${until}T00:00:00.000Z`)).getTime() + MILLISECONDS_PER_DAY : until) : void 0,
19154
+ untilExclusive
19155
+ };
19156
+ }
19157
+ function isEmptyRunDateRange(range) {
19158
+ const { since, until, untilExclusive } = resolveRunDateRange(range);
19159
+ if (!since || !until)
19160
+ return false;
19161
+ return untilExclusive ? since.getTime() >= until.getTime() : since.getTime() > until.getTime();
19162
+ }
19163
+ var ListAutomationRunsQuerySchema = external_exports.object({
19164
+ status: external_exports.enum(AUTOMATION_RUN_STATUSES).optional(),
19165
+ since: isoDateTimeOrDateQueryParamSchema.optional(),
19166
+ until: isoDateTimeOrDateQueryParamSchema.optional()
19167
+ }).superRefine((query, ctx) => {
19168
+ if (isEmptyRunDateRange(query)) {
19169
+ ctx.addIssue({
19170
+ code: "custom",
19171
+ path: ["since"],
19172
+ message: "since must not be after until"
19173
+ });
19174
+ }
19117
19175
  });
19118
- var ConnectorCapabilitiesSchema = external_exports.object({
19119
- mcp: external_exports.object({
19120
- serverUrl: external_exports.string(),
19121
- scopes: external_exports.array(external_exports.string()),
19122
- connectionIds: external_exports.array(external_exports.string())
19123
- }).optional(),
19124
- triggers: external_exports.object({
19125
- scopes: external_exports.array(external_exports.string()),
19126
- connectionIds: external_exports.array(external_exports.string()),
19127
- supportedEvents: external_exports.array(TriggerSupportedEventSchema)
19128
- }).optional()
19176
+ var AutomationRunResultSchema = external_exports.object({
19177
+ type: external_exports.literal("markdown"),
19178
+ text: external_exports.string().trim().min(1).max(AUTOMATION_RUN_RESULT_TEXT_MAX_LENGTH)
19129
19179
  });
19130
- var ConnectorSchema = external_exports.object({
19180
+ var RolloutCompatibleAutomationRunResultSchema = AutomationRunResultSchema.nullish().transform((result) => {
19181
+ return result ?? null;
19182
+ });
19183
+ var AutomationRunDtoSchema = external_exports.object({
19131
19184
  id: external_exports.string(),
19132
- name: external_exports.string(),
19133
- description: external_exports.string(),
19134
- iconUrl: external_exports.url().nullable().optional(),
19135
- capabilities: ConnectorCapabilitiesSchema,
19136
- /** Whether this connector has any user account setup flow. */
19137
- requiresConnection: external_exports.boolean(),
19138
- /**
19139
- * Whether the connector is a product-owned built-in service. This is
19140
- * independent from whether a third-party connector requires account setup.
19141
- */
19142
- isSystem: external_exports.boolean().optional(),
19143
- /**
19144
- * Managed connectors (e.g. WhatsApp) share a single linked identity across
19145
- * connection types — one onboarding covers both action and trigger uses.
19146
- * The FE collapses per-type rows into a single account when this is true.
19147
- */
19148
- isManaged: external_exports.boolean().optional()
19185
+ automationId: external_exports.string(),
19186
+ status: external_exports.enum(AUTOMATION_RUN_STATUSES),
19187
+ createdAt: external_exports.string(),
19188
+ updatedAt: external_exports.string(),
19189
+ failureReason: external_exports.string().nullish(),
19190
+ failureCategory: external_exports.enum(FAILURE_CATEGORIES).nullish(),
19191
+ result: RolloutCompatibleAutomationRunResultSchema,
19192
+ temporalUrl: external_exports.string().nullish()
19149
19193
  });
19150
- var GetConnectorsQuerySchema = external_exports.object({
19151
- query: external_exports.string().optional()
19194
+ var MAX_TOOL_CALL_ARGUMENT_KEYS = 32;
19195
+ var MAX_TOOL_CALL_ARGUMENT_KEY_LENGTH = 64;
19196
+ var MAX_TOOL_CALL_PREVIEW_LENGTH = 500;
19197
+ var MAX_TOOL_CALLS_WITH_PREVIEWS = 20;
19198
+ var MAX_TOOL_CALL_PREVIEW_TOTAL_LENGTH = 2e3;
19199
+ var MAX_TOOL_CALL_ENTITY_LENGTH = 100;
19200
+ var MAX_TOOL_CALL_RESULT_PARSE_LENGTH = 1e4;
19201
+ var TOOL_CALL_OUTCOMES = ["data", "empty", "no-result", "error"];
19202
+ var TOOL_CALL_ERROR_CATEGORIES = [
19203
+ "authentication",
19204
+ "permission",
19205
+ "rate-limit",
19206
+ "timeout",
19207
+ "connection",
19208
+ "invalid-request",
19209
+ "unknown"
19210
+ ];
19211
+ var ToolCallTimestampSchema = external_exports.union([external_exports.string(), external_exports.date()]).transform((timestamp) => timestamp instanceof Date ? timestamp.toISOString() : timestamp);
19212
+ var SafeToolCallDtoSchema = external_exports.object({
19213
+ id: external_exports.string(),
19214
+ name: external_exports.string(),
19215
+ argumentKeys: external_exports.array(external_exports.string()),
19216
+ hasResult: external_exports.boolean(),
19217
+ hasError: external_exports.boolean(),
19218
+ outcome: external_exports.enum(TOOL_CALL_OUTCOMES).optional(),
19219
+ errorCategory: external_exports.enum(TOOL_CALL_ERROR_CATEGORIES).optional(),
19220
+ resultPreview: external_exports.string().max(MAX_TOOL_CALL_PREVIEW_LENGTH).optional(),
19221
+ errorPreview: external_exports.string().max(MAX_TOOL_CALL_PREVIEW_LENGTH).optional(),
19222
+ timestamp: ToolCallTimestampSchema
19152
19223
  });
19153
- var connectorDetailsSchema = external_exports.object({
19224
+ var LegacyToolCallDtoInputSchema = external_exports.object({
19225
+ id: external_exports.string(),
19154
19226
  name: external_exports.string(),
19155
- displayName: external_exports.string(),
19227
+ arguments: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
19228
+ result: external_exports.unknown().optional(),
19229
+ error: external_exports.unknown().optional(),
19230
+ errorStatus: external_exports.number().int().min(100).max(599).optional(),
19231
+ errorCode: external_exports.string().optional(),
19232
+ timestamp: ToolCallTimestampSchema
19233
+ });
19234
+ var ToolCallDtoInputSchema = external_exports.union([SafeToolCallDtoSchema, LegacyToolCallDtoInputSchema]);
19235
+ var ToolCallDtoSchema = external_exports.preprocess((toolCall) => {
19236
+ const parsed = ToolCallDtoInputSchema.safeParse(toolCall);
19237
+ return parsed.success ? normalizeToolCallDto(parsed.data) : toolCall;
19238
+ }, SafeToolCallDtoSchema);
19239
+ var ToolCallDtosSchema = external_exports.preprocess((toolCalls) => {
19240
+ if (!Array.isArray(toolCalls)) {
19241
+ return toolCalls;
19242
+ }
19243
+ const parsedToolCalls = toolCalls.map((toolCall) => ToolCallDtoInputSchema.safeParse(toolCall)).filter((result) => result.success).map((result) => result.data);
19244
+ return parsedToolCalls.length === toolCalls.length ? normalizeToolCallDtos(parsedToolCalls) : toolCalls;
19245
+ }, external_exports.array(SafeToolCallDtoSchema));
19246
+ var AUTOMATION_RUN_DEBUG_REASONS = [
19247
+ "condition-not-met",
19248
+ "exit-condition-met",
19249
+ "ask-terminated",
19250
+ "ask-expired",
19251
+ "run-result-not-configured",
19252
+ "run-result-empty"
19253
+ ];
19254
+ var AutomationRunDebugReasonSchema = external_exports.enum(AUTOMATION_RUN_DEBUG_REASONS);
19255
+ var AutomationRunStepDtoOutputSchema = external_exports.object({
19256
+ stepId: external_exports.string(),
19257
+ type: external_exports.string(),
19156
19258
  description: external_exports.string(),
19157
- iconUrl: external_exports.string().nullable().optional()
19259
+ status: external_exports.enum(AUTOMATION_RUN_STATUSES),
19260
+ startedAt: external_exports.string().optional(),
19261
+ completedAt: external_exports.string().optional(),
19262
+ result: external_exports.unknown().optional(),
19263
+ error: external_exports.string().optional(),
19264
+ generatedCode: external_exports.string().optional(),
19265
+ toolCalls: ToolCallDtosSchema.optional(),
19266
+ debugReason: AutomationRunDebugReasonSchema.optional()
19158
19267
  });
19159
- var connectorListResponseSchema = external_exports.object({
19160
- data: external_exports.array(ConnectorSchema),
19161
- nextCursor: external_exports.string().nullable()
19268
+ var AutomationRunStepDtoSchema = AutomationRunStepDtoOutputSchema;
19269
+ var AutomationRunDetailDtoSchema = external_exports.object({
19270
+ id: external_exports.string(),
19271
+ automationId: external_exports.string(),
19272
+ status: external_exports.enum([...AUTOMATION_RUN_STATUSES, "data-expired"]),
19273
+ createdAt: external_exports.string(),
19274
+ updatedAt: external_exports.string(),
19275
+ failureReason: external_exports.string().nullish(),
19276
+ failureCategory: external_exports.enum(FAILURE_CATEGORIES).nullish(),
19277
+ result: RolloutCompatibleAutomationRunResultSchema,
19278
+ temporalUrl: external_exports.string().nullish(),
19279
+ steps: external_exports.array(AutomationRunStepDtoSchema)
19162
19280
  });
19163
-
19164
- // ../../packages/core/dist/esm/utils/duration.js
19165
- var durationUnits = ["ms", "s", "m", "h", "d", "w"];
19166
- var createDurationPattern = (units) => units.join("|");
19167
- var durationPattern = createDurationPattern(durationUnits);
19168
- var durationStringRegex = new RegExp(`^\\d+(${durationPattern})$`);
19169
- var durationStringWithCaptureRegex = new RegExp(`^(\\d+)(${durationPattern})$`);
19170
- var intervalScheduleRegex = new RegExp(`^\\d+(${durationPattern})(\\/\\d+(${durationPattern}))?$`);
19171
- function durationStringToMilliseconds(value) {
19172
- const match = value.match(durationStringWithCaptureRegex);
19173
- if (!match) {
19174
- throw new Error(`Invalid duration string: ${value}`);
19175
- }
19176
- const [, rawAmount, rawUnit] = match;
19177
- const multiplierMap = {
19178
- ms: 1,
19179
- s: 1e3,
19180
- m: 60 * 1e3,
19181
- h: 60 * 60 * 1e3,
19182
- d: 24 * 60 * 60 * 1e3,
19183
- w: 7 * 24 * 60 * 60 * 1e3
19184
- };
19185
- const multiplier = multiplierMap[rawUnit];
19186
- if (multiplier === void 0) {
19187
- throw new Error(`Invalid duration unit in string: ${value}`);
19281
+ function summarizeToolCallArgumentKeys(args) {
19282
+ const keys = Object.keys(args ?? {}).sort();
19283
+ const kept = keys.slice(0, MAX_TOOL_CALL_ARGUMENT_KEYS).map((key) => key.length > MAX_TOOL_CALL_ARGUMENT_KEY_LENGTH ? `${key.slice(0, MAX_TOOL_CALL_ARGUMENT_KEY_LENGTH)}\u2026` : key);
19284
+ if (keys.length > MAX_TOOL_CALL_ARGUMENT_KEYS) {
19285
+ kept.push(`\u2026+${keys.length - MAX_TOOL_CALL_ARGUMENT_KEYS} more`);
19188
19286
  }
19189
- return Number.parseInt(rawAmount, 10) * multiplier;
19287
+ return kept;
19190
19288
  }
19191
-
19192
- // ../../packages/core/dist/esm/utils/if-condition.js
19193
- var ifConditionReferencePattern = "(?:steps\\.[a-zA-Z][a-zA-Z0-9]*(?:\\.[a-zA-Z_]\\w*)*|trigger(?:\\.[a-zA-Z_]\\w*)+)";
19194
- var ifConditionRegex = new RegExp(`^\\{\\{\\s*${ifConditionReferencePattern}\\s*\\}\\}\\s*(?:==|!=)\\s*'[^']*'$`);
19195
- function isWord(segment, { underscore }) {
19196
- if (segment.length === 0)
19197
- return false;
19198
- for (let index = 0; index < segment.length; index++) {
19199
- const char = segment[index];
19200
- const isAlpha = char >= "a" && char <= "z" || char >= "A" && char <= "Z";
19201
- const isDigit = char >= "0" && char <= "9";
19202
- const isUnderscore = underscore && char === "_";
19203
- if (index === 0) {
19204
- if (!isAlpha && !isUnderscore)
19205
- return false;
19206
- } else if (!isAlpha && !isDigit && !isUnderscore) {
19207
- return false;
19208
- }
19289
+ function summarizeToolCallPayloadPreview(payload) {
19290
+ if (typeof payload === "string") {
19291
+ return summarizePayloadSize(payload);
19209
19292
  }
19210
- return true;
19293
+ if (Array.isArray(payload)) {
19294
+ return `${payload.length} item${payload.length === 1 ? "" : "s"} returned`;
19295
+ }
19296
+ if (payload && typeof payload === "object") {
19297
+ const keys = summarizeToolCallArgumentKeys(payload);
19298
+ return truncateToolCallPreview(keys.length === 0 ? "Object returned" : `Object returned with fields: ${keys.join(", ")}`);
19299
+ }
19300
+ return `${getPrimitivePayloadType(payload)} returned`;
19211
19301
  }
19212
- function isValidReference(reference) {
19213
- const parts = reference.split(".");
19214
- const [head, ...rest] = parts;
19215
- if (head === "steps") {
19216
- const [id, ...segments] = rest;
19217
- if (id === void 0 || !isWord(id, { underscore: false }))
19218
- return false;
19219
- return segments.every((segment) => isWord(segment, { underscore: true }));
19302
+ function summarizeToolCallResult(toolName, payload) {
19303
+ const result = parseJsonPayload(payload);
19304
+ if (payload === void 0 || result === null) {
19305
+ return { outcome: "no-result", preview: summarizeToolCallPayloadPreview(payload) };
19220
19306
  }
19221
- if (head === "trigger") {
19222
- if (rest.length === 0)
19223
- return false;
19224
- return rest.every((segment) => isWord(segment, { underscore: true }));
19307
+ const tool = getToolCallDescription(toolName);
19308
+ const collection = getResultCollection(result, tool.entity);
19309
+ const textCollection = typeof payload === "string" && payload.length > MAX_TOOL_CALL_RESULT_PARSE_LENGTH ? null : getTextCollectionSummary(result, tool);
19310
+ const outcome = collection?.length === 0 || textCollection === 0 ? "empty" : "data";
19311
+ if (collection) {
19312
+ const summary = `${tool.action} ${collection.length} ${formatEntity(tool.entity, collection.length)}`;
19313
+ const projectCount = getProjectCount(collection, tool.entity);
19314
+ return {
19315
+ outcome,
19316
+ preview: truncateToolCallPreview(projectCount > 1 ? `${summary} across ${projectCount} projects` : summary)
19317
+ };
19225
19318
  }
19226
- return false;
19319
+ if (textCollection !== null) {
19320
+ return {
19321
+ outcome,
19322
+ preview: truncateToolCallPreview(`${tool.action} ${textCollection} ${formatEntity(tool.entity, textCollection)}`)
19323
+ };
19324
+ }
19325
+ if (isRecord(result) && !["Found", "Returned"].includes(tool.action)) {
19326
+ return {
19327
+ outcome,
19328
+ preview: truncateToolCallPreview(tool.action === "Retrieved" ? `Retrieved ${tool.entity} details` : `${tool.action} ${formatEntity(tool.entity, 1)}`)
19329
+ };
19330
+ }
19331
+ return { outcome, preview: summarizeToolCallPayloadPreview(payload) };
19227
19332
  }
19228
- function parseIfCondition(condition) {
19229
- if (!condition.startsWith("{{"))
19230
- return null;
19231
- const closeIndex = condition.indexOf("}}");
19232
- if (closeIndex === -1)
19233
- return null;
19234
- const reference = condition.slice(2, closeIndex).trim();
19235
- if (!isValidReference(reference))
19236
- return null;
19237
- const afterReference = condition.slice(closeIndex + 2).trimStart();
19238
- const operator = afterReference.startsWith("==") ? "==" : afterReference.startsWith("!=") ? "!=" : null;
19239
- if (!operator)
19240
- return null;
19241
- const afterOperator = afterReference.slice(2).trimStart();
19242
- if (afterOperator.length < 2 || !afterOperator.startsWith("'") || !afterOperator.endsWith("'")) {
19243
- return null;
19333
+ function classifyToolCallOutcome(toolName, result, error61, errorDetails = {}) {
19334
+ if (error61 !== void 0) {
19335
+ return { outcome: "error", errorCategory: classifyToolCallError(error61, errorDetails) };
19244
19336
  }
19245
- const literal2 = afterOperator.slice(1, -1);
19246
- if (literal2.includes("'"))
19247
- return null;
19248
- return { reference, operator, literal: literal2 };
19337
+ return { outcome: summarizeToolCallResult(toolName, result).outcome };
19249
19338
  }
19250
-
19251
- // ../../packages/core/dist/esm/schemas/manifest.helpers.js
19252
- function getManifestTriggers(manifest) {
19253
- return manifest.triggers ?? [];
19339
+ function summarizePayloadSize(payload) {
19340
+ return `${payload.length} character${payload.length === 1 ? "" : "s"} returned`;
19254
19341
  }
19255
- function hasTriggerType(value) {
19256
- if (!value || typeof value !== "object")
19257
- return false;
19258
- const manifest = value;
19259
- const triggers = getManifestTriggers(manifest);
19260
- return triggers.length > 0 && typeof triggers[0]?.type === "string";
19342
+ function summarizeToolCallsWithPreviewLimits(toolCalls, summarize) {
19343
+ let previewsKept = 0;
19344
+ let previewLength = 0;
19345
+ return toolCalls.map((toolCall) => {
19346
+ const shouldIncludePreview = previewsKept < MAX_TOOL_CALLS_WITH_PREVIEWS && previewLength < MAX_TOOL_CALL_PREVIEW_TOTAL_LENGTH;
19347
+ const summary = summarize(toolCall, { includePreview: shouldIncludePreview });
19348
+ const nextPreviewLength = (summary.resultPreview?.length ?? 0) + (summary.errorPreview?.length ?? 0);
19349
+ if (nextPreviewLength === 0 || previewsKept < MAX_TOOL_CALLS_WITH_PREVIEWS && previewLength + nextPreviewLength <= MAX_TOOL_CALL_PREVIEW_TOTAL_LENGTH) {
19350
+ previewsKept += nextPreviewLength > 0 ? 1 : 0;
19351
+ previewLength += nextPreviewLength;
19352
+ return summary;
19353
+ }
19354
+ return stripToolCallPreviews(summary);
19355
+ });
19261
19356
  }
19262
- function isStringTypedSchema(schema) {
19263
- if (!schema)
19264
- return false;
19265
- if (schema === "string")
19266
- return true;
19267
- if (typeof schema !== "object" || schema === null)
19268
- return false;
19269
- const typedSchema = schema;
19270
- return typedSchema["type"] === "string";
19357
+ function normalizeToolCallDtos(toolCalls) {
19358
+ return summarizeToolCallsWithPreviewLimits(toolCalls, normalizeToolCallDto);
19271
19359
  }
19272
- function isPlainStringSchema(schema) {
19273
- if (!isStringTypedSchema(schema))
19274
- return false;
19275
- if (schema === "string")
19276
- return true;
19277
- return Object.keys(schema).length === 1;
19360
+ function normalizeToolCallDto(toolCall, options = {}) {
19361
+ const includePreview = options.includePreview ?? true;
19362
+ if ("argumentKeys" in toolCall) {
19363
+ return includePreview ? toolCall : stripToolCallPreviews(toolCall);
19364
+ }
19365
+ const hasResult = hasToolCallResult(toolCall.result);
19366
+ const resultSummary = includePreview && hasResult ? summarizeToolCallResult(toolCall.name, toolCall.result) : void 0;
19367
+ const diagnostic = toolCall.error === void 0 && resultSummary ? { outcome: resultSummary.outcome } : classifyToolCallOutcome(toolCall.name, toolCall.result, toolCall.error, {
19368
+ status: toolCall.errorStatus,
19369
+ code: toolCall.errorCode
19370
+ });
19371
+ return {
19372
+ id: toolCall.id,
19373
+ name: toolCall.name,
19374
+ argumentKeys: summarizeToolCallArgumentKeys(toolCall.arguments),
19375
+ hasResult,
19376
+ hasError: toolCall.error !== void 0,
19377
+ outcome: diagnostic.outcome,
19378
+ ...diagnostic.errorCategory ? { errorCategory: diagnostic.errorCategory } : {},
19379
+ ...resultSummary ? { resultPreview: resultSummary.preview } : {},
19380
+ ...includePreview && toolCall.error !== void 0 ? { errorPreview: summarizeToolCallPayloadPreview(toolCall.error) } : {},
19381
+ timestamp: toolCall.timestamp
19382
+ };
19278
19383
  }
19279
- function validatePropertyPathInSchema(schema, propertyPath) {
19280
- if (propertyPath.length === 0) {
19281
- return true;
19384
+ function hasToolCallResult(result) {
19385
+ return result !== void 0 && result !== null && result !== "null";
19386
+ }
19387
+ function stripToolCallPreviews(toolCall) {
19388
+ const { resultPreview: _resultPreview, errorPreview: _errorPreview, ...withoutPreview } = toolCall;
19389
+ return withoutPreview;
19390
+ }
19391
+ function truncateToolCallPreview(preview) {
19392
+ return truncate(preview, MAX_TOOL_CALL_PREVIEW_LENGTH);
19393
+ }
19394
+ function truncateToolCallEntity(entity) {
19395
+ return truncate(entity, MAX_TOOL_CALL_ENTITY_LENGTH);
19396
+ }
19397
+ function truncate(value, maxLength) {
19398
+ return value.length <= maxLength ? value : `${value.slice(0, maxLength - 1)}\u2026`;
19399
+ }
19400
+ function escapeRegExp(value) {
19401
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
19402
+ }
19403
+ function getPrimitivePayloadType(payload) {
19404
+ if (payload === null) {
19405
+ return "Null";
19282
19406
  }
19283
- if (!schema.type || schema.type !== "object") {
19284
- return false;
19407
+ switch (typeof payload) {
19408
+ case "boolean":
19409
+ return "Boolean";
19410
+ case "number":
19411
+ return "Number";
19412
+ case "bigint":
19413
+ return "BigInt";
19414
+ default:
19415
+ return "Value";
19285
19416
  }
19286
- const properties = schema.properties;
19287
- if (!properties)
19288
- return true;
19289
- const [currentProperty, ...remainingPath] = propertyPath;
19290
- if (!currentProperty)
19291
- return false;
19292
- const propertySchema = properties[currentProperty];
19293
- if (!propertySchema || typeof propertySchema !== "object") {
19294
- return false;
19417
+ }
19418
+ function classifyToolCallError(_error, { status, code }) {
19419
+ const statusCategory = status === void 0 ? void 0 : classifyToolCallHttpStatus(status);
19420
+ if (statusCategory) {
19421
+ return statusCategory;
19295
19422
  }
19296
- if (remainingPath.length > 0) {
19297
- return validatePropertyPathInSchema(propertySchema, remainingPath);
19423
+ const codeCategory = code === void 0 ? void 0 : classifyToolCallErrorCode(code);
19424
+ if (codeCategory) {
19425
+ return codeCategory;
19298
19426
  }
19299
- return true;
19427
+ return "unknown";
19300
19428
  }
19301
- function resolvePropertySchemaInSchema(schema, propertyPath) {
19302
- if (propertyPath.length === 0)
19303
- return schema;
19304
- if (!schema.type || schema.type !== "object")
19305
- return null;
19306
- const properties = schema.properties;
19307
- if (!properties)
19308
- return null;
19309
- const [currentProperty, ...remainingPath] = propertyPath;
19310
- if (!currentProperty)
19429
+ function classifyToolCallHttpStatus(status) {
19430
+ if (status === 401)
19431
+ return "authentication";
19432
+ if (status === 403)
19433
+ return "permission";
19434
+ if (status === 429)
19435
+ return "rate-limit";
19436
+ if (status === 400)
19437
+ return "invalid-request";
19438
+ if (status === 408 || status === 504)
19439
+ return "timeout";
19440
+ return void 0;
19441
+ }
19442
+ function classifyToolCallErrorCode(code) {
19443
+ const normalizedCode = code.toUpperCase();
19444
+ if (normalizedCode === "ETIMEDOUT")
19445
+ return "timeout";
19446
+ if (/^(?:ECONN\w*|ENOTFOUND|EAI_AGAIN)$/u.test(normalizedCode))
19447
+ return "connection";
19448
+ return void 0;
19449
+ }
19450
+ function parseJsonPayload(payload) {
19451
+ if (typeof payload !== "string" || payload.length > MAX_TOOL_CALL_RESULT_PARSE_LENGTH || !["{", "[", '"'].includes(payload[0] ?? "") && payload !== "null") {
19452
+ return payload;
19453
+ }
19454
+ let parsed = payload;
19455
+ for (let index = 0; index < 2 && typeof parsed === "string"; index += 1) {
19456
+ try {
19457
+ parsed = JSON.parse(parsed);
19458
+ } catch {
19459
+ break;
19460
+ }
19461
+ }
19462
+ return parsed;
19463
+ }
19464
+ function getTextCollectionSummary(payload, tool) {
19465
+ if (typeof payload !== "string" || tool.action !== "Found") {
19311
19466
  return null;
19312
- const propertySchema = properties[currentProperty];
19313
- if (!propertySchema || typeof propertySchema !== "object")
19467
+ }
19468
+ const match = new RegExp(`\\b(?:found|returned|matching|showing)\\s+(\\d+)\\s+(?:${escapeRegExp(tool.entity)}|${escapeRegExp(pluralize(tool.entity))})\\b`, "iu").exec(payload);
19469
+ if (!match?.[1]) {
19314
19470
  return null;
19315
- return resolvePropertySchemaInSchema(propertySchema, remainingPath);
19471
+ }
19472
+ const count = Number(match[1]);
19473
+ return Number.isSafeInteger(count) && count >= 0 ? count : null;
19316
19474
  }
19317
-
19318
- // ../../packages/core/dist/esm/schemas/manifest.schema.js
19319
- var uuidv7Regex = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-7[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/i;
19320
- var uuidv7Schema = external_exports.string().regex(uuidv7Regex);
19321
- var timezoneComponent = "[A-Za-z0-9+]+(?:[._+-][A-Za-z0-9+]+)*";
19322
- var durationStringSchema = external_exports.string().regex(durationStringRegex, "Must be a valid duration string (e.g., '500ms', '10s', '1m')").meta({
19323
- description: "A duration string, composed of a positive integer and a unit: 'ms', 's', 'm', 'h', 'd', 'w'."
19324
- });
19325
- var retrySchema = external_exports.object({
19326
- maxAttempts: external_exports.number().int().min(EXECUTION_RETRY_MIN_RETRIES).max(EXECUTION_RETRY_MAX_RETRIES).default(EXECUTION_RETRY_DEFAULT_RETRIES).meta({ description: "The number of retries after the initial step attempt." }),
19327
- strategy: external_exports.enum(["fixed", "exponential_backoff"]).default("exponential_backoff").meta({ description: "The backoff strategy to use for retries." }),
19328
- delay: durationStringSchema.default(EXECUTION_RETRY_DEFAULT_DELAY).meta({
19329
- description: "The initial delay between retries (e.g., '500ms', '1m', '10m')."
19330
- })
19331
- }).meta({ description: "Defines the retry policy for a failed step." });
19332
- var onErrorSchema = external_exports.object({
19333
- retries: retrySchema.optional()
19334
- }).meta({
19335
- description: "Defines the error handling policy for the automation or a specific step."
19336
- });
19337
- var executionTimeoutSchema = durationStringSchema.refine((value) => {
19338
- const milliseconds = durationStringToMilliseconds(value);
19339
- return milliseconds >= EXECUTION_TIMEOUT_MIN_MS && milliseconds <= EXECUTION_TIMEOUT_MAX_MS;
19340
- }, { message: "Execution timeout must be between 1 minute and 60 minutes" }).meta({
19341
- description: "Maximum runtime for a single step, expressed as a duration string (e.g., '10m')."
19342
- });
19343
- var executionPolicySchema = external_exports.object({
19344
- timeout: executionTimeoutSchema.optional().default(EXECUTION_TIMEOUT_DEFAULT),
19345
- onError: onErrorSchema.optional()
19346
- }).meta({
19347
- description: "Defines how long a step may run and how failures are retried. Available globally and per step."
19348
- });
19349
- var intervalScheduleSchema = external_exports.string().regex(intervalScheduleRegex, "Must be a valid interval string (e.g., '45m' or '6h/5h')").meta({ description: "An interval string (e.g., '45m' or '6h/5h')." });
19350
- var cronScheduleSchema = (() => {
19351
- function createCronField(rangePattern) {
19352
- return `(?:(?:(?:\\*|(?:${rangePattern})(?:-(?:${rangePattern}))?)(?:\\/\\d+)?)(?:,(?:(?:\\*|(?:${rangePattern})(?:-(?:${rangePattern}))?)(?:\\/\\d+)?))*)`;
19475
+ function getToolCallDescription(toolName) {
19476
+ const tokens = toolName.slice(toolName.lastIndexOf(".") + 1).toLowerCase().split(/[-_./]/);
19477
+ return {
19478
+ action: getToolCallAction(tokens),
19479
+ entity: getToolCallEntity(tokens)
19480
+ };
19481
+ }
19482
+ function getToolCallAction(tokens) {
19483
+ if (tokens.some((token) => ["find", "search", "list"].includes(token)))
19484
+ return "Found";
19485
+ if (tokens.some((token) => ["get", "read", "retrieve"].includes(token)))
19486
+ return "Retrieved";
19487
+ if (tokens.some((token) => ["add", "create"].includes(token)))
19488
+ return "Created";
19489
+ if (tokens.some((token) => ["update", "edit", "modify"].includes(token)))
19490
+ return "Updated";
19491
+ if (tokens.some((token) => ["delete", "remove"].includes(token)))
19492
+ return "Deleted";
19493
+ if (tokens.some((token) => ["uncomplete", "reopen"].includes(token)))
19494
+ return "Reopened";
19495
+ if (tokens.some((token) => ["complete", "close"].includes(token)))
19496
+ return "Completed";
19497
+ if (tokens.includes("archive"))
19498
+ return "Archived";
19499
+ if (tokens.some((token) => ["send", "reply", "post", "publish", "notify"].includes(token)))
19500
+ return "Sent";
19501
+ if (tokens.some((token) => ["copy", "clone", "duplicate"].includes(token)))
19502
+ return "Copied";
19503
+ if (tokens.some((token) => ["move", "transfer"].includes(token)))
19504
+ return "Moved";
19505
+ if (tokens.includes("rename"))
19506
+ return "Renamed";
19507
+ if (tokens.includes("trash"))
19508
+ return "Trashed";
19509
+ if (tokens.some((token) => ["restore", "untrash"].includes(token)))
19510
+ return "Restored";
19511
+ if (tokens.some((token) => ["append", "insert", "replace"].includes(token)))
19512
+ return "Updated";
19513
+ return "Returned";
19514
+ }
19515
+ function getToolCallEntity(tokens) {
19516
+ const actionIndex = tokens.findIndex((token) => TOOL_CALL_ACTION_TOKENS.has(token));
19517
+ const entityTokens = tokens.slice(actionIndex + 1);
19518
+ const qualifierIndex = entityTokens.findIndex((token) => TOOL_CALL_FILLER_TOKENS.has(token));
19519
+ const primaryTokens = qualifierIndex > 0 ? entityTokens.slice(0, qualifierIndex) : entityTokens;
19520
+ const entity = [...primaryTokens].reverse().find((token) => token.length > 1 && !TOOL_CALL_FILLER_TOKENS.has(token));
19521
+ return entity ? truncateToolCallEntity(singularize(entity)) : "item";
19522
+ }
19523
+ var TOOL_CALL_ACTION_TOKENS = /* @__PURE__ */ new Set([
19524
+ "find",
19525
+ "search",
19526
+ "list",
19527
+ "get",
19528
+ "read",
19529
+ "retrieve",
19530
+ "add",
19531
+ "create",
19532
+ "update",
19533
+ "edit",
19534
+ "modify",
19535
+ "delete",
19536
+ "remove",
19537
+ "uncomplete",
19538
+ "reopen",
19539
+ "complete",
19540
+ "close",
19541
+ "archive",
19542
+ "send",
19543
+ "reply",
19544
+ "post",
19545
+ "publish",
19546
+ "notify",
19547
+ "copy",
19548
+ "clone",
19549
+ "duplicate",
19550
+ "move",
19551
+ "transfer",
19552
+ "rename",
19553
+ "trash",
19554
+ "restore",
19555
+ "untrash",
19556
+ "append",
19557
+ "insert",
19558
+ "replace"
19559
+ ]);
19560
+ var TOOL_CALL_FILLER_TOKENS = /* @__PURE__ */ new Set(["by", "to", "from", "for", "with", "all"]);
19561
+ function getResultCollection(payload, entity) {
19562
+ if (Array.isArray(payload)) {
19563
+ return payload;
19353
19564
  }
19354
- const minutes = createCronField("[0-5]?\\d");
19355
- const hours = createCronField("[01]?\\d|2[0-3]");
19356
- const dayOfMonth = createCronField("0?[1-9]|[12]\\d|3[01]");
19357
- const month = createCronField("0?[1-9]|1[0-2]");
19358
- const dayOfWeek = createCronField("[0-6]");
19359
- const cronExpressionRegex = new RegExp(`^${minutes}\\s+${hours}\\s+${dayOfMonth}\\s+${month}\\s+${dayOfWeek}$`);
19360
- const timezoneRegex = new RegExp(`^${timezoneComponent}(?:/${timezoneComponent})*$`);
19361
- return external_exports.object({
19362
- timezone: external_exports.string().regex(timezoneRegex, "Must be a valid IANA Time Zone name").meta({
19363
- description: "IANA Time Zone name (e.g., America/New_York). Required for cron schedules."
19364
- }),
19365
- expression: external_exports.string().regex(cronExpressionRegex, "Must be a valid 5-part cron expression").meta({
19366
- description: "Standard 5-part cron expression (minute hour day month dayOfWeek)"
19367
- })
19368
- }).meta({
19369
- description: "A cron schedule with required timezone"
19370
- });
19371
- })();
19372
- var scheduleValueSchema = external_exports.union([intervalScheduleSchema, cronScheduleSchema]).meta({ description: "The schedule definition for the trigger." });
19373
- var scheduleTriggerSchema = external_exports.object({
19374
- type: external_exports.literal("schedule"),
19375
- value: scheduleValueSchema,
19376
- description: external_exports.string().optional().meta({ description: "A natural language description of what the trigger does." })
19377
- });
19378
- var webhookTriggerSchema = external_exports.object({
19379
- type: external_exports.literal("webhook"),
19380
- event: external_exports.string({
19381
- message: 'Webhook triggers require an "event" field with the event type (e.g., "todoist.item.added").'
19382
- }).regex(/^[a-z0-9-]+\.[a-z0-9_]+\.[a-z0-9_]+(\.[a-z0-9_]+)*$/, "Event type must be in the format 'connector.entity.action' with exactly 3 required parts, optionally followed by additional segments (e.g., 'todoist.item.added', 'google-calendar.event.created')").meta({
19383
- description: "The event type to listen for (e.g., 'todoist.item.added', 'gmail.message.new'). This references a specific trigger definition from a connector."
19384
- }),
19385
- outputSchema: external_exports.object({
19386
- type: external_exports.string({
19387
- message: 'outputSchema must be a valid JSON Schema with a "type" field. Use {"type": "string"} or {"type": "object", ...}.'
19388
- })
19389
- }, {
19390
- message: 'Webhook triggers require an "outputSchema" field describing the trigger payload structure.'
19391
- }).catchall(external_exports.unknown()).meta({
19392
- description: "A JSON Schema that describes the structure of the trigger payload."
19393
- }),
19394
- inputSchema: external_exports.record(external_exports.string(), external_exports.unknown()).optional().meta({
19395
- description: "An optional JSON Schema to filter the CloudEvent data field. Only events matching this schema will trigger the automation."
19396
- }),
19397
- description: external_exports.string().optional().meta({ description: "A natural language description of what the trigger does." })
19398
- });
19399
- var apiTriggerIdSchema = external_exports.string().regex(uuidv7Regex, "API trigger ID must be a valid UUIDv7");
19400
- var apiTriggerSchema = external_exports.object({
19401
- type: external_exports.literal("api"),
19402
- id: apiTriggerIdSchema.optional().meta({
19403
- description: "A system-generated unique ID for the trigger endpoint (UUIDv7 format). Auto-generated if not provided."
19404
- }),
19405
- outputSchema: external_exports.object({
19406
- type: external_exports.string({
19407
- message: 'outputSchema must be a valid JSON Schema with a "type" field. Use {"type": "string"} or {"type": "object", ...}.'
19408
- })
19409
- }, {
19410
- message: 'API triggers require an "outputSchema" field describing the trigger payload structure.'
19411
- }).catchall(external_exports.unknown()).meta({
19412
- description: "A JSON Schema that describes the structure of the trigger payload."
19413
- }),
19414
- inputSchema: external_exports.object({
19415
- type: external_exports.string({
19416
- message: 'inputSchema must be a valid JSON Schema with a "type" field. Use {"type": "string"} or {"type": "object", ...}.'
19417
- })
19418
- }, {
19419
- message: 'API triggers with input validation require an "inputSchema" field to validate incoming request payloads.'
19420
- }).catchall(external_exports.unknown()).optional().meta({
19421
- description: "Optional JSON Schema to validate incoming request payloads."
19422
- }),
19423
- description: external_exports.string().optional().meta({ description: "A natural language description of what the trigger does." })
19424
- });
19425
- var triggerSchema = external_exports.discriminatedUnion("type", [scheduleTriggerSchema, webhookTriggerSchema, apiTriggerSchema], {
19426
- message: 'Invalid trigger configuration. Schedule triggers need "value", webhook triggers need "event" and "outputSchema", API triggers need "outputSchema" (and optionally "id" and "inputSchema" for advanced features). Use the correct fields for your trigger type.'
19427
- }).meta({ description: "Defines the event that initiates an automation run." });
19428
- function validateTriggersConstraints(triggers, ctx) {
19429
- if (triggers.length === 0)
19430
- return;
19431
- const types = new Set(triggers.map((t) => t.type));
19432
- if (types.size > 1) {
19433
- ctx.addIssue({
19434
- code: external_exports.ZodIssueCode.custom,
19435
- message: "All triggers must be the same type. Cannot mix schedule, webhook, and api triggers."
19436
- });
19437
- return;
19565
+ if (!isRecord(payload)) {
19566
+ return null;
19438
19567
  }
19439
- const triggerType = triggers[0]?.type;
19440
- if ((triggerType === "schedule" || triggerType === "api") && triggers.length > 1) {
19441
- ctx.addIssue({
19442
- code: external_exports.ZodIssueCode.custom,
19443
- message: `Only one ${triggerType} trigger is allowed per automation.`
19444
- });
19445
- return;
19568
+ const collection = payload[entity] ?? payload[pluralize(entity)] ?? payload[`${entity}s`] ?? payload.items ?? payload.data ?? payload.results ?? payload.value;
19569
+ return Array.isArray(collection) ? collection : null;
19570
+ }
19571
+ function getProjectCount(items, entity) {
19572
+ if (entity !== "task") {
19573
+ return 0;
19446
19574
  }
19447
- if (triggerType === "webhook") {
19448
- const events = triggers.map((t) => t.type === "webhook" ? t.event : "");
19449
- const connectors = new Set(events.map((e) => e.split(".")[0]));
19450
- if (connectors.size > 1) {
19451
- ctx.addIssue({
19452
- code: external_exports.ZodIssueCode.custom,
19453
- message: `All webhook triggers must be from the same connector. Found: ${[...connectors].join(", ")}.`
19454
- });
19455
- return;
19575
+ const projectIds = /* @__PURE__ */ new Set();
19576
+ for (const item of items) {
19577
+ if (!isRecord(item)) {
19578
+ continue;
19456
19579
  }
19457
- const duplicateEvents = events.filter((e, i) => events.indexOf(e) !== i);
19458
- if (duplicateEvents.length > 0) {
19459
- ctx.addIssue({
19460
- code: external_exports.ZodIssueCode.custom,
19461
- message: `Duplicate trigger events are not allowed: ${[...new Set(duplicateEvents)].join(", ")}.`
19462
- });
19580
+ const projectId = item.projectId ?? item.project_id;
19581
+ if (typeof projectId === "string" || typeof projectId === "number") {
19582
+ projectIds.add(String(projectId));
19463
19583
  }
19464
19584
  }
19585
+ return projectIds.size;
19465
19586
  }
19466
- var triggersSchema = external_exports.array(triggerSchema).nonempty({ message: "At least one trigger is required." }).superRefine(validateTriggersConstraints).meta({
19467
- description: "An array of triggers for the automation. Multiple webhook triggers from the same connector are supported."
19468
- });
19469
- var stepIdSchema = external_exports.string().regex(/^[a-zA-Z][a-zA-Z0-9]*$/, "ID must be in camelCase format (no dashes or underscores allowed)").meta({ description: "A unique camelCase identifier for the step." });
19470
- var resultFromPathSchema = external_exports.string().regex(/^[a-zA-Z_]\w*(\.[a-zA-Z_]\w*)*$/, 'Path must be a dot-separated property path like "text" or "task.description".').meta({
19471
- description: 'Dot-separated property path relative to a step output, such as "text" or "task.description".'
19472
- });
19473
- var resultFromSchema = external_exports.object({
19474
- stepId: stepIdSchema.meta({
19475
- description: "The step whose output should be used as the persisted run result."
19476
- }),
19477
- path: resultFromPathSchema.optional()
19478
- }).strict().meta({
19479
- description: "Declares which step output becomes the persisted user-facing automation run result."
19480
- });
19481
- var stepOutputSchema = external_exports.object({
19482
- type: external_exports.string({
19483
- message: 'outputSchema must be a valid JSON Schema with a "type" field. Use {"type": "string"} or {"type": "object", ...}.'
19484
- })
19485
- }).catchall(external_exports.unknown()).meta({ description: "A JSON Schema object defining the expected output of this step." });
19486
- var searchStepOutputSchema = external_exports.object({
19487
- type: external_exports.literal("string").meta({
19488
- description: 'Must be "string" for search steps.'
19489
- })
19490
- }).strict().default({ type: "string" }).meta({
19491
- description: 'JSON Schema for search step output. Must be exactly {"type": "string"}.'
19587
+ function formatEntity(entity, count) {
19588
+ return count === 1 ? entity : pluralize(entity);
19589
+ }
19590
+ function singularize(value) {
19591
+ if (value === "people") {
19592
+ return "person";
19593
+ }
19594
+ if (value.endsWith("ies")) {
19595
+ return `${value.slice(0, -3)}y`;
19596
+ }
19597
+ if (value.endsWith("ses") || value.endsWith("xes") || value.endsWith("ches") || value.endsWith("shes")) {
19598
+ return value.slice(0, -2);
19599
+ }
19600
+ return value.endsWith("s") ? value.slice(0, -1) : value;
19601
+ }
19602
+ function pluralize(value) {
19603
+ if (value === "person") {
19604
+ return "people";
19605
+ }
19606
+ if (value.endsWith("y")) {
19607
+ return `${value.slice(0, -1)}ies`;
19608
+ }
19609
+ return value.endsWith("s") ? `${value}es` : `${value}s`;
19610
+ }
19611
+ function isRecord(value) {
19612
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
19613
+ }
19614
+
19615
+ // ../../packages/core/dist/esm/schemas/admin-reliability.schema.js
19616
+ var MAX_RELIABILITY_WINDOW_DAYS = 90;
19617
+ var MAX_RELIABILITY_WINDOW_MS = MAX_RELIABILITY_WINDOW_DAYS * 24 * 60 * 60 * 1e3;
19618
+ var adminReliabilityQuerySchema = external_exports.object({
19619
+ since: isoDateTimeOrDateQueryParamSchema.optional(),
19620
+ until: isoDateTimeOrDateQueryParamSchema.optional()
19621
+ }).refine((query) => {
19622
+ if (isEmptyRunDateRange(query))
19623
+ return false;
19624
+ const { since, until } = resolveRunDateRange(query);
19625
+ if (!since)
19626
+ return true;
19627
+ const rangeEnd = until ?? /* @__PURE__ */ new Date();
19628
+ return rangeEnd.getTime() - since.getTime() <= MAX_RELIABILITY_WINDOW_MS;
19629
+ }, {
19630
+ path: ["since"],
19631
+ message: `Date range must be at most ${MAX_RELIABILITY_WINDOW_DAYS} days`
19632
+ });
19633
+ var adminReliabilityTotalsSchema = external_exports.object({
19634
+ totalAutomations: external_exports.number(),
19635
+ enabledAutomations: external_exports.number(),
19636
+ totalLiveRuns: external_exports.number(),
19637
+ terminalLiveRuns: external_exports.number(),
19638
+ completedLiveRuns: external_exports.number(),
19639
+ failedLiveRuns: external_exports.number(),
19640
+ insufficientCreditFailures: external_exports.number(),
19641
+ reliabilityFailures: external_exports.number(),
19642
+ failureRate: external_exports.number(),
19643
+ reliabilityFailureRate: external_exports.number()
19644
+ });
19645
+ var adminReliabilityFailureCategorySchema = external_exports.object({
19646
+ category: external_exports.enum(FAILURE_CATEGORIES).nullable(),
19647
+ failures: external_exports.number(),
19648
+ shareOfFailedRuns: external_exports.number(),
19649
+ shareOfTerminalRuns: external_exports.number(),
19650
+ includedInReliabilityRate: external_exports.boolean()
19651
+ });
19652
+ var adminReliabilityDailyRowSchema = external_exports.object({
19653
+ date: external_exports.iso.date(),
19654
+ terminalLiveRuns: external_exports.number(),
19655
+ completedLiveRuns: external_exports.number(),
19656
+ failedLiveRuns: external_exports.number(),
19657
+ insufficientCreditFailures: external_exports.number(),
19658
+ reliabilityFailures: external_exports.number(),
19659
+ reliabilityFailureRate: external_exports.number()
19660
+ });
19661
+ var adminReliabilityResponseSchema = external_exports.object({
19662
+ since: external_exports.iso.datetime({ offset: true }),
19663
+ until: external_exports.iso.datetime({ offset: true }),
19664
+ totals: adminReliabilityTotalsSchema,
19665
+ failureCategories: external_exports.array(adminReliabilityFailureCategorySchema),
19666
+ daily: external_exports.array(adminReliabilityDailyRowSchema)
19492
19667
  });
19493
- var exitOnSchema = external_exports.record(external_exports.string(), external_exports.unknown()).meta({
19494
- description: "A JSON Schema object that terminates the automation's run if matched. Otherwise, or if omitted, the automation continues to the next step."
19668
+
19669
+ // ../../packages/core/dist/esm/schemas/connector.schema.js
19670
+ var TriggerSupportedEventSchema = external_exports.object({
19671
+ id: external_exports.string(),
19672
+ name: external_exports.string(),
19673
+ description: external_exports.string()
19495
19674
  });
19496
- var ifConditionSchema = external_exports.string().regex(ifConditionRegex, `Must be a single comparison, e.g. "{{steps.askUser.choice}} == 'archive'": a {{steps.<id>.<path>}} or {{trigger.<path>}} reference, then == or !=, then a single-quoted literal. No boolean combinators.`).meta({
19497
- description: `An optional condition gating whether this step runs, e.g. "{{steps.askUser.choice}} == 'archive'". A single comparison against an earlier step or trigger value; when false the step is skipped.`
19675
+ var ConnectorCapabilitiesSchema = external_exports.object({
19676
+ mcp: external_exports.object({
19677
+ serverUrl: external_exports.string(),
19678
+ scopes: external_exports.array(external_exports.string()),
19679
+ connectionIds: external_exports.array(external_exports.string())
19680
+ }).optional(),
19681
+ triggers: external_exports.object({
19682
+ scopes: external_exports.array(external_exports.string()),
19683
+ connectionIds: external_exports.array(external_exports.string()),
19684
+ supportedEvents: external_exports.array(TriggerSupportedEventSchema)
19685
+ }).optional()
19498
19686
  });
19499
- var baseStepSchema = external_exports.object({
19500
- id: stepIdSchema,
19501
- title: external_exports.string().optional().meta({ description: "A short human-readable title of the step." }),
19502
- description: external_exports.string().meta({ description: "A human-readable description of what the step does." }),
19503
- if: ifConditionSchema.optional(),
19504
- exitOn: exitOnSchema.optional().meta({
19505
- description: "A JSON Schema object that terminates the automation's run if the step output matches. If omitted or not matched, execution continues."
19506
- }),
19507
- execution: executionPolicySchema.optional().meta({
19508
- description: "Overrides the global execution policy (timeout and retries) for this step."
19509
- })
19687
+ var ConnectorSchema = external_exports.object({
19688
+ id: external_exports.string(),
19689
+ name: external_exports.string(),
19690
+ description: external_exports.string(),
19691
+ iconUrl: external_exports.url().nullable().optional(),
19692
+ capabilities: ConnectorCapabilitiesSchema,
19693
+ /** Whether this connector has any user account setup flow. */
19694
+ requiresConnection: external_exports.boolean(),
19695
+ /**
19696
+ * Whether the connector is a product-owned built-in service. This is
19697
+ * independent from whether a third-party connector requires account setup.
19698
+ */
19699
+ isSystem: external_exports.boolean().optional(),
19700
+ /**
19701
+ * Managed connectors (e.g. WhatsApp) share a single linked identity across
19702
+ * connection types — one onboarding covers both action and trigger uses.
19703
+ * The FE collapses per-type rows into a single account when this is true.
19704
+ */
19705
+ isManaged: external_exports.boolean().optional()
19510
19706
  });
19511
- var SUPPORTED_LLM_MODELS = [
19512
- "gemini-2.5-flash-lite",
19513
- "gemini-2.5-flash",
19514
- "gemini-3.1-pro",
19515
- "claude-haiku-4-5",
19516
- "claude-sonnet-4-6"
19517
- ];
19518
- var DEFAULT_LLM_MODEL = "claude-haiku-4-5";
19519
- var llmStepSchema = baseStepSchema.merge(external_exports.object({
19520
- type: external_exports.literal("llm"),
19521
- prompt: external_exports.string().meta({ description: "The prompt template to be sent to the LLM." }),
19522
- outputSchema: stepOutputSchema,
19523
- model: external_exports.enum(SUPPORTED_LLM_MODELS).meta({
19524
- description: `The specific model to use. Supported models: ${SUPPORTED_LLM_MODELS.join(", ")}.`
19525
- }),
19526
- tools: external_exports.record(external_exports.string(), external_exports.array(external_exports.string())).default({}).meta({
19527
- description: 'A mapping from MCP server connector IDs to arrays of their tool names (e.g., {"todoist": ["find-tasks-by-date"], "linear": ["create-issue"]}).'
19528
- })
19529
- })).strict();
19530
- var codegenStepSchema = baseStepSchema.merge(external_exports.object({
19531
- type: external_exports.literal("codegen"),
19532
- prompt: external_exports.string().meta({
19533
- description: "The goal or instruction for what the generated code should accomplish."
19534
- }),
19535
- outputSchema: stepOutputSchema,
19536
- model: external_exports.enum(SUPPORTED_LLM_MODELS).meta({
19537
- description: `The specific model to use for code generation. Supported models: ${SUPPORTED_LLM_MODELS.join(", ")}.`
19538
- }),
19539
- tools: external_exports.record(external_exports.string(), external_exports.array(external_exports.string())).default({}).meta({
19540
- description: 'A mapping from MCP server connector IDs to arrays of their tool names that will be available in the generated code (e.g., {"todoist": ["find-tasks", "update-tasks"]}).'
19541
- }),
19542
- extractedUrls: external_exports.array(external_exports.url()).optional().meta({
19543
- description: "URLs that will be fetched during step execution (auto-extracted from prompt examples)"
19544
- })
19545
- }));
19546
- var runtimeSchema = external_exports.object({
19547
- language: external_exports.literal("typescript").meta({
19548
- description: "Programming language. Only 'typescript' is supported in v0."
19549
- }),
19550
- version: external_exports.enum(SUPPORTED_RUNTIME_VERSIONS).meta({
19551
- description: "Language specification version. Supported values: '5.9', '6.0'."
19552
- })
19553
- }).meta({
19554
- description: "Defines the execution environment for the code step."
19707
+ var GetConnectorsQuerySchema = external_exports.object({
19708
+ query: external_exports.string().optional()
19555
19709
  });
19556
- var codeStepSchema = baseStepSchema.merge(external_exports.object({
19557
- type: external_exports.literal("code"),
19558
- outputSchema: stepOutputSchema,
19559
- runtime: runtimeSchema.optional().default({ language: "typescript", version: "5.9" }).meta({
19560
- description: "An object defining the execution environment. If omitted, it defaults to TypeScript 5.9."
19561
- }),
19562
- code: external_exports.string().meta({
19563
- description: "The code to execute. It has access to trigger and steps objects."
19564
- }),
19565
- extractedUrls: external_exports.array(external_exports.url()).optional().meta({
19566
- description: "URLs that will be fetched during step execution (auto-extracted from code)"
19567
- })
19568
- }));
19569
- var searchStepSchema = baseStepSchema.merge(external_exports.object({
19570
- type: external_exports.literal("search"),
19571
- prompt: external_exports.string().min(1).meta({
19572
- description: "The search prompt to execute using web search."
19573
- }),
19574
- outputSchema: searchStepOutputSchema,
19575
- model: external_exports.enum(SUPPORTED_LLM_MODELS).optional().meta({
19576
- description: `The specific model to use for processing search results. Defaults to ${DEFAULT_LLM_MODEL}.`
19577
- })
19578
- }));
19579
- var delayStepOutputSchema = external_exports.object({
19580
- type: external_exports.literal("object").meta({
19581
- description: 'Must be "object" for delay steps.'
19582
- })
19583
- }).catchall(external_exports.unknown()).meta({
19584
- description: 'JSON Schema for delay step output. Must include {"type": "object"}.'
19710
+ var connectorDetailsSchema = external_exports.object({
19711
+ name: external_exports.string(),
19712
+ displayName: external_exports.string(),
19713
+ description: external_exports.string(),
19714
+ iconUrl: external_exports.string().nullable().optional()
19585
19715
  });
19586
- var baseDelayStepSchema = baseStepSchema.omit({ execution: true });
19587
- var delayStepSchema = baseDelayStepSchema.merge(external_exports.object({
19588
- type: external_exports.literal("delay"),
19589
- duration: durationStringSchema.meta({
19590
- description: "Duration to delay the execution for as a duration string, e.g., '1d', '1w', '2h'."
19591
- }),
19592
- outputSchema: delayStepOutputSchema
19593
- })).strict();
19594
- var baseAskStepSchema = baseStepSchema.omit({ execution: true });
19595
- var askExpiresAfterSchema = durationStringSchema.refine((value) => durationStringToMilliseconds(value) <= ASK_EXPIRES_AFTER_MAX_MS, {
19596
- message: "Ask expiresAfter must not exceed 30 days"
19597
- }).meta({
19598
- description: "How long to wait for a response before the prompt expires, as a duration string (e.g. '1w'). Defaults to 1w, capped at 30d. On expiry the run terminates."
19716
+ var connectorListResponseSchema = external_exports.object({
19717
+ data: external_exports.array(ConnectorSchema),
19718
+ nextCursor: external_exports.string().nullable()
19599
19719
  });
19600
- var askOptionSchema = external_exports.object({
19601
- label: external_exports.string().min(1).meta({ description: "The button text shown to the user." }),
19602
- value: external_exports.string().min(1).meta({
19603
- description: "The machine value emitted as the choice. Unique within the step."
19604
- }),
19605
- terminate: external_exports.boolean().optional().default(false).meta({
19606
- description: 'When true, choosing this option stops the run and no later steps execute (the explicit "deny"/"stop" path).'
19607
- })
19608
- }).strict().meta({ description: "A single selectable option in an ask step." });
19609
- var askOptionsSchema = external_exports.array(askOptionSchema).min(2, "An ask step requires at least two options.").superRefine((options, ctx) => {
19610
- const seen = /* @__PURE__ */ new Set();
19611
- for (let index = 0; index < options.length; index++) {
19612
- const value = options[index]?.value;
19613
- if (value === void 0)
19614
- continue;
19615
- if (seen.has(value)) {
19616
- ctx.addIssue({
19617
- code: external_exports.ZodIssueCode.custom,
19618
- message: `Duplicate option value "${value}". Option values must be unique within an ask step.`,
19619
- path: [index, "value"]
19620
- });
19720
+
19721
+ // ../../packages/core/dist/esm/utils/duration.js
19722
+ var durationUnits = ["ms", "s", "m", "h", "d", "w"];
19723
+ var createDurationPattern = (units) => units.join("|");
19724
+ var durationPattern = createDurationPattern(durationUnits);
19725
+ var durationStringRegex = new RegExp(`^\\d+(${durationPattern})$`);
19726
+ var durationStringWithCaptureRegex = new RegExp(`^(\\d+)(${durationPattern})$`);
19727
+ var intervalScheduleRegex = new RegExp(`^\\d+(${durationPattern})(\\/\\d+(${durationPattern}))?$`);
19728
+ function durationStringToMilliseconds(value) {
19729
+ const match = value.match(durationStringWithCaptureRegex);
19730
+ if (!match) {
19731
+ throw new Error(`Invalid duration string: ${value}`);
19732
+ }
19733
+ const [, rawAmount, rawUnit] = match;
19734
+ const multiplierMap = {
19735
+ ms: 1,
19736
+ s: 1e3,
19737
+ m: 60 * 1e3,
19738
+ h: 60 * 60 * 1e3,
19739
+ d: 24 * 60 * 60 * 1e3,
19740
+ w: 7 * 24 * 60 * 60 * 1e3
19741
+ };
19742
+ const multiplier = multiplierMap[rawUnit];
19743
+ if (multiplier === void 0) {
19744
+ throw new Error(`Invalid duration unit in string: ${value}`);
19745
+ }
19746
+ return Number.parseInt(rawAmount, 10) * multiplier;
19747
+ }
19748
+
19749
+ // ../../packages/core/dist/esm/utils/if-condition.js
19750
+ var ifConditionReferencePattern = "(?:steps\\.[a-zA-Z][a-zA-Z0-9]*(?:\\.[a-zA-Z_]\\w*)*|trigger(?:\\.[a-zA-Z_]\\w*)+)";
19751
+ var ifConditionRegex = new RegExp(`^\\{\\{\\s*${ifConditionReferencePattern}\\s*\\}\\}\\s*(?:==|!=)\\s*'[^']*'$`);
19752
+ function isWord(segment, { underscore }) {
19753
+ if (segment.length === 0)
19754
+ return false;
19755
+ for (let index = 0; index < segment.length; index++) {
19756
+ const char = segment[index];
19757
+ const isAlpha = char >= "a" && char <= "z" || char >= "A" && char <= "Z";
19758
+ const isDigit = char >= "0" && char <= "9";
19759
+ const isUnderscore = underscore && char === "_";
19760
+ if (index === 0) {
19761
+ if (!isAlpha && !isUnderscore)
19762
+ return false;
19763
+ } else if (!isAlpha && !isDigit && !isUnderscore) {
19764
+ return false;
19621
19765
  }
19622
- seen.add(value);
19623
19766
  }
19624
- }).meta({ description: "Two or more options the user chooses between." });
19625
- var askStepOutputSchema = stepOutputSchema.refine((schema) => {
19626
- const properties = schema.properties;
19627
- return schema.type === "object" && typeof properties === "object" && properties !== null && "choice" in properties;
19628
- }, {
19629
- message: 'Ask steps must declare an outputSchema of type "object" with a "choice" property.'
19630
- }).meta({
19631
- description: 'A JSON Schema describing the ask step output. Must be an object with a "choice" property whose value is one of the option values.'
19632
- });
19633
- var askStepSchema = baseAskStepSchema.merge(external_exports.object({
19634
- type: external_exports.literal("ask"),
19635
- message: external_exports.string().min(1).meta({
19636
- description: 'The short headline shown to the user, e.g. "Archive or delete 3 inactive projects?".'
19637
- }),
19638
- details: external_exports.string().optional().meta({
19639
- description: "Optional longer markdown context, e.g. a preview of what each option would do."
19640
- }),
19641
- options: askOptionsSchema,
19642
- expiresAfter: askExpiresAfterSchema.optional().default(ASK_EXPIRES_AFTER_DEFAULT),
19643
- outputSchema: askStepOutputSchema
19644
- })).strict();
19645
- var stepSchema = external_exports.discriminatedUnion("type", [
19646
- llmStepSchema,
19647
- codegenStepSchema,
19648
- codeStepSchema,
19649
- searchStepSchema,
19650
- delayStepSchema,
19651
- askStepSchema
19652
- ]);
19653
- var MANIFEST_VERSIONS = [0, 1];
19654
- var CURRENT_MANIFEST_VERSION = MANIFEST_VERSIONS[MANIFEST_VERSIONS.length - 1];
19655
- var manifestVersionSchema = (() => {
19656
- const [firstVersion, ...otherVersions] = MANIFEST_VERSIONS;
19657
- let schema = external_exports.literal(firstVersion);
19658
- for (const version2 of otherVersions) {
19659
- schema = schema.or(external_exports.literal(version2));
19767
+ return true;
19768
+ }
19769
+ function isValidReference(reference) {
19770
+ const parts = reference.split(".");
19771
+ const [head, ...rest] = parts;
19772
+ if (head === "steps") {
19773
+ const [id, ...segments] = rest;
19774
+ if (id === void 0 || !isWord(id, { underscore: false }))
19775
+ return false;
19776
+ return segments.every((segment) => isWord(segment, { underscore: true }));
19660
19777
  }
19661
- return schema;
19662
- })().meta({
19663
- description: `The version of the manifest schema. Latest is ${MANIFEST_VERSIONS[MANIFEST_VERSIONS.length - 1]}.`
19664
- });
19665
- var manifestSchemaBase = external_exports.object({
19666
- manifestVersion: manifestVersionSchema,
19667
- name: external_exports.string().min(1).meta({
19668
- description: "A human-readable name for the automation that can include spaces."
19669
- }),
19670
- description: external_exports.string().meta({ description: "A human-readable description of the automation's purpose." }),
19671
- triggers: triggersSchema,
19672
- overlap: external_exports.enum([
19673
- "skip",
19674
- "buffer_one",
19675
- "buffer_all",
19676
- "cancel_other",
19677
- "terminate_other",
19678
- "allow_all"
19679
- ]).optional().default("skip").meta({
19680
- description: 'Defines policy for overlapping runs. Defaults to "skip".'
19681
- }),
19682
- execution: executionPolicySchema.optional().meta({ description: "Global defaults for timeout and retry behavior." }),
19683
- resultFrom: resultFromSchema.optional(),
19684
- steps: external_exports.array(stepSchema).nonempty().meta({ description: "An ordered array of step objects that define the workflow." })
19685
- }).meta({ description: "The root schema for an Automation Manifest, defining its behavior." });
19686
- var manifestSchema = manifestSchemaBase.superRefine(validateManifestResultFrom).superRefine(validateManifestTemplateVariables).superRefine(validateManifestAskOverlap).superRefine(validateManifestAskChoiceEnum);
19687
- var partialManifestSchema = manifestSchemaBase.partial().extend({
19688
- // Override steps to allow empty array for drafts (remove .nonempty() requirement)
19689
- steps: external_exports.array(stepSchema).default([]),
19690
- // Override triggers to allow empty array for drafts (remove .nonempty() but keep invariants)
19691
- triggers: external_exports.array(triggerSchema).superRefine(validateTriggersConstraints).optional()
19692
- }).required({
19693
- manifestVersion: true,
19694
- steps: true
19695
- });
19696
- var templateVariableRegex = /\{\{\s*[\w.]+\s*\}\}/g;
19697
- var codeStepReferenceRegex = /\bsteps\.([a-zA-Z][a-zA-Z0-9]*)(?:\.([a-zA-Z_][\w.]*))?/g;
19698
- var backtickStepReferenceRegex = /`steps\.([a-zA-Z][a-zA-Z0-9]*)(?:\.([a-zA-Z_][\w.]*))?`/g;
19699
- function extractStepReferences(text, regex) {
19700
- const references = [];
19701
- for (const match of text.matchAll(regex)) {
19702
- const stepId = match[1];
19703
- if (!stepId)
19704
- continue;
19705
- const propertyPathStr = match[2];
19706
- const propertyPath = propertyPathStr ? propertyPathStr.split(".") : [];
19707
- references.push({ stepId, propertyPath });
19778
+ if (head === "trigger") {
19779
+ if (rest.length === 0)
19780
+ return false;
19781
+ return rest.every((segment) => isWord(segment, { underscore: true }));
19708
19782
  }
19709
- return references;
19783
+ return false;
19710
19784
  }
19711
- function validateGuardReference({ stepId, referencedStepId, currentStepGuard, referencedGuard, sourceType }) {
19712
- if (referencedGuard === void 0 || referencedGuard === currentStepGuard) {
19785
+ function parseIfCondition(condition) {
19786
+ if (!condition.startsWith("{{"))
19787
+ return null;
19788
+ const closeIndex = condition.indexOf("}}");
19789
+ if (closeIndex === -1)
19790
+ return null;
19791
+ const reference = condition.slice(2, closeIndex).trim();
19792
+ if (!isValidReference(reference))
19793
+ return null;
19794
+ const afterReference = condition.slice(closeIndex + 2).trimStart();
19795
+ const operator = afterReference.startsWith("==") ? "==" : afterReference.startsWith("!=") ? "!=" : null;
19796
+ if (!operator)
19797
+ return null;
19798
+ const afterOperator = afterReference.slice(2).trimStart();
19799
+ if (afterOperator.length < 2 || !afterOperator.startsWith("'") || !afterOperator.endsWith("'")) {
19713
19800
  return null;
19714
19801
  }
19715
- return `Step "${stepId}" ${sourceType} references step "${referencedStepId}", which only runs when \`if: ${referencedGuard}\`. A step may reference a conditional step only when it carries the identical \`if\` condition.`;
19802
+ const literal2 = afterOperator.slice(1, -1);
19803
+ if (literal2.includes("'"))
19804
+ return null;
19805
+ return { reference, operator, literal: literal2 };
19716
19806
  }
19717
- function validateStepReference({ stepId, referencedStepId, propertyPath, context, sourceType, currentStepGuard }) {
19718
- if (!context.steps.has(referencedStepId)) {
19719
- return `Step "${stepId}" ${sourceType} references step "${referencedStepId}" before it is available. Steps can only reference outputs from earlier steps.`;
19720
- }
19721
- const guardError = validateGuardReference({
19722
- stepId,
19723
- referencedStepId,
19724
- currentStepGuard,
19725
- referencedGuard: context.guards.get(referencedStepId),
19726
- sourceType
19727
- });
19728
- if (guardError) {
19729
- return guardError;
19730
- }
19731
- if (propertyPath.length > 0) {
19732
- const stepOutputSchema2 = context.steps.get(referencedStepId);
19733
- if (stepOutputSchema2) {
19734
- const isValid = validatePropertyPathInSchema(stepOutputSchema2, propertyPath);
19735
- if (!isValid) {
19736
- return `Step "${stepId}" ${sourceType} references "steps.${referencedStepId}.${propertyPath.join(".")}" but this property path does not exist in step "${referencedStepId}"'s outputSchema.`;
19737
- }
19738
- }
19739
- }
19740
- return null;
19807
+
19808
+ // ../../packages/core/dist/esm/schemas/manifest.helpers.js
19809
+ function getManifestTriggers(manifest) {
19810
+ return manifest.triggers ?? [];
19741
19811
  }
19742
- function validateTemplateVariable({ variable, context, stepId, currentStepGuard, sourceType = "prompt" }) {
19743
- const parts = variable.split(".").filter(Boolean);
19744
- if (parts.length === 0) {
19745
- return null;
19812
+ function hasTriggerType(value) {
19813
+ if (!value || typeof value !== "object")
19814
+ return false;
19815
+ const manifest = value;
19816
+ const triggers = getManifestTriggers(manifest);
19817
+ return triggers.length > 0 && typeof triggers[0]?.type === "string";
19818
+ }
19819
+ function isStringTypedSchema(schema) {
19820
+ if (!schema)
19821
+ return false;
19822
+ if (schema === "string")
19823
+ return true;
19824
+ if (typeof schema !== "object" || schema === null)
19825
+ return false;
19826
+ const typedSchema = schema;
19827
+ return typedSchema["type"] === "string";
19828
+ }
19829
+ function isPlainStringSchema(schema) {
19830
+ if (!isStringTypedSchema(schema))
19831
+ return false;
19832
+ if (schema === "string")
19833
+ return true;
19834
+ return Object.keys(schema).length === 1;
19835
+ }
19836
+ function validatePropertyPathInSchema(schema, propertyPath) {
19837
+ if (propertyPath.length === 0) {
19838
+ return true;
19746
19839
  }
19747
- const [root13, ...rest] = parts;
19748
- if (root13 === "trigger") {
19749
- if (rest.length === 0) {
19750
- return `Step "${stepId}" ${sourceType} references "{{trigger}}" without a property. Use {{trigger.<property>}} syntax (e.g., {{trigger.payload}}, {{trigger.metadata}}).`;
19751
- }
19752
- if (context.triggerType === "schedule" && rest[0] === "payload") {
19753
- return `Step "${stepId}" references "{{trigger.payload}}" but schedule triggers have no payload. Add a step to fetch the data you need.`;
19754
- }
19755
- if (context.triggerType === "schedule" && rest[0] === "user") {
19756
- if (context.scheduleInjectsTimezone && rest.length === 2 && rest[1] === "timezone") {
19757
- return null;
19758
- }
19759
- return `Step "${stepId}" ${sourceType} references "{{trigger.${rest.join(".")}}}" but schedule triggers only provide "{{trigger.user.timezone}}", and only for cron schedules with a timezone. Interval schedules provide no trigger data.`;
19760
- }
19761
- if (context.triggerOutputSchemas.length > 0) {
19762
- const invalidSchemas = context.triggerOutputSchemas.filter((schema) => !validatePropertyPathInSchema(schema, rest));
19763
- if (invalidSchemas.length > 0) {
19764
- return `Step "${stepId}" ${sourceType} references "{{trigger.${rest.join(".")}}}" but this property path does not exist in all trigger outputSchemas. The path must be valid for every trigger since any one may fire the automation.`;
19765
- }
19766
- } else {
19767
- return `Step "${stepId}" ${sourceType} references "{{trigger.${rest.join(".")}}}" but this trigger type has no outputSchema and cannot provide trigger data at runtime.`;
19768
- }
19769
- return null;
19840
+ if (!schema.type || schema.type !== "object") {
19841
+ return false;
19770
19842
  }
19771
- if (root13 === "steps") {
19772
- const referencedStepId = rest[0];
19773
- if (!referencedStepId) {
19774
- return `Step "${stepId}" ${sourceType} references "steps" but is missing the step identifier in template variable "{{${variable}}}". Use {{steps.<stepId>}} syntax.`;
19775
- }
19776
- return validateStepReference({
19777
- stepId,
19778
- referencedStepId,
19779
- propertyPath: rest.slice(1),
19780
- context,
19781
- sourceType,
19782
- currentStepGuard
19783
- });
19843
+ const properties = schema.properties;
19844
+ if (!properties)
19845
+ return true;
19846
+ const [currentProperty, ...remainingPath] = propertyPath;
19847
+ if (!currentProperty)
19848
+ return false;
19849
+ const propertySchema = properties[currentProperty];
19850
+ if (!propertySchema || typeof propertySchema !== "object") {
19851
+ return false;
19784
19852
  }
19785
- return `Step "${stepId}" ${sourceType} references unsupported template variable "{{${variable}}}". Allowed roots are {{trigger...}} and {{steps.<stepId>...}}.`;
19786
- }
19787
- function extractTemplateVariables(prompt) {
19788
- const variables = [];
19789
- for (const match of prompt.matchAll(templateVariableRegex)) {
19790
- const variable = match[0]?.replace(/\{\{\s*|\s*\}\}/g, "").trim();
19791
- if (variable) {
19792
- variables.push(variable);
19793
- }
19853
+ if (remainingPath.length > 0) {
19854
+ return validatePropertyPathInSchema(propertySchema, remainingPath);
19794
19855
  }
19795
- return variables;
19856
+ return true;
19796
19857
  }
19797
- function resolveTriggersFromData(data) {
19798
- if (data.triggers && Array.isArray(data.triggers) && data.triggers.length > 0) {
19799
- return data.triggers;
19800
- }
19801
- return [];
19858
+ function resolvePropertySchemaInSchema(schema, propertyPath) {
19859
+ if (propertyPath.length === 0)
19860
+ return schema;
19861
+ if (!schema.type || schema.type !== "object")
19862
+ return null;
19863
+ const properties = schema.properties;
19864
+ if (!properties)
19865
+ return null;
19866
+ const [currentProperty, ...remainingPath] = propertyPath;
19867
+ if (!currentProperty)
19868
+ return null;
19869
+ const propertySchema = properties[currentProperty];
19870
+ if (!propertySchema || typeof propertySchema !== "object")
19871
+ return null;
19872
+ return resolvePropertySchemaInSchema(propertySchema, remainingPath);
19802
19873
  }
19803
- var BOUNDED_OVERLAP_MODES = ["skip", "buffer_one", "cancel_other", "terminate_other"];
19804
- function validateManifestAskOverlap(data, ctx) {
19805
- const steps = data.steps || [];
19806
- const hasAskStep = steps.some((step) => step?.type === "ask");
19807
- if (!hasAskStep)
19808
- return;
19809
- const overlap = data.overlap ?? "skip";
19810
- if (!BOUNDED_OVERLAP_MODES.includes(overlap)) {
19811
- ctx.addIssue({
19812
- code: external_exports.ZodIssueCode.custom,
19813
- message: `Manifests with an ask step must use a bounded overlap mode (${BOUNDED_OVERLAP_MODES.join(", ")}). "${overlap}" can accumulate parked or buffered runs while a prompt waits for a response.`,
19814
- path: ["overlap"]
19815
- });
19816
- }
19817
- }
19818
- function validateManifestAskChoiceEnum(data, ctx) {
19819
- const steps = data.steps || [];
19820
- for (let stepIndex = 0; stepIndex < steps.length; stepIndex++) {
19821
- const step = steps[stepIndex];
19822
- if (!step || step.type !== "ask")
19823
- continue;
19824
- const choicePath = ["steps", stepIndex, "outputSchema", "properties", "choice", "enum"];
19825
- const choiceSchema = step.outputSchema?.properties?.choice;
19826
- const choiceEnum = choiceSchema?.enum;
19827
- if (!Array.isArray(choiceEnum)) {
19828
- ctx.addIssue({
19829
- code: external_exports.ZodIssueCode.custom,
19830
- message: `Ask step "${step.id ?? stepIndex}" outputSchema must declare choice as an enum of the option values.`,
19831
- path: choicePath
19832
- });
19833
- continue;
19834
- }
19835
- const nonStringEnum = choiceEnum.filter((value) => typeof value !== "string");
19836
- if (nonStringEnum.length > 0) {
19837
- ctx.addIssue({
19838
- code: external_exports.ZodIssueCode.custom,
19839
- message: `Ask step "${step.id ?? stepIndex}" outputSchema choice enum must contain only string values; ask options always emit strings.`,
19840
- path: choicePath
19841
- });
19842
- continue;
19843
- }
19844
- const optionValues = (step.options ?? []).map((option) => option?.value).filter((value) => typeof value === "string");
19845
- const optionSet = new Set(optionValues);
19846
- const enumSet = new Set(choiceEnum);
19847
- const missing = optionValues.filter((value) => !enumSet.has(value));
19848
- const unexpected = choiceEnum.filter((value) => !optionSet.has(value));
19849
- if (missing.length === 0 && unexpected.length === 0)
19850
- continue;
19851
- const detail = [
19852
- missing.length > 0 ? `missing ${missing.map((v) => `'${v}'`).join(", ")}` : "",
19853
- unexpected.length > 0 ? `unexpected ${unexpected.map((v) => `'${v}'`).join(", ")}` : ""
19854
- ].filter(Boolean).join("; ");
19855
- ctx.addIssue({
19856
- code: external_exports.ZodIssueCode.custom,
19857
- message: `Ask step "${step.id ?? stepIndex}" outputSchema choice enum must list exactly the option values (${detail}).`,
19858
- path: choicePath
19859
- });
19860
- }
19861
- }
19862
- function validateManifestResultFrom(data, ctx) {
19863
- if (!data.resultFrom) {
19864
- return;
19874
+
19875
+ // ../../packages/core/dist/esm/schemas/manifest.schema.js
19876
+ var uuidv7Regex = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-7[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/i;
19877
+ var uuidv7Schema = external_exports.string().regex(uuidv7Regex);
19878
+ var timezoneComponent = "[A-Za-z0-9+]+(?:[._+-][A-Za-z0-9+]+)*";
19879
+ var durationStringSchema = external_exports.string().regex(durationStringRegex, "Must be a valid duration string (e.g., '500ms', '10s', '1m')").meta({
19880
+ description: "A duration string, composed of a positive integer and a unit: 'ms', 's', 'm', 'h', 'd', 'w'."
19881
+ });
19882
+ var retrySchema = external_exports.object({
19883
+ maxAttempts: external_exports.number().int().min(EXECUTION_RETRY_MIN_RETRIES).max(EXECUTION_RETRY_MAX_RETRIES).default(EXECUTION_RETRY_DEFAULT_RETRIES).meta({ description: "The number of retries after the initial step attempt." }),
19884
+ strategy: external_exports.enum(["fixed", "exponential_backoff"]).default("exponential_backoff").meta({ description: "The backoff strategy to use for retries." }),
19885
+ delay: durationStringSchema.default(EXECUTION_RETRY_DEFAULT_DELAY).meta({
19886
+ description: "The initial delay between retries (e.g., '500ms', '1m', '10m')."
19887
+ })
19888
+ }).meta({ description: "Defines the retry policy for a failed step." });
19889
+ var onErrorSchema = external_exports.object({
19890
+ retries: retrySchema.optional()
19891
+ }).meta({
19892
+ description: "Defines the error handling policy for the automation or a specific step."
19893
+ });
19894
+ var executionTimeoutSchema = durationStringSchema.refine((value) => {
19895
+ const milliseconds = durationStringToMilliseconds(value);
19896
+ return milliseconds >= EXECUTION_TIMEOUT_MIN_MS && milliseconds <= EXECUTION_TIMEOUT_MAX_MS;
19897
+ }, { message: "Execution timeout must be between 1 minute and 60 minutes" }).meta({
19898
+ description: "Maximum runtime for a single step, expressed as a duration string (e.g., '10m')."
19899
+ });
19900
+ var executionPolicySchema = external_exports.object({
19901
+ timeout: executionTimeoutSchema.optional().default(EXECUTION_TIMEOUT_DEFAULT),
19902
+ onError: onErrorSchema.optional()
19903
+ }).meta({
19904
+ description: "Defines how long a step may run and how failures are retried. Available globally and per step."
19905
+ });
19906
+ var intervalScheduleSchema = external_exports.string().regex(intervalScheduleRegex, "Must be a valid interval string (e.g., '45m' or '6h/5h')").meta({ description: "An interval string (e.g., '45m' or '6h/5h')." });
19907
+ var cronScheduleSchema = (() => {
19908
+ function createCronField(rangePattern) {
19909
+ return `(?:(?:(?:\\*|(?:${rangePattern})(?:-(?:${rangePattern}))?)(?:\\/\\d+)?)(?:,(?:(?:\\*|(?:${rangePattern})(?:-(?:${rangePattern}))?)(?:\\/\\d+)?))*)`;
19865
19910
  }
19866
- const steps = data.steps || [];
19867
- const referencedStep = steps.find((step) => step.id === data.resultFrom?.stepId);
19868
- if (!referencedStep) {
19869
- ctx.addIssue({
19870
- code: external_exports.ZodIssueCode.custom,
19871
- message: `resultFrom.stepId references unknown step "${data.resultFrom.stepId}".`,
19872
- path: ["resultFrom", "stepId"]
19873
- });
19911
+ const minutes = createCronField("[0-5]?\\d");
19912
+ const hours = createCronField("[01]?\\d|2[0-3]");
19913
+ const dayOfMonth = createCronField("0?[1-9]|[12]\\d|3[01]");
19914
+ const month = createCronField("0?[1-9]|1[0-2]");
19915
+ const dayOfWeek = createCronField("[0-6]");
19916
+ const cronExpressionRegex = new RegExp(`^${minutes}\\s+${hours}\\s+${dayOfMonth}\\s+${month}\\s+${dayOfWeek}$`);
19917
+ const timezoneRegex = new RegExp(`^${timezoneComponent}(?:/${timezoneComponent})*$`);
19918
+ return external_exports.object({
19919
+ timezone: external_exports.string().regex(timezoneRegex, "Must be a valid IANA Time Zone name").meta({
19920
+ description: "IANA Time Zone name (e.g., America/New_York). Required for cron schedules."
19921
+ }),
19922
+ expression: external_exports.string().regex(cronExpressionRegex, "Must be a valid 5-part cron expression").meta({
19923
+ description: "Standard 5-part cron expression (minute hour day month dayOfWeek)"
19924
+ })
19925
+ }).meta({
19926
+ description: "A cron schedule with required timezone"
19927
+ });
19928
+ })();
19929
+ var scheduleValueSchema = external_exports.union([intervalScheduleSchema, cronScheduleSchema]).meta({ description: "The schedule definition for the trigger." });
19930
+ var scheduleTriggerSchema = external_exports.object({
19931
+ type: external_exports.literal("schedule"),
19932
+ value: scheduleValueSchema,
19933
+ description: external_exports.string().optional().meta({ description: "A natural language description of what the trigger does." })
19934
+ });
19935
+ var webhookTriggerSchema = external_exports.object({
19936
+ type: external_exports.literal("webhook"),
19937
+ event: external_exports.string({
19938
+ message: 'Webhook triggers require an "event" field with the event type (e.g., "todoist.item.added").'
19939
+ }).regex(/^[a-z0-9-]+\.[a-z0-9_]+\.[a-z0-9_]+(\.[a-z0-9_]+)*$/, "Event type must be in the format 'connector.entity.action' with exactly 3 required parts, optionally followed by additional segments (e.g., 'todoist.item.added', 'google-calendar.event.created')").meta({
19940
+ description: "The event type to listen for (e.g., 'todoist.item.added', 'gmail.message.new'). This references a specific trigger definition from a connector."
19941
+ }),
19942
+ outputSchema: external_exports.object({
19943
+ type: external_exports.string({
19944
+ message: 'outputSchema must be a valid JSON Schema with a "type" field. Use {"type": "string"} or {"type": "object", ...}.'
19945
+ })
19946
+ }, {
19947
+ message: 'Webhook triggers require an "outputSchema" field describing the trigger payload structure.'
19948
+ }).catchall(external_exports.unknown()).meta({
19949
+ description: "A JSON Schema that describes the structure of the trigger payload."
19950
+ }),
19951
+ inputSchema: external_exports.record(external_exports.string(), external_exports.unknown()).optional().meta({
19952
+ description: "An optional JSON Schema to filter the CloudEvent data field. Only events matching this schema will trigger the automation."
19953
+ }),
19954
+ description: external_exports.string().optional().meta({ description: "A natural language description of what the trigger does." })
19955
+ });
19956
+ var apiTriggerIdSchema = external_exports.string().regex(uuidv7Regex, "API trigger ID must be a valid UUIDv7");
19957
+ var apiTriggerSchema = external_exports.object({
19958
+ type: external_exports.literal("api"),
19959
+ id: apiTriggerIdSchema.optional().meta({
19960
+ description: "A system-generated unique ID for the trigger endpoint (UUIDv7 format). Auto-generated if not provided."
19961
+ }),
19962
+ outputSchema: external_exports.object({
19963
+ type: external_exports.string({
19964
+ message: 'outputSchema must be a valid JSON Schema with a "type" field. Use {"type": "string"} or {"type": "object", ...}.'
19965
+ })
19966
+ }, {
19967
+ message: 'API triggers require an "outputSchema" field describing the trigger payload structure.'
19968
+ }).catchall(external_exports.unknown()).meta({
19969
+ description: "A JSON Schema that describes the structure of the trigger payload."
19970
+ }),
19971
+ inputSchema: external_exports.object({
19972
+ type: external_exports.string({
19973
+ message: 'inputSchema must be a valid JSON Schema with a "type" field. Use {"type": "string"} or {"type": "object", ...}.'
19974
+ })
19975
+ }, {
19976
+ message: 'API triggers with input validation require an "inputSchema" field to validate incoming request payloads.'
19977
+ }).catchall(external_exports.unknown()).optional().meta({
19978
+ description: "Optional JSON Schema to validate incoming request payloads."
19979
+ }),
19980
+ description: external_exports.string().optional().meta({ description: "A natural language description of what the trigger does." })
19981
+ });
19982
+ var triggerSchema = external_exports.discriminatedUnion("type", [scheduleTriggerSchema, webhookTriggerSchema, apiTriggerSchema], {
19983
+ message: 'Invalid trigger configuration. Schedule triggers need "value", webhook triggers need "event" and "outputSchema", API triggers need "outputSchema" (and optionally "id" and "inputSchema" for advanced features). Use the correct fields for your trigger type.'
19984
+ }).meta({ description: "Defines the event that initiates an automation run." });
19985
+ function validateTriggersConstraints(triggers, ctx) {
19986
+ if (triggers.length === 0)
19874
19987
  return;
19875
- }
19876
- if (referencedStep.if) {
19988
+ const types = new Set(triggers.map((t) => t.type));
19989
+ if (types.size > 1) {
19877
19990
  ctx.addIssue({
19878
19991
  code: external_exports.ZodIssueCode.custom,
19879
- message: `resultFrom.stepId references step "${referencedStep.id}", which only runs conditionally (\`if\`). resultFrom must point at a step that always runs.`,
19880
- path: ["resultFrom", "stepId"]
19992
+ message: "All triggers must be the same type. Cannot mix schedule, webhook, and api triggers."
19881
19993
  });
19882
19994
  return;
19883
19995
  }
19884
- const outputSchema = referencedStep.outputSchema;
19885
- if (!outputSchema) {
19996
+ const triggerType = triggers[0]?.type;
19997
+ if ((triggerType === "schedule" || triggerType === "api") && triggers.length > 1) {
19886
19998
  ctx.addIssue({
19887
19999
  code: external_exports.ZodIssueCode.custom,
19888
- message: `resultFrom.stepId references step "${referencedStep.id}" without an outputSchema.`,
19889
- path: ["resultFrom", "stepId"]
20000
+ message: `Only one ${triggerType} trigger is allowed per automation.`
19890
20001
  });
19891
20002
  return;
19892
20003
  }
19893
- if (!data.resultFrom.path) {
19894
- if (!isPlainStringSchema(outputSchema)) {
20004
+ if (triggerType === "webhook") {
20005
+ const events = triggers.map((t) => t.type === "webhook" ? t.event : "");
20006
+ const connectors = new Set(events.map((e) => e.split(".")[0]));
20007
+ if (connectors.size > 1) {
19895
20008
  ctx.addIssue({
19896
20009
  code: external_exports.ZodIssueCode.custom,
19897
- message: "resultFrom.path may be omitted only when the referenced step outputSchema is a string.",
19898
- path: ["resultFrom", "path"]
20010
+ message: `All webhook triggers must be from the same connector. Found: ${[...connectors].join(", ")}.`
20011
+ });
20012
+ return;
20013
+ }
20014
+ const duplicateEvents = events.filter((e, i) => events.indexOf(e) !== i);
20015
+ if (duplicateEvents.length > 0) {
20016
+ ctx.addIssue({
20017
+ code: external_exports.ZodIssueCode.custom,
20018
+ message: `Duplicate trigger events are not allowed: ${[...new Set(duplicateEvents)].join(", ")}.`
19899
20019
  });
19900
20020
  }
19901
- return;
19902
- }
19903
- const propertyPath = data.resultFrom.path.split(".");
19904
- const resolvedSchema = resolvePropertySchemaInSchema(outputSchema, propertyPath);
19905
- if (!resolvedSchema) {
19906
- ctx.addIssue({
19907
- code: external_exports.ZodIssueCode.custom,
19908
- message: `resultFrom.path "${data.resultFrom.path}" does not exist in step "${referencedStep.id}" outputSchema.`,
19909
- path: ["resultFrom", "path"]
19910
- });
19911
- return;
19912
- }
19913
- if (!isStringTypedSchema(resolvedSchema)) {
19914
- ctx.addIssue({
19915
- code: external_exports.ZodIssueCode.custom,
19916
- message: "resultFrom.path must resolve to a string value.",
19917
- path: ["resultFrom", "path"]
19918
- });
19919
- }
19920
- }
19921
- function isNonScalarSchema(schema) {
19922
- const type = schema.type;
19923
- if (type === "object" || type === "array")
19924
- return true;
19925
- if (Array.isArray(type) && (type.includes("object") || type.includes("array")))
19926
- return true;
19927
- const structural = schema;
19928
- return structural.properties !== void 0 || structural.items !== void 0;
19929
- }
19930
- function ifConditionLiteralError({ reference, literal: literal2, stepId, schema }) {
19931
- const enumValues = schema.enum;
19932
- if (Array.isArray(enumValues)) {
19933
- if (enumValues.some((value) => String(value) === literal2))
19934
- return null;
19935
- const allowed = enumValues.map((value) => `'${String(value)}'`).join(", ");
19936
- return `Step "${stepId}" condition compares "${reference}" to '${literal2}', which is not one of its allowed values: ${allowed}.`;
19937
- }
19938
- if (isNonScalarSchema(schema)) {
19939
- return `Step "${stepId}" condition compares "${reference}", which is an object or array, to a string literal and can never match. Reference a scalar property instead.`;
19940
20021
  }
19941
- return null;
19942
20022
  }
19943
- function validateIfConditionLiteral({ condition, context, stepId }) {
19944
- const parsed = parseIfCondition(condition);
19945
- if (!parsed)
19946
- return null;
19947
- const { reference, literal: literal2 } = parsed;
19948
- const parts = reference.split(".");
19949
- if (parts[0] === "steps") {
19950
- const referencedStepId = parts[1];
19951
- if (!referencedStepId)
19952
- return null;
19953
- const outputSchema = context.steps.get(referencedStepId);
19954
- if (!outputSchema)
19955
- return null;
19956
- const propertyPath = parts.slice(2);
19957
- const resolved = propertyPath.length === 0 ? outputSchema : resolvePropertySchemaInSchema(outputSchema, propertyPath);
19958
- if (!resolved)
19959
- return null;
19960
- return ifConditionLiteralError({ reference, literal: literal2, stepId, schema: resolved });
19961
- }
19962
- if (parts[0] === "trigger") {
19963
- const propertyPath = parts.slice(1);
19964
- if (propertyPath.length === 0)
19965
- return null;
19966
- let firstError = null;
19967
- let anyResolved = false;
19968
- for (const schema of context.triggerOutputSchemas) {
19969
- const resolved = resolvePropertySchemaInSchema(schema, propertyPath);
19970
- if (!resolved)
19971
- continue;
19972
- anyResolved = true;
19973
- const error61 = ifConditionLiteralError({ reference, literal: literal2, stepId, schema: resolved });
19974
- if (!error61)
19975
- return null;
19976
- firstError ??= error61;
19977
- }
19978
- return anyResolved ? firstError : null;
19979
- }
19980
- return null;
19981
- }
19982
- function validateManifestTemplateVariables(data, ctx) {
19983
- const triggers = resolveTriggersFromData(data);
19984
- const firstTrigger = triggers[0];
19985
- const scheduleValue = firstTrigger?.value;
19986
- const scheduleInjectsTimezone = firstTrigger?.type === "schedule" && typeof scheduleValue === "object" && scheduleValue !== null && typeof scheduleValue.timezone === "string";
19987
- const context = {
19988
- triggerType: firstTrigger?.type,
19989
- scheduleInjectsTimezone,
19990
- triggerOutputSchemas: [],
19991
- steps: /* @__PURE__ */ new Map(),
19992
- guards: /* @__PURE__ */ new Map()
19993
- };
19994
- for (const trigger of triggers) {
19995
- if ((trigger.type === "webhook" || trigger.type === "api" || trigger.type === "manual") && trigger.outputSchema) {
19996
- context.triggerOutputSchemas.push(trigger.outputSchema);
19997
- }
19998
- }
19999
- const steps = data.steps || [];
20000
- for (let stepIndex = 0; stepIndex < steps.length; stepIndex++) {
20001
- const step = steps[stepIndex];
20002
- if (!step)
20003
- continue;
20004
- const currentStepGuard = step.if;
20005
- const unavailableStepRefs = /* @__PURE__ */ new Set();
20006
- const propertyPathErrors = [];
20007
- const processStepRef = (ref, sourceType, fieldPath) => {
20008
- if (!context.steps.has(ref.stepId)) {
20009
- unavailableStepRefs.add(ref.stepId);
20010
- return;
20011
- }
20012
- const guardError = validateGuardReference({
20013
- stepId: step.id,
20014
- referencedStepId: ref.stepId,
20015
- currentStepGuard,
20016
- referencedGuard: context.guards.get(ref.stepId),
20017
- sourceType
20018
- });
20019
- if (guardError) {
20020
- propertyPathErrors.push({ message: guardError, path: fieldPath });
20021
- return;
20022
- }
20023
- const stepOutputSchema2 = context.steps.get(ref.stepId);
20024
- if (!stepOutputSchema2 || ref.propertyPath.length === 0) {
20025
- return;
20026
- }
20027
- const isValid = validatePropertyPathInSchema(stepOutputSchema2, ref.propertyPath);
20028
- if (!isValid) {
20029
- propertyPathErrors.push({
20030
- message: `Step "${step.id}" ${sourceType} references "steps.${ref.stepId}.${ref.propertyPath.join(".")}" but this property path does not exist in step "${ref.stepId}"'s outputSchema.`,
20031
- path: fieldPath
20032
- });
20033
- }
20034
- };
20035
- if (typeof step.if === "string") {
20036
- for (const variable of extractTemplateVariables(step.if)) {
20037
- const error61 = validateTemplateVariable({
20038
- variable,
20039
- context,
20040
- stepId: step.id,
20041
- currentStepGuard,
20042
- sourceType: "condition"
20043
- });
20044
- if (error61) {
20045
- ctx.addIssue({
20046
- code: external_exports.ZodIssueCode.custom,
20047
- message: error61,
20048
- path: ["steps", stepIndex, "if"]
20049
- });
20050
- }
20051
- }
20052
- const literalError = validateIfConditionLiteral({
20053
- condition: step.if,
20054
- context,
20055
- stepId: step.id
20056
- });
20057
- if (literalError) {
20058
- ctx.addIssue({
20059
- code: external_exports.ZodIssueCode.custom,
20060
- message: literalError,
20061
- path: ["steps", stepIndex, "if"]
20062
- });
20063
- }
20064
- }
20065
- if ("prompt" in step && typeof step.prompt === "string") {
20066
- const variables = extractTemplateVariables(step.prompt);
20067
- for (const variable of variables) {
20068
- const error61 = validateTemplateVariable({
20069
- variable,
20070
- context,
20071
- stepId: step.id,
20072
- currentStepGuard
20073
- });
20074
- if (!error61)
20075
- continue;
20076
- const parts = variable.split(".");
20077
- if (parts[0] === "steps" && parts[1] && !context.steps.has(parts[1]) && error61.includes("before it is available")) {
20078
- unavailableStepRefs.add(parts[1]);
20079
- } else {
20080
- ctx.addIssue({
20081
- code: external_exports.ZodIssueCode.custom,
20082
- message: error61,
20083
- path: ["steps", stepIndex, "prompt"]
20084
- });
20085
- }
20086
- }
20087
- const backtickRefs = extractStepReferences(step.prompt, backtickStepReferenceRegex);
20088
- for (const ref of backtickRefs) {
20089
- processStepRef(ref, "prompt", ["steps", stepIndex, "prompt"]);
20090
- }
20091
- }
20092
- if (step.type === "ask") {
20093
- const askTextFields = [
20094
- { fieldName: "message", value: "message" in step ? step.message : void 0 },
20095
- { fieldName: "details", value: "details" in step ? step.details : void 0 }
20096
- ];
20097
- for (const { fieldName, value } of askTextFields) {
20098
- if (typeof value !== "string")
20099
- continue;
20100
- for (const variable of extractTemplateVariables(value)) {
20101
- const error61 = validateTemplateVariable({
20102
- variable,
20103
- context,
20104
- stepId: step.id,
20105
- currentStepGuard,
20106
- sourceType: fieldName
20107
- });
20108
- if (!error61)
20109
- continue;
20110
- ctx.addIssue({
20111
- code: external_exports.ZodIssueCode.custom,
20112
- message: error61,
20113
- path: ["steps", stepIndex, fieldName]
20114
- });
20115
- }
20116
- }
20117
- }
20118
- if (step.type === "code" && "code" in step && typeof step.code === "string") {
20119
- const codeRefs = extractStepReferences(step.code, codeStepReferenceRegex);
20120
- for (const ref of codeRefs) {
20121
- processStepRef(ref, "code", ["steps", stepIndex, "code"]);
20122
- }
20123
- }
20124
- for (const referencedStepId of unavailableStepRefs) {
20125
- const sourceType = step.type === "code" ? "code" : "prompt";
20126
- const fieldName = step.type === "code" ? "code" : "prompt";
20127
- ctx.addIssue({
20128
- code: external_exports.ZodIssueCode.custom,
20129
- message: `Step "${step.id}" ${sourceType} references step "${referencedStepId}" before it is available. Steps can only reference outputs from earlier steps.`,
20130
- path: ["steps", stepIndex, fieldName]
20131
- });
20132
- }
20133
- for (const err of propertyPathErrors) {
20134
- ctx.addIssue({
20135
- code: external_exports.ZodIssueCode.custom,
20136
- message: err.message,
20137
- path: err.path
20138
- });
20139
- }
20140
- if (step.type === "llm" || step.type === "codegen" || step.type === "code" || step.type === "ask") {
20141
- context.steps.set(step.id, step.outputSchema);
20142
- } else if (step.type === "search") {
20143
- context.steps.set(step.id, { type: "string" });
20144
- }
20145
- context.guards.set(step.id, currentStepGuard);
20146
- }
20147
- }
20148
-
20149
- // ../../packages/core/dist/esm/schemas/admin-sample-automation.schema.js
20150
- var sampleAutomationIdSchema = external_exports.string().trim().min(1, "Sample automation ID is required").max(100, "Sample automation ID must be 100 characters or fewer").regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "Sample automation ID must use lowercase letters, numbers, and hyphens only");
20151
- var sampleAutomationInstructionsSchema = external_exports.string().nullable();
20152
- var adminSampleAutomationSchema = external_exports.object({
20153
- id: sampleAutomationIdSchema,
20154
- manifest: manifestSchema,
20155
- instructions: sampleAutomationInstructionsSchema.default(null),
20156
- connectorNames: external_exports.array(external_exports.string()),
20157
- connectorDetails: external_exports.array(connectorDetailsSchema),
20158
- isPublished: external_exports.boolean(),
20159
- isOneClickInstallEnabled: external_exports.boolean().default(false),
20160
- staffPick: external_exports.boolean().default(false),
20161
- createdAt: external_exports.string().datetime(),
20162
- updatedAt: external_exports.string().datetime()
20023
+ var triggersSchema = external_exports.array(triggerSchema).nonempty({ message: "At least one trigger is required." }).superRefine(validateTriggersConstraints).meta({
20024
+ description: "An array of triggers for the automation. Multiple webhook triggers from the same connector are supported."
20163
20025
  });
20164
- var adminSampleAutomationsSchema = external_exports.array(adminSampleAutomationSchema);
20165
- var createAdminSampleAutomationSchema = external_exports.object({
20166
- id: sampleAutomationIdSchema,
20167
- manifest: manifestSchema,
20168
- instructions: sampleAutomationInstructionsSchema.default(null),
20169
- isPublished: external_exports.boolean().default(false),
20170
- isOneClickInstallEnabled: external_exports.boolean().default(false),
20171
- staffPick: external_exports.boolean().default(false)
20026
+ var stepIdSchema = external_exports.string().regex(/^[a-zA-Z][a-zA-Z0-9]*$/, "ID must be in camelCase format (no dashes or underscores allowed)").meta({ description: "A unique camelCase identifier for the step." });
20027
+ var resultFromPathSchema = external_exports.string().regex(/^[a-zA-Z_]\w*(\.[a-zA-Z_]\w*)*$/, 'Path must be a dot-separated property path like "text" or "task.description".').meta({
20028
+ description: 'Dot-separated property path relative to a step output, such as "text" or "task.description".'
20172
20029
  });
20173
- var updateAdminSampleAutomationSchema = external_exports.object({
20174
- manifest: manifestSchema,
20175
- instructions: sampleAutomationInstructionsSchema.optional(),
20176
- isPublished: external_exports.boolean(),
20177
- isOneClickInstallEnabled: external_exports.boolean().optional(),
20178
- staffPick: external_exports.boolean().optional()
20030
+ var resultFromSchema = external_exports.object({
20031
+ stepId: stepIdSchema.meta({
20032
+ description: "The step whose output should be used as the persisted run result."
20033
+ }),
20034
+ path: resultFromPathSchema.optional()
20035
+ }).strict().meta({
20036
+ description: "Declares which step output becomes the persisted user-facing automation run result."
20179
20037
  });
20180
- var toggleAdminSampleAutomationPublicationSchema = external_exports.object({
20181
- isPublished: external_exports.boolean()
20038
+ var stepOutputSchema = external_exports.object({
20039
+ type: external_exports.string({
20040
+ message: 'outputSchema must be a valid JSON Schema with a "type" field. Use {"type": "string"} or {"type": "object", ...}.'
20041
+ })
20042
+ }).catchall(external_exports.unknown()).meta({ description: "A JSON Schema object defining the expected output of this step." });
20043
+ var searchStepOutputSchema = external_exports.object({
20044
+ type: external_exports.literal("string").meta({
20045
+ description: 'Must be "string" for search steps.'
20046
+ })
20047
+ }).strict().default({ type: "string" }).meta({
20048
+ description: 'JSON Schema for search step output. Must be exactly {"type": "string"}.'
20182
20049
  });
20183
- var deleteAdminSampleAutomationSchema = external_exports.object({
20184
- title: external_exports.string().min(1, "Sample automation title confirmation is required")
20050
+ var exitOnSchema = external_exports.record(external_exports.string(), external_exports.unknown()).meta({
20051
+ description: "A JSON Schema object that terminates the automation's run if matched. Otherwise, or if omitted, the automation continues to the next step."
20185
20052
  });
20186
-
20187
- // ../../packages/core/dist/esm/types/automation.types.js
20188
- var AUTOMATION_CONNECTION_STATUSES = ["ready", "almost-ready", "not-ready"];
20189
- var AUTOMATION_VISIBILITIES = ["private", "workspace"];
20190
- var AUTOMATION_DISABLED_REASONS = [
20191
- "creator-left-workspace",
20192
- "workspace-plan-lapsed"
20193
- ];
20194
-
20195
- // ../../packages/core/dist/esm/schemas/connection.schema.js
20196
- var CreateConnectionParamsSchema = external_exports.object({
20197
- connectorId: external_exports.string(),
20198
- capability: external_exports.enum(CONNECTION_TYPES),
20199
- name: external_exports.string().optional()
20200
- });
20201
- var CreateConnectionBodySchema = external_exports.object({
20202
- automationIds: external_exports.array(external_exports.uuid()).optional()
20203
- });
20204
- var OAuthConnectionSetupSchema = external_exports.object({
20205
- kind: external_exports.literal("oauth"),
20206
- authorizationUrl: external_exports.string().url()
20207
- });
20208
- var ManagedConnectionSetupSchema = external_exports.object({
20209
- kind: external_exports.literal("managed"),
20210
- code: external_exports.string().regex(MANAGED_CONNECTION_SETUP_CODE_EXACT_REGEX),
20211
- instructions: external_exports.string(),
20212
- deepLinkUrl: external_exports.string().url().nullable(),
20213
- expiresAt: external_exports.iso.datetime()
20214
- });
20215
- var ConnectionSetupSchema = external_exports.discriminatedUnion("kind", [
20216
- OAuthConnectionSetupSchema,
20217
- ManagedConnectionSetupSchema
20218
- ]);
20219
- var CreateConnectionResponseSchema = external_exports.object({
20220
- connectionId: external_exports.uuid(),
20221
- setup: ConnectionSetupSchema.nullable()
20222
- });
20223
- var ConnectionCapabilitySchema = external_exports.object({
20224
- capability: external_exports.enum(CONNECTION_TYPES),
20225
- scopes: external_exports.array(external_exports.string()),
20226
- authorizedAt: external_exports.iso.datetime()
20227
- });
20228
- var ConnectionSchema = external_exports.object({
20229
- id: external_exports.uuid(),
20230
- name: external_exports.string(),
20231
- connectorId: external_exports.string(),
20232
- connectorIconUrl: external_exports.url().nullable().optional(),
20233
- capabilities: external_exports.array(ConnectionCapabilitySchema),
20234
- accountDisplayLabel: external_exports.string().optional(),
20235
- status: external_exports.enum(CONNECTION_STATUSES).optional(),
20236
- createdAt: external_exports.string().optional(),
20237
- metadata: external_exports.record(external_exports.string(), external_exports.unknown()),
20238
- setup: ConnectionSetupSchema.nullable()
20239
- });
20240
- var ConnectionDetailsSchema = external_exports.object({
20241
- id: external_exports.uuid(),
20242
- name: external_exports.string(),
20243
- connectorId: external_exports.string(),
20244
- connectorIconUrl: external_exports.url().nullable().optional(),
20245
- capabilities: external_exports.array(ConnectionCapabilitySchema),
20246
- accountDisplayLabel: external_exports.string(),
20247
- status: external_exports.enum(CONNECTION_STATUSES),
20248
- createdAt: external_exports.string(),
20249
- metadata: external_exports.record(external_exports.string(), external_exports.unknown()),
20250
- setup: ConnectionSetupSchema.nullable()
20251
- });
20252
- var GetConnectionsQuerySchema = external_exports.object({
20253
- /**
20254
- * Capability filter. `type` is accepted as a deprecated alias during the
20255
- * rolling-deploy window where older FE/SDK builds may still send the old
20256
- * query param. Remove once all clients are on the post-PR1 SDK.
20257
- */
20258
- capability: external_exports.enum(CONNECTION_TYPES).optional(),
20259
- type: external_exports.enum(CONNECTION_TYPES).optional(),
20260
- connectorId: external_exports.string().optional(),
20261
- status: external_exports.enum(CONNECTION_STATUSES).optional()
20262
- });
20263
- var GetConnectionsResponseSchema = external_exports.object({
20264
- connections: external_exports.array(ConnectionSchema)
20265
- });
20266
- var DependentAutomationSchema = external_exports.object({
20267
- id: external_exports.string(),
20268
- name: external_exports.string(),
20269
- description: external_exports.string(),
20270
- enabled: external_exports.boolean(),
20271
- connectorStatus: external_exports.enum(["ready", "attention", "missing"]),
20272
- requiredConnectionTypes: external_exports.array(external_exports.enum(CONNECTION_TYPES))
20273
- });
20274
- var GetConnectionDependentAutomationsResponseSchema = external_exports.object({
20275
- automations: external_exports.array(DependentAutomationSchema)
20276
- });
20277
- var CONNECTION_IN_USE_CONFLICT_CODE = "connection_in_use_conflict";
20278
- var BlockingAutomationSchema = external_exports.object({
20279
- id: external_exports.string(),
20280
- name: external_exports.string()
20053
+ var ifConditionSchema = external_exports.string().regex(ifConditionRegex, `Must be a single comparison, e.g. "{{steps.askUser.choice}} == 'archive'": a {{steps.<id>.<path>}} or {{trigger.<path>}} reference, then == or !=, then a single-quoted literal. No boolean combinators.`).meta({
20054
+ description: `An optional condition gating whether this step runs, e.g. "{{steps.askUser.choice}} == 'archive'". A single comparison against an earlier step or trigger value; when false the step is skipped.`
20281
20055
  });
20282
- var ConnectionInUseConflictSchema = external_exports.object({
20283
- code: external_exports.literal(CONNECTION_IN_USE_CONFLICT_CODE),
20284
- message: external_exports.string(),
20285
- automations: external_exports.array(BlockingAutomationSchema)
20056
+ var baseStepSchema = external_exports.object({
20057
+ id: stepIdSchema,
20058
+ title: external_exports.string().optional().meta({ description: "A short human-readable title of the step." }),
20059
+ description: external_exports.string().meta({ description: "A human-readable description of what the step does." }),
20060
+ if: ifConditionSchema.optional(),
20061
+ exitOn: exitOnSchema.optional().meta({
20062
+ description: "A JSON Schema object that terminates the automation's run if the step output matches. If omitted or not matched, execution continues."
20063
+ }),
20064
+ execution: executionPolicySchema.optional().meta({
20065
+ description: "Overrides the global execution policy (timeout and retries) for this step."
20066
+ })
20286
20067
  });
20287
-
20288
- // ../../packages/core/dist/esm/schemas/automation.schema.js
20289
- var runStatusSchema = external_exports.discriminatedUnion("status", [
20290
- external_exports.object({
20291
- status: external_exports.literal("pending"),
20292
- lastRun: external_exports.iso.datetime().optional()
20068
+ var SUPPORTED_LLM_MODELS = [
20069
+ "gemini-2.5-flash-lite",
20070
+ "gemini-2.5-flash",
20071
+ "gemini-3.1-pro",
20072
+ "claude-haiku-4-5",
20073
+ "claude-sonnet-4-6"
20074
+ ];
20075
+ var DEFAULT_LLM_MODEL = "claude-haiku-4-5";
20076
+ var llmStepSchema = baseStepSchema.merge(external_exports.object({
20077
+ type: external_exports.literal("llm"),
20078
+ prompt: external_exports.string().meta({ description: "The prompt template to be sent to the LLM." }),
20079
+ outputSchema: stepOutputSchema,
20080
+ model: external_exports.enum(SUPPORTED_LLM_MODELS).meta({
20081
+ description: `The specific model to use. Supported models: ${SUPPORTED_LLM_MODELS.join(", ")}.`
20293
20082
  }),
20294
- external_exports.object({
20295
- status: external_exports.literal("running"),
20296
- lastRun: external_exports.iso.datetime()
20083
+ tools: external_exports.record(external_exports.string(), external_exports.array(external_exports.string())).default({}).meta({
20084
+ description: 'A mapping from MCP server connector IDs to arrays of their tool names (e.g., {"todoist": ["find-tasks-by-date"], "linear": ["create-issue"]}).'
20085
+ })
20086
+ })).strict();
20087
+ var codegenStepSchema = baseStepSchema.merge(external_exports.object({
20088
+ type: external_exports.literal("codegen"),
20089
+ prompt: external_exports.string().meta({
20090
+ description: "The goal or instruction for what the generated code should accomplish."
20297
20091
  }),
20298
- external_exports.object({
20299
- status: external_exports.literal("completed"),
20300
- lastRun: external_exports.iso.datetime()
20092
+ outputSchema: stepOutputSchema,
20093
+ model: external_exports.enum(SUPPORTED_LLM_MODELS).meta({
20094
+ description: `The specific model to use for code generation. Supported models: ${SUPPORTED_LLM_MODELS.join(", ")}.`
20301
20095
  }),
20302
- external_exports.object({
20303
- status: external_exports.literal("failed"),
20304
- lastRun: external_exports.iso.datetime(),
20305
- failureReason: external_exports.string().optional()
20096
+ tools: external_exports.record(external_exports.string(), external_exports.array(external_exports.string())).default({}).meta({
20097
+ description: 'A mapping from MCP server connector IDs to arrays of their tool names that will be available in the generated code (e.g., {"todoist": ["find-tasks", "update-tasks"]}).'
20306
20098
  }),
20307
- external_exports.object({
20308
- status: external_exports.literal("cancelled"),
20309
- lastRun: external_exports.iso.datetime()
20099
+ extractedUrls: external_exports.array(external_exports.url()).optional().meta({
20100
+ description: "URLs that will be fetched during step execution (auto-extracted from prompt examples)"
20310
20101
  })
20311
- ]);
20312
- var automationSchema = external_exports.object({
20313
- id: external_exports.string(),
20314
- connections: external_exports.array(ConnectionDetailsSchema),
20315
- enabled: external_exports.boolean(),
20316
- status: runStatusSchema,
20317
- connectionStatus: external_exports.enum(AUTOMATION_CONNECTION_STATUSES),
20318
- draftId: external_exports.string().nullable(),
20319
- manifest: external_exports.union([manifestSchema, external_exports.record(external_exports.string(), external_exports.unknown())]).nullable(),
20320
- updatedAt: external_exports.iso.datetime(),
20321
- // null means the automation belongs to the creator's personal space
20322
- workspaceId: external_exports.string().nullable(),
20323
- visibility: external_exports.enum(AUTOMATION_VISIBILITIES),
20324
- isOwner: external_exports.boolean(),
20325
- disabledReason: external_exports.enum(AUTOMATION_DISABLED_REASONS).nullable()
20326
- });
20327
- var automationListResponseSchema = external_exports.object({
20328
- data: external_exports.array(automationSchema),
20329
- nextCursor: external_exports.string().nullable()
20330
- });
20331
- var updateAutomationSchema = external_exports.object({
20332
- enabled: external_exports.boolean().optional(),
20333
- manifest: manifestSchema.optional()
20334
- }).refine((data) => Object.keys(data).length > 0, {
20335
- message: "At least one field must be provided"
20102
+ }));
20103
+ var runtimeSchema = external_exports.object({
20104
+ language: external_exports.literal("typescript").meta({
20105
+ description: "Programming language. Only 'typescript' is supported in v0."
20106
+ }),
20107
+ version: external_exports.enum(SUPPORTED_RUNTIME_VERSIONS).meta({
20108
+ description: "Language specification version. Supported values: '5.9', '6.0'."
20109
+ })
20110
+ }).meta({
20111
+ description: "Defines the execution environment for the code step."
20336
20112
  });
20337
- var updateAutomationMetadataSchema = external_exports.object({
20338
- name: external_exports.string().min(1),
20339
- description: external_exports.string()
20113
+ var codeStepSchema = baseStepSchema.merge(external_exports.object({
20114
+ type: external_exports.literal("code"),
20115
+ outputSchema: stepOutputSchema,
20116
+ runtime: runtimeSchema.optional().default({ language: "typescript", version: "5.9" }).meta({
20117
+ description: "An object defining the execution environment. If omitted, it defaults to TypeScript 5.9."
20118
+ }),
20119
+ code: external_exports.string().meta({
20120
+ description: "The code to execute. It has access to trigger and steps objects."
20121
+ }),
20122
+ extractedUrls: external_exports.array(external_exports.url()).optional().meta({
20123
+ description: "URLs that will be fetched during step execution (auto-extracted from code)"
20124
+ })
20125
+ }));
20126
+ var searchStepSchema = baseStepSchema.merge(external_exports.object({
20127
+ type: external_exports.literal("search"),
20128
+ prompt: external_exports.string().min(1).meta({
20129
+ description: "The search prompt to execute using web search."
20130
+ }),
20131
+ outputSchema: searchStepOutputSchema,
20132
+ model: external_exports.enum(SUPPORTED_LLM_MODELS).optional().meta({
20133
+ description: `The specific model to use for processing search results. Defaults to ${DEFAULT_LLM_MODEL}.`
20134
+ })
20135
+ }));
20136
+ var delayStepOutputSchema = external_exports.object({
20137
+ type: external_exports.literal("object").meta({
20138
+ description: 'Must be "object" for delay steps.'
20139
+ })
20140
+ }).catchall(external_exports.unknown()).meta({
20141
+ description: 'JSON Schema for delay step output. Must include {"type": "object"}.'
20340
20142
  });
20341
- var updateAutomationScheduleSchema = external_exports.object({
20342
- value: scheduleValueSchema
20143
+ var baseDelayStepSchema = baseStepSchema.omit({ execution: true });
20144
+ var delayStepSchema = baseDelayStepSchema.merge(external_exports.object({
20145
+ type: external_exports.literal("delay"),
20146
+ duration: durationStringSchema.meta({
20147
+ description: "Duration to delay the execution for as a duration string, e.g., '1d', '1w', '2h'."
20148
+ }),
20149
+ outputSchema: delayStepOutputSchema
20150
+ })).strict();
20151
+ var baseAskStepSchema = baseStepSchema.omit({ execution: true });
20152
+ var askExpiresAfterSchema = durationStringSchema.refine((value) => durationStringToMilliseconds(value) <= ASK_EXPIRES_AFTER_MAX_MS, {
20153
+ message: "Ask expiresAfter must not exceed 30 days"
20154
+ }).meta({
20155
+ description: "How long to wait for a response before the prompt expires, as a duration string (e.g. '1w'). Defaults to 1w, capped at 30d. On expiry the run terminates."
20343
20156
  });
20344
-
20345
- // ../../packages/core/dist/esm/schemas/automation-connection.schema.js
20346
- var AutomationConnectionMappingSchema = external_exports.object({
20347
- connectorId: external_exports.string().trim().min(1, "connectorId is required"),
20348
- connectionId: external_exports.string().trim().min(1, "connectionId is required")
20349
- }).strict();
20350
- var UpsertAutomationConnectionSchema = external_exports.object({
20351
- connectionId: uuidv7Schema.min(1, "connectionId is required"),
20352
- /**
20353
- * Manifest slot to bind this connection to. Required when the
20354
- * connection carries more than one capability (managed connectors
20355
- * can cover both `mcp` and `trigger` after a single
20356
- * onboarding). Optional for single-capability connections, where
20357
- * the service infers the role from `connection.capabilities[0]`.
20358
- */
20359
- connectionType: external_exports.enum(CONNECTION_TYPES).optional()
20360
- }).strict();
20361
- var SetAutomationConnectionsSchema = external_exports.object({
20362
- mappings: external_exports.array(AutomationConnectionMappingSchema).nonempty("mappings cannot be empty")
20363
- }).strict();
20364
- var RequiredConnectorSchema = external_exports.object({
20365
- connectorName: external_exports.string(),
20366
- connectorId: external_exports.string(),
20367
- connectorDisplayName: external_exports.string(),
20368
- description: external_exports.string(),
20369
- connectorIconUrl: external_exports.string().nullable(),
20370
- requiredTypes: external_exports.array(external_exports.enum(CONNECTION_TYPES)),
20371
- /** Whether uses of this connector require a Connection binding. */
20372
- requiresConnection: external_exports.boolean(),
20373
- /**
20374
- * Product-owned built-in status. Connection validation uses
20375
- * `requiresConnection` independently from this UI classification.
20376
- */
20377
- isSystem: external_exports.boolean().optional()
20378
- });
20379
- var AutomationConnectionSchema = external_exports.object({
20380
- connectorId: external_exports.string(),
20381
- connectionId: external_exports.uuid(),
20382
- connectorName: external_exports.string(),
20383
- connectorIconUrl: external_exports.url().nullable().optional(),
20384
- status: external_exports.enum(CONNECTION_STATUSES),
20385
- type: external_exports.enum(CONNECTION_TYPES)
20386
- });
20387
- var ConnectorTypeStatusSchema = external_exports.object({
20388
- required: external_exports.boolean(),
20389
- status: external_exports.enum(CONNECTOR_TYPE_STATUSES),
20390
- connection: ConnectionSchema.pick({
20391
- id: true,
20392
- accountDisplayLabel: true,
20393
- status: true,
20394
- createdAt: true
20395
- }).optional(),
20396
- setup: ConnectionSetupSchema.nullable()
20397
- });
20398
- var AutomationConnectorStatusSchema = external_exports.object({
20399
- id: external_exports.string(),
20400
- name: external_exports.string(),
20401
- description: external_exports.string(),
20402
- iconUrl: external_exports.string().nullable(),
20403
- mcp: ConnectorTypeStatusSchema,
20404
- trigger: ConnectorTypeStatusSchema
20405
- });
20406
- var AutomationConnectionValidationSchema = external_exports.object({
20407
- required: external_exports.array(RequiredConnectorSchema),
20408
- mapped: external_exports.array(AutomationConnectionSchema),
20409
- missing: external_exports.array(external_exports.object({
20410
- connectorName: external_exports.string(),
20411
- connectionType: external_exports.enum(CONNECTION_TYPES),
20412
- reason: external_exports.enum(CONNECTION_ISSUE_REASONS)
20413
- })),
20414
- connectors: external_exports.array(AutomationConnectorStatusSchema),
20415
- isReady: external_exports.boolean()
20416
- });
20417
- var GetRequiredConnectorsResponseSchema = external_exports.object({
20418
- required: external_exports.array(RequiredConnectorSchema)
20419
- });
20420
- var GetAutomationConnectionsResponseSchema = external_exports.object({
20421
- data: external_exports.array(AutomationConnectionSchema)
20157
+ var askOptionSchema = external_exports.object({
20158
+ label: external_exports.string().min(1).meta({ description: "The button text shown to the user." }),
20159
+ value: external_exports.string().min(1).meta({
20160
+ description: "The machine value emitted as the choice. Unique within the step."
20161
+ }),
20162
+ terminate: external_exports.boolean().optional().default(false).meta({
20163
+ description: 'When true, choosing this option stops the run and no later steps execute (the explicit "deny"/"stop" path).'
20164
+ })
20165
+ }).strict().meta({ description: "A single selectable option in an ask step." });
20166
+ var askOptionsSchema = external_exports.array(askOptionSchema).min(2, "An ask step requires at least two options.").superRefine((options, ctx) => {
20167
+ const seen = /* @__PURE__ */ new Set();
20168
+ for (let index = 0; index < options.length; index++) {
20169
+ const value = options[index]?.value;
20170
+ if (value === void 0)
20171
+ continue;
20172
+ if (seen.has(value)) {
20173
+ ctx.addIssue({
20174
+ code: external_exports.ZodIssueCode.custom,
20175
+ message: `Duplicate option value "${value}". Option values must be unique within an ask step.`,
20176
+ path: [index, "value"]
20177
+ });
20178
+ }
20179
+ seen.add(value);
20180
+ }
20181
+ }).meta({ description: "Two or more options the user chooses between." });
20182
+ var askStepOutputSchema = stepOutputSchema.refine((schema) => {
20183
+ const properties = schema.properties;
20184
+ return schema.type === "object" && typeof properties === "object" && properties !== null && "choice" in properties;
20185
+ }, {
20186
+ message: 'Ask steps must declare an outputSchema of type "object" with a "choice" property.'
20187
+ }).meta({
20188
+ description: 'A JSON Schema describing the ask step output. Must be an object with a "choice" property whose value is one of the option values.'
20422
20189
  });
20423
- var UpsertAutomationConnectionResponseSchema = external_exports.object({
20424
- data: AutomationConnectionSchema
20190
+ var askStepSchema = baseAskStepSchema.merge(external_exports.object({
20191
+ type: external_exports.literal("ask"),
20192
+ message: external_exports.string().min(1).meta({
20193
+ description: 'The short headline shown to the user, e.g. "Archive or delete 3 inactive projects?".'
20194
+ }),
20195
+ details: external_exports.string().optional().meta({
20196
+ description: "Optional longer markdown context, e.g. a preview of what each option would do."
20197
+ }),
20198
+ options: askOptionsSchema,
20199
+ expiresAfter: askExpiresAfterSchema.optional().default(ASK_EXPIRES_AFTER_DEFAULT),
20200
+ outputSchema: askStepOutputSchema
20201
+ })).strict();
20202
+ var stepSchema = external_exports.discriminatedUnion("type", [
20203
+ llmStepSchema,
20204
+ codegenStepSchema,
20205
+ codeStepSchema,
20206
+ searchStepSchema,
20207
+ delayStepSchema,
20208
+ askStepSchema
20209
+ ]);
20210
+ var MANIFEST_VERSIONS = [0, 1];
20211
+ var CURRENT_MANIFEST_VERSION = MANIFEST_VERSIONS[MANIFEST_VERSIONS.length - 1];
20212
+ var manifestVersionSchema = (() => {
20213
+ const [firstVersion, ...otherVersions] = MANIFEST_VERSIONS;
20214
+ let schema = external_exports.literal(firstVersion);
20215
+ for (const version2 of otherVersions) {
20216
+ schema = schema.or(external_exports.literal(version2));
20217
+ }
20218
+ return schema;
20219
+ })().meta({
20220
+ description: `The version of the manifest schema. Latest is ${MANIFEST_VERSIONS[MANIFEST_VERSIONS.length - 1]}.`
20425
20221
  });
20426
- var ManifestConnectionCheckResultSchema = AutomationConnectionValidationSchema.pick({
20427
- required: true,
20428
- connectors: true
20222
+ var manifestSchemaBase = external_exports.object({
20223
+ manifestVersion: manifestVersionSchema,
20224
+ name: external_exports.string().min(1).meta({
20225
+ description: "A human-readable name for the automation that can include spaces."
20226
+ }),
20227
+ description: external_exports.string().meta({ description: "A human-readable description of the automation's purpose." }),
20228
+ triggers: triggersSchema,
20229
+ overlap: external_exports.enum([
20230
+ "skip",
20231
+ "buffer_one",
20232
+ "buffer_all",
20233
+ "cancel_other",
20234
+ "terminate_other",
20235
+ "allow_all"
20236
+ ]).optional().default("skip").meta({
20237
+ description: 'Defines policy for overlapping runs. Defaults to "skip".'
20238
+ }),
20239
+ execution: executionPolicySchema.optional().meta({ description: "Global defaults for timeout and retry behavior." }),
20240
+ resultFrom: resultFromSchema.optional(),
20241
+ steps: external_exports.array(stepSchema).nonempty().meta({ description: "An ordered array of step objects that define the workflow." })
20242
+ }).meta({ description: "The root schema for an Automation Manifest, defining its behavior." });
20243
+ var manifestSchema = manifestSchemaBase.superRefine(validateManifestResultFrom).superRefine(validateManifestTemplateVariables).superRefine(validateManifestAskOverlap).superRefine(validateManifestAskChoiceEnum);
20244
+ var partialManifestSchema = manifestSchemaBase.partial().extend({
20245
+ // Override steps to allow empty array for drafts (remove .nonempty() requirement)
20246
+ steps: external_exports.array(stepSchema).default([]),
20247
+ // Override triggers to allow empty array for drafts (remove .nonempty() but keep invariants)
20248
+ triggers: external_exports.array(triggerSchema).superRefine(validateTriggersConstraints).optional()
20249
+ }).required({
20250
+ manifestVersion: true,
20251
+ steps: true
20429
20252
  });
20430
-
20431
- // ../../packages/core/dist/esm/types/failure-category.types.js
20432
- var FAILURE_CATEGORIES = [
20433
- "mcp_upstream_unavailable",
20434
- "mcp_client_missing",
20435
- "mcp_tool_missing",
20436
- "mcp_tool_call_failed",
20437
- "llm_schema_validation",
20438
- "llm_empty_response",
20439
- "json_parse_error",
20440
- "code_execution_error",
20441
- "code_generation_error",
20442
- "step_execution_error",
20443
- "step_failure_pattern",
20444
- "schema_validation",
20445
- "template_variable_error",
20446
- "invalid_configuration",
20447
- "invalid_manifest",
20448
- "automation_not_found",
20449
- "timeout",
20450
- "runtime_error",
20451
- "insufficient_credit",
20452
- "billing_unavailable",
20453
- "unknown"
20454
- ];
20455
-
20456
- // ../../packages/core/dist/esm/schemas/automation-run.schema.js
20457
- var AUTOMATION_RUN_RESULT_TEXT_MAX_LENGTH = 4e3;
20458
- var RunDateFilterSchema = external_exports.union([external_exports.iso.datetime({ offset: true }), external_exports.iso.date()]);
20459
- var DATE_ONLY_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
20460
- var MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1e3;
20461
- function resolveRunDateRange({ since, until }) {
20462
- const untilExclusive = until !== void 0 && DATE_ONLY_PATTERN.test(until);
20463
- return {
20464
- since: since ? new Date(DATE_ONLY_PATTERN.test(since) ? `${since}T00:00:00.000Z` : since) : void 0,
20465
- until: until ? new Date(untilExclusive ? (/* @__PURE__ */ new Date(`${until}T00:00:00.000Z`)).getTime() + MILLISECONDS_PER_DAY : until) : void 0,
20466
- untilExclusive
20467
- };
20253
+ var templateVariableRegex = /\{\{\s*[\w.]+\s*\}\}/g;
20254
+ var codeStepReferenceRegex = /\bsteps\.([a-zA-Z][a-zA-Z0-9]*)(?:\.([a-zA-Z_][\w.]*))?/g;
20255
+ var backtickStepReferenceRegex = /`steps\.([a-zA-Z][a-zA-Z0-9]*)(?:\.([a-zA-Z_][\w.]*))?`/g;
20256
+ function extractStepReferences(text, regex) {
20257
+ const references = [];
20258
+ for (const match of text.matchAll(regex)) {
20259
+ const stepId = match[1];
20260
+ if (!stepId)
20261
+ continue;
20262
+ const propertyPathStr = match[2];
20263
+ const propertyPath = propertyPathStr ? propertyPathStr.split(".") : [];
20264
+ references.push({ stepId, propertyPath });
20265
+ }
20266
+ return references;
20468
20267
  }
20469
- var ListAutomationRunsQuerySchema = external_exports.object({
20470
- status: external_exports.enum(AUTOMATION_RUN_STATUSES).optional(),
20471
- since: RunDateFilterSchema.optional(),
20472
- until: RunDateFilterSchema.optional()
20473
- }).superRefine((query, ctx) => {
20474
- const { since, until, untilExclusive } = resolveRunDateRange(query);
20475
- if (!since || !until) {
20476
- return;
20268
+ function validateGuardReference({ stepId, referencedStepId, currentStepGuard, referencedGuard, sourceType }) {
20269
+ if (referencedGuard === void 0 || referencedGuard === currentStepGuard) {
20270
+ return null;
20477
20271
  }
20478
- const isEmptyRange = untilExclusive ? since.getTime() >= until.getTime() : since.getTime() > until.getTime();
20479
- if (isEmptyRange) {
20480
- ctx.addIssue({
20481
- code: "custom",
20482
- path: ["since"],
20483
- message: "since must not be after until"
20484
- });
20272
+ return `Step "${stepId}" ${sourceType} references step "${referencedStepId}", which only runs when \`if: ${referencedGuard}\`. A step may reference a conditional step only when it carries the identical \`if\` condition.`;
20273
+ }
20274
+ function validateStepReference({ stepId, referencedStepId, propertyPath, context, sourceType, currentStepGuard }) {
20275
+ if (!context.steps.has(referencedStepId)) {
20276
+ return `Step "${stepId}" ${sourceType} references step "${referencedStepId}" before it is available. Steps can only reference outputs from earlier steps.`;
20485
20277
  }
20486
- });
20487
- var AutomationRunResultSchema = external_exports.object({
20488
- type: external_exports.literal("markdown"),
20489
- text: external_exports.string().trim().min(1).max(AUTOMATION_RUN_RESULT_TEXT_MAX_LENGTH)
20490
- });
20491
- var RolloutCompatibleAutomationRunResultSchema = AutomationRunResultSchema.nullish().transform((result) => {
20492
- return result ?? null;
20493
- });
20494
- var AutomationRunDtoSchema = external_exports.object({
20495
- id: external_exports.string(),
20496
- automationId: external_exports.string(),
20497
- status: external_exports.enum(AUTOMATION_RUN_STATUSES),
20498
- createdAt: external_exports.string(),
20499
- updatedAt: external_exports.string(),
20500
- failureReason: external_exports.string().nullish(),
20501
- failureCategory: external_exports.enum(FAILURE_CATEGORIES).nullish(),
20502
- result: RolloutCompatibleAutomationRunResultSchema,
20503
- temporalUrl: external_exports.string().nullish()
20504
- });
20505
- var MAX_TOOL_CALL_ARGUMENT_KEYS = 32;
20506
- var MAX_TOOL_CALL_ARGUMENT_KEY_LENGTH = 64;
20507
- var MAX_TOOL_CALL_PREVIEW_LENGTH = 500;
20508
- var MAX_TOOL_CALLS_WITH_PREVIEWS = 20;
20509
- var MAX_TOOL_CALL_PREVIEW_TOTAL_LENGTH = 2e3;
20510
- var MAX_TOOL_CALL_ENTITY_LENGTH = 100;
20511
- var MAX_TOOL_CALL_RESULT_PARSE_LENGTH = 1e4;
20512
- var TOOL_CALL_OUTCOMES = ["data", "empty", "no-result", "error"];
20513
- var TOOL_CALL_ERROR_CATEGORIES = [
20514
- "authentication",
20515
- "permission",
20516
- "rate-limit",
20517
- "timeout",
20518
- "connection",
20519
- "invalid-request",
20520
- "unknown"
20521
- ];
20522
- var ToolCallTimestampSchema = external_exports.union([external_exports.string(), external_exports.date()]).transform((timestamp) => timestamp instanceof Date ? timestamp.toISOString() : timestamp);
20523
- var SafeToolCallDtoSchema = external_exports.object({
20524
- id: external_exports.string(),
20525
- name: external_exports.string(),
20526
- argumentKeys: external_exports.array(external_exports.string()),
20527
- hasResult: external_exports.boolean(),
20528
- hasError: external_exports.boolean(),
20529
- outcome: external_exports.enum(TOOL_CALL_OUTCOMES).optional(),
20530
- errorCategory: external_exports.enum(TOOL_CALL_ERROR_CATEGORIES).optional(),
20531
- resultPreview: external_exports.string().max(MAX_TOOL_CALL_PREVIEW_LENGTH).optional(),
20532
- errorPreview: external_exports.string().max(MAX_TOOL_CALL_PREVIEW_LENGTH).optional(),
20533
- timestamp: ToolCallTimestampSchema
20534
- });
20535
- var LegacyToolCallDtoInputSchema = external_exports.object({
20536
- id: external_exports.string(),
20537
- name: external_exports.string(),
20538
- arguments: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
20539
- result: external_exports.unknown().optional(),
20540
- error: external_exports.unknown().optional(),
20541
- errorStatus: external_exports.number().int().min(100).max(599).optional(),
20542
- errorCode: external_exports.string().optional(),
20543
- timestamp: ToolCallTimestampSchema
20544
- });
20545
- var ToolCallDtoInputSchema = external_exports.union([SafeToolCallDtoSchema, LegacyToolCallDtoInputSchema]);
20546
- var ToolCallDtoSchema = external_exports.preprocess((toolCall) => {
20547
- const parsed = ToolCallDtoInputSchema.safeParse(toolCall);
20548
- return parsed.success ? normalizeToolCallDto(parsed.data) : toolCall;
20549
- }, SafeToolCallDtoSchema);
20550
- var ToolCallDtosSchema = external_exports.preprocess((toolCalls) => {
20551
- if (!Array.isArray(toolCalls)) {
20552
- return toolCalls;
20278
+ const guardError = validateGuardReference({
20279
+ stepId,
20280
+ referencedStepId,
20281
+ currentStepGuard,
20282
+ referencedGuard: context.guards.get(referencedStepId),
20283
+ sourceType
20284
+ });
20285
+ if (guardError) {
20286
+ return guardError;
20553
20287
  }
20554
- const parsedToolCalls = toolCalls.map((toolCall) => ToolCallDtoInputSchema.safeParse(toolCall)).filter((result) => result.success).map((result) => result.data);
20555
- return parsedToolCalls.length === toolCalls.length ? normalizeToolCallDtos(parsedToolCalls) : toolCalls;
20556
- }, external_exports.array(SafeToolCallDtoSchema));
20557
- var AUTOMATION_RUN_DEBUG_REASONS = [
20558
- "condition-not-met",
20559
- "exit-condition-met",
20560
- "ask-terminated",
20561
- "ask-expired",
20562
- "run-result-not-configured",
20563
- "run-result-empty"
20564
- ];
20565
- var AutomationRunDebugReasonSchema = external_exports.enum(AUTOMATION_RUN_DEBUG_REASONS);
20566
- var AutomationRunStepDtoOutputSchema = external_exports.object({
20567
- stepId: external_exports.string(),
20568
- type: external_exports.string(),
20569
- description: external_exports.string(),
20570
- status: external_exports.enum(AUTOMATION_RUN_STATUSES),
20571
- startedAt: external_exports.string().optional(),
20572
- completedAt: external_exports.string().optional(),
20573
- result: external_exports.unknown().optional(),
20574
- error: external_exports.string().optional(),
20575
- generatedCode: external_exports.string().optional(),
20576
- toolCalls: ToolCallDtosSchema.optional(),
20577
- debugReason: AutomationRunDebugReasonSchema.optional()
20578
- });
20579
- var AutomationRunStepDtoSchema = AutomationRunStepDtoOutputSchema;
20580
- var AutomationRunDetailDtoSchema = external_exports.object({
20581
- id: external_exports.string(),
20582
- automationId: external_exports.string(),
20583
- status: external_exports.enum([...AUTOMATION_RUN_STATUSES, "data-expired"]),
20584
- createdAt: external_exports.string(),
20585
- updatedAt: external_exports.string(),
20586
- failureReason: external_exports.string().nullish(),
20587
- failureCategory: external_exports.enum(FAILURE_CATEGORIES).nullish(),
20588
- result: RolloutCompatibleAutomationRunResultSchema,
20589
- temporalUrl: external_exports.string().nullish(),
20590
- steps: external_exports.array(AutomationRunStepDtoSchema)
20591
- });
20592
- function summarizeToolCallArgumentKeys(args) {
20593
- const keys = Object.keys(args ?? {}).sort();
20594
- const kept = keys.slice(0, MAX_TOOL_CALL_ARGUMENT_KEYS).map((key) => key.length > MAX_TOOL_CALL_ARGUMENT_KEY_LENGTH ? `${key.slice(0, MAX_TOOL_CALL_ARGUMENT_KEY_LENGTH)}\u2026` : key);
20595
- if (keys.length > MAX_TOOL_CALL_ARGUMENT_KEYS) {
20596
- kept.push(`\u2026+${keys.length - MAX_TOOL_CALL_ARGUMENT_KEYS} more`);
20288
+ if (propertyPath.length > 0) {
20289
+ const stepOutputSchema2 = context.steps.get(referencedStepId);
20290
+ if (stepOutputSchema2) {
20291
+ const isValid = validatePropertyPathInSchema(stepOutputSchema2, propertyPath);
20292
+ if (!isValid) {
20293
+ return `Step "${stepId}" ${sourceType} references "steps.${referencedStepId}.${propertyPath.join(".")}" but this property path does not exist in step "${referencedStepId}"'s outputSchema.`;
20294
+ }
20295
+ }
20597
20296
  }
20598
- return kept;
20297
+ return null;
20599
20298
  }
20600
- function summarizeToolCallPayloadPreview(payload) {
20601
- if (typeof payload === "string") {
20602
- return summarizePayloadSize(payload);
20299
+ function validateTemplateVariable({ variable, context, stepId, currentStepGuard, sourceType = "prompt" }) {
20300
+ const parts = variable.split(".").filter(Boolean);
20301
+ if (parts.length === 0) {
20302
+ return null;
20603
20303
  }
20604
- if (Array.isArray(payload)) {
20605
- return `${payload.length} item${payload.length === 1 ? "" : "s"} returned`;
20304
+ const [root13, ...rest] = parts;
20305
+ if (root13 === "trigger") {
20306
+ if (rest.length === 0) {
20307
+ return `Step "${stepId}" ${sourceType} references "{{trigger}}" without a property. Use {{trigger.<property>}} syntax (e.g., {{trigger.payload}}, {{trigger.metadata}}).`;
20308
+ }
20309
+ if (context.triggerType === "schedule" && rest[0] === "payload") {
20310
+ return `Step "${stepId}" references "{{trigger.payload}}" but schedule triggers have no payload. Add a step to fetch the data you need.`;
20311
+ }
20312
+ if (context.triggerType === "schedule" && rest[0] === "user") {
20313
+ if (context.scheduleInjectsTimezone && rest.length === 2 && rest[1] === "timezone") {
20314
+ return null;
20315
+ }
20316
+ return `Step "${stepId}" ${sourceType} references "{{trigger.${rest.join(".")}}}" but schedule triggers only provide "{{trigger.user.timezone}}", and only for cron schedules with a timezone. Interval schedules provide no trigger data.`;
20317
+ }
20318
+ if (context.triggerOutputSchemas.length > 0) {
20319
+ const invalidSchemas = context.triggerOutputSchemas.filter((schema) => !validatePropertyPathInSchema(schema, rest));
20320
+ if (invalidSchemas.length > 0) {
20321
+ return `Step "${stepId}" ${sourceType} references "{{trigger.${rest.join(".")}}}" but this property path does not exist in all trigger outputSchemas. The path must be valid for every trigger since any one may fire the automation.`;
20322
+ }
20323
+ } else {
20324
+ return `Step "${stepId}" ${sourceType} references "{{trigger.${rest.join(".")}}}" but this trigger type has no outputSchema and cannot provide trigger data at runtime.`;
20325
+ }
20326
+ return null;
20606
20327
  }
20607
- if (payload && typeof payload === "object") {
20608
- const keys = summarizeToolCallArgumentKeys(payload);
20609
- return truncateToolCallPreview(keys.length === 0 ? "Object returned" : `Object returned with fields: ${keys.join(", ")}`);
20328
+ if (root13 === "steps") {
20329
+ const referencedStepId = rest[0];
20330
+ if (!referencedStepId) {
20331
+ return `Step "${stepId}" ${sourceType} references "steps" but is missing the step identifier in template variable "{{${variable}}}". Use {{steps.<stepId>}} syntax.`;
20332
+ }
20333
+ return validateStepReference({
20334
+ stepId,
20335
+ referencedStepId,
20336
+ propertyPath: rest.slice(1),
20337
+ context,
20338
+ sourceType,
20339
+ currentStepGuard
20340
+ });
20610
20341
  }
20611
- return `${getPrimitivePayloadType(payload)} returned`;
20342
+ return `Step "${stepId}" ${sourceType} references unsupported template variable "{{${variable}}}". Allowed roots are {{trigger...}} and {{steps.<stepId>...}}.`;
20612
20343
  }
20613
- function summarizeToolCallResult(toolName, payload) {
20614
- const result = parseJsonPayload(payload);
20615
- if (payload === void 0 || result === null) {
20616
- return { outcome: "no-result", preview: summarizeToolCallPayloadPreview(payload) };
20617
- }
20618
- const tool = getToolCallDescription(toolName);
20619
- const collection = getResultCollection(result, tool.entity);
20620
- const textCollection = typeof payload === "string" && payload.length > MAX_TOOL_CALL_RESULT_PARSE_LENGTH ? null : getTextCollectionSummary(result, tool);
20621
- const outcome = collection?.length === 0 || textCollection === 0 ? "empty" : "data";
20622
- if (collection) {
20623
- const summary = `${tool.action} ${collection.length} ${formatEntity(tool.entity, collection.length)}`;
20624
- const projectCount = getProjectCount(collection, tool.entity);
20625
- return {
20626
- outcome,
20627
- preview: truncateToolCallPreview(projectCount > 1 ? `${summary} across ${projectCount} projects` : summary)
20628
- };
20629
- }
20630
- if (textCollection !== null) {
20631
- return {
20632
- outcome,
20633
- preview: truncateToolCallPreview(`${tool.action} ${textCollection} ${formatEntity(tool.entity, textCollection)}`)
20634
- };
20635
- }
20636
- if (isRecord(result) && !["Found", "Returned"].includes(tool.action)) {
20637
- return {
20638
- outcome,
20639
- preview: truncateToolCallPreview(tool.action === "Retrieved" ? `Retrieved ${tool.entity} details` : `${tool.action} ${formatEntity(tool.entity, 1)}`)
20640
- };
20344
+ function extractTemplateVariables(prompt) {
20345
+ const variables = [];
20346
+ for (const match of prompt.matchAll(templateVariableRegex)) {
20347
+ const variable = match[0]?.replace(/\{\{\s*|\s*\}\}/g, "").trim();
20348
+ if (variable) {
20349
+ variables.push(variable);
20350
+ }
20641
20351
  }
20642
- return { outcome, preview: summarizeToolCallPayloadPreview(payload) };
20352
+ return variables;
20643
20353
  }
20644
- function classifyToolCallOutcome(toolName, result, error61, errorDetails = {}) {
20645
- if (error61 !== void 0) {
20646
- return { outcome: "error", errorCategory: classifyToolCallError(error61, errorDetails) };
20354
+ function resolveTriggersFromData(data) {
20355
+ if (data.triggers && Array.isArray(data.triggers) && data.triggers.length > 0) {
20356
+ return data.triggers;
20647
20357
  }
20648
- return { outcome: summarizeToolCallResult(toolName, result).outcome };
20649
- }
20650
- function summarizePayloadSize(payload) {
20651
- return `${payload.length} character${payload.length === 1 ? "" : "s"} returned`;
20652
- }
20653
- function summarizeToolCallsWithPreviewLimits(toolCalls, summarize) {
20654
- let previewsKept = 0;
20655
- let previewLength = 0;
20656
- return toolCalls.map((toolCall) => {
20657
- const shouldIncludePreview = previewsKept < MAX_TOOL_CALLS_WITH_PREVIEWS && previewLength < MAX_TOOL_CALL_PREVIEW_TOTAL_LENGTH;
20658
- const summary = summarize(toolCall, { includePreview: shouldIncludePreview });
20659
- const nextPreviewLength = (summary.resultPreview?.length ?? 0) + (summary.errorPreview?.length ?? 0);
20660
- if (nextPreviewLength === 0 || previewsKept < MAX_TOOL_CALLS_WITH_PREVIEWS && previewLength + nextPreviewLength <= MAX_TOOL_CALL_PREVIEW_TOTAL_LENGTH) {
20661
- previewsKept += nextPreviewLength > 0 ? 1 : 0;
20662
- previewLength += nextPreviewLength;
20663
- return summary;
20664
- }
20665
- return stripToolCallPreviews(summary);
20666
- });
20667
- }
20668
- function normalizeToolCallDtos(toolCalls) {
20669
- return summarizeToolCallsWithPreviewLimits(toolCalls, normalizeToolCallDto);
20358
+ return [];
20670
20359
  }
20671
- function normalizeToolCallDto(toolCall, options = {}) {
20672
- const includePreview = options.includePreview ?? true;
20673
- if ("argumentKeys" in toolCall) {
20674
- return includePreview ? toolCall : stripToolCallPreviews(toolCall);
20360
+ var BOUNDED_OVERLAP_MODES = ["skip", "buffer_one", "cancel_other", "terminate_other"];
20361
+ function validateManifestAskOverlap(data, ctx) {
20362
+ const steps = data.steps || [];
20363
+ const hasAskStep = steps.some((step) => step?.type === "ask");
20364
+ if (!hasAskStep)
20365
+ return;
20366
+ const overlap = data.overlap ?? "skip";
20367
+ if (!BOUNDED_OVERLAP_MODES.includes(overlap)) {
20368
+ ctx.addIssue({
20369
+ code: external_exports.ZodIssueCode.custom,
20370
+ message: `Manifests with an ask step must use a bounded overlap mode (${BOUNDED_OVERLAP_MODES.join(", ")}). "${overlap}" can accumulate parked or buffered runs while a prompt waits for a response.`,
20371
+ path: ["overlap"]
20372
+ });
20675
20373
  }
20676
- const hasResult = hasToolCallResult(toolCall.result);
20677
- const resultSummary = includePreview && hasResult ? summarizeToolCallResult(toolCall.name, toolCall.result) : void 0;
20678
- const diagnostic = toolCall.error === void 0 && resultSummary ? { outcome: resultSummary.outcome } : classifyToolCallOutcome(toolCall.name, toolCall.result, toolCall.error, {
20679
- status: toolCall.errorStatus,
20680
- code: toolCall.errorCode
20681
- });
20682
- return {
20683
- id: toolCall.id,
20684
- name: toolCall.name,
20685
- argumentKeys: summarizeToolCallArgumentKeys(toolCall.arguments),
20686
- hasResult,
20687
- hasError: toolCall.error !== void 0,
20688
- outcome: diagnostic.outcome,
20689
- ...diagnostic.errorCategory ? { errorCategory: diagnostic.errorCategory } : {},
20690
- ...resultSummary ? { resultPreview: resultSummary.preview } : {},
20691
- ...includePreview && toolCall.error !== void 0 ? { errorPreview: summarizeToolCallPayloadPreview(toolCall.error) } : {},
20692
- timestamp: toolCall.timestamp
20693
- };
20694
20374
  }
20695
- function hasToolCallResult(result) {
20696
- return result !== void 0 && result !== null && result !== "null";
20697
- }
20698
- function stripToolCallPreviews(toolCall) {
20699
- const { resultPreview: _resultPreview, errorPreview: _errorPreview, ...withoutPreview } = toolCall;
20700
- return withoutPreview;
20701
- }
20702
- function truncateToolCallPreview(preview) {
20703
- return truncate(preview, MAX_TOOL_CALL_PREVIEW_LENGTH);
20704
- }
20705
- function truncateToolCallEntity(entity) {
20706
- return truncate(entity, MAX_TOOL_CALL_ENTITY_LENGTH);
20707
- }
20708
- function truncate(value, maxLength) {
20709
- return value.length <= maxLength ? value : `${value.slice(0, maxLength - 1)}\u2026`;
20710
- }
20711
- function escapeRegExp(value) {
20712
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
20375
+ function validateManifestAskChoiceEnum(data, ctx) {
20376
+ const steps = data.steps || [];
20377
+ for (let stepIndex = 0; stepIndex < steps.length; stepIndex++) {
20378
+ const step = steps[stepIndex];
20379
+ if (!step || step.type !== "ask")
20380
+ continue;
20381
+ const choicePath = ["steps", stepIndex, "outputSchema", "properties", "choice", "enum"];
20382
+ const choiceSchema = step.outputSchema?.properties?.choice;
20383
+ const choiceEnum = choiceSchema?.enum;
20384
+ if (!Array.isArray(choiceEnum)) {
20385
+ ctx.addIssue({
20386
+ code: external_exports.ZodIssueCode.custom,
20387
+ message: `Ask step "${step.id ?? stepIndex}" outputSchema must declare choice as an enum of the option values.`,
20388
+ path: choicePath
20389
+ });
20390
+ continue;
20391
+ }
20392
+ const nonStringEnum = choiceEnum.filter((value) => typeof value !== "string");
20393
+ if (nonStringEnum.length > 0) {
20394
+ ctx.addIssue({
20395
+ code: external_exports.ZodIssueCode.custom,
20396
+ message: `Ask step "${step.id ?? stepIndex}" outputSchema choice enum must contain only string values; ask options always emit strings.`,
20397
+ path: choicePath
20398
+ });
20399
+ continue;
20400
+ }
20401
+ const optionValues = (step.options ?? []).map((option) => option?.value).filter((value) => typeof value === "string");
20402
+ const optionSet = new Set(optionValues);
20403
+ const enumSet = new Set(choiceEnum);
20404
+ const missing = optionValues.filter((value) => !enumSet.has(value));
20405
+ const unexpected = choiceEnum.filter((value) => !optionSet.has(value));
20406
+ if (missing.length === 0 && unexpected.length === 0)
20407
+ continue;
20408
+ const detail = [
20409
+ missing.length > 0 ? `missing ${missing.map((v) => `'${v}'`).join(", ")}` : "",
20410
+ unexpected.length > 0 ? `unexpected ${unexpected.map((v) => `'${v}'`).join(", ")}` : ""
20411
+ ].filter(Boolean).join("; ");
20412
+ ctx.addIssue({
20413
+ code: external_exports.ZodIssueCode.custom,
20414
+ message: `Ask step "${step.id ?? stepIndex}" outputSchema choice enum must list exactly the option values (${detail}).`,
20415
+ path: choicePath
20416
+ });
20417
+ }
20713
20418
  }
20714
- function getPrimitivePayloadType(payload) {
20715
- if (payload === null) {
20716
- return "Null";
20419
+ function validateManifestResultFrom(data, ctx) {
20420
+ if (!data.resultFrom) {
20421
+ return;
20717
20422
  }
20718
- switch (typeof payload) {
20719
- case "boolean":
20720
- return "Boolean";
20721
- case "number":
20722
- return "Number";
20723
- case "bigint":
20724
- return "BigInt";
20725
- default:
20726
- return "Value";
20423
+ const steps = data.steps || [];
20424
+ const referencedStep = steps.find((step) => step.id === data.resultFrom?.stepId);
20425
+ if (!referencedStep) {
20426
+ ctx.addIssue({
20427
+ code: external_exports.ZodIssueCode.custom,
20428
+ message: `resultFrom.stepId references unknown step "${data.resultFrom.stepId}".`,
20429
+ path: ["resultFrom", "stepId"]
20430
+ });
20431
+ return;
20727
20432
  }
20728
- }
20729
- function classifyToolCallError(_error, { status, code }) {
20730
- const statusCategory = status === void 0 ? void 0 : classifyToolCallHttpStatus(status);
20731
- if (statusCategory) {
20732
- return statusCategory;
20433
+ if (referencedStep.if) {
20434
+ ctx.addIssue({
20435
+ code: external_exports.ZodIssueCode.custom,
20436
+ message: `resultFrom.stepId references step "${referencedStep.id}", which only runs conditionally (\`if\`). resultFrom must point at a step that always runs.`,
20437
+ path: ["resultFrom", "stepId"]
20438
+ });
20439
+ return;
20733
20440
  }
20734
- const codeCategory = code === void 0 ? void 0 : classifyToolCallErrorCode(code);
20735
- if (codeCategory) {
20736
- return codeCategory;
20441
+ const outputSchema = referencedStep.outputSchema;
20442
+ if (!outputSchema) {
20443
+ ctx.addIssue({
20444
+ code: external_exports.ZodIssueCode.custom,
20445
+ message: `resultFrom.stepId references step "${referencedStep.id}" without an outputSchema.`,
20446
+ path: ["resultFrom", "stepId"]
20447
+ });
20448
+ return;
20449
+ }
20450
+ if (!data.resultFrom.path) {
20451
+ if (!isPlainStringSchema(outputSchema)) {
20452
+ ctx.addIssue({
20453
+ code: external_exports.ZodIssueCode.custom,
20454
+ message: "resultFrom.path may be omitted only when the referenced step outputSchema is a string.",
20455
+ path: ["resultFrom", "path"]
20456
+ });
20457
+ }
20458
+ return;
20459
+ }
20460
+ const propertyPath = data.resultFrom.path.split(".");
20461
+ const resolvedSchema = resolvePropertySchemaInSchema(outputSchema, propertyPath);
20462
+ if (!resolvedSchema) {
20463
+ ctx.addIssue({
20464
+ code: external_exports.ZodIssueCode.custom,
20465
+ message: `resultFrom.path "${data.resultFrom.path}" does not exist in step "${referencedStep.id}" outputSchema.`,
20466
+ path: ["resultFrom", "path"]
20467
+ });
20468
+ return;
20469
+ }
20470
+ if (!isStringTypedSchema(resolvedSchema)) {
20471
+ ctx.addIssue({
20472
+ code: external_exports.ZodIssueCode.custom,
20473
+ message: "resultFrom.path must resolve to a string value.",
20474
+ path: ["resultFrom", "path"]
20475
+ });
20737
20476
  }
20738
- return "unknown";
20739
- }
20740
- function classifyToolCallHttpStatus(status) {
20741
- if (status === 401)
20742
- return "authentication";
20743
- if (status === 403)
20744
- return "permission";
20745
- if (status === 429)
20746
- return "rate-limit";
20747
- if (status === 400)
20748
- return "invalid-request";
20749
- if (status === 408 || status === 504)
20750
- return "timeout";
20751
- return void 0;
20752
20477
  }
20753
- function classifyToolCallErrorCode(code) {
20754
- const normalizedCode = code.toUpperCase();
20755
- if (normalizedCode === "ETIMEDOUT")
20756
- return "timeout";
20757
- if (/^(?:ECONN\w*|ENOTFOUND|EAI_AGAIN)$/u.test(normalizedCode))
20758
- return "connection";
20759
- return void 0;
20478
+ function isNonScalarSchema(schema) {
20479
+ const type = schema.type;
20480
+ if (type === "object" || type === "array")
20481
+ return true;
20482
+ if (Array.isArray(type) && (type.includes("object") || type.includes("array")))
20483
+ return true;
20484
+ const structural = schema;
20485
+ return structural.properties !== void 0 || structural.items !== void 0;
20760
20486
  }
20761
- function parseJsonPayload(payload) {
20762
- if (typeof payload !== "string" || payload.length > MAX_TOOL_CALL_RESULT_PARSE_LENGTH || !["{", "[", '"'].includes(payload[0] ?? "") && payload !== "null") {
20763
- return payload;
20487
+ function ifConditionLiteralError({ reference, literal: literal2, stepId, schema }) {
20488
+ const enumValues = schema.enum;
20489
+ if (Array.isArray(enumValues)) {
20490
+ if (enumValues.some((value) => String(value) === literal2))
20491
+ return null;
20492
+ const allowed = enumValues.map((value) => `'${String(value)}'`).join(", ");
20493
+ return `Step "${stepId}" condition compares "${reference}" to '${literal2}', which is not one of its allowed values: ${allowed}.`;
20764
20494
  }
20765
- let parsed = payload;
20766
- for (let index = 0; index < 2 && typeof parsed === "string"; index += 1) {
20767
- try {
20768
- parsed = JSON.parse(parsed);
20769
- } catch {
20770
- break;
20771
- }
20495
+ if (isNonScalarSchema(schema)) {
20496
+ return `Step "${stepId}" condition compares "${reference}", which is an object or array, to a string literal and can never match. Reference a scalar property instead.`;
20772
20497
  }
20773
- return parsed;
20498
+ return null;
20774
20499
  }
20775
- function getTextCollectionSummary(payload, tool) {
20776
- if (typeof payload !== "string" || tool.action !== "Found") {
20500
+ function validateIfConditionLiteral({ condition, context, stepId }) {
20501
+ const parsed = parseIfCondition(condition);
20502
+ if (!parsed)
20777
20503
  return null;
20504
+ const { reference, literal: literal2 } = parsed;
20505
+ const parts = reference.split(".");
20506
+ if (parts[0] === "steps") {
20507
+ const referencedStepId = parts[1];
20508
+ if (!referencedStepId)
20509
+ return null;
20510
+ const outputSchema = context.steps.get(referencedStepId);
20511
+ if (!outputSchema)
20512
+ return null;
20513
+ const propertyPath = parts.slice(2);
20514
+ const resolved = propertyPath.length === 0 ? outputSchema : resolvePropertySchemaInSchema(outputSchema, propertyPath);
20515
+ if (!resolved)
20516
+ return null;
20517
+ return ifConditionLiteralError({ reference, literal: literal2, stepId, schema: resolved });
20778
20518
  }
20779
- const match = new RegExp(`\\b(?:found|returned|matching|showing)\\s+(\\d+)\\s+(?:${escapeRegExp(tool.entity)}|${escapeRegExp(pluralize(tool.entity))})\\b`, "iu").exec(payload);
20780
- if (!match?.[1]) {
20781
- return null;
20519
+ if (parts[0] === "trigger") {
20520
+ const propertyPath = parts.slice(1);
20521
+ if (propertyPath.length === 0)
20522
+ return null;
20523
+ let firstError = null;
20524
+ let anyResolved = false;
20525
+ for (const schema of context.triggerOutputSchemas) {
20526
+ const resolved = resolvePropertySchemaInSchema(schema, propertyPath);
20527
+ if (!resolved)
20528
+ continue;
20529
+ anyResolved = true;
20530
+ const error61 = ifConditionLiteralError({ reference, literal: literal2, stepId, schema: resolved });
20531
+ if (!error61)
20532
+ return null;
20533
+ firstError ??= error61;
20534
+ }
20535
+ return anyResolved ? firstError : null;
20782
20536
  }
20783
- const count = Number(match[1]);
20784
- return Number.isSafeInteger(count) && count >= 0 ? count : null;
20537
+ return null;
20785
20538
  }
20786
- function getToolCallDescription(toolName) {
20787
- const tokens = toolName.slice(toolName.lastIndexOf(".") + 1).toLowerCase().split(/[-_./]/);
20788
- return {
20789
- action: getToolCallAction(tokens),
20790
- entity: getToolCallEntity(tokens)
20539
+ function validateManifestTemplateVariables(data, ctx) {
20540
+ const triggers = resolveTriggersFromData(data);
20541
+ const firstTrigger = triggers[0];
20542
+ const scheduleValue = firstTrigger?.value;
20543
+ const scheduleInjectsTimezone = firstTrigger?.type === "schedule" && typeof scheduleValue === "object" && scheduleValue !== null && typeof scheduleValue.timezone === "string";
20544
+ const context = {
20545
+ triggerType: firstTrigger?.type,
20546
+ scheduleInjectsTimezone,
20547
+ triggerOutputSchemas: [],
20548
+ steps: /* @__PURE__ */ new Map(),
20549
+ guards: /* @__PURE__ */ new Map()
20791
20550
  };
20792
- }
20793
- function getToolCallAction(tokens) {
20794
- if (tokens.some((token) => ["find", "search", "list"].includes(token)))
20795
- return "Found";
20796
- if (tokens.some((token) => ["get", "read", "retrieve"].includes(token)))
20797
- return "Retrieved";
20798
- if (tokens.some((token) => ["add", "create"].includes(token)))
20799
- return "Created";
20800
- if (tokens.some((token) => ["update", "edit", "modify"].includes(token)))
20801
- return "Updated";
20802
- if (tokens.some((token) => ["delete", "remove"].includes(token)))
20803
- return "Deleted";
20804
- if (tokens.some((token) => ["uncomplete", "reopen"].includes(token)))
20805
- return "Reopened";
20806
- if (tokens.some((token) => ["complete", "close"].includes(token)))
20807
- return "Completed";
20808
- if (tokens.includes("archive"))
20809
- return "Archived";
20810
- if (tokens.some((token) => ["send", "reply", "post", "publish", "notify"].includes(token)))
20811
- return "Sent";
20812
- if (tokens.some((token) => ["copy", "clone", "duplicate"].includes(token)))
20813
- return "Copied";
20814
- if (tokens.some((token) => ["move", "transfer"].includes(token)))
20815
- return "Moved";
20816
- if (tokens.includes("rename"))
20817
- return "Renamed";
20818
- if (tokens.includes("trash"))
20819
- return "Trashed";
20820
- if (tokens.some((token) => ["restore", "untrash"].includes(token)))
20821
- return "Restored";
20822
- if (tokens.some((token) => ["append", "insert", "replace"].includes(token)))
20823
- return "Updated";
20824
- return "Returned";
20825
- }
20826
- function getToolCallEntity(tokens) {
20827
- const actionIndex = tokens.findIndex((token) => TOOL_CALL_ACTION_TOKENS.has(token));
20828
- const entityTokens = tokens.slice(actionIndex + 1);
20829
- const qualifierIndex = entityTokens.findIndex((token) => TOOL_CALL_FILLER_TOKENS.has(token));
20830
- const primaryTokens = qualifierIndex > 0 ? entityTokens.slice(0, qualifierIndex) : entityTokens;
20831
- const entity = [...primaryTokens].reverse().find((token) => token.length > 1 && !TOOL_CALL_FILLER_TOKENS.has(token));
20832
- return entity ? truncateToolCallEntity(singularize(entity)) : "item";
20833
- }
20834
- var TOOL_CALL_ACTION_TOKENS = /* @__PURE__ */ new Set([
20835
- "find",
20836
- "search",
20837
- "list",
20838
- "get",
20839
- "read",
20840
- "retrieve",
20841
- "add",
20842
- "create",
20843
- "update",
20844
- "edit",
20845
- "modify",
20846
- "delete",
20847
- "remove",
20848
- "uncomplete",
20849
- "reopen",
20850
- "complete",
20851
- "close",
20852
- "archive",
20853
- "send",
20854
- "reply",
20855
- "post",
20856
- "publish",
20857
- "notify",
20858
- "copy",
20859
- "clone",
20860
- "duplicate",
20861
- "move",
20862
- "transfer",
20863
- "rename",
20864
- "trash",
20865
- "restore",
20866
- "untrash",
20867
- "append",
20868
- "insert",
20869
- "replace"
20870
- ]);
20871
- var TOOL_CALL_FILLER_TOKENS = /* @__PURE__ */ new Set(["by", "to", "from", "for", "with", "all"]);
20872
- function getResultCollection(payload, entity) {
20873
- if (Array.isArray(payload)) {
20874
- return payload;
20875
- }
20876
- if (!isRecord(payload)) {
20877
- return null;
20878
- }
20879
- const collection = payload[entity] ?? payload[pluralize(entity)] ?? payload[`${entity}s`] ?? payload.items ?? payload.data ?? payload.results ?? payload.value;
20880
- return Array.isArray(collection) ? collection : null;
20881
- }
20882
- function getProjectCount(items, entity) {
20883
- if (entity !== "task") {
20884
- return 0;
20551
+ for (const trigger of triggers) {
20552
+ if ((trigger.type === "webhook" || trigger.type === "api" || trigger.type === "manual") && trigger.outputSchema) {
20553
+ context.triggerOutputSchemas.push(trigger.outputSchema);
20554
+ }
20885
20555
  }
20886
- const projectIds = /* @__PURE__ */ new Set();
20887
- for (const item of items) {
20888
- if (!isRecord(item)) {
20556
+ const steps = data.steps || [];
20557
+ for (let stepIndex = 0; stepIndex < steps.length; stepIndex++) {
20558
+ const step = steps[stepIndex];
20559
+ if (!step)
20889
20560
  continue;
20561
+ const currentStepGuard = step.if;
20562
+ const unavailableStepRefs = /* @__PURE__ */ new Set();
20563
+ const propertyPathErrors = [];
20564
+ const processStepRef = (ref, sourceType, fieldPath) => {
20565
+ if (!context.steps.has(ref.stepId)) {
20566
+ unavailableStepRefs.add(ref.stepId);
20567
+ return;
20568
+ }
20569
+ const guardError = validateGuardReference({
20570
+ stepId: step.id,
20571
+ referencedStepId: ref.stepId,
20572
+ currentStepGuard,
20573
+ referencedGuard: context.guards.get(ref.stepId),
20574
+ sourceType
20575
+ });
20576
+ if (guardError) {
20577
+ propertyPathErrors.push({ message: guardError, path: fieldPath });
20578
+ return;
20579
+ }
20580
+ const stepOutputSchema2 = context.steps.get(ref.stepId);
20581
+ if (!stepOutputSchema2 || ref.propertyPath.length === 0) {
20582
+ return;
20583
+ }
20584
+ const isValid = validatePropertyPathInSchema(stepOutputSchema2, ref.propertyPath);
20585
+ if (!isValid) {
20586
+ propertyPathErrors.push({
20587
+ message: `Step "${step.id}" ${sourceType} references "steps.${ref.stepId}.${ref.propertyPath.join(".")}" but this property path does not exist in step "${ref.stepId}"'s outputSchema.`,
20588
+ path: fieldPath
20589
+ });
20590
+ }
20591
+ };
20592
+ if (typeof step.if === "string") {
20593
+ for (const variable of extractTemplateVariables(step.if)) {
20594
+ const error61 = validateTemplateVariable({
20595
+ variable,
20596
+ context,
20597
+ stepId: step.id,
20598
+ currentStepGuard,
20599
+ sourceType: "condition"
20600
+ });
20601
+ if (error61) {
20602
+ ctx.addIssue({
20603
+ code: external_exports.ZodIssueCode.custom,
20604
+ message: error61,
20605
+ path: ["steps", stepIndex, "if"]
20606
+ });
20607
+ }
20608
+ }
20609
+ const literalError = validateIfConditionLiteral({
20610
+ condition: step.if,
20611
+ context,
20612
+ stepId: step.id
20613
+ });
20614
+ if (literalError) {
20615
+ ctx.addIssue({
20616
+ code: external_exports.ZodIssueCode.custom,
20617
+ message: literalError,
20618
+ path: ["steps", stepIndex, "if"]
20619
+ });
20620
+ }
20890
20621
  }
20891
- const projectId = item.projectId ?? item.project_id;
20892
- if (typeof projectId === "string" || typeof projectId === "number") {
20893
- projectIds.add(String(projectId));
20622
+ if ("prompt" in step && typeof step.prompt === "string") {
20623
+ const variables = extractTemplateVariables(step.prompt);
20624
+ for (const variable of variables) {
20625
+ const error61 = validateTemplateVariable({
20626
+ variable,
20627
+ context,
20628
+ stepId: step.id,
20629
+ currentStepGuard
20630
+ });
20631
+ if (!error61)
20632
+ continue;
20633
+ const parts = variable.split(".");
20634
+ if (parts[0] === "steps" && parts[1] && !context.steps.has(parts[1]) && error61.includes("before it is available")) {
20635
+ unavailableStepRefs.add(parts[1]);
20636
+ } else {
20637
+ ctx.addIssue({
20638
+ code: external_exports.ZodIssueCode.custom,
20639
+ message: error61,
20640
+ path: ["steps", stepIndex, "prompt"]
20641
+ });
20642
+ }
20643
+ }
20644
+ const backtickRefs = extractStepReferences(step.prompt, backtickStepReferenceRegex);
20645
+ for (const ref of backtickRefs) {
20646
+ processStepRef(ref, "prompt", ["steps", stepIndex, "prompt"]);
20647
+ }
20894
20648
  }
20649
+ if (step.type === "ask") {
20650
+ const askTextFields = [
20651
+ { fieldName: "message", value: "message" in step ? step.message : void 0 },
20652
+ { fieldName: "details", value: "details" in step ? step.details : void 0 }
20653
+ ];
20654
+ for (const { fieldName, value } of askTextFields) {
20655
+ if (typeof value !== "string")
20656
+ continue;
20657
+ for (const variable of extractTemplateVariables(value)) {
20658
+ const error61 = validateTemplateVariable({
20659
+ variable,
20660
+ context,
20661
+ stepId: step.id,
20662
+ currentStepGuard,
20663
+ sourceType: fieldName
20664
+ });
20665
+ if (!error61)
20666
+ continue;
20667
+ ctx.addIssue({
20668
+ code: external_exports.ZodIssueCode.custom,
20669
+ message: error61,
20670
+ path: ["steps", stepIndex, fieldName]
20671
+ });
20672
+ }
20673
+ }
20674
+ }
20675
+ if (step.type === "code" && "code" in step && typeof step.code === "string") {
20676
+ const codeRefs = extractStepReferences(step.code, codeStepReferenceRegex);
20677
+ for (const ref of codeRefs) {
20678
+ processStepRef(ref, "code", ["steps", stepIndex, "code"]);
20679
+ }
20680
+ }
20681
+ for (const referencedStepId of unavailableStepRefs) {
20682
+ const sourceType = step.type === "code" ? "code" : "prompt";
20683
+ const fieldName = step.type === "code" ? "code" : "prompt";
20684
+ ctx.addIssue({
20685
+ code: external_exports.ZodIssueCode.custom,
20686
+ message: `Step "${step.id}" ${sourceType} references step "${referencedStepId}" before it is available. Steps can only reference outputs from earlier steps.`,
20687
+ path: ["steps", stepIndex, fieldName]
20688
+ });
20689
+ }
20690
+ for (const err of propertyPathErrors) {
20691
+ ctx.addIssue({
20692
+ code: external_exports.ZodIssueCode.custom,
20693
+ message: err.message,
20694
+ path: err.path
20695
+ });
20696
+ }
20697
+ if (step.type === "llm" || step.type === "codegen" || step.type === "code" || step.type === "ask") {
20698
+ context.steps.set(step.id, step.outputSchema);
20699
+ } else if (step.type === "search") {
20700
+ context.steps.set(step.id, { type: "string" });
20701
+ }
20702
+ context.guards.set(step.id, currentStepGuard);
20895
20703
  }
20896
- return projectIds.size;
20897
- }
20898
- function formatEntity(entity, count) {
20899
- return count === 1 ? entity : pluralize(entity);
20900
- }
20901
- function singularize(value) {
20902
- if (value === "people") {
20903
- return "person";
20904
- }
20905
- if (value.endsWith("ies")) {
20906
- return `${value.slice(0, -3)}y`;
20907
- }
20908
- if (value.endsWith("ses") || value.endsWith("xes") || value.endsWith("ches") || value.endsWith("shes")) {
20909
- return value.slice(0, -2);
20910
- }
20911
- return value.endsWith("s") ? value.slice(0, -1) : value;
20912
- }
20913
- function pluralize(value) {
20914
- if (value === "person") {
20915
- return "people";
20916
- }
20917
- if (value.endsWith("y")) {
20918
- return `${value.slice(0, -1)}ies`;
20919
- }
20920
- return value.endsWith("s") ? `${value}es` : `${value}s`;
20921
- }
20922
- function isRecord(value) {
20923
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
20924
20704
  }
20925
20705
 
20706
+ // ../../packages/core/dist/esm/schemas/admin-sample-automation.schema.js
20707
+ var sampleAutomationIdSchema = external_exports.string().trim().min(1, "Sample automation ID is required").max(100, "Sample automation ID must be 100 characters or fewer").regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "Sample automation ID must use lowercase letters, numbers, and hyphens only");
20708
+ var sampleAutomationInstructionsSchema = external_exports.string().nullable();
20709
+ var adminSampleAutomationSchema = external_exports.object({
20710
+ id: sampleAutomationIdSchema,
20711
+ manifest: manifestSchema,
20712
+ instructions: sampleAutomationInstructionsSchema.default(null),
20713
+ connectorNames: external_exports.array(external_exports.string()),
20714
+ connectorDetails: external_exports.array(connectorDetailsSchema),
20715
+ isPublished: external_exports.boolean(),
20716
+ isOneClickInstallEnabled: external_exports.boolean().default(false),
20717
+ staffPick: external_exports.boolean().default(false),
20718
+ createdAt: external_exports.string().datetime(),
20719
+ updatedAt: external_exports.string().datetime()
20720
+ });
20721
+ var adminSampleAutomationsSchema = external_exports.array(adminSampleAutomationSchema);
20722
+ var createAdminSampleAutomationSchema = external_exports.object({
20723
+ id: sampleAutomationIdSchema,
20724
+ manifest: manifestSchema,
20725
+ instructions: sampleAutomationInstructionsSchema.default(null),
20726
+ isPublished: external_exports.boolean().default(false),
20727
+ isOneClickInstallEnabled: external_exports.boolean().default(false),
20728
+ staffPick: external_exports.boolean().default(false)
20729
+ });
20730
+ var updateAdminSampleAutomationSchema = external_exports.object({
20731
+ manifest: manifestSchema,
20732
+ instructions: sampleAutomationInstructionsSchema.optional(),
20733
+ isPublished: external_exports.boolean(),
20734
+ isOneClickInstallEnabled: external_exports.boolean().optional(),
20735
+ staffPick: external_exports.boolean().optional()
20736
+ });
20737
+ var toggleAdminSampleAutomationPublicationSchema = external_exports.object({
20738
+ isPublished: external_exports.boolean()
20739
+ });
20740
+ var deleteAdminSampleAutomationSchema = external_exports.object({
20741
+ title: external_exports.string().min(1, "Sample automation title confirmation is required")
20742
+ });
20743
+
20744
+ // ../../packages/core/dist/esm/types/automation.types.js
20745
+ var AUTOMATION_CONNECTION_STATUSES = ["ready", "almost-ready", "not-ready"];
20746
+ var AUTOMATION_VISIBILITIES = ["private", "workspace"];
20747
+ var AUTOMATION_DISABLED_REASONS = [
20748
+ "creator-left-workspace",
20749
+ "workspace-plan-lapsed"
20750
+ ];
20751
+
20752
+ // ../../packages/core/dist/esm/schemas/connection.schema.js
20753
+ var CreateConnectionParamsSchema = external_exports.object({
20754
+ connectorId: external_exports.string(),
20755
+ capability: external_exports.enum(CONNECTION_TYPES),
20756
+ name: external_exports.string().optional()
20757
+ });
20758
+ var CreateConnectionBodySchema = external_exports.object({
20759
+ automationIds: external_exports.array(external_exports.uuid()).optional()
20760
+ });
20761
+ var OAuthConnectionSetupSchema = external_exports.object({
20762
+ kind: external_exports.literal("oauth"),
20763
+ authorizationUrl: external_exports.string().url()
20764
+ });
20765
+ var ManagedConnectionSetupSchema = external_exports.object({
20766
+ kind: external_exports.literal("managed"),
20767
+ code: external_exports.string().regex(MANAGED_CONNECTION_SETUP_CODE_EXACT_REGEX),
20768
+ instructions: external_exports.string(),
20769
+ deepLinkUrl: external_exports.string().url().nullable(),
20770
+ expiresAt: external_exports.iso.datetime()
20771
+ });
20772
+ var ConnectionSetupSchema = external_exports.discriminatedUnion("kind", [
20773
+ OAuthConnectionSetupSchema,
20774
+ ManagedConnectionSetupSchema
20775
+ ]);
20776
+ var CreateConnectionResponseSchema = external_exports.object({
20777
+ connectionId: external_exports.uuid(),
20778
+ setup: ConnectionSetupSchema.nullable()
20779
+ });
20780
+ var ConnectionCapabilitySchema = external_exports.object({
20781
+ capability: external_exports.enum(CONNECTION_TYPES),
20782
+ scopes: external_exports.array(external_exports.string()),
20783
+ authorizedAt: external_exports.iso.datetime()
20784
+ });
20785
+ var ConnectionSchema = external_exports.object({
20786
+ id: external_exports.uuid(),
20787
+ name: external_exports.string(),
20788
+ connectorId: external_exports.string(),
20789
+ connectorIconUrl: external_exports.url().nullable().optional(),
20790
+ capabilities: external_exports.array(ConnectionCapabilitySchema),
20791
+ accountDisplayLabel: external_exports.string().optional(),
20792
+ status: external_exports.enum(CONNECTION_STATUSES).optional(),
20793
+ createdAt: external_exports.string().optional(),
20794
+ metadata: external_exports.record(external_exports.string(), external_exports.unknown()),
20795
+ setup: ConnectionSetupSchema.nullable()
20796
+ });
20797
+ var ConnectionDetailsSchema = external_exports.object({
20798
+ id: external_exports.uuid(),
20799
+ name: external_exports.string(),
20800
+ connectorId: external_exports.string(),
20801
+ connectorIconUrl: external_exports.url().nullable().optional(),
20802
+ capabilities: external_exports.array(ConnectionCapabilitySchema),
20803
+ accountDisplayLabel: external_exports.string(),
20804
+ status: external_exports.enum(CONNECTION_STATUSES),
20805
+ createdAt: external_exports.string(),
20806
+ metadata: external_exports.record(external_exports.string(), external_exports.unknown()),
20807
+ setup: ConnectionSetupSchema.nullable()
20808
+ });
20809
+ var GetConnectionsQuerySchema = external_exports.object({
20810
+ /**
20811
+ * Capability filter. `type` is accepted as a deprecated alias during the
20812
+ * rolling-deploy window where older FE/SDK builds may still send the old
20813
+ * query param. Remove once all clients are on the post-PR1 SDK.
20814
+ */
20815
+ capability: external_exports.enum(CONNECTION_TYPES).optional(),
20816
+ type: external_exports.enum(CONNECTION_TYPES).optional(),
20817
+ connectorId: external_exports.string().optional(),
20818
+ status: external_exports.enum(CONNECTION_STATUSES).optional()
20819
+ });
20820
+ var GetConnectionsResponseSchema = external_exports.object({
20821
+ connections: external_exports.array(ConnectionSchema)
20822
+ });
20823
+ var DependentAutomationSchema = external_exports.object({
20824
+ id: external_exports.string(),
20825
+ name: external_exports.string(),
20826
+ description: external_exports.string(),
20827
+ enabled: external_exports.boolean(),
20828
+ connectorStatus: external_exports.enum(["ready", "attention", "missing"]),
20829
+ requiredConnectionTypes: external_exports.array(external_exports.enum(CONNECTION_TYPES))
20830
+ });
20831
+ var GetConnectionDependentAutomationsResponseSchema = external_exports.object({
20832
+ automations: external_exports.array(DependentAutomationSchema)
20833
+ });
20834
+ var CONNECTION_IN_USE_CONFLICT_CODE = "connection_in_use_conflict";
20835
+ var BlockingAutomationSchema = external_exports.object({
20836
+ id: external_exports.string(),
20837
+ name: external_exports.string()
20838
+ });
20839
+ var ConnectionInUseConflictSchema = external_exports.object({
20840
+ code: external_exports.literal(CONNECTION_IN_USE_CONFLICT_CODE),
20841
+ message: external_exports.string(),
20842
+ automations: external_exports.array(BlockingAutomationSchema)
20843
+ });
20844
+
20845
+ // ../../packages/core/dist/esm/schemas/automation.schema.js
20846
+ var runStatusSchema = external_exports.discriminatedUnion("status", [
20847
+ external_exports.object({
20848
+ status: external_exports.literal("pending"),
20849
+ lastRun: external_exports.iso.datetime().optional()
20850
+ }),
20851
+ external_exports.object({
20852
+ status: external_exports.literal("running"),
20853
+ lastRun: external_exports.iso.datetime()
20854
+ }),
20855
+ external_exports.object({
20856
+ status: external_exports.literal("completed"),
20857
+ lastRun: external_exports.iso.datetime()
20858
+ }),
20859
+ external_exports.object({
20860
+ status: external_exports.literal("failed"),
20861
+ lastRun: external_exports.iso.datetime(),
20862
+ failureReason: external_exports.string().optional()
20863
+ }),
20864
+ external_exports.object({
20865
+ status: external_exports.literal("cancelled"),
20866
+ lastRun: external_exports.iso.datetime()
20867
+ })
20868
+ ]);
20869
+ var automationSchema = external_exports.object({
20870
+ id: external_exports.string(),
20871
+ connections: external_exports.array(ConnectionDetailsSchema),
20872
+ enabled: external_exports.boolean(),
20873
+ status: runStatusSchema,
20874
+ connectionStatus: external_exports.enum(AUTOMATION_CONNECTION_STATUSES),
20875
+ draftId: external_exports.string().nullable(),
20876
+ manifest: external_exports.union([manifestSchema, external_exports.record(external_exports.string(), external_exports.unknown())]).nullable(),
20877
+ updatedAt: external_exports.iso.datetime(),
20878
+ // null means the automation belongs to the creator's personal space
20879
+ workspaceId: external_exports.string().nullable(),
20880
+ visibility: external_exports.enum(AUTOMATION_VISIBILITIES),
20881
+ isOwner: external_exports.boolean(),
20882
+ disabledReason: external_exports.enum(AUTOMATION_DISABLED_REASONS).nullable()
20883
+ });
20884
+ var automationListResponseSchema = external_exports.object({
20885
+ data: external_exports.array(automationSchema),
20886
+ nextCursor: external_exports.string().nullable()
20887
+ });
20888
+ var updateAutomationSchema = external_exports.object({
20889
+ enabled: external_exports.boolean().optional(),
20890
+ manifest: manifestSchema.optional()
20891
+ }).refine((data) => Object.keys(data).length > 0, {
20892
+ message: "At least one field must be provided"
20893
+ });
20894
+ var updateAutomationMetadataSchema = external_exports.object({
20895
+ name: external_exports.string().min(1),
20896
+ description: external_exports.string()
20897
+ });
20898
+ var updateAutomationScheduleSchema = external_exports.object({
20899
+ value: scheduleValueSchema
20900
+ });
20901
+
20902
+ // ../../packages/core/dist/esm/schemas/automation-connection.schema.js
20903
+ var AutomationConnectionMappingSchema = external_exports.object({
20904
+ connectorId: external_exports.string().trim().min(1, "connectorId is required"),
20905
+ connectionId: external_exports.string().trim().min(1, "connectionId is required")
20906
+ }).strict();
20907
+ var UpsertAutomationConnectionSchema = external_exports.object({
20908
+ connectionId: uuidv7Schema.min(1, "connectionId is required"),
20909
+ /**
20910
+ * Manifest slot to bind this connection to. Required when the
20911
+ * connection carries more than one capability (managed connectors
20912
+ * can cover both `mcp` and `trigger` after a single
20913
+ * onboarding). Optional for single-capability connections, where
20914
+ * the service infers the role from `connection.capabilities[0]`.
20915
+ */
20916
+ connectionType: external_exports.enum(CONNECTION_TYPES).optional()
20917
+ }).strict();
20918
+ var SetAutomationConnectionsSchema = external_exports.object({
20919
+ mappings: external_exports.array(AutomationConnectionMappingSchema).nonempty("mappings cannot be empty")
20920
+ }).strict();
20921
+ var RequiredConnectorSchema = external_exports.object({
20922
+ connectorName: external_exports.string(),
20923
+ connectorId: external_exports.string(),
20924
+ connectorDisplayName: external_exports.string(),
20925
+ description: external_exports.string(),
20926
+ connectorIconUrl: external_exports.string().nullable(),
20927
+ requiredTypes: external_exports.array(external_exports.enum(CONNECTION_TYPES)),
20928
+ /** Whether uses of this connector require a Connection binding. */
20929
+ requiresConnection: external_exports.boolean(),
20930
+ /**
20931
+ * Product-owned built-in status. Connection validation uses
20932
+ * `requiresConnection` independently from this UI classification.
20933
+ */
20934
+ isSystem: external_exports.boolean().optional()
20935
+ });
20936
+ var AutomationConnectionSchema = external_exports.object({
20937
+ connectorId: external_exports.string(),
20938
+ connectionId: external_exports.uuid(),
20939
+ connectorName: external_exports.string(),
20940
+ connectorIconUrl: external_exports.url().nullable().optional(),
20941
+ status: external_exports.enum(CONNECTION_STATUSES),
20942
+ type: external_exports.enum(CONNECTION_TYPES)
20943
+ });
20944
+ var ConnectorTypeStatusSchema = external_exports.object({
20945
+ required: external_exports.boolean(),
20946
+ status: external_exports.enum(CONNECTOR_TYPE_STATUSES),
20947
+ connection: ConnectionSchema.pick({
20948
+ id: true,
20949
+ accountDisplayLabel: true,
20950
+ status: true,
20951
+ createdAt: true
20952
+ }).optional(),
20953
+ setup: ConnectionSetupSchema.nullable()
20954
+ });
20955
+ var AutomationConnectorStatusSchema = external_exports.object({
20956
+ id: external_exports.string(),
20957
+ name: external_exports.string(),
20958
+ description: external_exports.string(),
20959
+ iconUrl: external_exports.string().nullable(),
20960
+ mcp: ConnectorTypeStatusSchema,
20961
+ trigger: ConnectorTypeStatusSchema
20962
+ });
20963
+ var AutomationConnectionValidationSchema = external_exports.object({
20964
+ required: external_exports.array(RequiredConnectorSchema),
20965
+ mapped: external_exports.array(AutomationConnectionSchema),
20966
+ missing: external_exports.array(external_exports.object({
20967
+ connectorName: external_exports.string(),
20968
+ connectionType: external_exports.enum(CONNECTION_TYPES),
20969
+ reason: external_exports.enum(CONNECTION_ISSUE_REASONS)
20970
+ })),
20971
+ connectors: external_exports.array(AutomationConnectorStatusSchema),
20972
+ isReady: external_exports.boolean()
20973
+ });
20974
+ var GetRequiredConnectorsResponseSchema = external_exports.object({
20975
+ required: external_exports.array(RequiredConnectorSchema)
20976
+ });
20977
+ var GetAutomationConnectionsResponseSchema = external_exports.object({
20978
+ data: external_exports.array(AutomationConnectionSchema)
20979
+ });
20980
+ var UpsertAutomationConnectionResponseSchema = external_exports.object({
20981
+ data: AutomationConnectionSchema
20982
+ });
20983
+ var ManifestConnectionCheckResultSchema = AutomationConnectionValidationSchema.pick({
20984
+ required: true,
20985
+ connectors: true
20986
+ });
20987
+
20926
20988
  // ../../packages/core/dist/esm/schemas/automation-execution.schema.js
20927
20989
  var AutomationRunDebugEventSchema = external_exports.union([
20928
20990
  external_exports.object({
@@ -21487,10 +21549,6 @@ var FILTER_OPERATORS = ["and", "or"];
21487
21549
  var MAX_COHORT_RESULTS = 366;
21488
21550
  var MAX_COHORT_ROWS = 120;
21489
21551
 
21490
- // ../../packages/core/dist/esm/schemas/query-param.schema.js
21491
- var booleanQueryParamSchema = external_exports.preprocess((value) => value === "true" ? true : value === "false" ? false : value, external_exports.boolean());
21492
- var booleanQueryFlagSchema = booleanQueryParamSchema.optional().transform((value) => value === true);
21493
-
21494
21552
  // ../../packages/core/dist/esm/schemas/cohorts.schema.js
21495
21553
  var FRONTEND_COHORT_EVENTS = [
21496
21554
  "session:started",
@@ -22465,16 +22523,28 @@ var AutomationsNetworkError = class extends Error {
22465
22523
  var AutomationsRequestError = class extends Error {
22466
22524
  httpStatusCode;
22467
22525
  responseData;
22468
- constructor(message, httpStatusCode, responseData) {
22526
+ /**
22527
+ * The scope the route required, taken from the `scope` parameter of a
22528
+ * `WWW-Authenticate` challenge. Undefined when the response carried no
22529
+ * challenge, or one without that parameter — and in a browser unless the
22530
+ * header is listed in `Access-Control-Expose-Headers`, which is why callers
22531
+ * must still work without it.
22532
+ */
22533
+ requiredScope;
22534
+ constructor(message, httpStatusCode, responseData, requiredScope2) {
22469
22535
  super(message);
22470
22536
  this.name = "AutomationsRequestError";
22471
22537
  this.httpStatusCode = httpStatusCode;
22472
22538
  this.responseData = responseData;
22539
+ this.requiredScope = requiredScope2;
22473
22540
  }
22474
22541
  isAuthError() {
22475
22542
  return this.httpStatusCode === 401 || this.httpStatusCode === 403;
22476
22543
  }
22477
22544
  };
22545
+ function parseChallengeScope(header) {
22546
+ return header?.match(/scope="([^"]+)"/)?.[1];
22547
+ }
22478
22548
 
22479
22549
  // ../../packages/sdk/dist/esm/transport/http-client.js
22480
22550
  function buildUrl(baseUrl, path, query) {
@@ -22534,7 +22604,7 @@ async function request(args) {
22534
22604
  const data = await readResponseBody(response);
22535
22605
  if (!response.ok) {
22536
22606
  const message = typeof data === "object" && data !== null && "message" in data ? String(data.message) : response.statusText || `Request failed with status ${response.status}`;
22537
- throw new AutomationsRequestError(message, response.status, data);
22607
+ throw new AutomationsRequestError(message, response.status, data, parseChallengeScope(response.headers.get("WWW-Authenticate")));
22538
22608
  }
22539
22609
  return data;
22540
22610
  }
@@ -23779,6 +23849,22 @@ function initialEnvironmentName() {
23779
23849
 
23780
23850
  // src/lib/auth-records.ts
23781
23851
  var CONFIG_VERSION = 1;
23852
+ async function getDefaultWorkspaceId() {
23853
+ return getEnvironmentDefault("default_workspace", getEnvironment().name);
23854
+ }
23855
+ async function setDefaultWorkspaceId(workspaceId) {
23856
+ await setEnvironmentDefault("default_workspace", getEnvironment().name, workspaceId);
23857
+ }
23858
+ async function getEnvironmentDefault(key, environment) {
23859
+ return (await read())[key]?.[environment] ?? null;
23860
+ }
23861
+ async function setEnvironmentDefault(key, environment, id) {
23862
+ const config2 = await read();
23863
+ const defaults = { ...config2[key] };
23864
+ if (id === null) delete defaults[environment];
23865
+ else defaults[environment] = id;
23866
+ await write({ ...config2, [key]: defaults });
23867
+ }
23782
23868
  function configPath() {
23783
23869
  return getConfigPath(SERVICE_NAME);
23784
23870
  }
@@ -23856,14 +23942,10 @@ function createAutomationsRecordStore() {
23856
23942
  await write({ ...config2, accounts: next, default_account: defaults });
23857
23943
  },
23858
23944
  async getDefaultId() {
23859
- return (await read()).default_account?.[environment()] ?? null;
23945
+ return getEnvironmentDefault("default_account", environment());
23860
23946
  },
23861
23947
  async setDefaultId(id) {
23862
- const config2 = await read();
23863
- const defaults = { ...config2.default_account };
23864
- if (id === null) delete defaults[environment()];
23865
- else defaults[environment()] = id;
23866
- await write({ ...config2, default_account: defaults });
23948
+ await setEnvironmentDefault("default_account", environment(), id);
23867
23949
  }
23868
23950
  };
23869
23951
  }
@@ -23912,28 +23994,32 @@ import { attachLoginCommand } from "@doist/cli-core/auth";
23912
23994
  import { CliError, getConfigPath as getConfigPath3, getErrorMessage as getErrorMessage3 } from "@doist/cli-core";
23913
23995
  import { refreshAccessToken } from "@doist/cli-core/auth";
23914
23996
 
23915
- // src/lib/auth-provider.ts
23916
- import { getErrorMessage } from "@doist/cli-core";
23917
- import { createPkceProvider } from "@doist/cli-core/auth";
23918
-
23919
23997
  // src/lib/auth-scopes.ts
23920
23998
  import { InvalidArgumentError as InvalidArgumentError3 } from "commander";
23921
- var IDENTITY_SCOPE = "user:read";
23999
+ var IDENTITY_SCOPES = ["user:read", "workspaces:read"];
23922
24000
  var WRITE_SCOPES = [
23923
24001
  "automations:automations:write",
23924
24002
  "automations:automations:execute",
23925
24003
  "automations:connections:write",
23926
- "automations:chat:write"
24004
+ "automations:chat:write",
24005
+ "automations:workspaces:write",
24006
+ "automations:workspaces:execute"
23927
24007
  ];
23928
24008
  var READ_ONLY_SCOPES = WRITE_SCOPES.flatMap(
23929
24009
  (code) => scopeDefinition(code).impliedScopes ?? []
23930
24010
  );
23931
24011
  var ADDITIONAL_WRITE_SCOPES = {
23932
- workspaces: ["automations:workspaces:write", "automations:workspaces:execute"],
23933
24012
  notifications: ["automations:notifications:write"],
23934
24013
  settings: ["automations:settings:write"],
23935
24014
  webhooks: ["automations:webhooks:write"]
23936
24015
  };
24016
+ function additionalScopeFlagFor(scope) {
24017
+ return ADDITIONAL_SCOPE_FLAGS.find(
24018
+ (flag) => ADDITIONAL_WRITE_SCOPES[flag].some(
24019
+ (code) => code === scope || (scopeDefinition(code).impliedScopes ?? []).includes(scope)
24020
+ )
24021
+ );
24022
+ }
23937
24023
  var ADDITIONAL_SCOPE_FLAGS = Object.keys(
23938
24024
  ADDITIONAL_WRITE_SCOPES
23939
24025
  );
@@ -23952,7 +24038,7 @@ function resolveScopes(context) {
23952
24038
  const additional = extractAdditionalScopes(context.flags).flatMap(
23953
24039
  (flag) => scopesFor(flag, context.readOnly)
23954
24040
  );
23955
- return [IDENTITY_SCOPE, ...base, ...additional];
24041
+ return [...IDENTITY_SCOPES, ...base, ...additional];
23956
24042
  }
23957
24043
  function parseAdditionalScopes(raw) {
23958
24044
  const requested = raw.split(",").map((part) => part.trim());
@@ -23992,6 +24078,8 @@ function formatScopesHelp() {
23992
24078
  }
23993
24079
 
23994
24080
  // src/lib/auth-provider.ts
24081
+ import { getErrorMessage } from "@doist/cli-core";
24082
+ import { createPkceProvider } from "@doist/cli-core/auth";
23995
24083
  var ERROR_HINTS = ["Try again: tda auth login"];
23996
24084
  function createAutomationsAuthProvider() {
23997
24085
  return {
@@ -24194,14 +24282,29 @@ function explainAuthFailure(error61, source) {
24194
24282
  return new CredentialError(
24195
24283
  "INSUFFICIENT_SCOPE",
24196
24284
  "Your login does not grant what this command needs.",
24197
- [
24198
- "Run `tda auth login` to authorize again, without --read-only.",
24199
- "Pass --additional-scopes if the command needs an opt-in scope."
24200
- ]
24285
+ insufficientScopeFixes(requiredScope(error61))
24201
24286
  );
24202
24287
  }
24203
24288
  return error61;
24204
24289
  }
24290
+ function insufficientScopeFixes(scope) {
24291
+ if (!scope) {
24292
+ return [
24293
+ "Run `tda auth login` to authorize again, without --read-only.",
24294
+ "Pass --additional-scopes if the command needs an opt-in scope."
24295
+ ];
24296
+ }
24297
+ const flag = additionalScopeFlagFor(scope);
24298
+ return [
24299
+ `This needs the \`${scope}\` scope, which your login does not have.`,
24300
+ flag ? `Run \`tda auth login --additional-scopes ${flag}\` to grant it.` : "Run `tda auth login` to authorize again, without --read-only."
24301
+ ];
24302
+ }
24303
+ function requiredScope(error61) {
24304
+ if (typeof error61 !== "object" || error61 === null || !("requiredScope" in error61)) return void 0;
24305
+ const scope = error61.requiredScope;
24306
+ return typeof scope === "string" ? scope : void 0;
24307
+ }
24205
24308
  function httpStatus(error61) {
24206
24309
  if (typeof error61 !== "object" || error61 === null || !("httpStatusCode" in error61))
24207
24310
  return void 0;
@@ -25090,6 +25193,26 @@ async function runApiCommand(command, operation, presentation, execution = {}) {
25090
25193
  }
25091
25194
  }
25092
25195
 
25196
+ // src/lib/workspace-scope.ts
25197
+ async function resolveWorkspaceScope(options) {
25198
+ if (options.workspace !== void 0 && options.personal) {
25199
+ throw new Error("Use either --workspace or --personal, not both.");
25200
+ }
25201
+ if (options.workspace !== void 0) {
25202
+ return options.workspace;
25203
+ }
25204
+ if (options.personal) {
25205
+ return null;
25206
+ }
25207
+ return getDefaultWorkspaceId();
25208
+ }
25209
+ function addWorkspaceScopeOptions(command) {
25210
+ return command.option(
25211
+ "--workspace <workspace-id>",
25212
+ "Act on this workspace instead of your personal space"
25213
+ ).option("--personal", "Act on your personal space, ignoring any pinned workspace");
25214
+ }
25215
+
25093
25216
  // src/commands/read/format.ts
25094
25217
  function formatAutomationList(page) {
25095
25218
  if (page.data.length === 0) return "No automations found.";
@@ -25311,44 +25434,67 @@ function capitalize(value) {
25311
25434
  // src/commands/automation/index.ts
25312
25435
  function registerAutomationCommand(program) {
25313
25436
  const automation = program.command("automation").description("Inspect and manage published automations");
25314
- addCursorPaginationOptions(automation.command("list").description("List automations")).action(
25315
- async (options, command) => {
25316
- await runApiCommand(
25317
- command,
25318
- ({ api, signal }) => collectCursorPages(options, (args) => api.automations.list(args, { signal })),
25319
- { human: formatAutomationList, json: summarizeAutomationPage },
25320
- {
25321
- spinner: "Loading automations...",
25322
- streamNdjson: ({ api, signal }) => streamAutomationList(
25437
+ addWorkspaceScopeOptions(
25438
+ addCursorPaginationOptions(automation.command("list").description("List automations"))
25439
+ ).action(async (options, command) => {
25440
+ await runApiCommand(
25441
+ command,
25442
+ async ({ api, signal }) => {
25443
+ const workspaceId = await resolveWorkspaceScope(options);
25444
+ return collectCursorPages(
25445
+ options,
25446
+ (args) => workspaceId === null ? api.automations.list(args, { signal }) : api.workspaceAutomations.list(workspaceId, args, { signal })
25447
+ );
25448
+ },
25449
+ { human: formatAutomationList, json: summarizeAutomationPage },
25450
+ {
25451
+ spinner: "Loading automations...",
25452
+ streamNdjson: async function* ({ api, signal }) {
25453
+ const workspaceId = await resolveWorkspaceScope(options);
25454
+ yield* streamAutomationList(
25323
25455
  options,
25324
- (args) => api.automations.list(args, { signal })
25325
- )
25456
+ (args) => workspaceId === null ? api.automations.list(args, { signal }) : api.workspaceAutomations.list(workspaceId, args, { signal })
25457
+ );
25326
25458
  }
25327
- );
25328
- }
25329
- );
25330
- automation.command("view").description("Show an automation and its manifest").argument("<automation-id>", "Automation ID").action(async (automationId, _options, command) => {
25459
+ }
25460
+ );
25461
+ });
25462
+ addWorkspaceScopeOptions(
25463
+ automation.command("view").description("Show an automation and its manifest").argument("<automation-id>", "Automation ID")
25464
+ ).action(async (automationId, options, command) => {
25331
25465
  await runApiCommand(
25332
25466
  command,
25333
- ({ api, signal }) => api.automations.get(automationId, { signal }),
25467
+ async ({ api, signal }) => {
25468
+ const workspaceId = await resolveWorkspaceScope(options);
25469
+ return workspaceId === null ? api.automations.get(automationId, { signal }) : api.workspaceAutomations.get(workspaceId, automationId, { signal });
25470
+ },
25334
25471
  { human: formatAutomation, json: summarizeAutomation },
25335
25472
  { spinner: "Loading automation..." }
25336
25473
  );
25337
25474
  });
25338
25475
  registerEnabledStateCommand(automation, "enable", true);
25339
25476
  registerEnabledStateCommand(automation, "disable", false);
25340
- automation.command("trigger").description("Start a live automation run without waiting for it to finish").argument("<automation-id>", "Automation ID").action(async (automationId, _options, command) => {
25477
+ addWorkspaceScopeOptions(
25478
+ automation.command("trigger").description("Start a live automation run without waiting for it to finish").argument("<automation-id>", "Automation ID")
25479
+ ).action(async (automationId, options, command) => {
25341
25480
  await runApiCommand(
25342
25481
  command,
25343
25482
  async ({ api, signal }) => {
25344
- const automation2 = await api.automations.get(automationId, { signal });
25483
+ const workspaceId = await resolveWorkspaceScope(options);
25484
+ const automation2 = workspaceId === null ? await api.automations.get(automationId, { signal }) : await api.workspaceAutomations.get(workspaceId, automationId, {
25485
+ signal
25486
+ });
25345
25487
  const triggerType = hasTriggerType(automation2.manifest) ? getManifestTriggers(automation2.manifest)[0]?.type : void 0;
25346
25488
  if (triggerType !== "schedule") {
25347
25489
  throw new Error(
25348
25490
  "This automation can only be triggered via its configured mechanism, not manually."
25349
25491
  );
25350
25492
  }
25351
- await api.automations.trigger(automationId, { signal });
25493
+ if (workspaceId === null) {
25494
+ await api.automations.trigger(automationId, { signal });
25495
+ } else {
25496
+ await api.workspaceAutomations.trigger(workspaceId, automationId, { signal });
25497
+ }
25352
25498
  return { action: "triggered", automationId };
25353
25499
  },
25354
25500
  { human: formatAutomationTrigger },
@@ -27552,6 +27698,83 @@ function registerUpdateCommand(program) {
27552
27698
  });
27553
27699
  }
27554
27700
 
27701
+ // src/commands/workspace/format.ts
27702
+ function formatWorkspaceList(workspaces) {
27703
+ if (workspaces.length === 0) {
27704
+ return "No workspaces. Automations you create belong to your personal space.";
27705
+ }
27706
+ return workspaces.map((workspace) => {
27707
+ const locked = workspace.access === "locked" ? " (locked: plan does not cover Automations)" : "";
27708
+ return `${workspace.id} ${workspace.name} ${workspace.plan} ${workspace.role}${locked}`;
27709
+ }).join("\n");
27710
+ }
27711
+ function summarizeWorkspaceList(workspaces) {
27712
+ return { workspaces };
27713
+ }
27714
+ function formatCurrentWorkspace(result) {
27715
+ return result.workspaceId === null ? "Personal space. Pin a workspace with `tda workspace use <workspace-id>`." : `Workspace ${result.workspaceId}`;
27716
+ }
27717
+ function formatWorkspaceSelection(result) {
27718
+ return result.action === "cleared" ? "Cleared. Commands now act on your personal space." : `Pinned ${result.name ?? result.workspaceId}. \`automation list\`, \`view\` and \`trigger\` now act on this workspace; everything else stays personal. Use --personal for a one-off.`;
27719
+ }
27720
+
27721
+ // src/commands/workspace/index.ts
27722
+ function registerWorkspaceCommand(program) {
27723
+ const workspace = program.command("workspace").description("List Todoist workspaces and choose which one commands act on");
27724
+ workspace.command("list").description("List the workspaces you can use").action(async (_options, command) => {
27725
+ await runApiCommand(
27726
+ command,
27727
+ ({ api, signal }) => api.workspaces.list({ signal }),
27728
+ {
27729
+ human: formatWorkspaceList,
27730
+ json: summarizeWorkspaceList,
27731
+ ndjson: (workspaces) => workspaces
27732
+ },
27733
+ { spinner: "Loading workspaces..." }
27734
+ );
27735
+ });
27736
+ workspace.command("use").description("Pin a workspace for subsequent commands").argument("<workspace-id>", "Workspace ID").action(async (workspaceId, _options, command) => {
27737
+ await runApiCommand(
27738
+ command,
27739
+ async ({ api, signal }) => {
27740
+ const workspaces = await api.workspaces.list({ signal });
27741
+ const match = workspaces.find((candidate) => candidate.id === workspaceId);
27742
+ if (!match) {
27743
+ throw new Error(
27744
+ `No workspace ${workspaceId} available to you. Run \`tda workspace list\` to see which are.`
27745
+ );
27746
+ }
27747
+ if (match.access !== "full") {
27748
+ throw new Error(
27749
+ `${match.name} is on a plan that does not cover Automations, so its automations cannot run. Run \`tda workspace list\` to see which are usable.`
27750
+ );
27751
+ }
27752
+ await setDefaultWorkspaceId(workspaceId);
27753
+ return { action: "selected", workspaceId, name: match.name };
27754
+ },
27755
+ { human: formatWorkspaceSelection },
27756
+ { spinner: "Checking workspace..." }
27757
+ );
27758
+ });
27759
+ workspace.command("current").description("Show which workspace commands act on").action(async (_options, command) => {
27760
+ await runLocalCommand(
27761
+ command,
27762
+ async () => ({ workspaceId: await getDefaultWorkspaceId() }),
27763
+ { human: formatCurrentWorkspace }
27764
+ );
27765
+ });
27766
+ workspace.command("clear").description("Unpin the workspace and go back to your personal space").action(async (_options, command) => {
27767
+ await runLocalCommand(
27768
+ command,
27769
+ async () => {
27770
+ await setDefaultWorkspaceId(null);
27771
+ return { action: "cleared", workspaceId: null };
27772
+ },
27773
+ { human: formatWorkspaceSelection }
27774
+ );
27775
+ });
27776
+ }
27777
+
27555
27778
  // src/lib/command-output.ts
27556
27779
  function configureCommandOutput(command) {
27557
27780
  return command.configureOutput({
@@ -27584,6 +27807,7 @@ function createProgram() {
27584
27807
  registerChatCommand(program);
27585
27808
  registerSkillCommand(program);
27586
27809
  registerUpdateCommand(program);
27810
+ registerWorkspaceCommand(program);
27587
27811
  return program;
27588
27812
  }
27589
27813
  function collect2(value, previous) {