fix(coding-agent): resolved third review pass on inspect_image vision mode

- read now treats an xd://-mounted inspect_image as available (top-level
  predicate OR mounted device gated by the effective mode), so default
  xdev sessions with a text-only model keep metadata-guidance reads
  instead of inlining images the provider boundary would scrub
- advisor tool session stops inheriting the primary's isToolActive and
  xdevRegistry: advisors cannot execute xd:// devices, so their reads
  inline images again
- setModelWithProviderSessionReset is now async and awaited at every
  callsite, so retry-fallback model switches cannot race the
  inspect_image tool-slate reconcile
- regression tests for both xd:// availability directions
This commit is contained in:
can1357
2026-07-27 23:07:26 +02:00
parent 1bba24f191
commit 392aac4e49
6 changed files with 86 additions and 30 deletions
+22 -10
View File
@@ -874,7 +874,7 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
1,
Math.min(session.settings.get("read.defaultLimit") ?? DEFAULT_MAX_LINES, DEFAULT_MAX_LINES),
);
this.#inspectImageActive = session.isToolActive?.("inspect_image") ?? isInspectImageToolActive(session);
this.#inspectImageActive = this.#resolveInspectImageAvailability();
this.description = this.#renderDescription();
}
@@ -894,21 +894,33 @@ export class ReadTool implements AgentTool<typeof readSchema, ReadToolDetails> {
});
}
/**
* Whether the agent can actually reach `inspect_image` right now: exposed
* top-level, or mounted as an `xd://` device while the effective mode wants
* it (mounted devices stay executable via `write xd://inspect_image`, so a
* metadata-only read remains actionable). Sessions with neither
* availability signal (tests, embedded use) fall back to the mode
* computation alone. Restricted slates (subagents without the tool and
* without xdev) resolve to unavailable, so those sessions get inline image
* blocks instead of guidance pointing at an absent tool.
*/
#resolveInspectImageAvailability(): boolean {
const topLevel = this.session.isToolActive?.("inspect_image");
const registry = this.session.xdevRegistry;
if (topLevel === undefined && registry === undefined) return isInspectImageToolActive(this.session);
if (topLevel === true) return true;
return registry?.get("inspect_image") !== undefined && isInspectImageToolActive(this.session);
}
/**
* Re-evaluate the effective inspect_image state; it can flip when the model
* or the `/vision` override changes after this tool was constructed. Keeps
* the behavior branch and the advertised description in lockstep. Called
* per image read and by tool reconciliation before prompt rebuilds.
*
* Actual tool availability wins over the mode computation: restricted
* sessions (explicit tool slates without `inspect_image`, e.g. subagents)
* must never see metadata-only reads pointing at an absent tool. Sessions
* without an `isToolActive` predicate (tests, embedded use) fall back to
* the mode check.
* per image read and by tool reconciliation before prompt rebuilds (which
* passes the post-change availability as `availableOverride`).
*/
syncInspectImageState(availableOverride?: boolean): boolean {
const active =
availableOverride ?? this.session.isToolActive?.("inspect_image") ?? isInspectImageToolActive(this.session);
const active = availableOverride ?? this.#resolveInspectImageAvailability();
if (active !== this.#inspectImageActive) {
this.#inspectImageActive = active;
this.description = this.#renderDescription();