skedyul 1.7.3 → 1.7.6
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/.build-stamp +1 -1
- package/dist/cli/index.js +24 -1
- package/dist/dedicated/server.js +24 -1
- package/dist/esm/index.mjs +63 -12
- package/dist/index.js +63 -12
- package/dist/ratelimit/context.d.ts +1 -1
- package/dist/ratelimit/types.d.ts +10 -0
- package/dist/scheduling/calculateWaitTime.d.ts +4 -2
- package/dist/scheduling/index.js +12 -9
- package/dist/scheduling/index.mjs +12 -9
- package/dist/scheduling/types-workflow.d.ts +7 -0
- package/dist/scheduling/types.d.ts +2 -0
- package/dist/server/tool-handler.d.ts +4 -0
- package/dist/server.js +24 -1
- package/dist/serverless/server.mjs +24 -1
- package/package.json +1 -1
package/dist/.build-stamp
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
1785913911217
|
package/dist/cli/index.js
CHANGED
|
@@ -3111,6 +3111,26 @@ function parsePreAcquiredLeases(raw) {
|
|
|
3111
3111
|
return void 0;
|
|
3112
3112
|
}
|
|
3113
3113
|
}
|
|
3114
|
+
function parseHeldMutexQueueKeys(raw) {
|
|
3115
|
+
if (!raw) {
|
|
3116
|
+
return void 0;
|
|
3117
|
+
}
|
|
3118
|
+
try {
|
|
3119
|
+
const parsed = JSON.parse(raw);
|
|
3120
|
+
if (!Array.isArray(parsed)) {
|
|
3121
|
+
return void 0;
|
|
3122
|
+
}
|
|
3123
|
+
const keys = parsed.filter(
|
|
3124
|
+
(item) => item != null && typeof item === "object" && typeof item.queueKey === "string" && typeof item.queueName === "string"
|
|
3125
|
+
).map((item) => ({
|
|
3126
|
+
queueKey: item.queueKey,
|
|
3127
|
+
queueName: item.queueName
|
|
3128
|
+
}));
|
|
3129
|
+
return keys.length > 0 ? keys : void 0;
|
|
3130
|
+
} catch {
|
|
3131
|
+
return void 0;
|
|
3132
|
+
}
|
|
3133
|
+
}
|
|
3114
3134
|
function buildToolMetadata(registry) {
|
|
3115
3135
|
return Object.values(registry).map((tool) => {
|
|
3116
3136
|
const toolConfig = tool.config ?? {};
|
|
@@ -3248,7 +3268,10 @@ function createCallToolHandler(registry, state, onMaxRequests) {
|
|
|
3248
3268
|
appInstallationId: trigger === "provision" ? void 0 : rawContext.appInstallationId,
|
|
3249
3269
|
invocation,
|
|
3250
3270
|
isProvisionContext: trigger === "provision",
|
|
3251
|
-
preAcquiredLeases: parsePreAcquiredLeases(toolEnv.SKEDYUL_RATE_LIMIT_LEASES)
|
|
3271
|
+
preAcquiredLeases: parsePreAcquiredLeases(toolEnv.SKEDYUL_RATE_LIMIT_LEASES),
|
|
3272
|
+
heldMutexQueueKeys: parseHeldMutexQueueKeys(
|
|
3273
|
+
toolEnv.SKEDYUL_HELD_MUTEX_QUEUE_KEYS
|
|
3274
|
+
)
|
|
3252
3275
|
};
|
|
3253
3276
|
const functionResult = await runWithConfig(requestConfig, async () => {
|
|
3254
3277
|
return await runWithRateLimitExecutionContext(rateLimitContext, async () => {
|
package/dist/dedicated/server.js
CHANGED
|
@@ -441,6 +441,26 @@ function parsePreAcquiredLeases(raw) {
|
|
|
441
441
|
return void 0;
|
|
442
442
|
}
|
|
443
443
|
}
|
|
444
|
+
function parseHeldMutexQueueKeys(raw) {
|
|
445
|
+
if (!raw) {
|
|
446
|
+
return void 0;
|
|
447
|
+
}
|
|
448
|
+
try {
|
|
449
|
+
const parsed = JSON.parse(raw);
|
|
450
|
+
if (!Array.isArray(parsed)) {
|
|
451
|
+
return void 0;
|
|
452
|
+
}
|
|
453
|
+
const keys = parsed.filter(
|
|
454
|
+
(item) => item != null && typeof item === "object" && typeof item.queueKey === "string" && typeof item.queueName === "string"
|
|
455
|
+
).map((item) => ({
|
|
456
|
+
queueKey: item.queueKey,
|
|
457
|
+
queueName: item.queueName
|
|
458
|
+
}));
|
|
459
|
+
return keys.length > 0 ? keys : void 0;
|
|
460
|
+
} catch {
|
|
461
|
+
return void 0;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
444
464
|
function buildToolMetadata(registry) {
|
|
445
465
|
return Object.values(registry).map((tool) => {
|
|
446
466
|
const toolConfig = tool.config ?? {};
|
|
@@ -578,7 +598,10 @@ function createCallToolHandler(registry, state, onMaxRequests) {
|
|
|
578
598
|
appInstallationId: trigger === "provision" ? void 0 : rawContext.appInstallationId,
|
|
579
599
|
invocation,
|
|
580
600
|
isProvisionContext: trigger === "provision",
|
|
581
|
-
preAcquiredLeases: parsePreAcquiredLeases(toolEnv.SKEDYUL_RATE_LIMIT_LEASES)
|
|
601
|
+
preAcquiredLeases: parsePreAcquiredLeases(toolEnv.SKEDYUL_RATE_LIMIT_LEASES),
|
|
602
|
+
heldMutexQueueKeys: parseHeldMutexQueueKeys(
|
|
603
|
+
toolEnv.SKEDYUL_HELD_MUTEX_QUEUE_KEYS
|
|
604
|
+
)
|
|
582
605
|
};
|
|
583
606
|
const functionResult = await runWithConfig(requestConfig, async () => {
|
|
584
607
|
return await runWithRateLimitExecutionContext(rateLimitContext, async () => {
|
package/dist/esm/index.mjs
CHANGED
|
@@ -3649,6 +3649,14 @@ function getActiveMutexQueueNames() {
|
|
|
3649
3649
|
names.add(operation.resolved.name);
|
|
3650
3650
|
}
|
|
3651
3651
|
}
|
|
3652
|
+
const held = getRateLimitExecutionContext()?.heldMutexQueueKeys;
|
|
3653
|
+
if (held?.length) {
|
|
3654
|
+
for (const entry of held) {
|
|
3655
|
+
if (entry.queueName) {
|
|
3656
|
+
names.add(entry.queueName);
|
|
3657
|
+
}
|
|
3658
|
+
}
|
|
3659
|
+
}
|
|
3652
3660
|
return [...names];
|
|
3653
3661
|
}
|
|
3654
3662
|
function shouldSkipNestedAcquire(queueName) {
|
|
@@ -3887,6 +3895,26 @@ function parsePreAcquiredLeases(raw) {
|
|
|
3887
3895
|
return void 0;
|
|
3888
3896
|
}
|
|
3889
3897
|
}
|
|
3898
|
+
function parseHeldMutexQueueKeys(raw) {
|
|
3899
|
+
if (!raw) {
|
|
3900
|
+
return void 0;
|
|
3901
|
+
}
|
|
3902
|
+
try {
|
|
3903
|
+
const parsed = JSON.parse(raw);
|
|
3904
|
+
if (!Array.isArray(parsed)) {
|
|
3905
|
+
return void 0;
|
|
3906
|
+
}
|
|
3907
|
+
const keys = parsed.filter(
|
|
3908
|
+
(item) => item != null && typeof item === "object" && typeof item.queueKey === "string" && typeof item.queueName === "string"
|
|
3909
|
+
).map((item) => ({
|
|
3910
|
+
queueKey: item.queueKey,
|
|
3911
|
+
queueName: item.queueName
|
|
3912
|
+
}));
|
|
3913
|
+
return keys.length > 0 ? keys : void 0;
|
|
3914
|
+
} catch {
|
|
3915
|
+
return void 0;
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3890
3918
|
function buildToolMetadata(registry) {
|
|
3891
3919
|
return Object.values(registry).map((tool) => {
|
|
3892
3920
|
const toolConfig = tool.config ?? {};
|
|
@@ -4024,7 +4052,10 @@ function createCallToolHandler(registry, state, onMaxRequests) {
|
|
|
4024
4052
|
appInstallationId: trigger === "provision" ? void 0 : rawContext.appInstallationId,
|
|
4025
4053
|
invocation,
|
|
4026
4054
|
isProvisionContext: trigger === "provision",
|
|
4027
|
-
preAcquiredLeases: parsePreAcquiredLeases(toolEnv.SKEDYUL_RATE_LIMIT_LEASES)
|
|
4055
|
+
preAcquiredLeases: parsePreAcquiredLeases(toolEnv.SKEDYUL_RATE_LIMIT_LEASES),
|
|
4056
|
+
heldMutexQueueKeys: parseHeldMutexQueueKeys(
|
|
4057
|
+
toolEnv.SKEDYUL_HELD_MUTEX_QUEUE_KEYS
|
|
4058
|
+
)
|
|
4028
4059
|
};
|
|
4029
4060
|
const functionResult = await runWithConfig(requestConfig, async () => {
|
|
4030
4061
|
return await runWithRateLimitExecutionContext(rateLimitContext, async () => {
|
|
@@ -6416,6 +6447,11 @@ async function queuedFetch(queueInput, fnOrPromise) {
|
|
|
6416
6447
|
function findPreAcquiredLease(queueKey, ctx) {
|
|
6417
6448
|
return ctx?.preAcquiredLeases?.find((lease) => lease.queueKey === queueKey);
|
|
6418
6449
|
}
|
|
6450
|
+
function isHeldMutexQueueKey(queueKey, ctx) {
|
|
6451
|
+
return Boolean(
|
|
6452
|
+
ctx?.heldMutexQueueKeys?.some((entry) => entry.queueKey === queueKey)
|
|
6453
|
+
);
|
|
6454
|
+
}
|
|
6419
6455
|
function shouldRethrowRateLimitCause(maxRetries, error, attempt, shouldRetryFn) {
|
|
6420
6456
|
if (maxRetries !== 0 || !shouldRetryFn(error, attempt)) {
|
|
6421
6457
|
return false;
|
|
@@ -6433,10 +6469,14 @@ async function executeWithRetries(operation, maxRetries) {
|
|
|
6433
6469
|
operation.resolved.queueKey,
|
|
6434
6470
|
rateLimitCtx
|
|
6435
6471
|
);
|
|
6472
|
+
const heldByPlatform = isHeldMutexQueueKey(
|
|
6473
|
+
operation.resolved.queueKey,
|
|
6474
|
+
rateLimitCtx
|
|
6475
|
+
);
|
|
6436
6476
|
if (shouldSkipNestedAcquire(operation.resolved.name)) {
|
|
6437
6477
|
return operation.fn();
|
|
6438
6478
|
}
|
|
6439
|
-
if (preAcquired) {
|
|
6479
|
+
if (preAcquired || heldByPlatform) {
|
|
6440
6480
|
try {
|
|
6441
6481
|
return await operation.fn();
|
|
6442
6482
|
} catch (error) {
|
|
@@ -7987,7 +8027,15 @@ var WaitInputRelativeSchema = z15.object({
|
|
|
7987
8027
|
mode: z15.literal("relative"),
|
|
7988
8028
|
amount: z15.number(),
|
|
7989
8029
|
unit: WaitUnitSchema,
|
|
7990
|
-
windows: z15.array(TimeWindowSlotSchema).optional()
|
|
8030
|
+
windows: z15.array(TimeWindowSlotSchema).optional(),
|
|
8031
|
+
/**
|
|
8032
|
+
* Optional ISO datetime to use as the base for the relative delay instead of
|
|
8033
|
+
* wall-clock `now`. Used for cadence chaining (schedule relative to a prior
|
|
8034
|
+
* message's scheduledAt). `waitTime` is still measured from wall-clock `now`.
|
|
8035
|
+
*/
|
|
8036
|
+
from: z15.union([z15.string(), z15.date()]).optional().describe(
|
|
8037
|
+
"ISO datetime base for the relative delay (e.g. previous message scheduledAt)"
|
|
8038
|
+
)
|
|
7991
8039
|
});
|
|
7992
8040
|
var WaitInputAbsoluteSchema = z15.object({
|
|
7993
8041
|
mode: z15.literal("absolute"),
|
|
@@ -8100,10 +8148,12 @@ function calculateWaitTime(step, now) {
|
|
|
8100
8148
|
};
|
|
8101
8149
|
}
|
|
8102
8150
|
case "relative": {
|
|
8151
|
+
const baseTime = step.from != null ? new Date(step.from).getTime() : nowTime;
|
|
8152
|
+
const baseDate = new Date(baseTime);
|
|
8103
8153
|
if (!step.amount && (!step.windows || step.windows.length === 0)) {
|
|
8104
8154
|
return {
|
|
8105
|
-
waitTime: 0,
|
|
8106
|
-
scheduledAt:
|
|
8155
|
+
waitTime: Math.max(0, baseTime - nowTime),
|
|
8156
|
+
scheduledAt: baseDate
|
|
8107
8157
|
};
|
|
8108
8158
|
}
|
|
8109
8159
|
let relativeDelay = 0;
|
|
@@ -8142,17 +8192,18 @@ function calculateWaitTime(step, now) {
|
|
|
8142
8192
|
}
|
|
8143
8193
|
}
|
|
8144
8194
|
if (!step.windows || step.windows.length === 0) {
|
|
8195
|
+
const scheduledAt = new Date(baseTime + relativeDelay);
|
|
8145
8196
|
return {
|
|
8146
|
-
waitTime:
|
|
8147
|
-
scheduledAt
|
|
8197
|
+
waitTime: Math.max(0, scheduledAt.getTime() - nowTime),
|
|
8198
|
+
scheduledAt
|
|
8148
8199
|
};
|
|
8149
8200
|
}
|
|
8150
|
-
const targetDate = new Date(
|
|
8201
|
+
const targetDate = new Date(baseTime + relativeDelay);
|
|
8151
8202
|
for (const window of step.windows) {
|
|
8152
8203
|
if (isTimeInWindowSlot(targetDate, window)) {
|
|
8153
8204
|
return {
|
|
8154
|
-
waitTime:
|
|
8155
|
-
scheduledAt:
|
|
8205
|
+
waitTime: Math.max(0, targetDate.getTime() - nowTime),
|
|
8206
|
+
scheduledAt: targetDate
|
|
8156
8207
|
};
|
|
8157
8208
|
}
|
|
8158
8209
|
}
|
|
@@ -8197,8 +8248,8 @@ function calculateWaitTime(step, now) {
|
|
|
8197
8248
|
msUntilWindowStart = daysMs + hoursAdjustment + minutesAdjustment;
|
|
8198
8249
|
}
|
|
8199
8250
|
const windowStartTimeMs = targetDate.getTime() + msUntilWindowStart;
|
|
8200
|
-
const
|
|
8201
|
-
const windowScheduledTime =
|
|
8251
|
+
const sameLocalDayAsBase = getLocalDateKey(baseDate, tz) === getLocalDateKey(targetDate, tz);
|
|
8252
|
+
const windowScheduledTime = sameLocalDayAsBase ? new Date(windowStartTimeMs + relativeDelay) : new Date(windowStartTimeMs);
|
|
8202
8253
|
const waitFromNow = windowScheduledTime.getTime() - nowTime;
|
|
8203
8254
|
if (waitFromNow < earliestWaitFromNow) {
|
|
8204
8255
|
earliestWaitFromNow = waitFromNow;
|
package/dist/index.js
CHANGED
|
@@ -4023,6 +4023,14 @@ function getActiveMutexQueueNames() {
|
|
|
4023
4023
|
names.add(operation.resolved.name);
|
|
4024
4024
|
}
|
|
4025
4025
|
}
|
|
4026
|
+
const held = getRateLimitExecutionContext()?.heldMutexQueueKeys;
|
|
4027
|
+
if (held?.length) {
|
|
4028
|
+
for (const entry of held) {
|
|
4029
|
+
if (entry.queueName) {
|
|
4030
|
+
names.add(entry.queueName);
|
|
4031
|
+
}
|
|
4032
|
+
}
|
|
4033
|
+
}
|
|
4026
4034
|
return [...names];
|
|
4027
4035
|
}
|
|
4028
4036
|
function shouldSkipNestedAcquire(queueName) {
|
|
@@ -4261,6 +4269,26 @@ function parsePreAcquiredLeases(raw) {
|
|
|
4261
4269
|
return void 0;
|
|
4262
4270
|
}
|
|
4263
4271
|
}
|
|
4272
|
+
function parseHeldMutexQueueKeys(raw) {
|
|
4273
|
+
if (!raw) {
|
|
4274
|
+
return void 0;
|
|
4275
|
+
}
|
|
4276
|
+
try {
|
|
4277
|
+
const parsed = JSON.parse(raw);
|
|
4278
|
+
if (!Array.isArray(parsed)) {
|
|
4279
|
+
return void 0;
|
|
4280
|
+
}
|
|
4281
|
+
const keys = parsed.filter(
|
|
4282
|
+
(item) => item != null && typeof item === "object" && typeof item.queueKey === "string" && typeof item.queueName === "string"
|
|
4283
|
+
).map((item) => ({
|
|
4284
|
+
queueKey: item.queueKey,
|
|
4285
|
+
queueName: item.queueName
|
|
4286
|
+
}));
|
|
4287
|
+
return keys.length > 0 ? keys : void 0;
|
|
4288
|
+
} catch {
|
|
4289
|
+
return void 0;
|
|
4290
|
+
}
|
|
4291
|
+
}
|
|
4264
4292
|
function buildToolMetadata(registry) {
|
|
4265
4293
|
return Object.values(registry).map((tool) => {
|
|
4266
4294
|
const toolConfig = tool.config ?? {};
|
|
@@ -4398,7 +4426,10 @@ function createCallToolHandler(registry, state, onMaxRequests) {
|
|
|
4398
4426
|
appInstallationId: trigger === "provision" ? void 0 : rawContext.appInstallationId,
|
|
4399
4427
|
invocation,
|
|
4400
4428
|
isProvisionContext: trigger === "provision",
|
|
4401
|
-
preAcquiredLeases: parsePreAcquiredLeases(toolEnv.SKEDYUL_RATE_LIMIT_LEASES)
|
|
4429
|
+
preAcquiredLeases: parsePreAcquiredLeases(toolEnv.SKEDYUL_RATE_LIMIT_LEASES),
|
|
4430
|
+
heldMutexQueueKeys: parseHeldMutexQueueKeys(
|
|
4431
|
+
toolEnv.SKEDYUL_HELD_MUTEX_QUEUE_KEYS
|
|
4432
|
+
)
|
|
4402
4433
|
};
|
|
4403
4434
|
const functionResult = await runWithConfig(requestConfig, async () => {
|
|
4404
4435
|
return await runWithRateLimitExecutionContext(rateLimitContext, async () => {
|
|
@@ -6790,6 +6821,11 @@ async function queuedFetch(queueInput, fnOrPromise) {
|
|
|
6790
6821
|
function findPreAcquiredLease(queueKey, ctx) {
|
|
6791
6822
|
return ctx?.preAcquiredLeases?.find((lease) => lease.queueKey === queueKey);
|
|
6792
6823
|
}
|
|
6824
|
+
function isHeldMutexQueueKey(queueKey, ctx) {
|
|
6825
|
+
return Boolean(
|
|
6826
|
+
ctx?.heldMutexQueueKeys?.some((entry) => entry.queueKey === queueKey)
|
|
6827
|
+
);
|
|
6828
|
+
}
|
|
6793
6829
|
function shouldRethrowRateLimitCause(maxRetries, error, attempt, shouldRetryFn) {
|
|
6794
6830
|
if (maxRetries !== 0 || !shouldRetryFn(error, attempt)) {
|
|
6795
6831
|
return false;
|
|
@@ -6807,10 +6843,14 @@ async function executeWithRetries(operation, maxRetries) {
|
|
|
6807
6843
|
operation.resolved.queueKey,
|
|
6808
6844
|
rateLimitCtx
|
|
6809
6845
|
);
|
|
6846
|
+
const heldByPlatform = isHeldMutexQueueKey(
|
|
6847
|
+
operation.resolved.queueKey,
|
|
6848
|
+
rateLimitCtx
|
|
6849
|
+
);
|
|
6810
6850
|
if (shouldSkipNestedAcquire(operation.resolved.name)) {
|
|
6811
6851
|
return operation.fn();
|
|
6812
6852
|
}
|
|
6813
|
-
if (preAcquired) {
|
|
6853
|
+
if (preAcquired || heldByPlatform) {
|
|
6814
6854
|
try {
|
|
6815
6855
|
return await operation.fn();
|
|
6816
6856
|
} catch (error) {
|
|
@@ -8361,7 +8401,15 @@ var WaitInputRelativeSchema = import_v413.z.object({
|
|
|
8361
8401
|
mode: import_v413.z.literal("relative"),
|
|
8362
8402
|
amount: import_v413.z.number(),
|
|
8363
8403
|
unit: WaitUnitSchema,
|
|
8364
|
-
windows: import_v413.z.array(TimeWindowSlotSchema).optional()
|
|
8404
|
+
windows: import_v413.z.array(TimeWindowSlotSchema).optional(),
|
|
8405
|
+
/**
|
|
8406
|
+
* Optional ISO datetime to use as the base for the relative delay instead of
|
|
8407
|
+
* wall-clock `now`. Used for cadence chaining (schedule relative to a prior
|
|
8408
|
+
* message's scheduledAt). `waitTime` is still measured from wall-clock `now`.
|
|
8409
|
+
*/
|
|
8410
|
+
from: import_v413.z.union([import_v413.z.string(), import_v413.z.date()]).optional().describe(
|
|
8411
|
+
"ISO datetime base for the relative delay (e.g. previous message scheduledAt)"
|
|
8412
|
+
)
|
|
8365
8413
|
});
|
|
8366
8414
|
var WaitInputAbsoluteSchema = import_v413.z.object({
|
|
8367
8415
|
mode: import_v413.z.literal("absolute"),
|
|
@@ -8474,10 +8522,12 @@ function calculateWaitTime(step, now) {
|
|
|
8474
8522
|
};
|
|
8475
8523
|
}
|
|
8476
8524
|
case "relative": {
|
|
8525
|
+
const baseTime = step.from != null ? new Date(step.from).getTime() : nowTime;
|
|
8526
|
+
const baseDate = new Date(baseTime);
|
|
8477
8527
|
if (!step.amount && (!step.windows || step.windows.length === 0)) {
|
|
8478
8528
|
return {
|
|
8479
|
-
waitTime: 0,
|
|
8480
|
-
scheduledAt:
|
|
8529
|
+
waitTime: Math.max(0, baseTime - nowTime),
|
|
8530
|
+
scheduledAt: baseDate
|
|
8481
8531
|
};
|
|
8482
8532
|
}
|
|
8483
8533
|
let relativeDelay = 0;
|
|
@@ -8516,17 +8566,18 @@ function calculateWaitTime(step, now) {
|
|
|
8516
8566
|
}
|
|
8517
8567
|
}
|
|
8518
8568
|
if (!step.windows || step.windows.length === 0) {
|
|
8569
|
+
const scheduledAt = new Date(baseTime + relativeDelay);
|
|
8519
8570
|
return {
|
|
8520
|
-
waitTime:
|
|
8521
|
-
scheduledAt
|
|
8571
|
+
waitTime: Math.max(0, scheduledAt.getTime() - nowTime),
|
|
8572
|
+
scheduledAt
|
|
8522
8573
|
};
|
|
8523
8574
|
}
|
|
8524
|
-
const targetDate = new Date(
|
|
8575
|
+
const targetDate = new Date(baseTime + relativeDelay);
|
|
8525
8576
|
for (const window of step.windows) {
|
|
8526
8577
|
if (isTimeInWindowSlot(targetDate, window)) {
|
|
8527
8578
|
return {
|
|
8528
|
-
waitTime:
|
|
8529
|
-
scheduledAt:
|
|
8579
|
+
waitTime: Math.max(0, targetDate.getTime() - nowTime),
|
|
8580
|
+
scheduledAt: targetDate
|
|
8530
8581
|
};
|
|
8531
8582
|
}
|
|
8532
8583
|
}
|
|
@@ -8571,8 +8622,8 @@ function calculateWaitTime(step, now) {
|
|
|
8571
8622
|
msUntilWindowStart = daysMs + hoursAdjustment + minutesAdjustment;
|
|
8572
8623
|
}
|
|
8573
8624
|
const windowStartTimeMs = targetDate.getTime() + msUntilWindowStart;
|
|
8574
|
-
const
|
|
8575
|
-
const windowScheduledTime =
|
|
8625
|
+
const sameLocalDayAsBase = getLocalDateKey(baseDate, tz) === getLocalDateKey(targetDate, tz);
|
|
8626
|
+
const windowScheduledTime = sameLocalDayAsBase ? new Date(windowStartTimeMs + relativeDelay) : new Date(windowStartTimeMs);
|
|
8576
8627
|
const waitFromNow = windowScheduledTime.getTime() - nowTime;
|
|
8577
8628
|
if (waitFromNow < earliestWaitFromNow) {
|
|
8578
8629
|
earliestWaitFromNow = waitFromNow;
|
|
@@ -4,7 +4,7 @@ export declare function getRateLimitExecutionContext(): RateLimitExecutionContex
|
|
|
4
4
|
export declare function runWithActiveQueuedOperation<T>(operation: ActiveQueuedOperation<T>, fn: () => Promise<T>): Promise<T>;
|
|
5
5
|
export declare function getActiveQueuedOperation<T>(): ActiveQueuedOperation<T> | undefined;
|
|
6
6
|
export declare function getActiveQueuedOperationStack(): ActiveQueuedOperation<unknown>[];
|
|
7
|
-
/** Active mutex queue names holding a lease in an outer queuedFetch. */
|
|
7
|
+
/** Active mutex queue names holding a lease in an outer queuedFetch or platform-held. */
|
|
8
8
|
export declare function getActiveMutexQueueNames(): string[];
|
|
9
9
|
/** True when an active mutex queue suppresses acquire for the target queue name. */
|
|
10
10
|
export declare function shouldSkipNestedAcquire(queueName: string): boolean;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { SerializableQueueConfig } from '../config/queue-config';
|
|
2
2
|
import type { AppInfo } from '../types/shared';
|
|
3
3
|
import type { InvocationContext } from '../types/invocation';
|
|
4
|
+
export interface HeldMutexQueueKey {
|
|
5
|
+
queueKey: string;
|
|
6
|
+
queueName: string;
|
|
7
|
+
}
|
|
4
8
|
export interface RateLimitExecutionContext {
|
|
5
9
|
app: AppInfo;
|
|
6
10
|
appInstallationId?: string;
|
|
@@ -11,6 +15,12 @@ export interface RateLimitExecutionContext {
|
|
|
11
15
|
queueKey: string;
|
|
12
16
|
leaseId: string;
|
|
13
17
|
}>;
|
|
18
|
+
/**
|
|
19
|
+
* Mutex queue keys held by platform Temporal orchestration for this tool call.
|
|
20
|
+
* queuedFetch skips acquire/release for matching keys; queue names count as active mutexes
|
|
21
|
+
* for suppressesQueues.
|
|
22
|
+
*/
|
|
23
|
+
heldMutexQueueKeys?: HeldMutexQueueKey[];
|
|
14
24
|
}
|
|
15
25
|
export interface QueueSelector {
|
|
16
26
|
queue: string;
|
|
@@ -9,8 +9,10 @@
|
|
|
9
9
|
import type { WaitInputType, CalculateWaitTimeResult } from './types-workflow';
|
|
10
10
|
/**
|
|
11
11
|
* Calculate the wait time for a wait step
|
|
12
|
-
* @param step The input parameters for the wait step
|
|
13
|
-
*
|
|
12
|
+
* @param step The input parameters for the wait step. Relative mode may include
|
|
13
|
+
* optional `from` (ISO datetime / Date) as the base for the delay instead of `now`.
|
|
14
|
+
* @param now Wall-clock current date/time. Used as the default relative base and
|
|
15
|
+
* always used to compute `waitTime` (ms until `scheduledAt`).
|
|
14
16
|
* @returns The time to wait in milliseconds and the scheduled date
|
|
15
17
|
*/
|
|
16
18
|
export declare function calculateWaitTime(step: WaitInputType, now: Date): CalculateWaitTimeResult;
|
package/dist/scheduling/index.js
CHANGED
|
@@ -128,10 +128,12 @@ function calculateWaitTime(step, now) {
|
|
|
128
128
|
};
|
|
129
129
|
}
|
|
130
130
|
case "relative": {
|
|
131
|
+
const baseTime = step.from != null ? new Date(step.from).getTime() : nowTime;
|
|
132
|
+
const baseDate = new Date(baseTime);
|
|
131
133
|
if (!step.amount && (!step.windows || step.windows.length === 0)) {
|
|
132
134
|
return {
|
|
133
|
-
waitTime: 0,
|
|
134
|
-
scheduledAt:
|
|
135
|
+
waitTime: Math.max(0, baseTime - nowTime),
|
|
136
|
+
scheduledAt: baseDate
|
|
135
137
|
};
|
|
136
138
|
}
|
|
137
139
|
let relativeDelay = 0;
|
|
@@ -170,17 +172,18 @@ function calculateWaitTime(step, now) {
|
|
|
170
172
|
}
|
|
171
173
|
}
|
|
172
174
|
if (!step.windows || step.windows.length === 0) {
|
|
175
|
+
const scheduledAt = new Date(baseTime + relativeDelay);
|
|
173
176
|
return {
|
|
174
|
-
waitTime:
|
|
175
|
-
scheduledAt
|
|
177
|
+
waitTime: Math.max(0, scheduledAt.getTime() - nowTime),
|
|
178
|
+
scheduledAt
|
|
176
179
|
};
|
|
177
180
|
}
|
|
178
|
-
const targetDate = new Date(
|
|
181
|
+
const targetDate = new Date(baseTime + relativeDelay);
|
|
179
182
|
for (const window of step.windows) {
|
|
180
183
|
if (isTimeInWindowSlot(targetDate, window)) {
|
|
181
184
|
return {
|
|
182
|
-
waitTime:
|
|
183
|
-
scheduledAt:
|
|
185
|
+
waitTime: Math.max(0, targetDate.getTime() - nowTime),
|
|
186
|
+
scheduledAt: targetDate
|
|
184
187
|
};
|
|
185
188
|
}
|
|
186
189
|
}
|
|
@@ -225,8 +228,8 @@ function calculateWaitTime(step, now) {
|
|
|
225
228
|
msUntilWindowStart = daysMs + hoursAdjustment + minutesAdjustment;
|
|
226
229
|
}
|
|
227
230
|
const windowStartTimeMs = targetDate.getTime() + msUntilWindowStart;
|
|
228
|
-
const
|
|
229
|
-
const windowScheduledTime =
|
|
231
|
+
const sameLocalDayAsBase = getLocalDateKey(baseDate, tz) === getLocalDateKey(targetDate, tz);
|
|
232
|
+
const windowScheduledTime = sameLocalDayAsBase ? new Date(windowStartTimeMs + relativeDelay) : new Date(windowStartTimeMs);
|
|
230
233
|
const waitFromNow = windowScheduledTime.getTime() - nowTime;
|
|
231
234
|
if (waitFromNow < earliestWaitFromNow) {
|
|
232
235
|
earliestWaitFromNow = waitFromNow;
|
|
@@ -100,10 +100,12 @@ function calculateWaitTime(step, now) {
|
|
|
100
100
|
};
|
|
101
101
|
}
|
|
102
102
|
case "relative": {
|
|
103
|
+
const baseTime = step.from != null ? new Date(step.from).getTime() : nowTime;
|
|
104
|
+
const baseDate = new Date(baseTime);
|
|
103
105
|
if (!step.amount && (!step.windows || step.windows.length === 0)) {
|
|
104
106
|
return {
|
|
105
|
-
waitTime: 0,
|
|
106
|
-
scheduledAt:
|
|
107
|
+
waitTime: Math.max(0, baseTime - nowTime),
|
|
108
|
+
scheduledAt: baseDate
|
|
107
109
|
};
|
|
108
110
|
}
|
|
109
111
|
let relativeDelay = 0;
|
|
@@ -142,17 +144,18 @@ function calculateWaitTime(step, now) {
|
|
|
142
144
|
}
|
|
143
145
|
}
|
|
144
146
|
if (!step.windows || step.windows.length === 0) {
|
|
147
|
+
const scheduledAt = new Date(baseTime + relativeDelay);
|
|
145
148
|
return {
|
|
146
|
-
waitTime:
|
|
147
|
-
scheduledAt
|
|
149
|
+
waitTime: Math.max(0, scheduledAt.getTime() - nowTime),
|
|
150
|
+
scheduledAt
|
|
148
151
|
};
|
|
149
152
|
}
|
|
150
|
-
const targetDate = new Date(
|
|
153
|
+
const targetDate = new Date(baseTime + relativeDelay);
|
|
151
154
|
for (const window of step.windows) {
|
|
152
155
|
if (isTimeInWindowSlot(targetDate, window)) {
|
|
153
156
|
return {
|
|
154
|
-
waitTime:
|
|
155
|
-
scheduledAt:
|
|
157
|
+
waitTime: Math.max(0, targetDate.getTime() - nowTime),
|
|
158
|
+
scheduledAt: targetDate
|
|
156
159
|
};
|
|
157
160
|
}
|
|
158
161
|
}
|
|
@@ -197,8 +200,8 @@ function calculateWaitTime(step, now) {
|
|
|
197
200
|
msUntilWindowStart = daysMs + hoursAdjustment + minutesAdjustment;
|
|
198
201
|
}
|
|
199
202
|
const windowStartTimeMs = targetDate.getTime() + msUntilWindowStart;
|
|
200
|
-
const
|
|
201
|
-
const windowScheduledTime =
|
|
203
|
+
const sameLocalDayAsBase = getLocalDateKey(baseDate, tz) === getLocalDateKey(targetDate, tz);
|
|
204
|
+
const windowScheduledTime = sameLocalDayAsBase ? new Date(windowStartTimeMs + relativeDelay) : new Date(windowStartTimeMs);
|
|
202
205
|
const waitFromNow = windowScheduledTime.getTime() - nowTime;
|
|
203
206
|
if (waitFromNow < earliestWaitFromNow) {
|
|
204
207
|
earliestWaitFromNow = waitFromNow;
|
|
@@ -33,6 +33,13 @@ export interface WaitInputRelative {
|
|
|
33
33
|
amount: number;
|
|
34
34
|
unit: WaitUnit;
|
|
35
35
|
windows?: TimeWindowSlot[];
|
|
36
|
+
/**
|
|
37
|
+
* Optional ISO datetime (or Date) to use as the base for the relative delay
|
|
38
|
+
* instead of wall-clock `now`. Used for cadence chaining: schedule message N
|
|
39
|
+
* relative to message N-1's `scheduledAt`.
|
|
40
|
+
* `waitTime` in the result is still measured from wall-clock `now`.
|
|
41
|
+
*/
|
|
42
|
+
from?: string | Date;
|
|
36
43
|
}
|
|
37
44
|
export interface WaitInputAbsolute {
|
|
38
45
|
mode: 'absolute';
|
|
@@ -107,6 +107,7 @@ export declare const WaitInputRelativeSchema: z.ZodObject<{
|
|
|
107
107
|
}>>;
|
|
108
108
|
timezone: z.ZodOptional<z.ZodString>;
|
|
109
109
|
}, z.core.$strip>>>;
|
|
110
|
+
from: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodDate]>>;
|
|
110
111
|
}, z.core.$strip>;
|
|
111
112
|
export declare const WaitInputAbsoluteSchema: z.ZodObject<{
|
|
112
113
|
mode: z.ZodLiteral<"absolute">;
|
|
@@ -153,6 +154,7 @@ export declare const WaitInputSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
153
154
|
}>>;
|
|
154
155
|
timezone: z.ZodOptional<z.ZodString>;
|
|
155
156
|
}, z.core.$strip>>>;
|
|
157
|
+
from: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodDate]>>;
|
|
156
158
|
}, z.core.$strip>, z.ZodObject<{
|
|
157
159
|
mode: z.ZodLiteral<"absolute">;
|
|
158
160
|
scheduleAt: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
@@ -4,6 +4,10 @@ export declare function parsePreAcquiredLeases(raw: string | undefined): Array<{
|
|
|
4
4
|
queueKey: string;
|
|
5
5
|
leaseId: string;
|
|
6
6
|
}> | undefined;
|
|
7
|
+
export declare function parseHeldMutexQueueKeys(raw: string | undefined): Array<{
|
|
8
|
+
queueKey: string;
|
|
9
|
+
queueName: string;
|
|
10
|
+
}> | undefined;
|
|
7
11
|
/**
|
|
8
12
|
* Builds tool metadata array from a tool registry
|
|
9
13
|
*/
|
package/dist/server.js
CHANGED
|
@@ -441,6 +441,26 @@ function parsePreAcquiredLeases(raw) {
|
|
|
441
441
|
return void 0;
|
|
442
442
|
}
|
|
443
443
|
}
|
|
444
|
+
function parseHeldMutexQueueKeys(raw) {
|
|
445
|
+
if (!raw) {
|
|
446
|
+
return void 0;
|
|
447
|
+
}
|
|
448
|
+
try {
|
|
449
|
+
const parsed = JSON.parse(raw);
|
|
450
|
+
if (!Array.isArray(parsed)) {
|
|
451
|
+
return void 0;
|
|
452
|
+
}
|
|
453
|
+
const keys = parsed.filter(
|
|
454
|
+
(item) => item != null && typeof item === "object" && typeof item.queueKey === "string" && typeof item.queueName === "string"
|
|
455
|
+
).map((item) => ({
|
|
456
|
+
queueKey: item.queueKey,
|
|
457
|
+
queueName: item.queueName
|
|
458
|
+
}));
|
|
459
|
+
return keys.length > 0 ? keys : void 0;
|
|
460
|
+
} catch {
|
|
461
|
+
return void 0;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
444
464
|
function buildToolMetadata(registry) {
|
|
445
465
|
return Object.values(registry).map((tool) => {
|
|
446
466
|
const toolConfig = tool.config ?? {};
|
|
@@ -578,7 +598,10 @@ function createCallToolHandler(registry, state, onMaxRequests) {
|
|
|
578
598
|
appInstallationId: trigger === "provision" ? void 0 : rawContext.appInstallationId,
|
|
579
599
|
invocation,
|
|
580
600
|
isProvisionContext: trigger === "provision",
|
|
581
|
-
preAcquiredLeases: parsePreAcquiredLeases(toolEnv.SKEDYUL_RATE_LIMIT_LEASES)
|
|
601
|
+
preAcquiredLeases: parsePreAcquiredLeases(toolEnv.SKEDYUL_RATE_LIMIT_LEASES),
|
|
602
|
+
heldMutexQueueKeys: parseHeldMutexQueueKeys(
|
|
603
|
+
toolEnv.SKEDYUL_HELD_MUTEX_QUEUE_KEYS
|
|
604
|
+
)
|
|
582
605
|
};
|
|
583
606
|
const functionResult = await runWithConfig(requestConfig, async () => {
|
|
584
607
|
return await runWithRateLimitExecutionContext(rateLimitContext, async () => {
|
|
@@ -402,6 +402,26 @@ function parsePreAcquiredLeases(raw) {
|
|
|
402
402
|
return void 0;
|
|
403
403
|
}
|
|
404
404
|
}
|
|
405
|
+
function parseHeldMutexQueueKeys(raw) {
|
|
406
|
+
if (!raw) {
|
|
407
|
+
return void 0;
|
|
408
|
+
}
|
|
409
|
+
try {
|
|
410
|
+
const parsed = JSON.parse(raw);
|
|
411
|
+
if (!Array.isArray(parsed)) {
|
|
412
|
+
return void 0;
|
|
413
|
+
}
|
|
414
|
+
const keys = parsed.filter(
|
|
415
|
+
(item) => item != null && typeof item === "object" && typeof item.queueKey === "string" && typeof item.queueName === "string"
|
|
416
|
+
).map((item) => ({
|
|
417
|
+
queueKey: item.queueKey,
|
|
418
|
+
queueName: item.queueName
|
|
419
|
+
}));
|
|
420
|
+
return keys.length > 0 ? keys : void 0;
|
|
421
|
+
} catch {
|
|
422
|
+
return void 0;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
405
425
|
function buildToolMetadata(registry) {
|
|
406
426
|
return Object.values(registry).map((tool) => {
|
|
407
427
|
const toolConfig = tool.config ?? {};
|
|
@@ -539,7 +559,10 @@ function createCallToolHandler(registry, state, onMaxRequests) {
|
|
|
539
559
|
appInstallationId: trigger === "provision" ? void 0 : rawContext.appInstallationId,
|
|
540
560
|
invocation,
|
|
541
561
|
isProvisionContext: trigger === "provision",
|
|
542
|
-
preAcquiredLeases: parsePreAcquiredLeases(toolEnv.SKEDYUL_RATE_LIMIT_LEASES)
|
|
562
|
+
preAcquiredLeases: parsePreAcquiredLeases(toolEnv.SKEDYUL_RATE_LIMIT_LEASES),
|
|
563
|
+
heldMutexQueueKeys: parseHeldMutexQueueKeys(
|
|
564
|
+
toolEnv.SKEDYUL_HELD_MUTEX_QUEUE_KEYS
|
|
565
|
+
)
|
|
543
566
|
};
|
|
544
567
|
const functionResult = await runWithConfig(requestConfig, async () => {
|
|
545
568
|
return await runWithRateLimitExecutionContext(rateLimitContext, async () => {
|