fix: deliver tool result images (screenshots) to Telegram#15987
fix: deliver tool result images (screenshots) to Telegram#15987strelov1 wants to merge 1 commit intoopenclaw:mainfrom
Conversation
Tool results containing images (browser screenshots, canvas snapshots) were silently dropped due to three independent bugs: 1. emitToolOutput() gated by verboseLevel === "full" — most users never hit this path 2. Tool output wrapped in code fence before MEDIA: parsing — tokens inside fences are skipped 3. normalizeStreamingText() skipped media-only payloads (no text → skip) Fix by extracting image paths directly from tool result content blocks via new extractToolResultMediaPaths(), emitting them through onToolResult regardless of verbose level, and allowing media-only payloads through normalizeStreamingText(). Closes openclaw#11735 Co-Authored-By: Claude Opus 4.6 <[email protected]>
Changes SummaryThis PR fixes a bug where tool result images (screenshots, canvas snapshots) were not being delivered to messaging platforms like Telegram. The fix extracts image paths directly from tool result content blocks and ensures media is emitted regardless of verbose level settings. Type: bugfix Components Affected: Agent tool result processing, Telegram/messaging channel media delivery, Streaming text normalization Files Changed
Architecture Impact
Risk Areas: Media-only payloads now allowed through normalizeStreamingText - verify no regressions with empty text handling, Tool result media emission now bypasses verbose level check - ensure this doesn't cause unexpected media delivery in minimal verbosity modes, New extractToolResultMediaPaths logic assumes specific structure of tool result content blocks (type='image', details.path) Suggestions
Full review in progress... | Powered by diffray |
| export function extractToolResultMediaPaths(result: unknown): string[] | undefined { | ||
| if (!result || typeof result !== "object") { | ||
| return undefined; | ||
| } | ||
| const record = result as Record<string, unknown>; | ||
| const content = Array.isArray(record.content) ? record.content : null; | ||
| if (!content) { | ||
| return undefined; | ||
| } | ||
|
|
||
| const hasImage = content.some( | ||
| (item) => | ||
| item && typeof item === "object" && (item as Record<string, unknown>).type === "image", | ||
| ); | ||
| if (!hasImage) { | ||
| return undefined; | ||
| } | ||
|
|
||
| const details = record.details as Record<string, unknown> | undefined; | ||
| const path = typeof details?.path === "string" ? details.path.trim() : ""; | ||
| return path ? [path] : undefined; | ||
| } |
There was a problem hiding this comment.
🟠 HIGH - New function extractToolResultMediaPaths lacks test coverage
Agent: testing
Category: quality
Description:
The new extractToolResultMediaPaths() function extracts image file paths from tool results. This is critical functionality for the bug fix (delivering screenshots to Telegram) but has zero test coverage. The function handles multiple edge cases that are untested.
Suggestion:
Add comprehensive unit tests covering: (1) successful extraction with valid image + details.path, (2) returns undefined when result is null/undefined, (3) returns undefined when content is not an array, (4) returns undefined when no image blocks exist, (5) returns undefined when details.path is missing/empty.
Confidence: 90%
Rule: test_coverage_new_functionality
Review ID: ab3a23fa-d862-4878-a790-f1e2a5909240
Rate it 👍 or 👎 to improve future reviews | Powered by diffray
| // Emit media from tool image results regardless of verbose level. | ||
| if (ctx.params.onToolResult && !isToolError) { | ||
| const mediaPaths = extractToolResultMediaPaths(result); | ||
| if (mediaPaths && mediaPaths.length > 0) { | ||
| try { | ||
| void ctx.params.onToolResult({ mediaUrls: mediaPaths }); | ||
| } catch { | ||
| /* ignore */ | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
🟠 HIGH - New media emission code in handleToolExecutionEnd lacks test coverage
Agent: testing
Category: quality
Description:
The new code that calls extractToolResultMediaPaths() and emits media URLs via onToolResult callback is untested. This is the core fix for the bug - emitting tool result images regardless of verbose level.
Suggestion:
Add integration tests verifying: (1) onToolResult is called with mediaUrls when tool returns image result, (2) onToolResult is NOT called when tool returns error, (3) mediaUrls array contains correct path.
Confidence: 75%
Rule: test_coverage_new_functionality
Review ID: ab3a23fa-d862-4878-a790-f1e2a5909240
Rate it 👍 or 👎 to improve future reviews | Powered by diffray
Review Summary
Validated 86 issues: 25 kept, 61 filtered Issues Found: 25💬 See 4 individual line comment(s) for details. 📊 16 unique issue type(s) across 25 location(s) 📋 Full issue list (click to expand)🟠 HIGH - Unhandled promise rejection in fetch operation (2 occurrences)Agent: react Category: bug 📍 View all locations
Rule: 🟠 HIGH - Array access without bounds check on mediaList[0]Agent: bugs Category: bug File: Description: The code accesses Suggestion: Check array length before access: Confidence: 72% Rule: 🟠 HIGH - Large function with multiple responsibilities (425 lines) (3 occurrences)Agent: architecture Category: quality 📍 View all locations
Rule: 🟠 HIGH - Multiple JSON.parse operations on untrusted dataAgent: bugs Category: bug File: Description: The code performs nested JSON.parse operations on webhook data. While inside a try-catch, the error handling may not differentiate between parse errors at different stages. Suggestion: Use individual try-catch blocks for each JSON.parse to provide more specific error messages for debugging. Confidence: 68% Rule: 🟠 HIGH - New function extractToolResultMediaPaths lacks test coverage (2 occurrences)Agent: testing Category: quality 📍 View all locations
Rule: 🟠 HIGH - Unsafe any type in API responseAgent: typescript Category: bug File: Description: Using 'any' type for Feishu API response bypasses all type checking. While optional chaining is used, a proper interface would provide better maintainability. Suggestion: Define a proper interface for the expected response structure: interface FeishuUserResponse { data?: { user?: { name?: string; display_name?: string; nickname?: string; en_name?: string } } } Confidence: 65% Rule: 🟡 MEDIUM - Multiple fallback behaviors with unclear contractAgent: architecture Category: quality File: Description: The downloadImageFeishu function silently tries 7+ different response formats without documenting which is expected or why fallbacks exist. This makes debugging difficult. Suggestion: Document the expected SDK response format explicitly. Add logging when fallbacks are used so the unexpected format is visible. Confidence: 75% Rule: 🟡 MEDIUM - Missing URL validation before external fetch (2 occurrences)Agent: security Category: security 📍 View all locations
Rule: 🟡 MEDIUM - Nullish coalescing preserves invalid 0 value for textChunkLimitAgent: bugs Category: bug File: Description: The code uses Suggestion: Use a guard to ensure the value is positive: Confidence: 70% Rule: 🟡 MEDIUM - Swallowed error in file cleanupAgent: react Category: bug File: Description: The fs.promises.unlink() cleanup operation has an empty catch block that silently swallows errors. While cleanup errors may be less critical, they should at least be logged for debugging purposes. Suggestion: Log the cleanup error instead of silently swallowing it: Confidence: 65% Rule: 🟡 MEDIUM - Floating promise in webhook mode lacks void operatorAgent: nodejs Category: bug File: Description: In webhook mode (fireAndForget=true), handleFeishuMessage() is called and the returned promise is handled with .catch() but not awaited. While intentional for webhook timeout, using void operator would clarify intent. Suggestion: Mark the intentional fire-and-forget with void operator for clarity: Confidence: 60% Rule: 🟡 MEDIUM - Silent error handling without logging (4 occurrences)Agent: react Category: quality 📍 View all locations
} catch (err) {
log?.(`feishu: fail... | 75% |
| `extensions/feishu/src/bot.ts:264-266` | The catch block in parseMediaKeys() silently returns an empty object without logging the JSON parsin... | Add error logging with context:
```typescript
} catch (err) {
log?.(`feishu: failed to parse media... | 75% |
| `extensions/feishu/src/bot.ts:308-310` | The catch block in parsePostContent() silently returns a Chinese fallback message without logging th... | Add error logging:
```typescript
} catch (err) {
log?.(`feishu: failed to parse post content: ${St... | 75% |
| `extensions/googlechat/src/monitor.ts:80-82` | The catch block in resolveWebhookPath() silently returns null when URL parsing fails. This makes it ... | Add error logging:
```typescript
} catch (err) {
runtime.log?.(`googlechat: failed to parse webhoo... | 70% |
</details>
**Rule:** `ts_log_errors_instead_of_failing_silently`
---
#### 🟡 MEDIUM - Raw API error message exposed to user (2 occurrences)
**Agent:** [security](https://diffray.ai/agents/security)
**Category:** security
<details>
<summary>📍 View all locations</summary>
| File | Description | Suggestion | Confidence |
|------|-------------|------------|------------|
| `extensions/feishu/src/docx.ts:86-88` | The error thrown exposes the raw error message from the Feishu API (res.msg) directly. This could le... | Wrap with generic message and log original: throw new Error('Failed to convert document content'); /... | 70% |
| `extensions/matrix/src/matrix/client/config.ts:127-130` | The Matrix login error exposes raw error response text from the homeserver directly. This could leak... | Map server error messages to generic user-friendly messages. Log original errorText for debugging. | 70% |
</details>
**Rule:** `sec_internal_details_in_user_errors`
---
#### 🟡 MEDIUM - Catch block without error context in logging
**Agent:** refactoring
**Category:** quality
**File:** `extensions/bluebubbles/src/monitor.ts:1115-1126`
**Description:** The catch block in resolveBlueBubblesAckReaction() logs a message but doesn't include the actual error details. This makes it difficult to debug why reaction normalization is failing.
**Suggestion:** Include the error in the log message: `ack reaction skipped (unsupported for BlueBubbles): ${raw}, error: ${String(err)}`
**Confidence:** 70%
**Rule:** `ts_include_error_context_in_structured_logs`
---
#### 🟡 MEDIUM - Non-null assertion without validation
**Agent:** typescript
**Category:** quality
**File:** `extensions/bluebubbles/src/monitor.ts:49-50`
**Description:** Using non-null assertion (!) on Map.keys().next().value after size check. While logically safe due to size >= DEDUP_MAX_SIZE check on line 48, the assertion could mask issues if map is mutated concurrently.
**Suggestion:** Add explicit check: const first = processedMessageIds.keys().next().value; if (first) { processedMessageIds.delete(first); }
**Confidence:** 65%
**Rule:** `ts_non_null_assertion`
---
#### 🔵 LOW - Duplicated response format handling logic
**Agent:** [quality](https://diffray.ai/agents/quality)
**Category:** quality
**File:** `extensions/feishu/src/media.ts:132-177`
**Description:** The downloadMessageResourceFeishu function duplicates the exact same response format handling logic as downloadImageFeishu. This is a clear DRY violation that increases maintenance burden.
**Suggestion:** Extract the response format handling into a shared helper function: `async function extractBufferFromFeishuResponse(response: any): Promise<Buffer>` and call it from both functions.
**Confidence:** 90%
**Rule:** `quality_early_return_instead_nested`
---
</details>
<details>
<summary>ℹ️ 21 issue(s) outside PR diff (click to expand)</summary>
> These issues were found in lines not modified in this PR.
#### 🟠 HIGH - Unhandled promise rejection in fetch operation (2 occurrences)
**Agent:** react
**Category:** bug
<details>
<summary>📍 View all locations</summary>
| File | Description | Suggestion | Confidence |
|------|-------------|------------|------------|
| `extensions/matrix/src/matrix/client/config.ts:116-129` | The fetch operation for Matrix login at line 116 is not wrapped in a try-catch block. Network errors... | Wrap the fetch operation in a try-catch block with descriptive error handling | 78% |
| `extensions/matrix/src/matrix/client/config.ts:72` | The tempClient.getUserId() call is not wrapped in a try-catch block. If the API call fails (network ... | Wrap the getUserId call in a try-catch block with proper error messaging: `try { const whoami = awai... | 75% |
</details>
**Rule:** `ts_handle_async_operations_with_proper_erro`
---
#### 🟠 HIGH - Array access without bounds check on mediaList[0]
**Agent:** [bugs](https://diffray.ai/agents/bugs)
**Category:** bug
**File:** `extensions/feishu/src/bot.ts:474-476`
**Description:** The code accesses `mediaList[0]` without checking if the array is non-empty. If `buildFeishuMediaPayload` is called with an empty array, first will be undefined.
**Suggestion:** Check array length before access: `const first = mediaList.length > 0 ? mediaList[0] : undefined;`
**Confidence:** 72%
**Rule:** `bug_array_bounds`
---
#### 🟠 HIGH - Large function with multiple responsibilities (425 lines) (3 occurrences)
**Agent:** [architecture](https://diffray.ai/agents/architecture)
**Category:** quality
<details>
<summary>📍 View all locations</summary>
| File | Description | Suggestion | Confidence |
|------|-------------|------------|------------|
| `extensions/feishu/src/bot.ts:522-947` | The handleFeishuMessage function is 425+ lines with multiple distinct responsibilities: message dedu... | Break into focused functions: validateAndDeduplicateMessage(), resolveMessageContext(), enforceAcces... | 88% |
| `extensions/googlechat/src/monitor.ts:417-765` | The processMessageWithPipeline function is 348 lines handling bot detection, media download, policy ... | Extract separate functions for each concern: validateMessageSource(), enforceAccessControl(), resolv... | 85% |
| `extensions/matrix/src/matrix/monitor/index.ts:31-339` | The monitorMatrixProvider function is 308 lines handling config resolution, allowlist normalization,... | Extract: resolveAndNormalizeConfig() for the allowlist/room resolution (lines 124-220), initializeMa... | 82% |
</details>
**Rule:** `arch_extract_complex_logic`
---
#### 🟠 HIGH - Multiple JSON.parse operations on untrusted data
**Agent:** [bugs](https://diffray.ai/agents/bugs)
**Category:** bug
**File:** `extensions/bluebubbles/src/monitor.ts:549-555`
**Description:** The code performs nested JSON.parse operations on webhook data. While inside a try-catch, the error handling may not differentiate between parse errors at different stages.
**Suggestion:** Use individual try-catch blocks for each JSON.parse to provide more specific error messages for debugging.
**Confidence:** 68%
**Rule:** `bug_missing_try_catch`
---
#### 🟠 HIGH - Unsafe any type in API response
**Agent:** typescript
**Category:** bug
**File:** `extensions/feishu/src/bot.ts:126`
**Description:** Using 'any' type for Feishu API response bypasses all type checking. While optional chaining is used, a proper interface would provide better maintainability.
**Suggestion:** Define a proper interface for the expected response structure: interface FeishuUserResponse { data?: { user?: { name?: string; display_name?: string; nickname?: string; en_name?: string } } }
**Confidence:** 65%
**Rule:** `ts_prefer_unknown_over_any`
---
#### 🟡 MEDIUM - Multiple fallback behaviors with unclear contract
**Agent:** [architecture](https://diffray.ai/agents/architecture)
**Category:** quality
**File:** `extensions/feishu/src/media.ts:49-94`
**Description:** The downloadImageFeishu function silently tries 7+ different response formats without documenting which is expected or why fallbacks exist. This makes debugging difficult.
**Suggestion:** Document the expected SDK response format explicitly. Add logging when fallbacks are used so the unexpected format is visible.
**Confidence:** 75%
**Rule:** `arch_unclear_api_contract`
---
#### 🟡 MEDIUM - Missing URL validation before external fetch (2 occurrences)
**Agent:** [security](https://diffray.ai/agents/security)
**Category:** security
<details>
<summary>📍 View all locations</summary>
| File | Description | Suggestion | Confidence |
|------|-------------|------------|------------|
| `extensions/feishu/src/docx.ts:170-175` | The downloadImage function fetches from user-controlled URLs without validation. This could enable S... | Validate URLs before fetching: 1) Use URL constructor to parse, 2) Check protocol is http/https only... | 85% |
| `extensions/feishu/src/media.ts:496-503` | The sendMediaFeishu function fetches from remote URLs without validation, similar to the docx.ts iss... | Validate URLs before fetching, checking protocol and blocklisting private IP ranges and cloud metada... | 80% |
</details>
**Rule:** `security_missing_input_validation`
---
#### 🟡 MEDIUM - Nullish coalescing preserves invalid 0 value for textChunkLimit
**Agent:** [bugs](https://diffray.ai/agents/bugs)
**Category:** bug
**File:** `extensions/googlechat/src/monitor.ts:871`
**Description:** The code uses `account.config.textChunkLimit ?? 4000` which will preserve an explicitly configured value of 0. Since 0 is not a valid chunk limit (cannot chunk text into 0-byte segments), this could cause runtime errors or infinite loops in the chunking logic.
**Suggestion:** Use a guard to ensure the value is positive: `Math.max(1, account.config.textChunkLimit ?? 4000)` or add validation: `(account.config.textChunkLimit && account.config.textChunkLimit > 0) ? account.config.textChunkLimit : 4000`
**Confidence:** 70%
**Rule:** `js_nullish_coalescing_numeric`
---
#### 🟡 MEDIUM - Swallowed error in file cleanup
**Agent:** react
**Category:** bug
**File:** `extensions/feishu/src/media.ts:73-74`
**Description:** The fs.promises.unlink() cleanup operation has an empty catch block that silently swallows errors. While cleanup errors may be less critical, they should at least be logged for debugging purposes.
**Suggestion:** Log the cleanup error instead of silently swallowing it: `.catch((err) => console.debug('cleanup failed:', err))`
**Confidence:** 65%
**Rule:** `ts_re_throw_or_return_errors_to_propagate_f`
---
#### 🟡 MEDIUM - Floating promise in webhook mode lacks void operator
**Agent:** nodejs
**Category:** bug
**File:** `extensions/feishu/src/monitor.ts:62-68`
**Description:** In webhook mode (fireAndForget=true), handleFeishuMessage() is called and the returned promise is handled with .catch() but not awaited. While intentional for webhook timeout, using void operator would clarify intent.
**Suggestion:** Mark the intentional fire-and-forget with void operator for clarity: `void promise.catch((err) => { ... });`
**Confidence:** 60%
**Rule:** `node_floating_promise`
---
#### 🟡 MEDIUM - Silent error handling without logging (3 occurrences)
**Agent:** react
**Category:** quality
<details>
<summary>📍 View all locations</summary>
| File | Description | Suggestion | Confidence |
|------|-------------|------------|------------|
| `extensions/feishu/src/bot.ts:211-213` | The catch block in parseMessageContent() silently returns a fallback value without logging the parsi... | Add error logging before returning the fallback:
```typescript
} catch (err) {
log?.(`feishu: fail... | 75% |
| `extensions/feishu/src/bot.ts:264-266` | The catch block in parseMediaKeys() silently returns an empty object without logging the JSON parsin... | Add error logging with context:
```typescript
} catch (err) {
log?.(`feishu: failed to parse media... | 75% |
| `extensions/feishu/src/bot.ts:308-310` | The catch block in parsePostContent() silently returns a Chinese fallback message without logging th... | Add error logging:
```typescript
} catch (err) {
log?.(`feishu: failed to parse post content: ${St... | 75% |
</details>
**Rule:** `ts_log_errors_instead_of_failing_silently`
---
#### 🟡 MEDIUM - Raw API error message exposed to user (2 occurrences)
**Agent:** [security](https://diffray.ai/agents/security)
**Category:** security
<details>
<summary>📍 View all locations</summary>
| File | Description | Suggestion | Confidence |
|------|-------------|------------|------------|
| `extensions/feishu/src/docx.ts:86-88` | The error thrown exposes the raw error message from the Feishu API (res.msg) directly. This could le... | Wrap with generic message and log original: throw new Error('Failed to convert document content'); /... | 70% |
| `extensions/matrix/src/matrix/client/config.ts:127-130` | The Matrix login error exposes raw error response text from the homeserver directly. This could leak... | Map server error messages to generic user-friendly messages. Log original errorText for debugging. | 70% |
</details>
**Rule:** `sec_internal_details_in_user_errors`
---
#### 🟡 MEDIUM - Catch block without error context in logging
**Agent:** refactoring
**Category:** quality
**File:** `extensions/bluebubbles/src/monitor.ts:1115-1126`
**Description:** The catch block in resolveBlueBubblesAckReaction() logs a message but doesn't include the actual error details. This makes it difficult to debug why reaction normalization is failing.
**Suggestion:** Include the error in the log message: `ack reaction skipped (unsupported for BlueBubbles): ${raw}, error: ${String(err)}`
**Confidence:** 70%
**Rule:** `ts_include_error_context_in_structured_logs`
---
#### 🟡 MEDIUM - Non-null assertion without validation
**Agent:** typescript
**Category:** quality
**File:** `extensions/bluebubbles/src/monitor.ts:49-50`
**Description:** Using non-null assertion (!) on Map.keys().next().value after size check. While logically safe due to size >= DEDUP_MAX_SIZE check on line 48, the assertion could mask issues if map is mutated concurrently.
**Suggestion:** Add explicit check: const first = processedMessageIds.keys().next().value; if (first) { processedMessageIds.delete(first); }
**Confidence:** 65%
**Rule:** `ts_non_null_assertion`
---
</details>
<details>
<summary>🔇 2 low-severity issue(s) not posted (min: medium)</summary>
> Issues below `medium` severity are saved but not posted as comments.
> View all issues in the full review details.
</details>
🔗 [View full review details](https://app.diffray.ai/reviews/ab3a23fa-d862-4878-a790-f1e2a5909240)
---
<sub>Review ID: `ab3a23fa-d862-4878-a790-f1e2a5909240`</sub>
<sub>Rate it 👍 or 👎 to improve future reviews | Powered by <a href="https://diffray.ai?utm_source=github-private-note">diffray</a></sub> |
| export function extractToolResultMediaPaths(result: unknown): string[] | undefined { | ||
| if (!result || typeof result !== "object") { | ||
| return undefined; | ||
| } | ||
| const record = result as Record<string, unknown>; | ||
| const content = Array.isArray(record.content) ? record.content : null; | ||
| if (!content) { | ||
| return undefined; | ||
| } | ||
|
|
||
| const hasImage = content.some( | ||
| (item) => | ||
| item && typeof item === "object" && (item as Record<string, unknown>).type === "image", | ||
| ); | ||
| if (!hasImage) { | ||
| return undefined; | ||
| } | ||
|
|
||
| const details = record.details as Record<string, unknown> | undefined; | ||
| const path = typeof details?.path === "string" ? details.path.trim() : ""; | ||
| return path ? [path] : undefined; | ||
| } |
There was a problem hiding this comment.
function only extracts details.path once, but multiple images could exist in the content array
If a tool result contains multiple image content blocks, only the first details.path is returned. Consider iterating through all image content blocks or clarifying that only single-image results are supported.
Currently:
const hasImage = content.some(...) // checks if ANY image exists
return path ? [path] : undefined; // but only returns ONE pathPrompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/pi-embedded-subscribe.tools.ts
Line: 121:142
Comment:
function only extracts `details.path` once, but multiple images could exist in the `content` array
If a tool result contains multiple image content blocks, only the first `details.path` is returned. Consider iterating through all image content blocks or clarifying that only single-image results are supported.
Currently:
```typescript
const hasImage = content.some(...) // checks if ANY image exists
return path ? [path] : undefined; // but only returns ONE path
```
How can I resolve this? If you propose a fix, please make it concise.| export function extractToolResultMediaPaths(result: unknown): string[] | undefined { | ||
| if (!result || typeof result !== "object") { | ||
| return undefined; | ||
| } | ||
| const record = result as Record<string, unknown>; | ||
| const content = Array.isArray(record.content) ? record.content : null; | ||
| if (!content) { | ||
| return undefined; | ||
| } | ||
|
|
||
| const hasImage = content.some( | ||
| (item) => | ||
| item && typeof item === "object" && (item as Record<string, unknown>).type === "image", | ||
| ); | ||
| if (!hasImage) { | ||
| return undefined; | ||
| } | ||
|
|
||
| const details = record.details as Record<string, unknown> | undefined; | ||
| const path = typeof details?.path === "string" ? details.path.trim() : ""; | ||
| return path ? [path] : undefined; | ||
| } |
There was a problem hiding this comment.
add unit tests for extractToolResultMediaPaths to cover edge cases (multiple images, missing path, invalid inputs)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/pi-embedded-subscribe.tools.ts
Line: 121:142
Comment:
add unit tests for `extractToolResultMediaPaths` to cover edge cases (multiple images, missing path, invalid inputs)
<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>
How can I resolve this? If you propose a fix, please make it concise.
Summary
extractToolResultMediaPaths(), bypassing broken MEDIA: text parsingonToolResultregardless of verbose level (was gated byverboseLevel === "full")normalizeStreamingText()(was skipping when text was empty)Closes #11735
Test plan
npx vitest run src/agents/pi-embedded-subscribe.tools— 2/2 passednpx vitest run src/auto-reply/reply/agent-runner— 45/45 passed🤖 Generated with Claude Code
Greptile Overview
Greptile Summary
Fixed screenshot delivery to Telegram by bypassing broken MEDIA: text parsing and verbose level restrictions. Introduces
extractToolResultMediaPaths()to directly extract image paths from tool result content blocks, decouples media emission from theverboseLevel === "full"gate inhandleToolExecutionEnd(), and allows media-only payloads throughnormalizeStreamingText().Key changes:
Minor suggestion:
extractToolResultMediaPaths(src/agents/pi-embedded-subscribe.tools.ts:121)Confidence Score: 4/5
Last reviewed commit: 213a8e4
(4/5) You can add custom instructions or style guidelines for the agent here!