skedyul 1.5.13 → 1.7.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.
@@ -410,6 +410,8 @@ var CRMPageSchemaZ = z3.object({
410
410
  icon: z3.string().optional(),
411
411
  modelHandle: z3.string(),
412
412
  parentPath: z3.string().optional(),
413
+ isDefault: z3.boolean().optional(),
414
+ visibility: z3.enum(["team", "private"]).optional(),
413
415
  baseQuery: z3.unknown().optional(),
414
416
  blocks: z3.array(CRMBlockSchemaZ).optional()
415
417
  });
@@ -1285,7 +1287,45 @@ var ModelDefinitionSchema = z8.object({
1285
1287
  addDefaultPages: z8.boolean().optional(),
1286
1288
  addNavigation: z8.boolean().optional(),
1287
1289
  /** Root path for developer resource UI (e.g., '/access_requests'). Links internal model to a provision.pages entry. */
1288
- page: z8.string().optional()
1290
+ page: z8.string().optional(),
1291
+ env: EnvSchemaSchema.optional()
1292
+ });
1293
+ var EntityFieldDefinitionSchema = z8.object({
1294
+ handle: z8.string(),
1295
+ label: z8.string(),
1296
+ description: z8.string().optional(),
1297
+ type: z8.enum([
1298
+ "string",
1299
+ "long_string",
1300
+ "number",
1301
+ "boolean",
1302
+ "date",
1303
+ "datetime",
1304
+ "object"
1305
+ ]).optional(),
1306
+ matchCandidate: z8.boolean().optional(),
1307
+ required: z8.boolean().optional(),
1308
+ options: z8.array(
1309
+ z8.union([
1310
+ z8.string(),
1311
+ z8.object({ value: z8.string(), label: z8.string() })
1312
+ ])
1313
+ ).optional()
1314
+ });
1315
+ var EntityRelationshipDefinitionSchema = z8.object({
1316
+ handle: z8.string(),
1317
+ label: z8.string(),
1318
+ targetEntity: z8.string().optional(),
1319
+ description: z8.string().optional()
1320
+ });
1321
+ var EntityDefinitionSchema = z8.object({
1322
+ handle: z8.string(),
1323
+ label: z8.string(),
1324
+ labelPlural: z8.string().optional(),
1325
+ description: z8.string().optional(),
1326
+ fields: z8.array(EntityFieldDefinitionSchema),
1327
+ contextFields: z8.array(EntityFieldDefinitionSchema).optional(),
1328
+ relationships: z8.array(EntityRelationshipDefinitionSchema).optional()
1289
1329
  });
1290
1330
  var RelationshipCardinalitySchema = z8.enum([
1291
1331
  "ONE_TO_ONE",
@@ -1855,6 +1895,8 @@ var ProvisionConfigSchema = z8.object({
1855
1895
  env: EnvSchemaSchema.optional(),
1856
1896
  /** INTERNAL model definitions (app-owned, not visible to users) */
1857
1897
  models: z8.array(ModelDefinitionSchema).optional(),
1898
+ /** App entity contracts for install-time CRM maps (not shared models) */
1899
+ entities: z8.array(EntityDefinitionSchema).optional(),
1858
1900
  /** Relationship definitions between INTERNAL models */
1859
1901
  relationships: z8.array(RelationshipDefinitionSchema).optional(),
1860
1902
  channels: z8.array(ChannelDefinitionSchema).optional(),
@@ -2097,6 +2139,56 @@ function getQueueConfigWithRetry(queueName) {
2097
2139
  return serializable;
2098
2140
  }
2099
2141
 
2142
+ // src/sequencer/config-loader.ts
2143
+ import * as fs2 from "fs";
2144
+ import * as path2 from "path";
2145
+ var cachedRuntimeSequencers = null;
2146
+ function getRuntimeConfigPath2() {
2147
+ if (process.env.LAMBDA_TASK_ROOT) {
2148
+ return path2.join(process.env.LAMBDA_TASK_ROOT, ".skedyul", "config.json");
2149
+ }
2150
+ return path2.join(process.cwd(), ".skedyul", "config.json");
2151
+ }
2152
+ function loadRuntimeConfigFile2() {
2153
+ const configPath = getRuntimeConfigPath2();
2154
+ if (!fs2.existsSync(configPath)) {
2155
+ return null;
2156
+ }
2157
+ try {
2158
+ const raw = fs2.readFileSync(configPath, "utf-8");
2159
+ return JSON.parse(raw);
2160
+ } catch {
2161
+ return null;
2162
+ }
2163
+ }
2164
+ var registeredServerConfig2 = null;
2165
+ function registerSequencerConfig(config) {
2166
+ registeredServerConfig2 = config;
2167
+ cachedRuntimeSequencers = config.sequencers ?? null;
2168
+ }
2169
+ function clearRegisteredSequencerConfig() {
2170
+ registeredServerConfig2 = null;
2171
+ cachedRuntimeSequencers = null;
2172
+ }
2173
+ function getSequencerDefinitions() {
2174
+ if (cachedRuntimeSequencers) {
2175
+ return cachedRuntimeSequencers;
2176
+ }
2177
+ if (registeredServerConfig2?.sequencers) {
2178
+ cachedRuntimeSequencers = registeredServerConfig2.sequencers;
2179
+ return cachedRuntimeSequencers;
2180
+ }
2181
+ const fileConfig = loadRuntimeConfigFile2();
2182
+ if (fileConfig?.sequencers) {
2183
+ cachedRuntimeSequencers = fileConfig.sequencers;
2184
+ return cachedRuntimeSequencers;
2185
+ }
2186
+ return {};
2187
+ }
2188
+ function getSequencerConfig(sequencerName) {
2189
+ return getSequencerDefinitions()[sequencerName];
2190
+ }
2191
+
2100
2192
  // src/server/utils/schema.ts
2101
2193
  import * as z9 from "zod";
2102
2194
  function normalizeBilling(billing) {
@@ -2503,25 +2595,28 @@ function createInstanceClient(config) {
2503
2595
  pagination: pagination ?? { page: 1, total: 0, hasMore: false, limit: args?.limit ?? 50 }
2504
2596
  };
2505
2597
  },
2506
- async get(modelHandle, id) {
2598
+ async get(modelHandle, id, options) {
2507
2599
  const { data } = await callCoreWithConfig("instance.get", {
2508
2600
  modelHandle,
2509
- id
2601
+ id,
2602
+ ...options?.includeEnv ? { includeEnv: true } : {}
2510
2603
  });
2511
2604
  return data;
2512
2605
  },
2513
- async create(modelHandle, data) {
2606
+ async create(modelHandle, data, options) {
2514
2607
  const { data: inst } = await callCoreWithConfig("instance.create", {
2515
2608
  modelHandle,
2516
- data
2609
+ data,
2610
+ ...options?.env ? { env: options.env } : {}
2517
2611
  });
2518
2612
  return inst;
2519
2613
  },
2520
- async update(modelHandle, id, data) {
2614
+ async update(modelHandle, id, data, options) {
2521
2615
  const { data: inst } = await callCoreWithConfig("instance.update", {
2522
2616
  modelHandle,
2523
2617
  id,
2524
- data
2618
+ data,
2619
+ ...options?.env ? { env: options.env } : {}
2525
2620
  });
2526
2621
  return inst;
2527
2622
  },
@@ -2620,10 +2715,11 @@ var instance = {
2620
2715
  * const record = await instance.get('phone_number', 'ins_abc123')
2621
2716
  * ```
2622
2717
  */
2623
- async get(modelHandle, id) {
2718
+ async get(modelHandle, id, options) {
2624
2719
  const { data } = await callCore("instance.get", {
2625
2720
  modelHandle,
2626
- id
2721
+ id,
2722
+ ...options?.includeEnv ? { includeEnv: true } : {}
2627
2723
  });
2628
2724
  return data;
2629
2725
  },
@@ -2640,10 +2736,11 @@ var instance = {
2640
2736
  * })
2641
2737
  * ```
2642
2738
  */
2643
- async create(modelHandle, data) {
2739
+ async create(modelHandle, data, options) {
2644
2740
  const { data: instance2 } = await callCore("instance.create", {
2645
2741
  modelHandle,
2646
- data
2742
+ data,
2743
+ ...options?.env ? { env: options.env } : {}
2647
2744
  });
2648
2745
  return instance2;
2649
2746
  },
@@ -2660,11 +2757,12 @@ var instance = {
2660
2757
  * })
2661
2758
  * ```
2662
2759
  */
2663
- async update(modelHandle, id, data) {
2760
+ async update(modelHandle, id, data, options) {
2664
2761
  const { data: instance2 } = await callCore("instance.update", {
2665
2762
  modelHandle,
2666
2763
  id,
2667
- data
2764
+ data,
2765
+ ...options?.env ? { env: options.env } : {}
2668
2766
  });
2669
2767
  return instance2;
2670
2768
  },
@@ -4031,7 +4129,7 @@ function printStartupLog(config, tools, port) {
4031
4129
 
4032
4130
  // src/server/route-handlers/adapters.ts
4033
4131
  function fromLambdaEvent(event2) {
4034
- const path5 = event2.path || event2.rawPath || "/";
4132
+ const path6 = event2.path || event2.rawPath || "/";
4035
4133
  const method = event2.httpMethod || event2.requestContext?.http?.method || "POST";
4036
4134
  const forwardedProto = event2.headers?.["x-forwarded-proto"] ?? event2.headers?.["X-Forwarded-Proto"];
4037
4135
  const protocol = forwardedProto ?? "https";
@@ -4039,9 +4137,9 @@ function fromLambdaEvent(event2) {
4039
4137
  const queryString = event2.queryStringParameters ? "?" + new URLSearchParams(
4040
4138
  event2.queryStringParameters
4041
4139
  ).toString() : "";
4042
- const url = `${protocol}://${host}${path5}${queryString}`;
4140
+ const url = `${protocol}://${host}${path6}${queryString}`;
4043
4141
  return {
4044
- path: path5,
4142
+ path: path6,
4045
4143
  method,
4046
4144
  headers: event2.headers,
4047
4145
  query: event2.queryStringParameters ?? {},
@@ -4123,8 +4221,8 @@ function parseBodyByContentType(req) {
4123
4221
  }
4124
4222
 
4125
4223
  // src/server/route-handlers/handlers.ts
4126
- import * as fs2 from "fs";
4127
- import * as path2 from "path";
4224
+ import * as fs3 from "fs";
4225
+ import * as path3 from "path";
4128
4226
 
4129
4227
  // src/server/core-api-handler.ts
4130
4228
  async function handleCoreMethod(method, params) {
@@ -4592,7 +4690,7 @@ async function handleOAuthCallback(parsedBody, hooks) {
4592
4690
  }
4593
4691
 
4594
4692
  // src/server/handlers/webhook-handler.ts
4595
- function parseWebhookRequest(parsedBody, method, url, path5, headers, query, rawBody, appIdHeader, appVersionIdHeader) {
4693
+ function parseWebhookRequest(parsedBody, method, url, path6, headers, query, rawBody, appIdHeader, appVersionIdHeader) {
4596
4694
  const isEnvelope = typeof parsedBody === "object" && parsedBody !== null && "env" in parsedBody && "request" in parsedBody && "context" in parsedBody;
4597
4695
  if (isEnvelope) {
4598
4696
  const envelope = parsedBody;
@@ -4646,7 +4744,7 @@ function parseWebhookRequest(parsedBody, method, url, path5, headers, query, raw
4646
4744
  const webhookRequest = {
4647
4745
  method,
4648
4746
  url,
4649
- path: path5,
4747
+ path: path6,
4650
4748
  headers,
4651
4749
  query,
4652
4750
  body: parsedBody,
@@ -4675,9 +4773,17 @@ async function executeWebhookHandler(handle, webhookRegistry, data) {
4675
4773
  };
4676
4774
  let webhookResponse;
4677
4775
  try {
4776
+ const rateLimitContext = {
4777
+ app: data.webhookContext.app,
4778
+ appInstallationId: isRuntimeWebhookContext(data.webhookContext) ? data.webhookContext.appInstallationId : void 0,
4779
+ invocation: data.invocation,
4780
+ isProvisionContext: !isRuntimeWebhookContext(data.webhookContext)
4781
+ };
4678
4782
  webhookResponse = await runWithLogContext({ invocation: data.invocation }, async () => {
4679
4783
  return await runWithConfig(requestConfig, async () => {
4680
- return await webhookDef.handler(data.webhookRequest, data.webhookContext);
4784
+ return await runWithRateLimitExecutionContext(rateLimitContext, async () => {
4785
+ return await webhookDef.handler(data.webhookRequest, data.webhookContext);
4786
+ });
4681
4787
  });
4682
4788
  });
4683
4789
  } catch (err) {
@@ -4704,7 +4810,7 @@ function isMethodAllowed(webhookRegistry, handle, method) {
4704
4810
 
4705
4811
  // src/server/route-handlers/handlers.ts
4706
4812
  function getConfigFilePath() {
4707
- return process.env.LAMBDA_TASK_ROOT ? path2.join(process.env.LAMBDA_TASK_ROOT, ".skedyul", "config.json") : ".skedyul/config.json";
4813
+ return process.env.LAMBDA_TASK_ROOT ? path3.join(process.env.LAMBDA_TASK_ROOT, ".skedyul", "config.json") : ".skedyul/config.json";
4708
4814
  }
4709
4815
  function findToolInRegistry(registry, toolName) {
4710
4816
  for (const [key, t] of Object.entries(registry)) {
@@ -4743,8 +4849,8 @@ function handleHealthRoute(ctx) {
4743
4849
  function handleConfigRoute(ctx) {
4744
4850
  const configFilePath = getConfigFilePath();
4745
4851
  try {
4746
- if (fs2.existsSync(configFilePath)) {
4747
- const fileConfig = JSON.parse(fs2.readFileSync(configFilePath, "utf-8"));
4852
+ if (fs3.existsSync(configFilePath)) {
4853
+ const fileConfig = JSON.parse(fs3.readFileSync(configFilePath, "utf-8"));
4748
4854
  return { status: 200, body: fileConfig };
4749
4855
  }
4750
4856
  } catch (err) {
@@ -5432,6 +5538,7 @@ installContextLogger();
5432
5538
  function createSkedyulServer(config) {
5433
5539
  mergeRuntimeEnv();
5434
5540
  registerQueueConfig(config);
5541
+ registerSequencerConfig(config);
5435
5542
  const registry = config.tools;
5436
5543
  const webhookRegistry = config.webhooks;
5437
5544
  if (config.coreApi?.service) {
@@ -5680,6 +5787,9 @@ function defineConfig(config) {
5680
5787
  function defineModel(model) {
5681
5788
  return model;
5682
5789
  }
5790
+ function defineEntity(entity) {
5791
+ return entity;
5792
+ }
5683
5793
  function defineChannel(channel) {
5684
5794
  return channel;
5685
5795
  }
@@ -5700,13 +5810,13 @@ function defineNavigation(navigation) {
5700
5810
  }
5701
5811
 
5702
5812
  // src/config/schema-loader.ts
5703
- import * as fs3 from "fs";
5704
- import * as path3 from "path";
5813
+ import * as fs4 from "fs";
5814
+ import * as path4 from "path";
5705
5815
  import * as os from "os";
5706
5816
 
5707
5817
  // src/config/resolver.ts
5708
- import * as fs4 from "fs";
5709
- import * as path4 from "path";
5818
+ import * as fs5 from "fs";
5819
+ import * as path5 from "path";
5710
5820
 
5711
5821
  // src/config/utils.ts
5712
5822
  function getAllEnvKeys(config) {
@@ -5809,7 +5919,7 @@ function getApiToken() {
5809
5919
  const config = getConfig();
5810
5920
  return config.apiToken || process.env.SKEDYUL_API_TOKEN || "";
5811
5921
  }
5812
- async function callRateLimitApi(path5, body) {
5922
+ async function callRateLimitApi(path6, body) {
5813
5923
  const baseUrl = getApiBaseUrl();
5814
5924
  const token2 = getApiToken();
5815
5925
  if (!baseUrl || !token2) {
@@ -5817,7 +5927,7 @@ async function callRateLimitApi(path5, body) {
5817
5927
  "SKEDYUL_API_URL and SKEDYUL_API_TOKEN are required for platform queue coordination"
5818
5928
  );
5819
5929
  }
5820
- const response = await fetch(`${baseUrl}/api/internal/ratelimit/${path5}`, {
5930
+ const response = await fetch(`${baseUrl}/api/internal/ratelimit/${path6}`, {
5821
5931
  method: "POST",
5822
5932
  headers: {
5823
5933
  Authorization: `Bearer ${token2}`,
@@ -5827,11 +5937,11 @@ async function callRateLimitApi(path5, body) {
5827
5937
  });
5828
5938
  const payload = await response.json().catch(() => null);
5829
5939
  if (!response.ok || payload?.success === false) {
5830
- const message = payload?.errors?.[0]?.message ?? `Queue coordination API ${path5} failed with status ${response.status}`;
5940
+ const message = payload?.errors?.[0]?.message ?? `Queue coordination API ${path6} failed with status ${response.status}`;
5831
5941
  throw new RateLimitBackendError(message, response.status);
5832
5942
  }
5833
5943
  if (!payload?.data) {
5834
- throw new RateLimitBackendError(`Queue coordination API ${path5} returned empty data`);
5944
+ throw new RateLimitBackendError(`Queue coordination API ${path6} returned empty data`);
5835
5945
  }
5836
5946
  return payload.data;
5837
5947
  }
@@ -6286,6 +6396,406 @@ async function queuedFetchResponse(queueInput, url, init) {
6286
6396
  return queuedFetch(queueInput, () => fetch(url, init));
6287
6397
  }
6288
6398
 
6399
+ // src/sequencer/errors.ts
6400
+ var SequencerContextError = class extends Error {
6401
+ constructor(message) {
6402
+ super(message);
6403
+ this.code = "SEQUENCER_CONTEXT_ERROR";
6404
+ this.name = "SequencerContextError";
6405
+ }
6406
+ };
6407
+ var SequencerNotFoundError = class extends Error {
6408
+ constructor(name) {
6409
+ super(`Sequencer "${name}" is not defined in skedyul.config`);
6410
+ this.code = "SEQUENCER_NOT_FOUND";
6411
+ this.name = "SequencerNotFoundError";
6412
+ }
6413
+ };
6414
+ var SequencerBackendError = class extends Error {
6415
+ constructor(message, statusCode) {
6416
+ super(message);
6417
+ this.code = "SEQUENCER_BACKEND_ERROR";
6418
+ this.name = "SequencerBackendError";
6419
+ this.statusCode = statusCode;
6420
+ }
6421
+ };
6422
+ var SequencerLockError = class extends Error {
6423
+ constructor(message) {
6424
+ super(message);
6425
+ this.code = "SEQUENCER_LOCK_ERROR";
6426
+ this.name = "SequencerLockError";
6427
+ }
6428
+ };
6429
+
6430
+ // src/sequencer/resolve-sequencer-key.ts
6431
+ function resolveEndpointHandle2(config, invocation) {
6432
+ if (config.endpoint) {
6433
+ return config.endpoint;
6434
+ }
6435
+ if (invocation?.toolHandle) {
6436
+ return invocation.toolHandle;
6437
+ }
6438
+ if (invocation?.serverHookHandle) {
6439
+ return invocation.serverHookHandle;
6440
+ }
6441
+ throw new SequencerContextError(
6442
+ `Cannot resolve endpoint handle for sequencer scope "${config.scope}". Set endpoint in sequencer config or ensure invocation.toolHandle/serverHookHandle is available.`
6443
+ );
6444
+ }
6445
+ function appendSubKey2(base, subKey) {
6446
+ return subKey ? `${base}:${subKey}` : base;
6447
+ }
6448
+ function resolveSequencerKey(sequencerName, config, ctx, subKey) {
6449
+ const { app, appInstallationId, invocation, isProvisionContext: isProvisionContext2 } = ctx;
6450
+ switch (config.scope) {
6451
+ case "provision": {
6452
+ const base = `seq:pv:${app.versionId}:${sequencerName}`;
6453
+ return appendSubKey2(base, subKey);
6454
+ }
6455
+ case "install": {
6456
+ if (!appInstallationId) {
6457
+ throw new SequencerContextError(
6458
+ `Sequencer "${sequencerName}" with scope "install" requires appInstallationId in context`
6459
+ );
6460
+ }
6461
+ const base = `seq:in:${appInstallationId}:${sequencerName}`;
6462
+ return appendSubKey2(base, subKey);
6463
+ }
6464
+ case "provision_endpoint": {
6465
+ if (!isProvisionContext2 && appInstallationId) {
6466
+ throw new SequencerContextError(
6467
+ `Sequencer "${sequencerName}" with scope "provision_endpoint" requires provision context (no install)`
6468
+ );
6469
+ }
6470
+ const endpointHandle = resolveEndpointHandle2(config, invocation);
6471
+ const base = `seq:pep:${app.versionId}:${endpointHandle}:${sequencerName}`;
6472
+ return appendSubKey2(base, subKey);
6473
+ }
6474
+ case "install_endpoint": {
6475
+ if (!appInstallationId) {
6476
+ throw new SequencerContextError(
6477
+ `Sequencer "${sequencerName}" with scope "install_endpoint" requires appInstallationId in context`
6478
+ );
6479
+ }
6480
+ const endpointHandle = resolveEndpointHandle2(config, invocation);
6481
+ const base = `seq:iep:${appInstallationId}:${endpointHandle}:${sequencerName}`;
6482
+ return appendSubKey2(base, subKey);
6483
+ }
6484
+ case "global": {
6485
+ const base = `seq:gl:${app.versionId}:${sequencerName}`;
6486
+ return appendSubKey2(base, subKey);
6487
+ }
6488
+ default: {
6489
+ const _exhaustive = config.scope;
6490
+ throw new SequencerContextError(`Unknown sequencer scope: ${String(_exhaustive)}`);
6491
+ }
6492
+ }
6493
+ }
6494
+
6495
+ // src/sequencer/backends/platform.ts
6496
+ function getApiBaseUrl2() {
6497
+ const config = getConfig();
6498
+ if (config.baseUrl) {
6499
+ return config.baseUrl.replace(/\/+$/, "");
6500
+ }
6501
+ return (process.env.SKEDYUL_API_URL ?? process.env.SKEDYUL_NODE_URL ?? "").replace(/\/+$/, "");
6502
+ }
6503
+ function getApiToken2() {
6504
+ const config = getConfig();
6505
+ return config.apiToken || process.env.SKEDYUL_API_TOKEN || "";
6506
+ }
6507
+ async function callSequencerApi(path6, body) {
6508
+ const baseUrl = getApiBaseUrl2();
6509
+ const token2 = getApiToken2();
6510
+ if (!baseUrl || !token2) {
6511
+ throw new SequencerBackendError(
6512
+ "SKEDYUL_API_URL and SKEDYUL_API_TOKEN are required for platform sequencer coordination"
6513
+ );
6514
+ }
6515
+ const response = await fetch(`${baseUrl}/api/internal/sequencer/${path6}`, {
6516
+ method: "POST",
6517
+ headers: {
6518
+ Authorization: `Bearer ${token2}`,
6519
+ "Content-Type": "application/json"
6520
+ },
6521
+ body: JSON.stringify(body)
6522
+ });
6523
+ const payload = await response.json().catch(() => null);
6524
+ if (!response.ok || payload?.success === false) {
6525
+ const message = payload?.errors?.[0]?.message ?? `Sequencer coordination API ${path6} failed with status ${response.status}`;
6526
+ throw new SequencerBackendError(message, response.status);
6527
+ }
6528
+ if (!payload?.data) {
6529
+ throw new SequencerBackendError(`Sequencer coordination API ${path6} returned empty data`);
6530
+ }
6531
+ return payload.data;
6532
+ }
6533
+ var PlatformSequencerBackend = class {
6534
+ async allow(sequencerKey, input) {
6535
+ return callSequencerApi("allow", {
6536
+ sequencerKey,
6537
+ timestamp: input.timestamp,
6538
+ leaseId: input.leaseId,
6539
+ watermarkTtlMs: input.watermarkTtlMs
6540
+ });
6541
+ }
6542
+ async acquire(sequencerKey, input) {
6543
+ return callSequencerApi("acquire", {
6544
+ sequencerKey,
6545
+ leaseId: input.leaseId,
6546
+ timestamp: input.timestamp,
6547
+ lockTtlMs: input.lockTtlMs,
6548
+ watermarkTtlMs: input.watermarkTtlMs
6549
+ });
6550
+ }
6551
+ async release(sequencerKey, leaseId) {
6552
+ await callSequencerApi("release", {
6553
+ sequencerKey,
6554
+ leaseId
6555
+ });
6556
+ }
6557
+ };
6558
+ var platformSequencerBackend = new PlatformSequencerBackend();
6559
+
6560
+ // src/sequencer/backends/memory.ts
6561
+ function generateLeaseId2() {
6562
+ return `mem_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
6563
+ }
6564
+ var MemorySequencerBackend = class {
6565
+ constructor() {
6566
+ this.state = /* @__PURE__ */ new Map();
6567
+ }
6568
+ getState(sequencerKey) {
6569
+ let state = this.state.get(sequencerKey);
6570
+ if (!state) {
6571
+ state = { watermark: 0, lockLeaseId: null, lockExpiresAt: 0 };
6572
+ this.state.set(sequencerKey, state);
6573
+ }
6574
+ if (state.lockLeaseId && state.lockExpiresAt <= Date.now()) {
6575
+ state.lockLeaseId = null;
6576
+ state.lockExpiresAt = 0;
6577
+ }
6578
+ return state;
6579
+ }
6580
+ async allow(sequencerKey, input) {
6581
+ void input.watermarkTtlMs;
6582
+ const state = this.getState(sequencerKey);
6583
+ if (input.timestamp < state.watermark) {
6584
+ return { allowed: false, reason: "stale", watermark: state.watermark };
6585
+ }
6586
+ if (state.lockLeaseId && input.leaseId && state.lockLeaseId !== input.leaseId) {
6587
+ return { allowed: false, reason: "locked", watermark: state.watermark };
6588
+ }
6589
+ state.watermark = Math.max(state.watermark, input.timestamp);
6590
+ return { allowed: true, reason: "ok", watermark: state.watermark };
6591
+ }
6592
+ async acquire(sequencerKey, input) {
6593
+ void input.watermarkTtlMs;
6594
+ const state = this.getState(sequencerKey);
6595
+ const leaseId = input.leaseId || generateLeaseId2();
6596
+ if (state.lockLeaseId && state.lockLeaseId !== leaseId) {
6597
+ return { acquired: false, reason: "locked" };
6598
+ }
6599
+ state.lockLeaseId = leaseId;
6600
+ state.lockExpiresAt = Date.now() + (input.lockTtlMs ?? 6e4);
6601
+ if (input.timestamp !== void 0) {
6602
+ state.watermark = Math.max(state.watermark, input.timestamp);
6603
+ }
6604
+ return { acquired: true, reason: "ok" };
6605
+ }
6606
+ async release(sequencerKey, leaseId) {
6607
+ const state = this.state.get(sequencerKey);
6608
+ if (!state || state.lockLeaseId !== leaseId) {
6609
+ return;
6610
+ }
6611
+ state.lockLeaseId = null;
6612
+ state.lockExpiresAt = 0;
6613
+ }
6614
+ resetForTests() {
6615
+ this.state.clear();
6616
+ }
6617
+ };
6618
+ var memorySequencerBackend = new MemorySequencerBackend();
6619
+
6620
+ // src/sequencer/backends/index.ts
6621
+ var cachedBackend2 = null;
6622
+ function shouldForceMemoryBackend2() {
6623
+ const flag = process.env.SKEDYUL_SEQUENCER_MEMORY;
6624
+ return flag === "1" || flag === "true";
6625
+ }
6626
+ function getSequencerBackend() {
6627
+ if (cachedBackend2) {
6628
+ return cachedBackend2;
6629
+ }
6630
+ if (shouldForceMemoryBackend2()) {
6631
+ cachedBackend2 = memorySequencerBackend;
6632
+ return cachedBackend2;
6633
+ }
6634
+ cachedBackend2 = createResilientBackend2();
6635
+ return cachedBackend2;
6636
+ }
6637
+ function createResilientBackend2() {
6638
+ return {
6639
+ async allow(sequencerKey, input) {
6640
+ try {
6641
+ return await platformSequencerBackend.allow(sequencerKey, input);
6642
+ } catch (error) {
6643
+ if (shouldFallbackToMemory2(error)) {
6644
+ return memorySequencerBackend.allow(sequencerKey, input);
6645
+ }
6646
+ throw error;
6647
+ }
6648
+ },
6649
+ async acquire(sequencerKey, input) {
6650
+ try {
6651
+ return await platformSequencerBackend.acquire(sequencerKey, input);
6652
+ } catch (error) {
6653
+ if (shouldFallbackToMemory2(error)) {
6654
+ return memorySequencerBackend.acquire(sequencerKey, input);
6655
+ }
6656
+ throw error;
6657
+ }
6658
+ },
6659
+ async release(sequencerKey, leaseId) {
6660
+ if (leaseId.startsWith("mem_")) {
6661
+ return memorySequencerBackend.release(sequencerKey, leaseId);
6662
+ }
6663
+ try {
6664
+ await platformSequencerBackend.release(sequencerKey, leaseId);
6665
+ } catch (error) {
6666
+ if (shouldFallbackToMemory2(error)) {
6667
+ return memorySequencerBackend.release(sequencerKey, leaseId);
6668
+ }
6669
+ throw error;
6670
+ }
6671
+ }
6672
+ };
6673
+ }
6674
+ function shouldFallbackToMemory2(error) {
6675
+ if (shouldForceMemoryBackend2()) {
6676
+ return true;
6677
+ }
6678
+ if (error instanceof SequencerBackendError) {
6679
+ if (error.statusCode === 404 || error.statusCode === 503) {
6680
+ return true;
6681
+ }
6682
+ }
6683
+ if (error instanceof TypeError) {
6684
+ return true;
6685
+ }
6686
+ const nodeEnv = process.env.NODE_ENV;
6687
+ return nodeEnv === "development" || nodeEnv === "test";
6688
+ }
6689
+ function resetSequencerBackendForTests() {
6690
+ cachedBackend2 = null;
6691
+ }
6692
+
6693
+ // src/sequencer/index.ts
6694
+ function normalizeSequencerInput(input) {
6695
+ if (typeof input === "string") {
6696
+ return { name: input };
6697
+ }
6698
+ return { name: input.sequencer, subKey: input.key };
6699
+ }
6700
+ function mergeContext(ctxOverride) {
6701
+ const ctx = getRateLimitExecutionContext();
6702
+ if (!ctx?.app?.versionId) {
6703
+ throw new SequencerContextError(
6704
+ "Cannot resolve sequencer without app context. Ensure sequencer runs inside a tool/hook/webhook handler."
6705
+ );
6706
+ }
6707
+ return { ...ctx, ...ctxOverride };
6708
+ }
6709
+ function resolveSequencer(sequencerInput, ctxOverride) {
6710
+ const ctx = mergeContext(ctxOverride);
6711
+ const { name, subKey } = normalizeSequencerInput(sequencerInput);
6712
+ const config = getSequencerConfig(name);
6713
+ if (!config) {
6714
+ throw new SequencerNotFoundError(name);
6715
+ }
6716
+ const sequencerKey = resolveSequencerKey(name, config, ctx, subKey);
6717
+ return { name, sequencerKey, config };
6718
+ }
6719
+ function createSequencerHandle(sequencerInput, ctxOverride) {
6720
+ return {
6721
+ allow(input) {
6722
+ return allow(sequencerInput, input, ctxOverride);
6723
+ },
6724
+ acquire(input) {
6725
+ return acquire(sequencerInput, input, ctxOverride);
6726
+ },
6727
+ release(input) {
6728
+ return release(sequencerInput, input, ctxOverride);
6729
+ }
6730
+ };
6731
+ }
6732
+ function disabledAllowResult() {
6733
+ return { allowed: true, reason: "disabled" };
6734
+ }
6735
+ async function allow(sequencerInput, input, ctxOverride) {
6736
+ const { name, subKey } = normalizeSequencerInput(sequencerInput);
6737
+ const config = getSequencerConfig(name);
6738
+ if (!config || config.enabled !== true) {
6739
+ return disabledAllowResult();
6740
+ }
6741
+ const resolvedSubKey = input.key ?? subKey;
6742
+ const resolved = resolveSequencer(
6743
+ { sequencer: name, key: resolvedSubKey },
6744
+ ctxOverride
6745
+ );
6746
+ const backend = getSequencerBackend();
6747
+ return backend.allow(resolved.sequencerKey, {
6748
+ timestamp: input.timestamp,
6749
+ leaseId: input.leaseId,
6750
+ watermarkTtlMs: resolved.config.watermarkTtlMs
6751
+ });
6752
+ }
6753
+ async function acquire(sequencerInput, input, ctxOverride) {
6754
+ const { name, subKey } = normalizeSequencerInput(sequencerInput);
6755
+ const config = getSequencerConfig(name);
6756
+ if (!config || config.enabled !== true) {
6757
+ return;
6758
+ }
6759
+ const resolvedSubKey = input.key ?? subKey;
6760
+ const resolved = resolveSequencer(
6761
+ { sequencer: name, key: resolvedSubKey },
6762
+ ctxOverride
6763
+ );
6764
+ const backend = getSequencerBackend();
6765
+ const result = await backend.acquire(resolved.sequencerKey, {
6766
+ leaseId: input.leaseId,
6767
+ timestamp: input.timestamp,
6768
+ lockTtlMs: input.lockTtlMs ?? resolved.config.lockTtlMs,
6769
+ watermarkTtlMs: resolved.config.watermarkTtlMs
6770
+ });
6771
+ if (!result.acquired) {
6772
+ throw new SequencerLockError(
6773
+ `Sequencer "${name}" lock held by another lease (${result.reason})`
6774
+ );
6775
+ }
6776
+ }
6777
+ async function release(sequencerInput, input, ctxOverride) {
6778
+ const { name, subKey } = normalizeSequencerInput(sequencerInput);
6779
+ const config = getSequencerConfig(name);
6780
+ if (!config || config.enabled !== true) {
6781
+ return;
6782
+ }
6783
+ const resolvedSubKey = input.key ?? subKey;
6784
+ const resolved = resolveSequencer(
6785
+ { sequencer: name, key: resolvedSubKey },
6786
+ ctxOverride
6787
+ );
6788
+ const backend = getSequencerBackend();
6789
+ await backend.release(resolved.sequencerKey, input.leaseId);
6790
+ }
6791
+ var sequencer = {
6792
+ allow,
6793
+ acquire,
6794
+ release,
6795
+ resolve: resolveSequencer,
6796
+ createHandle: createSequencerHandle
6797
+ };
6798
+
6289
6799
  // src/triggers/types.ts
6290
6800
  import { z as z12 } from "zod/v4";
6291
6801
  var CRMDataSchema = z12.object({
@@ -6390,16 +6900,16 @@ function evaluateTemplate(template, context) {
6390
6900
  const templateRegex = /\{\{\s*([^}]+)\s*\}\}/g;
6391
6901
  const singleMatch = template.match(/^\{\{\s*([^}]+)\s*\}\}$/);
6392
6902
  if (singleMatch) {
6393
- const path5 = singleMatch[1].trim();
6394
- return resolvePath(context, path5);
6903
+ const path6 = singleMatch[1].trim();
6904
+ return resolvePath(context, path6);
6395
6905
  }
6396
- return template.replace(templateRegex, (_, path5) => {
6397
- const value = resolvePath(context, path5.trim());
6906
+ return template.replace(templateRegex, (_, path6) => {
6907
+ const value = resolvePath(context, path6.trim());
6398
6908
  return value === void 0 || value === null ? "" : String(value);
6399
6909
  });
6400
6910
  }
6401
- function resolvePath(obj, path5) {
6402
- const parts = path5.split(".");
6911
+ function resolvePath(obj, path6) {
6912
+ const parts = path6.split(".");
6403
6913
  let current = obj;
6404
6914
  for (const part of parts) {
6405
6915
  if (current === null || current === void 0) {
@@ -6420,14 +6930,14 @@ function evaluateCondition(condition, context) {
6420
6930
  const expression = match[1].trim();
6421
6931
  const eqMatch = expression.match(/^(.+?)\s*==\s*['"](.+)['"]$/);
6422
6932
  if (eqMatch) {
6423
- const [, path5, expectedValue] = eqMatch;
6424
- const actualValue = resolvePath(context, path5.trim());
6933
+ const [, path6, expectedValue] = eqMatch;
6934
+ const actualValue = resolvePath(context, path6.trim());
6425
6935
  return actualValue === expectedValue;
6426
6936
  }
6427
6937
  const neqMatch = expression.match(/^(.+?)\s*!=\s*['"](.+)['"]$/);
6428
6938
  if (neqMatch) {
6429
- const [, path5, expectedValue] = neqMatch;
6430
- const actualValue = resolvePath(context, path5.trim());
6939
+ const [, path6, expectedValue] = neqMatch;
6940
+ const actualValue = resolvePath(context, path6.trim());
6431
6941
  return actualValue !== expectedValue;
6432
6942
  }
6433
6943
  const value = resolvePath(context, expression);
@@ -7762,6 +8272,9 @@ export {
7762
8272
  DatePickerComponentDefinitionSchema,
7763
8273
  DayOfWeekSchema,
7764
8274
  EmptyFormComponentDefinitionSchema,
8275
+ EntityDefinitionSchema,
8276
+ EntityFieldDefinitionSchema,
8277
+ EntityRelationshipDefinitionSchema,
7765
8278
  EnvSchemaSchema,
7766
8279
  EnvVariableDefinitionSchema,
7767
8280
  EnvVisibilitySchema,
@@ -7879,6 +8392,10 @@ export {
7879
8392
  SelectComponentDefinitionSchema,
7880
8393
  SemanticMemoryConfigSchema2 as SemanticMemoryConfigSchema,
7881
8394
  SenderContextSchema,
8395
+ SequencerBackendError,
8396
+ SequencerContextError,
8397
+ SequencerLockError,
8398
+ SequencerNotFoundError,
7882
8399
  SkedyulConfigSchema,
7883
8400
  SkillExampleSchema,
7884
8401
  SkillMetadataSchema,
@@ -7934,6 +8451,7 @@ export {
7934
8451
  buildAgentContext,
7935
8452
  calculateWaitTime,
7936
8453
  call,
8454
+ clearRegisteredSequencerConfig,
7937
8455
  communicationChannel,
7938
8456
  compileAgent,
7939
8457
  compileWorkflow,
@@ -7953,6 +8471,7 @@ export {
7953
8471
  createPermissionError,
7954
8472
  createQueueHandle,
7955
8473
  createRateLimitError,
8474
+ createSequencerHandle,
7956
8475
  createServerHookContext,
7957
8476
  createSuccessResponse,
7958
8477
  createTimeoutError,
@@ -7966,6 +8485,7 @@ export {
7966
8485
  defineAgent,
7967
8486
  defineChannel,
7968
8487
  defineConfig,
8488
+ defineEntity,
7969
8489
  defineEnv,
7970
8490
  defineModel,
7971
8491
  defineNavigation,
@@ -7991,6 +8511,8 @@ export {
7991
8511
  getRateLimitExecutionContext,
7992
8512
  getRequiredInstallEnvKeys,
7993
8513
  getRetryDelay,
8514
+ getSequencerConfig,
8515
+ getSequencerDefinitions,
7994
8516
  instance,
7995
8517
  isChannelDependency,
7996
8518
  isCronContext,
@@ -8006,20 +8528,29 @@ export {
8006
8528
  isTimeInWindowSlot2 as isTimeInWindowSlot,
8007
8529
  isWorkflowDependency,
8008
8530
  matchesTrigger,
8531
+ memorySequencerBackend,
8009
8532
  parseCRMSchema,
8010
8533
  parseEstimationFromBilling,
8534
+ platformSequencerBackend,
8011
8535
  queuedFetch,
8012
8536
  queuedFetchResponse,
8013
8537
  registerQueueConfig,
8538
+ registerSequencerConfig,
8014
8539
  report,
8015
8540
  requeue,
8541
+ resetSequencerBackendForTests,
8016
8542
  resolveInputMappings,
8017
8543
  resolveQueue,
8544
+ resolveSequencer,
8018
8545
  resource,
8019
8546
  runWithConfig,
8020
8547
  runWithRateLimitExecutionContext,
8021
8548
  safeParseCRMSchema,
8022
8549
  safeParseConfig,
8550
+ sequencer,
8551
+ acquire as sequencerAcquire,
8552
+ allow as sequencerAllow,
8553
+ release as sequencerRelease,
8023
8554
  server,
8024
8555
  toGsm7,
8025
8556
  token,