@sider-ai/chrome-openclaw-sider 1.0.31 → 1.0.34

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.
@@ -32,6 +32,7 @@ import {
32
32
  parseUsageTotals,
33
33
  toRecord
34
34
  } from "./channel-util.js";
35
+ const NO_QUOTA_REMAINING_TEXT = "NO_QUOTA_REMAINING";
35
36
  function recordSiderPersistedAgentMessage(params) {
36
37
  const binding = resolveSiderSessionBinding(params.sessionKey);
37
38
  const runId = binding?.currentRunId;
@@ -63,6 +64,13 @@ function recordSiderPersistedAgentMessage(params) {
63
64
  runState.usage = fallbackUsage;
64
65
  }
65
66
  }
67
+ maybeSendNoQuotaRemainingError({
68
+ binding,
69
+ runId,
70
+ runState,
71
+ source: message,
72
+ context: "before_message_write.no_quota_remaining"
73
+ });
66
74
  const parsed = parseAssistantOutput(message);
67
75
  if (!parsed) {
68
76
  return;
@@ -166,6 +174,75 @@ function recordSiderLlmOutputUsage(params) {
166
174
  if (parsed && hasUsageTotals(parsed)) {
167
175
  runState.usage = parsed;
168
176
  }
177
+ if (binding) {
178
+ maybeSendNoQuotaRemainingError({
179
+ binding,
180
+ runId,
181
+ runState,
182
+ source: params.lastAssistant,
183
+ context: "llm_output.no_quota_remaining"
184
+ });
185
+ }
186
+ }
187
+ function maybeSendNoQuotaRemainingError(params) {
188
+ if (params.runState.noQuotaRemainingErrorSent || !isNoQuotaRemainingAssistantError(params.source)) {
189
+ return;
190
+ }
191
+ params.runState.noQuotaRemainingErrorSent = true;
192
+ const quotaPart = {
193
+ ...buildTextPart(NO_QUOTA_REMAINING_TEXT),
194
+ payload: {
195
+ text: NO_QUOTA_REMAINING_TEXT,
196
+ is_error: true
197
+ }
198
+ };
199
+ enqueueCompletedParts({
200
+ runId: params.runId,
201
+ parts: [
202
+ quotaPart
203
+ ],
204
+ sessionId: params.binding.sessionId,
205
+ accountId: params.binding.account.accountId,
206
+ provider: params.runState.provider,
207
+ model: params.runState.model
208
+ });
209
+ void (async () => {
210
+ try {
211
+ await flushQueuedPartsExceptTailForRun({
212
+ runId: params.runId,
213
+ account: params.binding.account,
214
+ sessionId: params.binding.sessionId,
215
+ context: `${params.context}.except_tail`
216
+ });
217
+ await flushFinalQueuedPartForRun({
218
+ runId: params.runId,
219
+ account: params.binding.account,
220
+ sessionId: params.binding.sessionId,
221
+ context: `${params.context}.final_tail`
222
+ });
223
+ } catch (err) {
224
+ logWarn("sider failed to persist NO_QUOTA_REMAINING final message", {
225
+ accountId: params.binding.account.accountId,
226
+ sessionId: params.binding.sessionId,
227
+ runId: params.runId,
228
+ context: params.context,
229
+ error: String(err)
230
+ });
231
+ }
232
+ })();
233
+ }
234
+ function isNoQuotaRemainingAssistantError(source) {
235
+ const assistant = toRecord(source);
236
+ if (!assistant) {
237
+ return false;
238
+ }
239
+ for (const key of ["errorBody", "errorMessage", "errorCode", "errorType", "content"]) {
240
+ const value = assistant[key];
241
+ if (typeof value === "string" && value.includes(NO_QUOTA_REMAINING_TEXT)) {
242
+ return true;
243
+ }
244
+ }
245
+ return false;
169
246
  }
170
247
  async function emitSiderToolHookEvent(params) {
171
248
  const binding = resolveSiderSessionBinding(params.sessionKey);
@@ -28,6 +28,7 @@ type DispatchedSiderParts = {
28
28
  messageId: string;
29
29
  conversationId: string;
30
30
  };
31
+ declare function buildSiderDispatchReceipt(dispatchResult: DispatchedSiderParts): Record<string, unknown>;
31
32
  declare function readSiderSendText(params: Record<string, unknown>): string | undefined;
32
33
  declare function collectSiderSendAttachments(params: Record<string, unknown>): NormalizedSiderAttachment[];
33
34
  declare function prepareSiderSendParts(params: {
@@ -70,4 +71,4 @@ declare function sendToSider(params: {
70
71
  conversationId: string;
71
72
  }>;
72
73
 
73
- export { type DispatchedSiderParts, type PreparedSiderSend, collectSiderSendAttachments, dispatchSiderParts, handleSiderSendAction, prepareSiderSendParts, readSiderSendText, sendToSider };
74
+ export { type DispatchedSiderParts, type PreparedSiderSend, buildSiderDispatchReceipt, collectSiderSendAttachments, dispatchSiderParts, handleSiderSendAction, prepareSiderSendParts, readSiderSendText, sendToSider };
@@ -20,6 +20,21 @@ import {
20
20
  startDeferredSiderUploads
21
21
  } from "./media-upload.js";
22
22
  import { buildTextPart } from "./channel-builders.js";
23
+ function buildSiderDispatchReceipt(dispatchResult) {
24
+ if (dispatchResult.kind === "sent") {
25
+ return {
26
+ ok: true,
27
+ status: "sent",
28
+ messageId: dispatchResult.messageId,
29
+ conversationId: dispatchResult.conversationId
30
+ };
31
+ }
32
+ return {
33
+ ok: true,
34
+ runId: dispatchResult.runId,
35
+ sessionKey: dispatchResult.sessionKey
36
+ };
37
+ }
23
38
  const SIDER_ATTACHMENT_SOURCE_KEYS = [
24
39
  "path",
25
40
  "filePath",
@@ -246,7 +261,7 @@ async function handleSiderSendAction(params) {
246
261
  })
247
262
  )
248
263
  );
249
- await dispatchSiderParts({
264
+ const dispatchResult = await dispatchSiderParts({
250
265
  account: prepared.account,
251
266
  sessionId: prepared.sessionId,
252
267
  parts: prepared.parts,
@@ -254,11 +269,13 @@ async function handleSiderSendAction(params) {
254
269
  allowMergeActiveRun: true,
255
270
  context: "action.send"
256
271
  });
272
+ const receipt = buildSiderDispatchReceipt(dispatchResult);
257
273
  if (fileResults.length === 0) {
258
- return { ok: true };
274
+ return receipt;
259
275
  }
260
276
  return {
261
277
  ...fileResults[0],
278
+ ...receipt,
262
279
  files: fileResults,
263
280
  fileCount: fileResults.length
264
281
  };
@@ -282,6 +299,7 @@ async function sendToSider(params) {
282
299
  };
283
300
  }
284
301
  export {
302
+ buildSiderDispatchReceipt,
285
303
  collectSiderSendAttachments,
286
304
  dispatchSiderParts,
287
305
  handleSiderSendAction,
@@ -93,6 +93,8 @@ type SiderRunState = {
93
93
  pendingFinalText?: string;
94
94
  pendingFinalThinking?: string;
95
95
  pendingFinalStopReason?: string;
96
+ noQuotaRemainingErrorSent?: boolean;
97
+ noQuotaRemainingFollowupErrorSuppressed?: boolean;
96
98
  updatedAt: number;
97
99
  };
98
100
  type SiderRealtimeLaneState = {
@@ -68,10 +68,12 @@ import {
68
68
  buildPendingMessageContextForRun,
69
69
  consumePendingMessageContextForRun,
70
70
  resolvePendingFinalTextForRun,
71
+ resolveSiderRunState,
71
72
  updateSiderRunStateContext
72
73
  } from "./channel-state.js";
73
74
  import { buildTextPart } from "./channel-builders.js";
74
75
  import {
76
+ buildSiderDispatchReceipt,
75
77
  dispatchSiderParts,
76
78
  handleSiderSendAction,
77
79
  sendToSider
@@ -97,6 +99,7 @@ import {
97
99
  finalizeQueuedRunMessages
98
100
  } from "./channel-streaming.js";
99
101
  import { runManagedSiderRelayMonitor } from "./channel-monitor.js";
102
+ const NO_QUOTA_REMAINING_TEXT = "NO_QUOTA_REMAINING";
100
103
  import { setSiderRuntime } from "./channel-runtime.js";
101
104
  import {
102
105
  recordSiderPersistedAgentMessage,
@@ -371,6 +374,15 @@ async function handleInboundRealtimeMessage(params) {
371
374
  if (payload.isError === true && !SIDER_PUSH_TOOL_ERROR_WARNINGS && typeof payload.text === "string" && SIDER_TOOL_ERROR_WARNING_TEXT_RE.test(payload.text)) {
372
375
  return;
373
376
  }
377
+ if (shouldSuppressFollowupErrorAfterNoQuotaRemaining(streamState.currentRunId, payload)) {
378
+ logDebug("skip sider deliver: follow-up error after NO_QUOTA_REMAINING", {
379
+ accountId: account.accountId,
380
+ sessionId,
381
+ runId: streamState.currentRunId,
382
+ kind: info.kind
383
+ });
384
+ return;
385
+ }
374
386
  logDebug("sider dispatch deliver payload", {
375
387
  accountId: account.accountId,
376
388
  sessionId,
@@ -517,7 +529,9 @@ async function handleInboundRealtimeMessage(params) {
517
529
  }
518
530
  return;
519
531
  }
520
- const pendingFinalPayloads = [...streamState.pendingFinalPayloads];
532
+ const pendingFinalPayloads = streamState.pendingFinalPayloads.filter(
533
+ (payload) => !shouldSuppressFollowupErrorAfterNoQuotaRemaining(streamState.currentRunId, payload)
534
+ );
521
535
  streamState.pendingFinalPayloads = [];
522
536
  if (pendingFinalPayloads.length > 0) {
523
537
  const pendingRunFinalText = resolvePendingFinalTextForRun(streamState.currentRunId).trim();
@@ -660,6 +674,28 @@ async function handleInboundRealtimeMessage(params) {
660
674
  releaseSiderTyping({ account, sessionId });
661
675
  }
662
676
  }
677
+ function shouldSuppressFollowupErrorAfterNoQuotaRemaining(runId, payload) {
678
+ const runState = resolveSiderRunState(runId);
679
+ if (runState?.noQuotaRemainingErrorSent !== true) {
680
+ return false;
681
+ }
682
+ if (runState.noQuotaRemainingFollowupErrorSuppressed === true) {
683
+ return false;
684
+ }
685
+ if (payload.isError !== true && payload.is_error !== true) {
686
+ return false;
687
+ }
688
+ const text = typeof payload.text === "string" ? payload.text.trim() : "";
689
+ if (text.includes(NO_QUOTA_REMAINING_TEXT)) {
690
+ return false;
691
+ }
692
+ runState.noQuotaRemainingFollowupErrorSuppressed = true;
693
+ logDebug("suppressing follow-up error after NO_QUOTA_REMAINING", {
694
+ runId,
695
+ textLength: text.length
696
+ });
697
+ return true;
698
+ }
663
699
  const siderPlugin = {
664
700
  id: SIDER_CHANNEL_ID,
665
701
  ...siderChannelPluginCommon,
@@ -737,7 +773,7 @@ const siderPlugin = {
737
773
  source: savedAttachment.path,
738
774
  path: savedAttachment.path
739
775
  });
740
- await dispatchSiderParts({
776
+ const dispatchResult = await dispatchSiderParts({
741
777
  account,
742
778
  sessionId,
743
779
  parts: sendParts,
@@ -745,7 +781,10 @@ const siderPlugin = {
745
781
  allowMergeActiveRun: true,
746
782
  context: "action.sendAttachment"
747
783
  });
748
- return jsonResult(attachmentResult);
784
+ return jsonResult({
785
+ ...attachmentResult,
786
+ ...buildSiderDispatchReceipt(dispatchResult)
787
+ });
749
788
  }
750
789
  },
751
790
  outbound: {
@@ -10,7 +10,7 @@ declare const SIDER_CHANNEL_DOCS_LABEL = "sider";
10
10
  declare const SIDER_CHANNEL_BLURB = "Command your OpenClaw from Chrome Sidebar directly.";
11
11
  declare const SIDER_CHANNEL_ALIASES: readonly ["sider"];
12
12
  declare const SIDER_DEFAULT_BASE_URL = "https://sider.ai/api/claw/self";
13
- declare const SIDER_PUSH_TOOL_ERROR_WARNINGS = true;
13
+ declare const SIDER_PUSH_TOOL_ERROR_WARNINGS: boolean;
14
14
  declare const SIDER_TOOL_ERROR_WARNING_TEXT_RE: RegExp;
15
15
  declare function readSiderBaseUrlEnv(): string | undefined;
16
16
  declare function readDefaultSiderSetupTokenEnv(): string | undefined;
@@ -10,7 +10,7 @@ const SIDER_CHANNEL_DOCS_LABEL = "sider";
10
10
  const SIDER_CHANNEL_BLURB = "Command your OpenClaw from Chrome Sidebar directly.";
11
11
  const SIDER_CHANNEL_ALIASES = ["sider"];
12
12
  const SIDER_DEFAULT_BASE_URL = "https://sider.ai/api/claw/self";
13
- const SIDER_PUSH_TOOL_ERROR_WARNINGS = true;
13
+ const SIDER_PUSH_TOOL_ERROR_WARNINGS = false ? true : false;
14
14
  const SIDER_TOOL_ERROR_WARNING_TEXT_RE = /^⚠️ [^\n]+ failed(?:: [^\n]+)?$/u;
15
15
  function readSiderBaseUrlEnv() {
16
16
  return process.env.SIDER_GATEWAY_URL;
@@ -1,4 +1,4 @@
1
- const SIDER_PLUGIN_VERSION = "1.0.31";
1
+ const SIDER_PLUGIN_VERSION = "1.0.34";
2
2
  const SIDER_USER_AGENT = `openclaw-sider/${SIDER_PLUGIN_VERSION}`;
3
3
  export {
4
4
  SIDER_PLUGIN_VERSION,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sider-ai/chrome-openclaw-sider",
3
3
  "private": false,
4
- "version": "1.0.31",
4
+ "version": "1.0.34",
5
5
  "description": "Official Chrome channel plugin for connecting OpenClaw in Sider Chrome extension",
6
6
  "type": "module",
7
7
  "files": [