ofiere-openclaw-plugin 4.56.5 → 4.56.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/src/tools.js +42 -0
- package/package.json +1 -1
- package/src/tools.ts +44 -0
package/dist/src/tools.js
CHANGED
|
@@ -254,6 +254,7 @@ function registerTaskOps(api, supabase, userId, resolveAgent, timezone) {
|
|
|
254
254
|
` • "every N weeks" → recurrence_type:"weekly" + recurrence_interval:N\n` +
|
|
255
255
|
` • "every N months" → recurrence_type:"monthly" + recurrence_interval:N\n` +
|
|
256
256
|
`🛑 NEVER use daily+1 (or hourly+24, etc.) when the user said a minute-level cadence — the task will fire ONCE per 24h instead of every N minutes. This was a real prod bug (v1 smoke 2026-05-17).\n` +
|
|
257
|
+
`🚦 BURST GUARD (Fix #3, 2026-05-17): when you create >3 recurring tasks within 60s for the same user, the server auto-staggers each new task's first fire by (count × min(recurrence_interval_secs, 1800)) so 6+ tasks with a shared T0 anchor do NOT all burst on the same pg_cron tick. Pass distinct start_date values per task if you need precise first-fire times.\n` +
|
|
257
258
|
`\n` +
|
|
258
259
|
`Approvals: Use add_approval to request sign-off from humans or agents. Approvals are separate from workflow gate nodes.\n` +
|
|
259
260
|
`Status: PENDING, IN_PROGRESS, DONE, FAILED | Priority: 0=LOW, 1=MEDIUM, 2=HIGH, 3=CRITICAL`,
|
|
@@ -593,6 +594,21 @@ function humanizeCadence(type, interval) {
|
|
|
593
594
|
const unit = unitMap[type] || type;
|
|
594
595
|
return interval === 1 ? `every ${unit}` : `every ${interval} ${unit}s`;
|
|
595
596
|
}
|
|
597
|
+
// Fix #3 (2026-05-17): bulk-create stagger helper. Maps recurrence_type +
|
|
598
|
+
// interval to cadence in seconds; used to compute the per-slot offset
|
|
599
|
+
// applied when a user bulk-creates >3 recurring scheduler_events within a
|
|
600
|
+
// 60s window (see handleCreateTask below). monthly approximated as 30 days.
|
|
601
|
+
function recurrenceIntervalSeconds(rtype, ri) {
|
|
602
|
+
const safe = Math.max(1, ri || 1);
|
|
603
|
+
switch (rtype) {
|
|
604
|
+
case "minutely": return 60 * safe;
|
|
605
|
+
case "hourly": return 3600 * safe;
|
|
606
|
+
case "daily": return 86400 * safe;
|
|
607
|
+
case "weekly": return 604800 * safe;
|
|
608
|
+
case "monthly": return 2592000 * safe;
|
|
609
|
+
default: return 60;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
596
612
|
function validateRecurrence(params) {
|
|
597
613
|
const rtRaw = params.recurrence_type ?? "none";
|
|
598
614
|
const rt = rtRaw.toLowerCase();
|
|
@@ -844,6 +860,32 @@ async function handleCreateTask(supabase, userId, resolveAgent, params, fallback
|
|
|
844
860
|
if (nextRunAtEpoch <= nowEpoch) {
|
|
845
861
|
nextRunAtEpoch = nowEpoch + 60 + Math.floor(Math.random() * 300);
|
|
846
862
|
}
|
|
863
|
+
// Fix #3 (2026-05-17): bulk-recurring stagger safety net. Fix #2
|
|
864
|
+
// only jitters past-time bumps; an LLM that intentionally seeds
|
|
865
|
+
// 6+ recurring tasks with a shared FUTURE T0 anchor still bursts
|
|
866
|
+
// every tick. Detect the burst by counting recent recurring
|
|
867
|
+
// scheduler_events for this user (last 60s); once >3 exist, push
|
|
868
|
+
// the new task's first fire by (recent × per-slot) seconds where
|
|
869
|
+
// per-slot = min(recurrence_interval_secs, 1800). For 6 minutely
|
|
870
|
+
// creates this spreads first-fires across ~5 min instead of one
|
|
871
|
+
// pg_cron tick. Cap per-slot at 30min so long-interval cadences
|
|
872
|
+
// (hourly/daily) don't push first fires hours out.
|
|
873
|
+
const rTypeStr = params.recurrence_type || "none";
|
|
874
|
+
const rIntervalNum = params.recurrence_interval || 1;
|
|
875
|
+
if (rTypeStr !== "none" && rIntervalNum > 0) {
|
|
876
|
+
const sinceISO = new Date(Date.now() - 60_000).toISOString();
|
|
877
|
+
const { count: recentCount } = await supabase.from("scheduler_events")
|
|
878
|
+
.select("id", { count: "exact", head: true })
|
|
879
|
+
.eq("user_id", userId)
|
|
880
|
+
.neq("recurrence_type", "none")
|
|
881
|
+
.gte("created_at", sinceISO);
|
|
882
|
+
const recent = recentCount ?? 0;
|
|
883
|
+
if (recent >= 3) {
|
|
884
|
+
const ivSecs = recurrenceIntervalSeconds(rTypeStr, rIntervalNum);
|
|
885
|
+
const perSlotSecs = Math.min(ivSecs, 1800);
|
|
886
|
+
nextRunAtEpoch += recent * perSlotSecs;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
847
889
|
await supabase.from("scheduler_events").insert({
|
|
848
890
|
id: crypto.randomUUID(),
|
|
849
891
|
user_id: userId,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ofiere-openclaw-plugin",
|
|
3
|
-
"version": "4.56.
|
|
3
|
+
"version": "4.56.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "OpenClaw plugin for Ofiere PM - 18 meta-tools covering tasks, agents, projects, scheduling, knowledge, workflows, notifications, memory, prompts, constellation, space file management, execution plan builder, SOP management, agent brain, talent management, corporate frameworks, agent office canvas, and PM gate approvals",
|
|
6
6
|
"keywords": [
|
package/src/tools.ts
CHANGED
|
@@ -292,6 +292,7 @@ function registerTaskOps(
|
|
|
292
292
|
` • "every N weeks" → recurrence_type:"weekly" + recurrence_interval:N\n` +
|
|
293
293
|
` • "every N months" → recurrence_type:"monthly" + recurrence_interval:N\n` +
|
|
294
294
|
`🛑 NEVER use daily+1 (or hourly+24, etc.) when the user said a minute-level cadence — the task will fire ONCE per 24h instead of every N minutes. This was a real prod bug (v1 smoke 2026-05-17).\n` +
|
|
295
|
+
`🚦 BURST GUARD (Fix #3, 2026-05-17): when you create >3 recurring tasks within 60s for the same user, the server auto-staggers each new task's first fire by (count × min(recurrence_interval_secs, 1800)) so 6+ tasks with a shared T0 anchor do NOT all burst on the same pg_cron tick. Pass distinct start_date values per task if you need precise first-fire times.\n` +
|
|
295
296
|
`\n` +
|
|
296
297
|
`Approvals: Use add_approval to request sign-off from humans or agents. Approvals are separate from workflow gate nodes.\n` +
|
|
297
298
|
`Status: PENDING, IN_PROGRESS, DONE, FAILED | Priority: 0=LOW, 1=MEDIUM, 2=HIGH, 3=CRITICAL`,
|
|
@@ -660,6 +661,22 @@ function humanizeCadence(type: string, interval: number): string {
|
|
|
660
661
|
return interval === 1 ? `every ${unit}` : `every ${interval} ${unit}s`;
|
|
661
662
|
}
|
|
662
663
|
|
|
664
|
+
// Fix #3 (2026-05-17): bulk-create stagger helper. Maps recurrence_type +
|
|
665
|
+
// interval to cadence in seconds; used to compute the per-slot offset
|
|
666
|
+
// applied when a user bulk-creates >3 recurring scheduler_events within a
|
|
667
|
+
// 60s window (see handleCreateTask below). monthly approximated as 30 days.
|
|
668
|
+
function recurrenceIntervalSeconds(rtype: string, ri: number): number {
|
|
669
|
+
const safe = Math.max(1, ri || 1);
|
|
670
|
+
switch (rtype) {
|
|
671
|
+
case "minutely": return 60 * safe;
|
|
672
|
+
case "hourly": return 3600 * safe;
|
|
673
|
+
case "daily": return 86400 * safe;
|
|
674
|
+
case "weekly": return 604800 * safe;
|
|
675
|
+
case "monthly": return 2592000 * safe;
|
|
676
|
+
default: return 60;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
663
680
|
function validateRecurrence(
|
|
664
681
|
params: Record<string, unknown>,
|
|
665
682
|
): { ok: true } | { ok: false; reason: string } {
|
|
@@ -941,6 +958,33 @@ async function handleCreateTask(
|
|
|
941
958
|
nextRunAtEpoch = nowEpoch + 60 + Math.floor(Math.random() * 300);
|
|
942
959
|
}
|
|
943
960
|
|
|
961
|
+
// Fix #3 (2026-05-17): bulk-recurring stagger safety net. Fix #2
|
|
962
|
+
// only jitters past-time bumps; an LLM that intentionally seeds
|
|
963
|
+
// 6+ recurring tasks with a shared FUTURE T0 anchor still bursts
|
|
964
|
+
// every tick. Detect the burst by counting recent recurring
|
|
965
|
+
// scheduler_events for this user (last 60s); once >3 exist, push
|
|
966
|
+
// the new task's first fire by (recent × per-slot) seconds where
|
|
967
|
+
// per-slot = min(recurrence_interval_secs, 1800). For 6 minutely
|
|
968
|
+
// creates this spreads first-fires across ~5 min instead of one
|
|
969
|
+
// pg_cron tick. Cap per-slot at 30min so long-interval cadences
|
|
970
|
+
// (hourly/daily) don't push first fires hours out.
|
|
971
|
+
const rTypeStr = (params.recurrence_type as string) || "none";
|
|
972
|
+
const rIntervalNum = (params.recurrence_interval as number) || 1;
|
|
973
|
+
if (rTypeStr !== "none" && rIntervalNum > 0) {
|
|
974
|
+
const sinceISO = new Date(Date.now() - 60_000).toISOString();
|
|
975
|
+
const { count: recentCount } = await supabase.from("scheduler_events")
|
|
976
|
+
.select("id", { count: "exact", head: true })
|
|
977
|
+
.eq("user_id", userId)
|
|
978
|
+
.neq("recurrence_type", "none")
|
|
979
|
+
.gte("created_at", sinceISO);
|
|
980
|
+
const recent = recentCount ?? 0;
|
|
981
|
+
if (recent >= 3) {
|
|
982
|
+
const ivSecs = recurrenceIntervalSeconds(rTypeStr, rIntervalNum);
|
|
983
|
+
const perSlotSecs = Math.min(ivSecs, 1800);
|
|
984
|
+
nextRunAtEpoch += recent * perSlotSecs;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
|
|
944
988
|
await supabase.from("scheduler_events").insert({
|
|
945
989
|
id: crypto.randomUUID(),
|
|
946
990
|
user_id: userId,
|