clay-server 2.20.0-beta.1 → 2.20.0-beta.2

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.
@@ -13,6 +13,7 @@ var slashActiveIdx = -1;
13
13
  var slashFiltered = [];
14
14
  var isComposing = false;
15
15
  var isRemoteInput = false;
16
+ var scheduleDelayMs = 0; // 0 = no schedule, >0 = delay in ms
16
17
 
17
18
  export function hasSendableContent() {
18
19
  return !!(
@@ -23,6 +24,36 @@ export function hasSendableContent() {
23
24
  );
24
25
  }
25
26
 
27
+ export function getScheduleDelay() {
28
+ return scheduleDelayMs;
29
+ }
30
+
31
+ export function clearScheduleDelay() {
32
+ scheduleDelayMs = 0;
33
+ var btn = document.getElementById("schedule-btn");
34
+ if (btn) {
35
+ btn.classList.remove("schedule-active", "schedule-expanded");
36
+ var lbl = btn.querySelector(".schedule-delay-label");
37
+ if (lbl) lbl.remove();
38
+ var inp = btn.querySelector(".schedule-inline-input");
39
+ if (inp) inp.remove();
40
+ btn.title = "Schedule message";
41
+ }
42
+ }
43
+
44
+ export function setScheduleBtnDisabled(disabled) {
45
+ var btn = document.getElementById("schedule-btn");
46
+ if (!btn) return;
47
+ btn.disabled = disabled;
48
+ if (disabled) {
49
+ btn.style.opacity = "0.3";
50
+ btn.style.pointerEvents = "none";
51
+ } else {
52
+ btn.style.opacity = "";
53
+ btn.style.pointerEvents = "";
54
+ }
55
+ }
56
+
26
57
  export var builtinCommands = [
27
58
  { name: "clear", desc: "Clear conversation" },
28
59
  { name: "context", desc: "Context window usage" },
@@ -131,19 +162,17 @@ export function sendMessage() {
131
162
 
132
163
  var pastes = pendingPastes.map(function (p) { return p.text; });
133
164
 
134
- // Schedule mode: queue message for after rate limit resets
135
- if (ctx.isScheduleMode && ctx.isScheduleMode()) {
136
- var resetsAt = ctx.getRateLimitResetsAt ? ctx.getRateLimitResetsAt() : null;
137
- if (resetsAt && resetsAt > Date.now()) {
138
- ctx.ws.send(JSON.stringify({ type: "schedule_message", text: text || "", resetsAt: resetsAt }));
139
- if (ctx.exitScheduleMode) ctx.exitScheduleMode();
140
- ctx.inputEl.value = "";
141
- sendInputSync();
142
- clearPendingImages();
143
- autoResize();
144
- ctx.inputEl.focus();
145
- return;
146
- }
165
+ // Scheduled message: queue message with timer delay
166
+ if (scheduleDelayMs > 0) {
167
+ var resetsAt = Date.now() + scheduleDelayMs;
168
+ ctx.ws.send(JSON.stringify({ type: "schedule_message", text: text || "", resetsAt: resetsAt }));
169
+ clearScheduleDelay();
170
+ ctx.inputEl.value = "";
171
+ sendInputSync();
172
+ clearPendingImages();
173
+ autoResize();
174
+ ctx.inputEl.focus();
175
+ return;
147
176
  }
148
177
 
149
178
  ctx.addUserMessage(text, images.length > 0 ? images : null, pastes.length > 0 ? pastes : null);
@@ -578,6 +607,111 @@ export function initInput(_ctx) {
578
607
  });
579
608
  }
580
609
 
610
+ // Schedule button — inline expand with minute input
611
+ var scheduleBtn = document.getElementById("schedule-btn");
612
+ var scheduleInlineInput = null;
613
+ var scheduleInlineLabel = null;
614
+ var scheduleOutsideHandler = null;
615
+
616
+ function formatDelayLabel(ms) {
617
+ var mins = Math.round(ms / 60000);
618
+ if (mins < 60) return mins + "m";
619
+ var hrs = Math.floor(mins / 60);
620
+ var rem = mins % 60;
621
+ return rem > 0 ? hrs + "h " + rem + "m" : hrs + "h";
622
+ }
623
+
624
+ function collapseScheduleBtn() {
625
+ if (!scheduleBtn) return;
626
+ scheduleBtn.classList.remove("schedule-expanded");
627
+ if (scheduleInlineInput) { scheduleInlineInput.remove(); scheduleInlineInput = null; }
628
+ if (scheduleInlineLabel) { scheduleInlineLabel.remove(); scheduleInlineLabel = null; }
629
+ if (scheduleOutsideHandler) {
630
+ document.removeEventListener("mousedown", scheduleOutsideHandler);
631
+ scheduleOutsideHandler = null;
632
+ }
633
+ }
634
+
635
+ function setScheduleDelay(ms) {
636
+ scheduleDelayMs = ms;
637
+ if (!scheduleBtn) return;
638
+ collapseScheduleBtn();
639
+ if (ms > 0) {
640
+ scheduleBtn.classList.add("schedule-active", "schedule-expanded");
641
+ scheduleBtn.title = "Scheduled: " + formatDelayLabel(ms) + " (click to clear)";
642
+ scheduleInlineLabel = document.createElement("span");
643
+ scheduleInlineLabel.className = "schedule-delay-label";
644
+ scheduleInlineLabel.textContent = formatDelayLabel(ms);
645
+ scheduleBtn.appendChild(scheduleInlineLabel);
646
+ } else {
647
+ scheduleBtn.classList.remove("schedule-active", "schedule-expanded");
648
+ scheduleBtn.title = "Schedule message";
649
+ }
650
+ }
651
+
652
+ function expandScheduleInput() {
653
+ if (!scheduleBtn) return;
654
+ scheduleBtn.classList.add("schedule-expanded");
655
+ scheduleInlineInput = document.createElement("input");
656
+ scheduleInlineInput.type = "number";
657
+ scheduleInlineInput.min = "1";
658
+ scheduleInlineInput.max = "1440";
659
+ scheduleInlineInput.placeholder = "min";
660
+ scheduleInlineInput.className = "schedule-inline-input";
661
+ scheduleBtn.appendChild(scheduleInlineInput);
662
+
663
+ setTimeout(function () { scheduleInlineInput.focus(); }, 0);
664
+
665
+ scheduleInlineInput.addEventListener("click", function (e) { e.stopPropagation(); });
666
+ scheduleInlineInput.addEventListener("keydown", function (e) {
667
+ if (e.key === "Enter") {
668
+ e.preventDefault();
669
+ var val = parseInt(scheduleInlineInput.value, 10);
670
+ if (val >= 1 && val <= 1440) {
671
+ setScheduleDelay(val * 60000);
672
+ } else {
673
+ collapseScheduleBtn();
674
+ }
675
+ } else if (e.key === "Escape") {
676
+ collapseScheduleBtn();
677
+ }
678
+ });
679
+
680
+ // Close on outside click
681
+ setTimeout(function () {
682
+ scheduleOutsideHandler = function (ev) {
683
+ if (!scheduleBtn.contains(ev.target)) {
684
+ if (scheduleInlineInput) {
685
+ var val = parseInt(scheduleInlineInput.value, 10);
686
+ if (val >= 1 && val <= 1440) {
687
+ setScheduleDelay(val * 60000);
688
+ } else {
689
+ collapseScheduleBtn();
690
+ }
691
+ }
692
+ document.removeEventListener("mousedown", scheduleOutsideHandler);
693
+ scheduleOutsideHandler = null;
694
+ }
695
+ };
696
+ document.addEventListener("mousedown", scheduleOutsideHandler);
697
+ }, 0);
698
+ }
699
+
700
+ if (scheduleBtn) {
701
+ scheduleBtn.addEventListener("click", function (e) {
702
+ e.stopPropagation();
703
+ if (scheduleDelayMs > 0) {
704
+ setScheduleDelay(0);
705
+ return;
706
+ }
707
+ if (scheduleInlineInput) {
708
+ collapseScheduleBtn();
709
+ } else {
710
+ expandScheduleInput();
711
+ }
712
+ });
713
+ }
714
+
581
715
  // Paste handler
582
716
  document.addEventListener("paste", function (e) {
583
717
  // Don't intercept paste when typing in modals or other non-chat inputs
@@ -961,7 +961,19 @@ function renderSheetSessions(listEl) {
961
961
  })(cachedProjectList[ci]);
962
962
  }
963
963
 
964
- for (var mi = 0; mi < cachedMates.length; mi++) {
964
+ var favoriteChipMates = cachedMates.filter(function (m) {
965
+ if (cachedDmRemovedUsers[m.id]) return false;
966
+ if (cachedDmFavorites.indexOf(m.id) !== -1) return true;
967
+ if (cachedDmUnread[m.id] && cachedDmUnread[m.id] > 0) return true;
968
+ return false;
969
+ });
970
+ var sortedChipMates = favoriteChipMates.sort(function (a, b) {
971
+ var aBuiltin = a.builtinKey ? 1 : 0;
972
+ var bBuiltin = b.builtinKey ? 1 : 0;
973
+ if (aBuiltin !== bBuiltin) return bBuiltin - aBuiltin;
974
+ return (a.createdAt || 0) - (b.createdAt || 0);
975
+ });
976
+ for (var mi = 0; mi < sortedChipMates.length; mi++) {
965
977
  (function (mate) {
966
978
  var mp = mate.profile || {};
967
979
  var chip = document.createElement("button");
@@ -1000,7 +1012,7 @@ function renderSheetSessions(listEl) {
1000
1012
  }
1001
1013
 
1002
1014
  chips.push(chip);
1003
- })(cachedMates[mi]);
1015
+ })(sortedChipMates[mi]);
1004
1016
  }
1005
1017
 
1006
1018
  for (var i = 0; i < chips.length; i++) {
@@ -2419,15 +2431,13 @@ function showMateCtxMenu(anchorEl, mate) {
2419
2431
  menu.appendChild(editItem);
2420
2432
 
2421
2433
  var removeItem = document.createElement("button");
2422
- removeItem.className = "project-ctx-item project-ctx-delete";
2423
- removeItem.innerHTML = iconHtml("trash-2") + " <span>Remove Mate</span>";
2434
+ removeItem.className = "project-ctx-item";
2435
+ removeItem.innerHTML = iconHtml("star-off") + " <span>Remove from favorites</span>";
2424
2436
  removeItem.addEventListener("click", function (e) {
2425
2437
  e.stopPropagation();
2426
- var iconRect = anchorEl.getBoundingClientRect();
2427
- spawnDustParticles(iconRect.left + iconRect.width / 2, iconRect.top + iconRect.height / 2);
2428
2438
  closeUserCtxMenu();
2429
2439
  if (ctx.sendWs) {
2430
- ctx.sendWs({ type: "mate_delete", mateId: mate.id });
2440
+ ctx.sendWs({ type: "dm_remove_favorite", targetUserId: mate.id });
2431
2441
  }
2432
2442
  });
2433
2443
  menu.appendChild(removeItem);
@@ -3800,8 +3810,20 @@ export function renderUserStrip(allUsers, onlineUserIds, myUserId, dmFavorites,
3800
3810
  }
3801
3811
  }
3802
3812
 
3803
- // Render mates
3804
- for (var mi = 0; mi < cachedMates.length; mi++) {
3813
+ // Render mates (only favorites, built-in first, then user-created)
3814
+ var favoriteMates = cachedMates.filter(function (m) {
3815
+ if (cachedDmRemovedUsers[m.id]) return false;
3816
+ if (cachedDmFavorites.indexOf(m.id) !== -1) return true;
3817
+ if (cachedDmUnread[m.id] && cachedDmUnread[m.id] > 0) return true;
3818
+ return false;
3819
+ });
3820
+ var sortedMates = favoriteMates.sort(function (a, b) {
3821
+ var aBuiltin = a.builtinKey ? 1 : 0;
3822
+ var bBuiltin = b.builtinKey ? 1 : 0;
3823
+ if (aBuiltin !== bBuiltin) return bBuiltin - aBuiltin;
3824
+ return (a.createdAt || 0) - (b.createdAt || 0);
3825
+ });
3826
+ for (var mi = 0; mi < sortedMates.length; mi++) {
3805
3827
  (function (mate) {
3806
3828
  var mp = mate.profile || {};
3807
3829
  var mateSlug = "mate-" + mate.id;
@@ -3876,7 +3898,7 @@ export function renderUserStrip(allUsers, onlineUserIds, myUserId, dmFavorites,
3876
3898
  });
3877
3899
 
3878
3900
  container.appendChild(el);
3879
- })(cachedMates[mi]);
3901
+ })(sortedMates[mi]);
3880
3902
  }
3881
3903
 
3882
3904
  // Show container if we have mates even with no other users
@@ -3913,13 +3935,12 @@ function toggleDmUserPicker(anchorEl) {
3913
3935
  var searchInput = document.createElement("input");
3914
3936
  searchInput.className = "dm-user-picker-search";
3915
3937
  searchInput.type = "text";
3916
- searchInput.placeholder = "Search users...";
3938
+ searchInput.placeholder = "Search mates and users...";
3917
3939
  picker.appendChild(searchInput);
3918
3940
 
3919
- // Scrollable list
3941
+ // User list element (appended later, after USERS label)
3920
3942
  var listEl = document.createElement("div");
3921
3943
  listEl.className = "dm-user-picker-list";
3922
- picker.appendChild(listEl);
3923
3944
 
3924
3945
  // Position the picker above the + button
3925
3946
  document.body.appendChild(picker);
@@ -3976,10 +3997,193 @@ function toggleDmUserPicker(anchorEl) {
3976
3997
  }
3977
3998
  }
3978
3999
 
4000
+ // --- MATES section ---
4001
+ var matesSectionLabel = document.createElement("div");
4002
+ matesSectionLabel.className = "dm-user-picker-section";
4003
+ matesSectionLabel.textContent = "Mates";
4004
+ picker.appendChild(matesSectionLabel);
4005
+
4006
+ var matesListEl = document.createElement("div");
4007
+ matesListEl.className = "dm-user-picker-list dm-mates-list";
4008
+ picker.appendChild(matesListEl);
4009
+
4010
+ // Update scroll gradient hint
4011
+ function updateMatesScrollHint() {
4012
+ var isOverflow = matesListEl.scrollHeight > matesListEl.clientHeight + 2;
4013
+ if (!isOverflow) {
4014
+ matesListEl.classList.add("no-overflow");
4015
+ matesListEl.classList.remove("scrolled-bottom");
4016
+ return;
4017
+ }
4018
+ matesListEl.classList.remove("no-overflow");
4019
+ var atBottom = matesListEl.scrollTop + matesListEl.clientHeight >= matesListEl.scrollHeight - 4;
4020
+ if (atBottom) {
4021
+ matesListEl.classList.add("scrolled-bottom");
4022
+ } else {
4023
+ matesListEl.classList.remove("scrolled-bottom");
4024
+ }
4025
+ }
4026
+ matesListEl.addEventListener("scroll", updateMatesScrollHint);
4027
+
4028
+ function renderMatesList(filter) {
4029
+ matesListEl.innerHTML = "";
4030
+ var allMates = cachedMates || [];
4031
+ if (filter) {
4032
+ var lf = filter.toLowerCase();
4033
+ allMates = allMates.filter(function (m) {
4034
+ var name = (m.profile && m.profile.displayName) || m.name || "";
4035
+ return name.toLowerCase().indexOf(lf) !== -1;
4036
+ });
4037
+ }
4038
+ // Build unified list: installed builtins, deleted builtins, user-created
4039
+ var availBuiltins = (ctx.availableBuiltins && ctx.availableBuiltins()) || [];
4040
+ var entries = [];
4041
+ // 1. Installed builtin mates
4042
+ for (var si = 0; si < allMates.length; si++) {
4043
+ if (allMates[si].builtinKey) entries.push({ type: "mate", data: allMates[si] });
4044
+ }
4045
+ // 2. Deleted builtins (only when not filtering)
4046
+ if (!filter) {
4047
+ for (var di = 0; di < availBuiltins.length; di++) {
4048
+ entries.push({ type: "deleted", data: availBuiltins[di] });
4049
+ }
4050
+ }
4051
+ // 3. User-created mates
4052
+ var userMates = allMates.filter(function (m) { return !m.builtinKey; });
4053
+ userMates.sort(function (a, b) { return (a.createdAt || 0) - (b.createdAt || 0); });
4054
+ for (var ui = 0; ui < userMates.length; ui++) {
4055
+ entries.push({ type: "mate", data: userMates[ui] });
4056
+ }
4057
+
4058
+ for (var i = 0; i < entries.length; i++) {
4059
+ var entry = entries[i];
4060
+ if (entry.type === "deleted") {
4061
+ // Deleted builtin: show with "+ Add" button
4062
+ (function (b) {
4063
+ var bItem = document.createElement("div");
4064
+ bItem.className = "dm-user-picker-item dm-user-picker-builtin-item";
4065
+ bItem.style.opacity = "0.5";
4066
+ var bAv = document.createElement("img");
4067
+ bAv.className = "dm-user-picker-avatar";
4068
+ bAv.src = b.avatarCustom || "";
4069
+ bAv.alt = b.displayName;
4070
+ bItem.appendChild(bAv);
4071
+ var bNameWrap = document.createElement("div");
4072
+ bNameWrap.style.cssText = "flex:1;min-width:0;";
4073
+ var bName = document.createElement("span");
4074
+ bName.className = "dm-user-picker-name";
4075
+ bName.textContent = b.displayName;
4076
+ bNameWrap.appendChild(bName);
4077
+ var bBio = document.createElement("div");
4078
+ bBio.style.cssText = "font-size:11px;color:var(--text-dimmer);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;";
4079
+ bBio.textContent = "Deleted";
4080
+ bNameWrap.appendChild(bBio);
4081
+ bItem.appendChild(bNameWrap);
4082
+ var bAddBtn = document.createElement("button");
4083
+ bAddBtn.style.cssText = "border:none;background:none;cursor:pointer;padding:2px 6px;color:var(--accent, #6366f1);font-size:12px;font-weight:600;white-space:nowrap;";
4084
+ bAddBtn.textContent = "+ Add";
4085
+ bAddBtn.title = "Re-add " + b.displayName;
4086
+ bAddBtn.addEventListener("click", function (e) {
4087
+ e.stopPropagation();
4088
+ if (ctx.sendWs) ctx.sendWs({ type: "mate_readd_builtin", builtinKey: b.key });
4089
+ closeDmUserPicker();
4090
+ });
4091
+ bItem.appendChild(bAddBtn);
4092
+ bItem.addEventListener("click", function () {
4093
+ if (ctx.sendWs) ctx.sendWs({ type: "mate_readd_builtin", builtinKey: b.key });
4094
+ closeDmUserPicker();
4095
+ });
4096
+ matesListEl.appendChild(bItem);
4097
+ })(entry.data);
4098
+ } else {
4099
+ // Normal mate
4100
+ (function (m) {
4101
+ var mp = m.profile || {};
4102
+ var isFav = cachedDmFavorites.indexOf(m.id) !== -1;
4103
+ var item = document.createElement("div");
4104
+ item.className = "dm-user-picker-item";
4105
+ if (isFav) item.classList.add("dm-picker-fav");
4106
+ var av = document.createElement("img");
4107
+ av.className = "dm-user-picker-avatar";
4108
+ av.src = mateAvatarUrl(m, 28);
4109
+ av.alt = mp.displayName || m.name || "Mate";
4110
+ item.appendChild(av);
4111
+ var nameWrap = document.createElement("div");
4112
+ nameWrap.style.cssText = "flex:1;min-width:0;";
4113
+ var name = document.createElement("span");
4114
+ name.className = "dm-user-picker-name";
4115
+ name.textContent = mp.displayName || m.name || "Mate";
4116
+ nameWrap.appendChild(name);
4117
+ if (m.bio) {
4118
+ var bio = document.createElement("div");
4119
+ bio.style.cssText = "font-size:11px;color:var(--text-dimmer);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;";
4120
+ bio.textContent = m.bio;
4121
+ nameWrap.appendChild(bio);
4122
+ }
4123
+ item.appendChild(nameWrap);
4124
+ // Delete button with inline confirm
4125
+ var delBtn = document.createElement("button");
4126
+ delBtn.className = "dm-picker-del-btn";
4127
+ delBtn.innerHTML = iconHtml("trash-2");
4128
+ delBtn.title = "Delete mate";
4129
+ delBtn.addEventListener("click", function (e) {
4130
+ e.stopPropagation();
4131
+ var origHtml = item.innerHTML;
4132
+ item.innerHTML = "";
4133
+ item.style.justifyContent = "center";
4134
+ item.style.gap = "6px";
4135
+ var confirmMsg = document.createElement("span");
4136
+ confirmMsg.style.cssText = "font-size:12px;color:var(--text-dimmer);";
4137
+ confirmMsg.textContent = m.builtinKey ? "Delete? You can re-add later." : "Delete permanently?";
4138
+ item.appendChild(confirmMsg);
4139
+ var yesBtn = document.createElement("button");
4140
+ yesBtn.style.cssText = "border:none;background:var(--danger,#e74c3c);color:#fff;padding:3px 10px;border-radius:4px;font-size:12px;cursor:pointer;";
4141
+ yesBtn.textContent = "Delete";
4142
+ yesBtn.addEventListener("click", function (e2) {
4143
+ e2.stopPropagation();
4144
+ if (ctx.sendWs) ctx.sendWs({ type: "mate_delete", mateId: m.id });
4145
+ closeDmUserPicker();
4146
+ });
4147
+ item.appendChild(yesBtn);
4148
+ var noBtn = document.createElement("button");
4149
+ noBtn.style.cssText = "border:1px solid var(--border);background:none;color:var(--text);padding:3px 10px;border-radius:4px;font-size:12px;cursor:pointer;";
4150
+ noBtn.textContent = "Cancel";
4151
+ noBtn.addEventListener("click", function (e2) {
4152
+ e2.stopPropagation();
4153
+ item.innerHTML = origHtml;
4154
+ item.style.justifyContent = "";
4155
+ item.style.gap = "";
4156
+ refreshIcons();
4157
+ });
4158
+ item.appendChild(noBtn);
4159
+ });
4160
+ item.appendChild(delBtn);
4161
+ item.addEventListener("click", function () {
4162
+ if (ctx.openDm) ctx.openDm(m.id);
4163
+ if (!isFav && ctx.sendWs) ctx.sendWs({ type: "dm_add_favorite", targetUserId: m.id });
4164
+ closeDmUserPicker();
4165
+ });
4166
+ matesListEl.appendChild(item);
4167
+ })(entry.data);
4168
+ }
4169
+ }
4170
+
4171
+ if (entries.length === 0 && filter) {
4172
+ var emptyEl = document.createElement("div");
4173
+ emptyEl.className = "dm-user-picker-empty";
4174
+ emptyEl.textContent = "No mates found";
4175
+ matesListEl.appendChild(emptyEl);
4176
+ }
4177
+ refreshIcons();
4178
+ requestAnimationFrame(updateMatesScrollHint);
4179
+ }
4180
+
3979
4181
  // Create Mate option
3980
4182
  var createMateEl = document.createElement("div");
3981
4183
  createMateEl.className = "dm-user-picker-create-mate";
3982
- createMateEl.innerHTML = iconHtml("bot") + " <span>Create a Mate</span>";
4184
+ var hasCustomMates = (cachedMates || []).some(function (m) { return !m.builtinKey; });
4185
+ var createMateLabel = hasCustomMates ? "Create a Mate" : "Create a Mate for what you're doing";
4186
+ createMateEl.innerHTML = iconHtml("bot") + " <span>" + createMateLabel + "</span>";
3983
4187
  createMateEl.addEventListener("click", function () {
3984
4188
  closeDmUserPicker();
3985
4189
  if (ctx.openMateWizard) ctx.openMateWizard();
@@ -3997,10 +4201,14 @@ function toggleDmUserPicker(anchorEl) {
3997
4201
  sectionLabel.className = "dm-user-picker-section";
3998
4202
  sectionLabel.textContent = "Users";
3999
4203
  picker.appendChild(sectionLabel);
4204
+ picker.appendChild(listEl);
4000
4205
 
4206
+ renderMatesList("");
4001
4207
  renderPickerList("");
4002
4208
  searchInput.addEventListener("input", function () {
4003
- renderPickerList(searchInput.value);
4209
+ var val = searchInput.value;
4210
+ renderMatesList(val);
4211
+ renderPickerList(val);
4004
4212
  });
4005
4213
 
4006
4214
  // Focus search
package/lib/sdk-bridge.js CHANGED
@@ -999,23 +999,14 @@ function createSDKBridge(opts) {
999
999
  var workerACEnabled = session.onQueryComplete || (typeof opts.getAutoContinueSetting === "function" && opts.getAutoContinueSetting(session));
1000
1000
  if (session.rateLimitResetsAt && session.rateLimitResetsAt > Date.now()
1001
1001
  && workerACEnabled && !session.destroying) {
1002
- var wacDelay = session.rateLimitResetsAt - Date.now() + 3000;
1003
1002
  var wacResetsAt = session.rateLimitResetsAt;
1004
1003
  session.rateLimitResetsAt = null;
1005
1004
  session.rateLimitAutoContinuePending = true;
1006
1005
  workerDidScheduleAC = true;
1007
- console.log("[sdk-bridge] Rate limited (worker), scheduling auto-continue in " + Math.round(wacDelay / 1000) + "s for session " + session.localId);
1008
- sendAndRecord(session, { type: "auto_continue_scheduled", resetsAt: wacResetsAt });
1009
- session.autoContinueTimer = setTimeout(function () {
1010
- session.autoContinueTimer = null;
1011
- session.rateLimitAutoContinuePending = false;
1012
- if (session.destroying) return;
1013
- console.log("[sdk-bridge] Auto-continue (worker) firing for session " + session.localId);
1014
- session.isProcessing = true;
1015
- onProcessingChanged();
1016
- sendAndRecord(session, { type: "auto_continue_fired" });
1017
- startQuery(session, "continue", null, session.lastLinuxUser || null);
1018
- }, wacDelay);
1006
+ console.log("[sdk-bridge] Rate limited (worker), scheduling auto-continue via scheduleMessage for session " + session.localId);
1007
+ if (typeof opts.scheduleMessage === "function") {
1008
+ opts.scheduleMessage(session, "continue", wacResetsAt);
1009
+ }
1019
1010
  }
1020
1011
  if (session.onQueryComplete && !workerDidScheduleAC) {
1021
1012
  try { session.onQueryComplete(session); } catch (err) {
@@ -1439,23 +1430,14 @@ function createSDKBridge(opts) {
1439
1430
  var acEnabled = session.onQueryComplete || (typeof opts.getAutoContinueSetting === "function" && opts.getAutoContinueSetting(session));
1440
1431
  if (session.rateLimitResetsAt && session.rateLimitResetsAt > Date.now()
1441
1432
  && acEnabled && !session.destroying) {
1442
- var acDelay = session.rateLimitResetsAt - Date.now() + 3000;
1443
1433
  var acResetsAt = session.rateLimitResetsAt;
1444
1434
  session.rateLimitResetsAt = null;
1445
1435
  session.rateLimitAutoContinuePending = true;
1446
1436
  didScheduleAutoContinue = true;
1447
- console.log("[sdk-bridge] Rate limited, scheduling auto-continue in " + Math.round(acDelay / 1000) + "s for session " + session.localId);
1448
- sendAndRecord(session, { type: "auto_continue_scheduled", resetsAt: acResetsAt });
1449
- session.autoContinueTimer = setTimeout(function () {
1450
- session.autoContinueTimer = null;
1451
- session.rateLimitAutoContinuePending = false;
1452
- if (session.destroying) return;
1453
- console.log("[sdk-bridge] Auto-continue firing for session " + session.localId);
1454
- session.isProcessing = true;
1455
- onProcessingChanged();
1456
- sendAndRecord(session, { type: "auto_continue_fired" });
1457
- startQuery(session, "continue", null, session.lastLinuxUser || null);
1458
- }, acDelay);
1437
+ console.log("[sdk-bridge] Rate limited, scheduling auto-continue via scheduleMessage for session " + session.localId);
1438
+ if (typeof opts.scheduleMessage === "function") {
1439
+ opts.scheduleMessage(session, "continue", acResetsAt);
1440
+ }
1459
1441
  }
1460
1442
 
1461
1443
  // Ralph Loop: notify completion so loop orchestrator can proceed