crawd 0.8.3 → 0.8.4

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.
@@ -11443,7 +11443,7 @@ var GatewayClient = class _GatewayClient {
11443
11443
  };
11444
11444
  var STARTUP_GRACE_MS = 3e4;
11445
11445
  var SLEEP_CHECK_INTERVAL_MS = 1e4;
11446
- var Coordinator = class {
11446
+ var Coordinator = class _Coordinator {
11447
11447
  buffer = [];
11448
11448
  timer = null;
11449
11449
  triggerFn;
@@ -11648,14 +11648,18 @@ var Coordinator = class {
11648
11648
  this._busy = true;
11649
11649
  try {
11650
11650
  const replies = await this.triggerFn(this.config.vibePrompt);
11651
- if (replies.some((r) => r.trim().toUpperCase() === "NO_REPLY")) {
11651
+ const agentReplies = replies.filter((r) => !this.isApiError(r));
11652
+ if (agentReplies.some((r) => r.trim().toUpperCase() === "NO_REPLY")) {
11652
11653
  noReply = true;
11653
- } else if (!this.isCompliantReply(replies)) {
11654
- misaligned = replies.filter((r) => {
11654
+ } else if (!this.isCompliantReply(agentReplies)) {
11655
+ misaligned = agentReplies.filter((r) => {
11655
11656
  const t = r.trim().toUpperCase();
11656
11657
  return t !== "NO_REPLY" && t !== "LIVESTREAM_REPLIED";
11657
11658
  });
11658
11659
  }
11660
+ if (replies.length > agentReplies.length) {
11661
+ this.logger.warn(`[Coordinator] Filtered ${replies.length - agentReplies.length} API error(s) from vibe response`);
11662
+ }
11659
11663
  } catch (err) {
11660
11664
  this.logger.error("[Coordinator] Vibe failed:", err);
11661
11665
  } finally {
@@ -11715,12 +11719,17 @@ var Coordinator = class {
11715
11719
  get busy() {
11716
11720
  return this._busy;
11717
11721
  }
11722
+ /** Detect API/gateway errors surfaced as reply strings (e.g. rate limits) */
11723
+ static API_ERROR_RE = /^\d{3}\s+(status\s+code|error)|^rate\s*limit|^too\s+many\s+requests|^overloaded|^server\s+error/i;
11724
+ isApiError(reply) {
11725
+ return _Coordinator.API_ERROR_RE.test(reply.trim());
11726
+ }
11718
11727
  /** Check if agent replies are compliant (NO_REPLY or LIVESTREAM_REPLIED) */
11719
11728
  isCompliantReply(replies) {
11720
11729
  if (replies.length === 0) return true;
11721
11730
  return replies.every((r) => {
11722
11731
  const t = r.trim().toUpperCase();
11723
- return t === "NO_REPLY" || t === "LIVESTREAM_REPLIED";
11732
+ return t === "NO_REPLY" || t === "LIVESTREAM_REPLIED" || this.isApiError(r);
11724
11733
  });
11725
11734
  }
11726
11735
  /** Send misalignment correction when agent responds with plaintext */
@@ -11746,8 +11755,12 @@ var Coordinator = class {
11746
11755
  this._busy = true;
11747
11756
  try {
11748
11757
  const replies = await this.triggerFn(batchText);
11749
- if (!this.isCompliantReply(replies)) {
11750
- await this.sendMisalignment(replies.filter((r) => {
11758
+ const agentReplies = replies.filter((r) => !this.isApiError(r));
11759
+ if (replies.length > agentReplies.length) {
11760
+ this.logger.warn(`[Coordinator] Filtered ${replies.length - agentReplies.length} API error(s) from chat response`);
11761
+ }
11762
+ if (!this.isCompliantReply(agentReplies)) {
11763
+ await this.sendMisalignment(agentReplies.filter((r) => {
11751
11764
  const t = r.trim().toUpperCase();
11752
11765
  return t !== "NO_REPLY" && t !== "LIVESTREAM_REPLIED";
11753
11766
  }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "crawd",
3
- "version": "0.8.3",
3
+ "version": "0.8.4",
4
4
  "description": "CLI for crawd.bot - AI agent livestreaming platform",
5
5
  "type": "module",
6
6
  "types": "./dist/types.d.ts",
@@ -765,14 +765,19 @@ export class Coordinator {
765
765
  this._busy = true
766
766
  try {
767
767
  const replies = await this.triggerFn(this.config.vibePrompt)
768
- if (replies.some(r => r.trim().toUpperCase() === 'NO_REPLY')) {
768
+ // Filter out API errors (429s, rate limits) — not agent responses
769
+ const agentReplies = replies.filter(r => !this.isApiError(r))
770
+ if (agentReplies.some(r => r.trim().toUpperCase() === 'NO_REPLY')) {
769
771
  noReply = true
770
- } else if (!this.isCompliantReply(replies)) {
771
- misaligned = replies.filter(r => {
772
+ } else if (!this.isCompliantReply(agentReplies)) {
773
+ misaligned = agentReplies.filter(r => {
772
774
  const t = r.trim().toUpperCase()
773
775
  return t !== 'NO_REPLY' && t !== 'LIVESTREAM_REPLIED'
774
776
  })
775
777
  }
778
+ if (replies.length > agentReplies.length) {
779
+ this.logger.warn(`[Coordinator] Filtered ${replies.length - agentReplies.length} API error(s) from vibe response`)
780
+ }
776
781
  } catch (err) {
777
782
  this.logger.error('[Coordinator] Vibe failed:', err)
778
783
  } finally {
@@ -849,12 +854,19 @@ export class Coordinator {
849
854
  /** Whether the coordinator is busy processing a flush or talk */
850
855
  get busy(): boolean { return this._busy }
851
856
 
857
+ /** Detect API/gateway errors surfaced as reply strings (e.g. rate limits) */
858
+ private static readonly API_ERROR_RE = /^\d{3}\s+(status\s+code|error)|^rate\s*limit|^too\s+many\s+requests|^overloaded|^server\s+error/i
859
+
860
+ private isApiError(reply: string): boolean {
861
+ return Coordinator.API_ERROR_RE.test(reply.trim())
862
+ }
863
+
852
864
  /** Check if agent replies are compliant (NO_REPLY or LIVESTREAM_REPLIED) */
853
865
  private isCompliantReply(replies: string[]): boolean {
854
866
  if (replies.length === 0) return true
855
867
  return replies.every(r => {
856
868
  const t = r.trim().toUpperCase()
857
- return t === 'NO_REPLY' || t === 'LIVESTREAM_REPLIED'
869
+ return t === 'NO_REPLY' || t === 'LIVESTREAM_REPLIED' || this.isApiError(r)
858
870
  })
859
871
  }
860
872
 
@@ -889,8 +901,12 @@ export class Coordinator {
889
901
  this._busy = true
890
902
  try {
891
903
  const replies = await this.triggerFn(batchText)
892
- if (!this.isCompliantReply(replies)) {
893
- await this.sendMisalignment(replies.filter(r => {
904
+ const agentReplies = replies.filter(r => !this.isApiError(r))
905
+ if (replies.length > agentReplies.length) {
906
+ this.logger.warn(`[Coordinator] Filtered ${replies.length - agentReplies.length} API error(s) from chat response`)
907
+ }
908
+ if (!this.isCompliantReply(agentReplies)) {
909
+ await this.sendMisalignment(agentReplies.filter(r => {
894
910
  const t = r.trim().toUpperCase()
895
911
  return t !== 'NO_REPLY' && t !== 'LIVESTREAM_REPLIED'
896
912
  }))