Repository navigation
fix(agent-core-v2): stop advertising unavailable ReadMediaFile to non-multimodal models #3046
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
eb33399
3172f5e
015bd35
7a6feff
346eac1
436bf92
51ebff5
e4032e5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@moonshot-ai/kimi-code": patch | ||
| --- | ||
|
|
||
| Stop pointing models without image or video input capability to the unavailable ReadMediaFile tool. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,7 @@ | ||
| import type { IHostFileSystem } from '#/os/interface/hostFileSystem'; | ||
| import { IAgentRuntimeService, inspectAgentRuntime } from '#/agent/runtimeBinding/agentRuntime'; | ||
| import { IAgentToolRegistryService } from '#/agent/toolRegistry/toolRegistry'; | ||
| import { IAgentToolPolicyService } from '#/agent/toolPolicy/toolPolicy'; | ||
| import { RuntimeWorkspaceView } from '#/runtime/runtimeWorkspaceView'; | ||
| import { unwrapErrorCause } from '#/_base/errors/errors'; | ||
| import { ISessionSkillCatalog } from '#/session/sessionSkillCatalog/skillCatalog'; | ||
|
|
@@ -180,12 +182,16 @@ async function* decodedLines(lines: readonly string[]): AsyncGenerator<string> { | |
| yield* lines; | ||
| } | ||
|
|
||
| function notReadableFileOutput(path: string): string { | ||
| return ( | ||
| `"${path}" is not readable as UTF-8 text. ` + | ||
| 'If it is an image or video, use ReadMediaFile. ' + | ||
| 'For other binary formats, use Bash or an MCP tool if available.' | ||
| ); | ||
| function notReadableFileOutput(path: string, mediaReadable: boolean, bashAvailable: boolean): string { | ||
| const binary = bashAvailable ? 'use Bash or an MCP tool if available.' : 'use an available MCP tool.'; | ||
| if (mediaReadable) { | ||
| return ( | ||
| `"${path}" is not readable as UTF-8 text. ` + | ||
| 'If it is an image or video, use ReadMediaFile. ' + | ||
| `For other binary formats, ${binary}` | ||
| ); | ||
| } | ||
| return `"${path}" is not readable as UTF-8 text. For binary formats, ${binary}`; | ||
| } | ||
|
|
||
| function notUtf8DecodableFileOutput(path: string): string { | ||
|
|
@@ -196,23 +202,58 @@ function notUtf8DecodableFileOutput(path: string): string { | |
| ); | ||
| } | ||
|
|
||
| const READ_DESCRIPTION = renderPrompt(readDescriptionTemplate, { | ||
| MAX_LINES, | ||
| MAX_BYTES_KB: MAX_BYTES / 1024, | ||
| MAX_LINE_LENGTH, | ||
| }); | ||
| function binaryFormatsHint(mediaReadable: boolean, bashAvailable: boolean): string { | ||
| const binary = bashAvailable ? 'Bash or an MCP tool' : 'an available MCP tool'; | ||
| return mediaReadable | ||
| ? `use \`ReadMediaFile\` for images or video, and ${binary} for other binary formats.` | ||
| : `use ${binary} for binary formats.`; | ||
| } | ||
|
|
||
| export class ReadTool implements IReadTool { | ||
| declare readonly _serviceBrand: undefined; | ||
| readonly name = 'Read' as const; | ||
| readonly description = READ_DESCRIPTION; | ||
| readonly parameters: Record<string, unknown> = toInputJsonSchema(ReadInputSchema); | ||
| constructor( | ||
| @IAgentRuntimeService private readonly runtime: IAgentRuntimeService, | ||
| @ISessionWorkspaceContext private readonly workspaceCtx: ISessionWorkspaceContext, | ||
| @ISessionSkillCatalog private readonly skillCatalog: ISessionSkillCatalog, | ||
| @IAgentToolRegistryService private readonly toolRegistry: IAgentToolRegistryService, | ||
| @IAgentToolPolicyService private readonly toolPolicy: IAgentToolPolicyService, | ||
| ) {} | ||
|
|
||
| get description(): string { | ||
| return renderPrompt(readDescriptionTemplate, { | ||
| MAX_LINES, | ||
| MAX_BYTES_KB: MAX_BYTES / 1024, | ||
| MAX_LINE_LENGTH, | ||
| BINARY_FORMATS_HINT: binaryFormatsHint(this.mediaReadable(), this.bashAvailable()), | ||
| }); | ||
| } | ||
|
|
||
| private mediaReadable(): boolean { | ||
| return this.mediaRegistered() && this.toolPolicy.isToolActive('ReadMediaFile'); | ||
| } | ||
|
|
||
| private mediaRegistered(): boolean { | ||
| return this.toolRegistry.resolve('ReadMediaFile') !== undefined; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a multimodal model registers AGENTS.md reference: packages/agent-core-v2/AGENTS.md:L7-L7 Useful? React with 👍 / 👎. |
||
| } | ||
|
|
||
| private bashAvailable(): boolean { | ||
| return ( | ||
| this.toolRegistry.resolve('Bash') !== undefined && this.toolPolicy.isToolActive('Bash') | ||
| ); | ||
| } | ||
|
|
||
| private mediaUnavailableOutput(path: string, kind: string): string { | ||
| const reason = this.mediaRegistered() | ||
| ? 'The ReadMediaFile tool is disabled by the active tool policy, so image and video reading is unavailable.' | ||
| : 'Image and video reading is unavailable for this agent.'; | ||
| const fallback = this.bashAvailable() | ||
| ? ' Use Bash or an MCP tool to inspect or convert it.' | ||
| : ' Use an available MCP tool to inspect or convert it.'; | ||
| return `"${path}" is a ${kind} file. ${reason}${fallback}`; | ||
| } | ||
|
|
||
| private workspaceConfig(view: RuntimeWorkspaceView): WorkspaceConfig { | ||
| return { workspaceDir: view.workDir, additionalDirs: view.additionalDirs }; | ||
| } | ||
|
|
@@ -275,7 +316,9 @@ export class ReadTool implements IReadTool { | |
| if (fileType.kind === 'image' || fileType.kind === 'video') { | ||
| return { | ||
| isError: true, | ||
| output: `"${args.path}" is a ${fileType.kind} file. Use ReadMediaFile to read image or video files.`, | ||
| output: this.mediaReadable() | ||
| ? `"${args.path}" is a ${fileType.kind} file. Use ReadMediaFile to read image or video files.` | ||
| : this.mediaUnavailableOutput(args.path, fileType.kind), | ||
| }; | ||
| } | ||
|
|
||
|
|
@@ -298,7 +341,7 @@ export class ReadTool implements IReadTool { | |
| } else if (fileType.kind === 'unknown') { | ||
| return { | ||
| isError: true, | ||
| output: notReadableFileOutput(args.path), | ||
| output: notReadableFileOutput(args.path, this.mediaReadable(), this.bashAvailable()), | ||
| }; | ||
| } else { | ||
| lines = fs.readLines(safePath, { errors: 'strict' }); | ||
|
|
@@ -353,7 +396,7 @@ export class ReadTool implements IReadTool { | |
|
|
||
| for await (const rawLine of lines) { | ||
| if (containsNulByte(rawLine)) { | ||
| return { isError: true, output: notReadableFileOutput(displayPath) }; | ||
| return { isError: true, output: notReadableFileOutput(displayPath, this.mediaReadable(), this.bashAvailable()) }; | ||
| } | ||
| currentLineNo += 1; | ||
| updateLineEndingFlags(flags, rawLine); | ||
|
|
@@ -411,7 +454,7 @@ export class ReadTool implements IReadTool { | |
|
|
||
| for await (const rawLine of lines) { | ||
| if (containsNulByte(rawLine)) { | ||
| return { isError: true, output: notReadableFileOutput(displayPath) }; | ||
| return { isError: true, output: notReadableFileOutput(displayPath, this.mediaReadable(), this.bashAvailable()) }; | ||
| } | ||
| currentLineNo += 1; | ||
| updateLineEndingFlags(flags, rawLine); | ||
|
|
||
Large diffs are not rendered by default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When Bash is unavailable, this still tells the model to use an MCP tool without checking whether the registry contains any policy-active MCP tools. Fresh evidence in this revision is the new fallback itself: the
planprofile atpackages/agent-core-v2/src/features/plan/profile/plan.ts:8-15allows neither Bash normcp__*, so a text-only plan agent receives guidance to use a tool it cannot call. Inspect active MCP registry entries before mentioning MCP, or report that no binary-reading fallback is available.Useful? React with 👍 / 👎.