fix: filter rate_limit_event from Claude Remote/Local chat paths#423
fix: filter rate_limit_event from Claude Remote/Local chat paths#423tiann merged 4 commits intotiann:mainfrom
Conversation
There was a problem hiding this comment.
Findings
- No findings on the added/modified lines.
Summary
- Review mode: initial
- No issues found in the current diff.
- Residual risk: the new
shared/src/messages.tsbranch is covered indirectly through CLI tests, but there is still no direct shared/web regression coverage for that consumer path.
Testing
- Not run (automation environment:
bunwas not available, so the targeted Vitest files could not be executed)
HAPI Bot
Claude SDK emits rate_limit_event messages that are not recognized by the web normalizer, causing them to fall through to the safeStringify fallback and render as raw JSON in the chat UI. Add an early return in isClaudeChatVisibleMessage to mark rate_limit_event as non-visible, preventing it from reaching the web layer via the local session scanner path.
…erter Guard against rate_limit_event at the top of convert(), before UUID generation and sidechain bookkeeping. This prevents phantom UUIDs from corrupting the parent chain when a rate_limit_event carries a parent_tool_use_id, and stops the event from reaching the web UI via the remote SDK message path. Includes parent-chain-integrity test to verify dropped events do not break UUID continuity.
1792f82 to
6430850
Compare
There was a problem hiding this comment.
Findings
- [Major] Displayable Claude rate-limit statuses are now dropped instead of converted into the event format the web already understands, so
allowed_warning/rejecteddisappear entirely on the SDK path. Evidencecli/src/claude/utils/sdkToLogConverter.ts:93,shared/src/messages.ts:42,cli/src/agent/rateLimitParser.ts:61,web/src/chat/reducerEvents.ts:3
Suggested fix:if (sdkMessage.type === 'rate_limit_event') { const text = formatClaudeRateLimitEvent((sdkMessage as any).rate_limit_info) if (!text) return null return { ...baseFields, type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text }] } } }
Summary
Review mode: follow-up after new commits
- 1 major issue found.
- The raw-JSON leak is blocked, but this implementation also removes Claude usage-limit warnings/rejections on the structured SDK path instead of translating them into the existing UI event format.
Testing
- Not run (automation environment:
bunwas not available, so the targeted Vitest files could not be executed)
HAPI Bot
| * Convert SDK message to log format | ||
| */ | ||
| convert(sdkMessage: SDKMessage): RawJSONLines | null { | ||
| if (sdkMessage.type === 'rate_limit_event') return null |
There was a problem hiding this comment.
[MAJOR] This drops every rate_limit_event, including the displayable allowed_warning / rejected cases. ACP already converts those statuses into the Claude AI usage limit ... text that the web reducer turns into limit-warning / limit-reached events (cli/src/agent/rateLimitParser.ts:61, web/src/chat/reducerEvents.ts:3). Returning null here fixes the raw-JSON leak by removing the warning entirely on the SDK path.
Suggested fix:
if (sdkMessage.type === 'rate_limit_event') {
const text = formatClaudeRateLimitEvent((sdkMessage as any).rate_limit_info)
if (!text) return null
return {
...baseFields,
type: 'assistant',
message: {
role: 'assistant',
content: [{ type: 'text', text }]
}
}
}The previous commit suppressed all rate_limit_event messages, but
allowed_warning and rejected statuses should be converted to the
pipe-delimited text format the web layer already understands.
- allowed: suppress (no display needed)
- allowed_warning → "Claude AI usage limit warning|{resetsAt}|{pct}|{type}"
- rejected → "Claude AI usage limit reached|{resetsAt}|{type}"
Addresses review feedback on PR tiann#423.
There was a problem hiding this comment.
Findings
- [Major] Displayable SDK
rate_limit_eventmessages now self-parent.convertRateLimitEvent()assignsthis.lastUuid = uuidbefore fillingparentUuid, so everyallowed_warning/rejectedmessage points to its own UUID instead of the previous message. That breaks the converter's existing parent-chain invariant fromcli/src/claude/utils/sdkToLogConverter.test.ts:222, and the new tests only cover parent preservation for suppressed events atcli/src/claude/utils/sdkToLogConverter.test.ts:325.
Suggested fix:const parentUuid = this.lastUuid const uuid = randomUUID() const timestamp = new Date().toISOString() this.lastUuid = uuid return { parentUuid, ... }
Summary
Review mode: follow-up after new commits
- 1 major issue found.
- The latest diff fixes the earlier warning/rejected drop, but it now emits a self-referential parent link for each displayable SDK rate-limit event.
Testing
- Not run (automation environment:
bunwas not installed, so the targeted Vitest files could not be executed)
HAPI Bot
| this.lastUuid = uuid | ||
|
|
||
| return { | ||
| parentUuid: this.lastUuid, |
There was a problem hiding this comment.
[MAJOR] this.lastUuid has already been overwritten with uuid on the line above, so this stores the new message's own UUID in parentUuid. Every displayable allowed_warning / rejected event becomes self-parented instead of pointing at the previous message, which breaks the converter's existing parent-chain invariant (cli/src/claude/utils/sdkToLogConverter.test.ts:222). The added tests only cover the suppressed-event chain (cli/src/claude/utils/sdkToLogConverter.test.ts:325).
Suggested fix:
const parentUuid = this.lastUuid
const uuid = randomUUID()
const timestamp = new Date().toISOString()
this.lastUuid = uuid
return {
parentUuid,
...
}The previous commit set this.lastUuid before reading it for parentUuid, causing every converted rate_limit_event to be a self-referencing node (parentUuid === uuid). Capture parentUuid before assigning the new uuid, matching the ordering used by the main convert() method. Add test for parent chain continuity through converted (non-suppressed) rate_limit_event messages.
There was a problem hiding this comment.
Findings
- No actionable issues found in the latest head.
Summary
Review mode: follow-up after new commits
- No issues found in the current diff after checking the touched Claude SDK conversion, shared visibility filter, and existing web event parsing path.
- Residual risk: static review only; the targeted Vitest files were not executed because
bunis not installed in this automation environment.
Testing
- Not run (automation environment:
bunnot installed)
HAPI Bot
Summary
PR #388 handled
rate_limit_eventdisplay for the ACP path, but Claude Remote (SDK) and Local (JSONL scanner) paths still leak the raw JSON into the web UI chat.This adds filtering at two layers:
sdkToLogConverter.convert()— early return before UUID generation and sidechain bookkeeping, preventing the event from reaching the web via the remote SDK pathisClaudeChatVisibleMessage()— marksrate_limit_eventas non-visible, covering the local session scanner path and acting as a second line of defenseTest plan
rate_limit_eventreturnsnullfrom converterisClaudeChatVisibleMessage({ type: 'rate_limit_event' })returnsfalse