skedyul 1.5.13 → 1.6.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
@@ -199,6 +199,10 @@ __export(index_exports, {
199
199
  SelectComponentDefinitionSchema: () => SelectComponentDefinitionSchema,
200
200
  SemanticMemoryConfigSchema: () => SemanticMemoryConfigSchema2,
201
201
  SenderContextSchema: () => SenderContextSchema,
202
+ SequencerBackendError: () => SequencerBackendError,
203
+ SequencerContextError: () => SequencerContextError,
204
+ SequencerLockError: () => SequencerLockError,
205
+ SequencerNotFoundError: () => SequencerNotFoundError,
202
206
  SkedyulConfigSchema: () => SkedyulConfigSchema,
203
207
  SkillExampleSchema: () => SkillExampleSchema,
204
208
  SkillMetadataSchema: () => SkillMetadataSchema,
@@ -254,6 +258,7 @@ __export(index_exports, {
254
258
  buildAgentContext: () => buildAgentContext,
255
259
  calculateWaitTime: () => calculateWaitTime,
256
260
  call: () => call,
261
+ clearRegisteredSequencerConfig: () => clearRegisteredSequencerConfig,
257
262
  communicationChannel: () => communicationChannel,
258
263
  compileAgent: () => compileAgent,
259
264
  compileWorkflow: () => compileWorkflow,
@@ -273,6 +278,7 @@ __export(index_exports, {
273
278
  createPermissionError: () => createPermissionError,
274
279
  createQueueHandle: () => createQueueHandle,
275
280
  createRateLimitError: () => createRateLimitError,
281
+ createSequencerHandle: () => createSequencerHandle,
276
282
  createServerHookContext: () => createServerHookContext,
277
283
  createSuccessResponse: () => createSuccessResponse,
278
284
  createTimeoutError: () => createTimeoutError,
@@ -311,6 +317,8 @@ __export(index_exports, {
311
317
  getRateLimitExecutionContext: () => getRateLimitExecutionContext,
312
318
  getRequiredInstallEnvKeys: () => getRequiredInstallEnvKeys,
313
319
  getRetryDelay: () => getRetryDelay,
320
+ getSequencerConfig: () => getSequencerConfig,
321
+ getSequencerDefinitions: () => getSequencerDefinitions,
314
322
  instance: () => instance,
315
323
  isChannelDependency: () => isChannelDependency,
316
324
  isCronContext: () => isCronContext,
@@ -326,20 +334,29 @@ __export(index_exports, {
326
334
  isTimeInWindowSlot: () => isTimeInWindowSlot2,
327
335
  isWorkflowDependency: () => isWorkflowDependency,
328
336
  matchesTrigger: () => matchesTrigger,
337
+ memorySequencerBackend: () => memorySequencerBackend,
329
338
  parseCRMSchema: () => parseCRMSchema,
330
339
  parseEstimationFromBilling: () => parseEstimationFromBilling,
340
+ platformSequencerBackend: () => platformSequencerBackend,
331
341
  queuedFetch: () => queuedFetch,
332
342
  queuedFetchResponse: () => queuedFetchResponse,
333
343
  registerQueueConfig: () => registerQueueConfig,
344
+ registerSequencerConfig: () => registerSequencerConfig,
334
345
  report: () => report,
335
346
  requeue: () => requeue,
347
+ resetSequencerBackendForTests: () => resetSequencerBackendForTests,
336
348
  resolveInputMappings: () => resolveInputMappings,
337
349
  resolveQueue: () => resolveQueue,
350
+ resolveSequencer: () => resolveSequencer,
338
351
  resource: () => resource,
339
352
  runWithConfig: () => runWithConfig,
340
353
  runWithRateLimitExecutionContext: () => runWithRateLimitExecutionContext,
341
354
  safeParseCRMSchema: () => safeParseCRMSchema,
342
355
  safeParseConfig: () => safeParseConfig,
356
+ sequencer: () => sequencer,
357
+ sequencerAcquire: () => acquire,
358
+ sequencerAllow: () => allow,
359
+ sequencerRelease: () => release,
343
360
  server: () => server,
344
361
  toGsm7: () => toGsm7,
345
362
  token: () => token,
@@ -760,6 +777,8 @@ var CRMPageSchemaZ = import_v43.z.object({
760
777
  icon: import_v43.z.string().optional(),
761
778
  modelHandle: import_v43.z.string(),
762
779
  parentPath: import_v43.z.string().optional(),
780
+ isDefault: import_v43.z.boolean().optional(),
781
+ visibility: import_v43.z.enum(["team", "private"]).optional(),
763
782
  baseQuery: import_v43.z.unknown().optional(),
764
783
  blocks: import_v43.z.array(CRMBlockSchemaZ).optional()
765
784
  });
@@ -1635,7 +1654,8 @@ var ModelDefinitionSchema = import_v48.z.object({
1635
1654
  addDefaultPages: import_v48.z.boolean().optional(),
1636
1655
  addNavigation: import_v48.z.boolean().optional(),
1637
1656
  /** Root path for developer resource UI (e.g., '/access_requests'). Links internal model to a provision.pages entry. */
1638
- page: import_v48.z.string().optional()
1657
+ page: import_v48.z.string().optional(),
1658
+ env: EnvSchemaSchema.optional()
1639
1659
  });
1640
1660
  var RelationshipCardinalitySchema = import_v48.z.enum([
1641
1661
  "ONE_TO_ONE",
@@ -2447,6 +2467,56 @@ function getQueueConfigWithRetry(queueName) {
2447
2467
  return serializable;
2448
2468
  }
2449
2469
 
2470
+ // src/sequencer/config-loader.ts
2471
+ var fs2 = __toESM(require("fs"));
2472
+ var path2 = __toESM(require("path"));
2473
+ var cachedRuntimeSequencers = null;
2474
+ function getRuntimeConfigPath2() {
2475
+ if (process.env.LAMBDA_TASK_ROOT) {
2476
+ return path2.join(process.env.LAMBDA_TASK_ROOT, ".skedyul", "config.json");
2477
+ }
2478
+ return path2.join(process.cwd(), ".skedyul", "config.json");
2479
+ }
2480
+ function loadRuntimeConfigFile2() {
2481
+ const configPath = getRuntimeConfigPath2();
2482
+ if (!fs2.existsSync(configPath)) {
2483
+ return null;
2484
+ }
2485
+ try {
2486
+ const raw = fs2.readFileSync(configPath, "utf-8");
2487
+ return JSON.parse(raw);
2488
+ } catch {
2489
+ return null;
2490
+ }
2491
+ }
2492
+ var registeredServerConfig2 = null;
2493
+ function registerSequencerConfig(config) {
2494
+ registeredServerConfig2 = config;
2495
+ cachedRuntimeSequencers = config.sequencers ?? null;
2496
+ }
2497
+ function clearRegisteredSequencerConfig() {
2498
+ registeredServerConfig2 = null;
2499
+ cachedRuntimeSequencers = null;
2500
+ }
2501
+ function getSequencerDefinitions() {
2502
+ if (cachedRuntimeSequencers) {
2503
+ return cachedRuntimeSequencers;
2504
+ }
2505
+ if (registeredServerConfig2?.sequencers) {
2506
+ cachedRuntimeSequencers = registeredServerConfig2.sequencers;
2507
+ return cachedRuntimeSequencers;
2508
+ }
2509
+ const fileConfig = loadRuntimeConfigFile2();
2510
+ if (fileConfig?.sequencers) {
2511
+ cachedRuntimeSequencers = fileConfig.sequencers;
2512
+ return cachedRuntimeSequencers;
2513
+ }
2514
+ return {};
2515
+ }
2516
+ function getSequencerConfig(sequencerName) {
2517
+ return getSequencerDefinitions()[sequencerName];
2518
+ }
2519
+
2450
2520
  // src/server/utils/schema.ts
2451
2521
  var z9 = __toESM(require("zod"));
2452
2522
  function normalizeBilling(billing) {
@@ -2853,25 +2923,28 @@ function createInstanceClient(config) {
2853
2923
  pagination: pagination ?? { page: 1, total: 0, hasMore: false, limit: args?.limit ?? 50 }
2854
2924
  };
2855
2925
  },
2856
- async get(modelHandle, id) {
2926
+ async get(modelHandle, id, options) {
2857
2927
  const { data } = await callCoreWithConfig("instance.get", {
2858
2928
  modelHandle,
2859
- id
2929
+ id,
2930
+ ...options?.includeEnv ? { includeEnv: true } : {}
2860
2931
  });
2861
2932
  return data;
2862
2933
  },
2863
- async create(modelHandle, data) {
2934
+ async create(modelHandle, data, options) {
2864
2935
  const { data: inst } = await callCoreWithConfig("instance.create", {
2865
2936
  modelHandle,
2866
- data
2937
+ data,
2938
+ ...options?.env ? { env: options.env } : {}
2867
2939
  });
2868
2940
  return inst;
2869
2941
  },
2870
- async update(modelHandle, id, data) {
2942
+ async update(modelHandle, id, data, options) {
2871
2943
  const { data: inst } = await callCoreWithConfig("instance.update", {
2872
2944
  modelHandle,
2873
2945
  id,
2874
- data
2946
+ data,
2947
+ ...options?.env ? { env: options.env } : {}
2875
2948
  });
2876
2949
  return inst;
2877
2950
  },
@@ -2970,10 +3043,11 @@ var instance = {
2970
3043
  * const record = await instance.get('phone_number', 'ins_abc123')
2971
3044
  * ```
2972
3045
  */
2973
- async get(modelHandle, id) {
3046
+ async get(modelHandle, id, options) {
2974
3047
  const { data } = await callCore("instance.get", {
2975
3048
  modelHandle,
2976
- id
3049
+ id,
3050
+ ...options?.includeEnv ? { includeEnv: true } : {}
2977
3051
  });
2978
3052
  return data;
2979
3053
  },
@@ -2990,10 +3064,11 @@ var instance = {
2990
3064
  * })
2991
3065
  * ```
2992
3066
  */
2993
- async create(modelHandle, data) {
3067
+ async create(modelHandle, data, options) {
2994
3068
  const { data: instance2 } = await callCore("instance.create", {
2995
3069
  modelHandle,
2996
- data
3070
+ data,
3071
+ ...options?.env ? { env: options.env } : {}
2997
3072
  });
2998
3073
  return instance2;
2999
3074
  },
@@ -3010,11 +3085,12 @@ var instance = {
3010
3085
  * })
3011
3086
  * ```
3012
3087
  */
3013
- async update(modelHandle, id, data) {
3088
+ async update(modelHandle, id, data, options) {
3014
3089
  const { data: instance2 } = await callCore("instance.update", {
3015
3090
  modelHandle,
3016
3091
  id,
3017
- data
3092
+ data,
3093
+ ...options?.env ? { env: options.env } : {}
3018
3094
  });
3019
3095
  return instance2;
3020
3096
  },
@@ -4381,7 +4457,7 @@ function printStartupLog(config, tools, port) {
4381
4457
 
4382
4458
  // src/server/route-handlers/adapters.ts
4383
4459
  function fromLambdaEvent(event2) {
4384
- const path5 = event2.path || event2.rawPath || "/";
4460
+ const path6 = event2.path || event2.rawPath || "/";
4385
4461
  const method = event2.httpMethod || event2.requestContext?.http?.method || "POST";
4386
4462
  const forwardedProto = event2.headers?.["x-forwarded-proto"] ?? event2.headers?.["X-Forwarded-Proto"];
4387
4463
  const protocol = forwardedProto ?? "https";
@@ -4389,9 +4465,9 @@ function fromLambdaEvent(event2) {
4389
4465
  const queryString = event2.queryStringParameters ? "?" + new URLSearchParams(
4390
4466
  event2.queryStringParameters
4391
4467
  ).toString() : "";
4392
- const url = `${protocol}://${host}${path5}${queryString}`;
4468
+ const url = `${protocol}://${host}${path6}${queryString}`;
4393
4469
  return {
4394
- path: path5,
4470
+ path: path6,
4395
4471
  method,
4396
4472
  headers: event2.headers,
4397
4473
  query: event2.queryStringParameters ?? {},
@@ -4473,8 +4549,8 @@ function parseBodyByContentType(req) {
4473
4549
  }
4474
4550
 
4475
4551
  // src/server/route-handlers/handlers.ts
4476
- var fs2 = __toESM(require("fs"));
4477
- var path2 = __toESM(require("path"));
4552
+ var fs3 = __toESM(require("fs"));
4553
+ var path3 = __toESM(require("path"));
4478
4554
 
4479
4555
  // src/server/core-api-handler.ts
4480
4556
  async function handleCoreMethod(method, params) {
@@ -4942,7 +5018,7 @@ async function handleOAuthCallback(parsedBody, hooks) {
4942
5018
  }
4943
5019
 
4944
5020
  // src/server/handlers/webhook-handler.ts
4945
- function parseWebhookRequest(parsedBody, method, url, path5, headers, query, rawBody, appIdHeader, appVersionIdHeader) {
5021
+ function parseWebhookRequest(parsedBody, method, url, path6, headers, query, rawBody, appIdHeader, appVersionIdHeader) {
4946
5022
  const isEnvelope = typeof parsedBody === "object" && parsedBody !== null && "env" in parsedBody && "request" in parsedBody && "context" in parsedBody;
4947
5023
  if (isEnvelope) {
4948
5024
  const envelope = parsedBody;
@@ -4996,7 +5072,7 @@ function parseWebhookRequest(parsedBody, method, url, path5, headers, query, raw
4996
5072
  const webhookRequest = {
4997
5073
  method,
4998
5074
  url,
4999
- path: path5,
5075
+ path: path6,
5000
5076
  headers,
5001
5077
  query,
5002
5078
  body: parsedBody,
@@ -5025,9 +5101,17 @@ async function executeWebhookHandler(handle, webhookRegistry, data) {
5025
5101
  };
5026
5102
  let webhookResponse;
5027
5103
  try {
5104
+ const rateLimitContext = {
5105
+ app: data.webhookContext.app,
5106
+ appInstallationId: isRuntimeWebhookContext(data.webhookContext) ? data.webhookContext.appInstallationId : void 0,
5107
+ invocation: data.invocation,
5108
+ isProvisionContext: !isRuntimeWebhookContext(data.webhookContext)
5109
+ };
5028
5110
  webhookResponse = await runWithLogContext({ invocation: data.invocation }, async () => {
5029
5111
  return await runWithConfig(requestConfig, async () => {
5030
- return await webhookDef.handler(data.webhookRequest, data.webhookContext);
5112
+ return await runWithRateLimitExecutionContext(rateLimitContext, async () => {
5113
+ return await webhookDef.handler(data.webhookRequest, data.webhookContext);
5114
+ });
5031
5115
  });
5032
5116
  });
5033
5117
  } catch (err) {
@@ -5054,7 +5138,7 @@ function isMethodAllowed(webhookRegistry, handle, method) {
5054
5138
 
5055
5139
  // src/server/route-handlers/handlers.ts
5056
5140
  function getConfigFilePath() {
5057
- return process.env.LAMBDA_TASK_ROOT ? path2.join(process.env.LAMBDA_TASK_ROOT, ".skedyul", "config.json") : ".skedyul/config.json";
5141
+ return process.env.LAMBDA_TASK_ROOT ? path3.join(process.env.LAMBDA_TASK_ROOT, ".skedyul", "config.json") : ".skedyul/config.json";
5058
5142
  }
5059
5143
  function findToolInRegistry(registry, toolName) {
5060
5144
  for (const [key, t] of Object.entries(registry)) {
@@ -5093,8 +5177,8 @@ function handleHealthRoute(ctx) {
5093
5177
  function handleConfigRoute(ctx) {
5094
5178
  const configFilePath = getConfigFilePath();
5095
5179
  try {
5096
- if (fs2.existsSync(configFilePath)) {
5097
- const fileConfig = JSON.parse(fs2.readFileSync(configFilePath, "utf-8"));
5180
+ if (fs3.existsSync(configFilePath)) {
5181
+ const fileConfig = JSON.parse(fs3.readFileSync(configFilePath, "utf-8"));
5098
5182
  return { status: 200, body: fileConfig };
5099
5183
  }
5100
5184
  } catch (err) {
@@ -5782,6 +5866,7 @@ installContextLogger();
5782
5866
  function createSkedyulServer(config) {
5783
5867
  mergeRuntimeEnv();
5784
5868
  registerQueueConfig(config);
5869
+ registerSequencerConfig(config);
5785
5870
  const registry = config.tools;
5786
5871
  const webhookRegistry = config.webhooks;
5787
5872
  if (config.coreApi?.service) {
@@ -6050,13 +6135,13 @@ function defineNavigation(navigation) {
6050
6135
  }
6051
6136
 
6052
6137
  // src/config/schema-loader.ts
6053
- var fs3 = __toESM(require("fs"));
6054
- var path3 = __toESM(require("path"));
6138
+ var fs4 = __toESM(require("fs"));
6139
+ var path4 = __toESM(require("path"));
6055
6140
  var os = __toESM(require("os"));
6056
6141
 
6057
6142
  // src/config/resolver.ts
6058
- var fs4 = __toESM(require("fs"));
6059
- var path4 = __toESM(require("path"));
6143
+ var fs5 = __toESM(require("fs"));
6144
+ var path5 = __toESM(require("path"));
6060
6145
 
6061
6146
  // src/config/utils.ts
6062
6147
  function getAllEnvKeys(config) {
@@ -6159,7 +6244,7 @@ function getApiToken() {
6159
6244
  const config = getConfig();
6160
6245
  return config.apiToken || process.env.SKEDYUL_API_TOKEN || "";
6161
6246
  }
6162
- async function callRateLimitApi(path5, body) {
6247
+ async function callRateLimitApi(path6, body) {
6163
6248
  const baseUrl = getApiBaseUrl();
6164
6249
  const token2 = getApiToken();
6165
6250
  if (!baseUrl || !token2) {
@@ -6167,7 +6252,7 @@ async function callRateLimitApi(path5, body) {
6167
6252
  "SKEDYUL_API_URL and SKEDYUL_API_TOKEN are required for platform queue coordination"
6168
6253
  );
6169
6254
  }
6170
- const response = await fetch(`${baseUrl}/api/internal/ratelimit/${path5}`, {
6255
+ const response = await fetch(`${baseUrl}/api/internal/ratelimit/${path6}`, {
6171
6256
  method: "POST",
6172
6257
  headers: {
6173
6258
  Authorization: `Bearer ${token2}`,
@@ -6177,11 +6262,11 @@ async function callRateLimitApi(path5, body) {
6177
6262
  });
6178
6263
  const payload = await response.json().catch(() => null);
6179
6264
  if (!response.ok || payload?.success === false) {
6180
- const message = payload?.errors?.[0]?.message ?? `Queue coordination API ${path5} failed with status ${response.status}`;
6265
+ const message = payload?.errors?.[0]?.message ?? `Queue coordination API ${path6} failed with status ${response.status}`;
6181
6266
  throw new RateLimitBackendError(message, response.status);
6182
6267
  }
6183
6268
  if (!payload?.data) {
6184
- throw new RateLimitBackendError(`Queue coordination API ${path5} returned empty data`);
6269
+ throw new RateLimitBackendError(`Queue coordination API ${path6} returned empty data`);
6185
6270
  }
6186
6271
  return payload.data;
6187
6272
  }
@@ -6636,6 +6721,406 @@ async function queuedFetchResponse(queueInput, url, init) {
6636
6721
  return queuedFetch(queueInput, () => fetch(url, init));
6637
6722
  }
6638
6723
 
6724
+ // src/sequencer/errors.ts
6725
+ var SequencerContextError = class extends Error {
6726
+ constructor(message) {
6727
+ super(message);
6728
+ this.code = "SEQUENCER_CONTEXT_ERROR";
6729
+ this.name = "SequencerContextError";
6730
+ }
6731
+ };
6732
+ var SequencerNotFoundError = class extends Error {
6733
+ constructor(name) {
6734
+ super(`Sequencer "${name}" is not defined in skedyul.config`);
6735
+ this.code = "SEQUENCER_NOT_FOUND";
6736
+ this.name = "SequencerNotFoundError";
6737
+ }
6738
+ };
6739
+ var SequencerBackendError = class extends Error {
6740
+ constructor(message, statusCode) {
6741
+ super(message);
6742
+ this.code = "SEQUENCER_BACKEND_ERROR";
6743
+ this.name = "SequencerBackendError";
6744
+ this.statusCode = statusCode;
6745
+ }
6746
+ };
6747
+ var SequencerLockError = class extends Error {
6748
+ constructor(message) {
6749
+ super(message);
6750
+ this.code = "SEQUENCER_LOCK_ERROR";
6751
+ this.name = "SequencerLockError";
6752
+ }
6753
+ };
6754
+
6755
+ // src/sequencer/resolve-sequencer-key.ts
6756
+ function resolveEndpointHandle2(config, invocation) {
6757
+ if (config.endpoint) {
6758
+ return config.endpoint;
6759
+ }
6760
+ if (invocation?.toolHandle) {
6761
+ return invocation.toolHandle;
6762
+ }
6763
+ if (invocation?.serverHookHandle) {
6764
+ return invocation.serverHookHandle;
6765
+ }
6766
+ throw new SequencerContextError(
6767
+ `Cannot resolve endpoint handle for sequencer scope "${config.scope}". Set endpoint in sequencer config or ensure invocation.toolHandle/serverHookHandle is available.`
6768
+ );
6769
+ }
6770
+ function appendSubKey2(base, subKey) {
6771
+ return subKey ? `${base}:${subKey}` : base;
6772
+ }
6773
+ function resolveSequencerKey(sequencerName, config, ctx, subKey) {
6774
+ const { app, appInstallationId, invocation, isProvisionContext: isProvisionContext2 } = ctx;
6775
+ switch (config.scope) {
6776
+ case "provision": {
6777
+ const base = `seq:pv:${app.versionId}:${sequencerName}`;
6778
+ return appendSubKey2(base, subKey);
6779
+ }
6780
+ case "install": {
6781
+ if (!appInstallationId) {
6782
+ throw new SequencerContextError(
6783
+ `Sequencer "${sequencerName}" with scope "install" requires appInstallationId in context`
6784
+ );
6785
+ }
6786
+ const base = `seq:in:${appInstallationId}:${sequencerName}`;
6787
+ return appendSubKey2(base, subKey);
6788
+ }
6789
+ case "provision_endpoint": {
6790
+ if (!isProvisionContext2 && appInstallationId) {
6791
+ throw new SequencerContextError(
6792
+ `Sequencer "${sequencerName}" with scope "provision_endpoint" requires provision context (no install)`
6793
+ );
6794
+ }
6795
+ const endpointHandle = resolveEndpointHandle2(config, invocation);
6796
+ const base = `seq:pep:${app.versionId}:${endpointHandle}:${sequencerName}`;
6797
+ return appendSubKey2(base, subKey);
6798
+ }
6799
+ case "install_endpoint": {
6800
+ if (!appInstallationId) {
6801
+ throw new SequencerContextError(
6802
+ `Sequencer "${sequencerName}" with scope "install_endpoint" requires appInstallationId in context`
6803
+ );
6804
+ }
6805
+ const endpointHandle = resolveEndpointHandle2(config, invocation);
6806
+ const base = `seq:iep:${appInstallationId}:${endpointHandle}:${sequencerName}`;
6807
+ return appendSubKey2(base, subKey);
6808
+ }
6809
+ case "global": {
6810
+ const base = `seq:gl:${app.versionId}:${sequencerName}`;
6811
+ return appendSubKey2(base, subKey);
6812
+ }
6813
+ default: {
6814
+ const _exhaustive = config.scope;
6815
+ throw new SequencerContextError(`Unknown sequencer scope: ${String(_exhaustive)}`);
6816
+ }
6817
+ }
6818
+ }
6819
+
6820
+ // src/sequencer/backends/platform.ts
6821
+ function getApiBaseUrl2() {
6822
+ const config = getConfig();
6823
+ if (config.baseUrl) {
6824
+ return config.baseUrl.replace(/\/+$/, "");
6825
+ }
6826
+ return (process.env.SKEDYUL_API_URL ?? process.env.SKEDYUL_NODE_URL ?? "").replace(/\/+$/, "");
6827
+ }
6828
+ function getApiToken2() {
6829
+ const config = getConfig();
6830
+ return config.apiToken || process.env.SKEDYUL_API_TOKEN || "";
6831
+ }
6832
+ async function callSequencerApi(path6, body) {
6833
+ const baseUrl = getApiBaseUrl2();
6834
+ const token2 = getApiToken2();
6835
+ if (!baseUrl || !token2) {
6836
+ throw new SequencerBackendError(
6837
+ "SKEDYUL_API_URL and SKEDYUL_API_TOKEN are required for platform sequencer coordination"
6838
+ );
6839
+ }
6840
+ const response = await fetch(`${baseUrl}/api/internal/sequencer/${path6}`, {
6841
+ method: "POST",
6842
+ headers: {
6843
+ Authorization: `Bearer ${token2}`,
6844
+ "Content-Type": "application/json"
6845
+ },
6846
+ body: JSON.stringify(body)
6847
+ });
6848
+ const payload = await response.json().catch(() => null);
6849
+ if (!response.ok || payload?.success === false) {
6850
+ const message = payload?.errors?.[0]?.message ?? `Sequencer coordination API ${path6} failed with status ${response.status}`;
6851
+ throw new SequencerBackendError(message, response.status);
6852
+ }
6853
+ if (!payload?.data) {
6854
+ throw new SequencerBackendError(`Sequencer coordination API ${path6} returned empty data`);
6855
+ }
6856
+ return payload.data;
6857
+ }
6858
+ var PlatformSequencerBackend = class {
6859
+ async allow(sequencerKey, input) {
6860
+ return callSequencerApi("allow", {
6861
+ sequencerKey,
6862
+ timestamp: input.timestamp,
6863
+ leaseId: input.leaseId,
6864
+ watermarkTtlMs: input.watermarkTtlMs
6865
+ });
6866
+ }
6867
+ async acquire(sequencerKey, input) {
6868
+ return callSequencerApi("acquire", {
6869
+ sequencerKey,
6870
+ leaseId: input.leaseId,
6871
+ timestamp: input.timestamp,
6872
+ lockTtlMs: input.lockTtlMs,
6873
+ watermarkTtlMs: input.watermarkTtlMs
6874
+ });
6875
+ }
6876
+ async release(sequencerKey, leaseId) {
6877
+ await callSequencerApi("release", {
6878
+ sequencerKey,
6879
+ leaseId
6880
+ });
6881
+ }
6882
+ };
6883
+ var platformSequencerBackend = new PlatformSequencerBackend();
6884
+
6885
+ // src/sequencer/backends/memory.ts
6886
+ function generateLeaseId2() {
6887
+ return `mem_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
6888
+ }
6889
+ var MemorySequencerBackend = class {
6890
+ constructor() {
6891
+ this.state = /* @__PURE__ */ new Map();
6892
+ }
6893
+ getState(sequencerKey) {
6894
+ let state = this.state.get(sequencerKey);
6895
+ if (!state) {
6896
+ state = { watermark: 0, lockLeaseId: null, lockExpiresAt: 0 };
6897
+ this.state.set(sequencerKey, state);
6898
+ }
6899
+ if (state.lockLeaseId && state.lockExpiresAt <= Date.now()) {
6900
+ state.lockLeaseId = null;
6901
+ state.lockExpiresAt = 0;
6902
+ }
6903
+ return state;
6904
+ }
6905
+ async allow(sequencerKey, input) {
6906
+ void input.watermarkTtlMs;
6907
+ const state = this.getState(sequencerKey);
6908
+ if (input.timestamp < state.watermark) {
6909
+ return { allowed: false, reason: "stale", watermark: state.watermark };
6910
+ }
6911
+ if (state.lockLeaseId && input.leaseId && state.lockLeaseId !== input.leaseId) {
6912
+ return { allowed: false, reason: "locked", watermark: state.watermark };
6913
+ }
6914
+ state.watermark = Math.max(state.watermark, input.timestamp);
6915
+ return { allowed: true, reason: "ok", watermark: state.watermark };
6916
+ }
6917
+ async acquire(sequencerKey, input) {
6918
+ void input.watermarkTtlMs;
6919
+ const state = this.getState(sequencerKey);
6920
+ const leaseId = input.leaseId || generateLeaseId2();
6921
+ if (state.lockLeaseId && state.lockLeaseId !== leaseId) {
6922
+ return { acquired: false, reason: "locked" };
6923
+ }
6924
+ state.lockLeaseId = leaseId;
6925
+ state.lockExpiresAt = Date.now() + (input.lockTtlMs ?? 6e4);
6926
+ if (input.timestamp !== void 0) {
6927
+ state.watermark = Math.max(state.watermark, input.timestamp);
6928
+ }
6929
+ return { acquired: true, reason: "ok" };
6930
+ }
6931
+ async release(sequencerKey, leaseId) {
6932
+ const state = this.state.get(sequencerKey);
6933
+ if (!state || state.lockLeaseId !== leaseId) {
6934
+ return;
6935
+ }
6936
+ state.lockLeaseId = null;
6937
+ state.lockExpiresAt = 0;
6938
+ }
6939
+ resetForTests() {
6940
+ this.state.clear();
6941
+ }
6942
+ };
6943
+ var memorySequencerBackend = new MemorySequencerBackend();
6944
+
6945
+ // src/sequencer/backends/index.ts
6946
+ var cachedBackend2 = null;
6947
+ function shouldForceMemoryBackend2() {
6948
+ const flag = process.env.SKEDYUL_SEQUENCER_MEMORY;
6949
+ return flag === "1" || flag === "true";
6950
+ }
6951
+ function getSequencerBackend() {
6952
+ if (cachedBackend2) {
6953
+ return cachedBackend2;
6954
+ }
6955
+ if (shouldForceMemoryBackend2()) {
6956
+ cachedBackend2 = memorySequencerBackend;
6957
+ return cachedBackend2;
6958
+ }
6959
+ cachedBackend2 = createResilientBackend2();
6960
+ return cachedBackend2;
6961
+ }
6962
+ function createResilientBackend2() {
6963
+ return {
6964
+ async allow(sequencerKey, input) {
6965
+ try {
6966
+ return await platformSequencerBackend.allow(sequencerKey, input);
6967
+ } catch (error) {
6968
+ if (shouldFallbackToMemory2(error)) {
6969
+ return memorySequencerBackend.allow(sequencerKey, input);
6970
+ }
6971
+ throw error;
6972
+ }
6973
+ },
6974
+ async acquire(sequencerKey, input) {
6975
+ try {
6976
+ return await platformSequencerBackend.acquire(sequencerKey, input);
6977
+ } catch (error) {
6978
+ if (shouldFallbackToMemory2(error)) {
6979
+ return memorySequencerBackend.acquire(sequencerKey, input);
6980
+ }
6981
+ throw error;
6982
+ }
6983
+ },
6984
+ async release(sequencerKey, leaseId) {
6985
+ if (leaseId.startsWith("mem_")) {
6986
+ return memorySequencerBackend.release(sequencerKey, leaseId);
6987
+ }
6988
+ try {
6989
+ await platformSequencerBackend.release(sequencerKey, leaseId);
6990
+ } catch (error) {
6991
+ if (shouldFallbackToMemory2(error)) {
6992
+ return memorySequencerBackend.release(sequencerKey, leaseId);
6993
+ }
6994
+ throw error;
6995
+ }
6996
+ }
6997
+ };
6998
+ }
6999
+ function shouldFallbackToMemory2(error) {
7000
+ if (shouldForceMemoryBackend2()) {
7001
+ return true;
7002
+ }
7003
+ if (error instanceof SequencerBackendError) {
7004
+ if (error.statusCode === 404 || error.statusCode === 503) {
7005
+ return true;
7006
+ }
7007
+ }
7008
+ if (error instanceof TypeError) {
7009
+ return true;
7010
+ }
7011
+ const nodeEnv = process.env.NODE_ENV;
7012
+ return nodeEnv === "development" || nodeEnv === "test";
7013
+ }
7014
+ function resetSequencerBackendForTests() {
7015
+ cachedBackend2 = null;
7016
+ }
7017
+
7018
+ // src/sequencer/index.ts
7019
+ function normalizeSequencerInput(input) {
7020
+ if (typeof input === "string") {
7021
+ return { name: input };
7022
+ }
7023
+ return { name: input.sequencer, subKey: input.key };
7024
+ }
7025
+ function mergeContext(ctxOverride) {
7026
+ const ctx = getRateLimitExecutionContext();
7027
+ if (!ctx?.app?.versionId) {
7028
+ throw new SequencerContextError(
7029
+ "Cannot resolve sequencer without app context. Ensure sequencer runs inside a tool/hook/webhook handler."
7030
+ );
7031
+ }
7032
+ return { ...ctx, ...ctxOverride };
7033
+ }
7034
+ function resolveSequencer(sequencerInput, ctxOverride) {
7035
+ const ctx = mergeContext(ctxOverride);
7036
+ const { name, subKey } = normalizeSequencerInput(sequencerInput);
7037
+ const config = getSequencerConfig(name);
7038
+ if (!config) {
7039
+ throw new SequencerNotFoundError(name);
7040
+ }
7041
+ const sequencerKey = resolveSequencerKey(name, config, ctx, subKey);
7042
+ return { name, sequencerKey, config };
7043
+ }
7044
+ function createSequencerHandle(sequencerInput, ctxOverride) {
7045
+ return {
7046
+ allow(input) {
7047
+ return allow(sequencerInput, input, ctxOverride);
7048
+ },
7049
+ acquire(input) {
7050
+ return acquire(sequencerInput, input, ctxOverride);
7051
+ },
7052
+ release(input) {
7053
+ return release(sequencerInput, input, ctxOverride);
7054
+ }
7055
+ };
7056
+ }
7057
+ function disabledAllowResult() {
7058
+ return { allowed: true, reason: "disabled" };
7059
+ }
7060
+ async function allow(sequencerInput, input, ctxOverride) {
7061
+ const { name, subKey } = normalizeSequencerInput(sequencerInput);
7062
+ const config = getSequencerConfig(name);
7063
+ if (!config || config.enabled !== true) {
7064
+ return disabledAllowResult();
7065
+ }
7066
+ const resolvedSubKey = input.key ?? subKey;
7067
+ const resolved = resolveSequencer(
7068
+ { sequencer: name, key: resolvedSubKey },
7069
+ ctxOverride
7070
+ );
7071
+ const backend = getSequencerBackend();
7072
+ return backend.allow(resolved.sequencerKey, {
7073
+ timestamp: input.timestamp,
7074
+ leaseId: input.leaseId,
7075
+ watermarkTtlMs: resolved.config.watermarkTtlMs
7076
+ });
7077
+ }
7078
+ async function acquire(sequencerInput, input, ctxOverride) {
7079
+ const { name, subKey } = normalizeSequencerInput(sequencerInput);
7080
+ const config = getSequencerConfig(name);
7081
+ if (!config || config.enabled !== true) {
7082
+ return;
7083
+ }
7084
+ const resolvedSubKey = input.key ?? subKey;
7085
+ const resolved = resolveSequencer(
7086
+ { sequencer: name, key: resolvedSubKey },
7087
+ ctxOverride
7088
+ );
7089
+ const backend = getSequencerBackend();
7090
+ const result = await backend.acquire(resolved.sequencerKey, {
7091
+ leaseId: input.leaseId,
7092
+ timestamp: input.timestamp,
7093
+ lockTtlMs: input.lockTtlMs ?? resolved.config.lockTtlMs,
7094
+ watermarkTtlMs: resolved.config.watermarkTtlMs
7095
+ });
7096
+ if (!result.acquired) {
7097
+ throw new SequencerLockError(
7098
+ `Sequencer "${name}" lock held by another lease (${result.reason})`
7099
+ );
7100
+ }
7101
+ }
7102
+ async function release(sequencerInput, input, ctxOverride) {
7103
+ const { name, subKey } = normalizeSequencerInput(sequencerInput);
7104
+ const config = getSequencerConfig(name);
7105
+ if (!config || config.enabled !== true) {
7106
+ return;
7107
+ }
7108
+ const resolvedSubKey = input.key ?? subKey;
7109
+ const resolved = resolveSequencer(
7110
+ { sequencer: name, key: resolvedSubKey },
7111
+ ctxOverride
7112
+ );
7113
+ const backend = getSequencerBackend();
7114
+ await backend.release(resolved.sequencerKey, input.leaseId);
7115
+ }
7116
+ var sequencer = {
7117
+ allow,
7118
+ acquire,
7119
+ release,
7120
+ resolve: resolveSequencer,
7121
+ createHandle: createSequencerHandle
7122
+ };
7123
+
6639
7124
  // src/triggers/types.ts
6640
7125
  var import_v410 = require("zod/v4");
6641
7126
  var CRMDataSchema = import_v410.z.object({
@@ -6740,16 +7225,16 @@ function evaluateTemplate(template, context) {
6740
7225
  const templateRegex = /\{\{\s*([^}]+)\s*\}\}/g;
6741
7226
  const singleMatch = template.match(/^\{\{\s*([^}]+)\s*\}\}$/);
6742
7227
  if (singleMatch) {
6743
- const path5 = singleMatch[1].trim();
6744
- return resolvePath(context, path5);
7228
+ const path6 = singleMatch[1].trim();
7229
+ return resolvePath(context, path6);
6745
7230
  }
6746
- return template.replace(templateRegex, (_, path5) => {
6747
- const value = resolvePath(context, path5.trim());
7231
+ return template.replace(templateRegex, (_, path6) => {
7232
+ const value = resolvePath(context, path6.trim());
6748
7233
  return value === void 0 || value === null ? "" : String(value);
6749
7234
  });
6750
7235
  }
6751
- function resolvePath(obj, path5) {
6752
- const parts = path5.split(".");
7236
+ function resolvePath(obj, path6) {
7237
+ const parts = path6.split(".");
6753
7238
  let current = obj;
6754
7239
  for (const part of parts) {
6755
7240
  if (current === null || current === void 0) {
@@ -6770,14 +7255,14 @@ function evaluateCondition(condition, context) {
6770
7255
  const expression = match[1].trim();
6771
7256
  const eqMatch = expression.match(/^(.+?)\s*==\s*['"](.+)['"]$/);
6772
7257
  if (eqMatch) {
6773
- const [, path5, expectedValue] = eqMatch;
6774
- const actualValue = resolvePath(context, path5.trim());
7258
+ const [, path6, expectedValue] = eqMatch;
7259
+ const actualValue = resolvePath(context, path6.trim());
6775
7260
  return actualValue === expectedValue;
6776
7261
  }
6777
7262
  const neqMatch = expression.match(/^(.+?)\s*!=\s*['"](.+)['"]$/);
6778
7263
  if (neqMatch) {
6779
- const [, path5, expectedValue] = neqMatch;
6780
- const actualValue = resolvePath(context, path5.trim());
7264
+ const [, path6, expectedValue] = neqMatch;
7265
+ const actualValue = resolvePath(context, path6.trim());
6781
7266
  return actualValue !== expectedValue;
6782
7267
  }
6783
7268
  const value = resolvePath(context, expression);
@@ -8230,6 +8715,10 @@ var index_default = { z: import_v414.z };
8230
8715
  SelectComponentDefinitionSchema,
8231
8716
  SemanticMemoryConfigSchema,
8232
8717
  SenderContextSchema,
8718
+ SequencerBackendError,
8719
+ SequencerContextError,
8720
+ SequencerLockError,
8721
+ SequencerNotFoundError,
8233
8722
  SkedyulConfigSchema,
8234
8723
  SkillExampleSchema,
8235
8724
  SkillMetadataSchema,
@@ -8285,6 +8774,7 @@ var index_default = { z: import_v414.z };
8285
8774
  buildAgentContext,
8286
8775
  calculateWaitTime,
8287
8776
  call,
8777
+ clearRegisteredSequencerConfig,
8288
8778
  communicationChannel,
8289
8779
  compileAgent,
8290
8780
  compileWorkflow,
@@ -8304,6 +8794,7 @@ var index_default = { z: import_v414.z };
8304
8794
  createPermissionError,
8305
8795
  createQueueHandle,
8306
8796
  createRateLimitError,
8797
+ createSequencerHandle,
8307
8798
  createServerHookContext,
8308
8799
  createSuccessResponse,
8309
8800
  createTimeoutError,
@@ -8341,6 +8832,8 @@ var index_default = { z: import_v414.z };
8341
8832
  getRateLimitExecutionContext,
8342
8833
  getRequiredInstallEnvKeys,
8343
8834
  getRetryDelay,
8835
+ getSequencerConfig,
8836
+ getSequencerDefinitions,
8344
8837
  instance,
8345
8838
  isChannelDependency,
8346
8839
  isCronContext,
@@ -8356,20 +8849,29 @@ var index_default = { z: import_v414.z };
8356
8849
  isTimeInWindowSlot,
8357
8850
  isWorkflowDependency,
8358
8851
  matchesTrigger,
8852
+ memorySequencerBackend,
8359
8853
  parseCRMSchema,
8360
8854
  parseEstimationFromBilling,
8855
+ platformSequencerBackend,
8361
8856
  queuedFetch,
8362
8857
  queuedFetchResponse,
8363
8858
  registerQueueConfig,
8859
+ registerSequencerConfig,
8364
8860
  report,
8365
8861
  requeue,
8862
+ resetSequencerBackendForTests,
8366
8863
  resolveInputMappings,
8367
8864
  resolveQueue,
8865
+ resolveSequencer,
8368
8866
  resource,
8369
8867
  runWithConfig,
8370
8868
  runWithRateLimitExecutionContext,
8371
8869
  safeParseCRMSchema,
8372
8870
  safeParseConfig,
8871
+ sequencer,
8872
+ sequencerAcquire,
8873
+ sequencerAllow,
8874
+ sequencerRelease,
8373
8875
  server,
8374
8876
  toGsm7,
8375
8877
  token,