fix(coding-agent): fix CI type check and image-only steering test

- type-check subagent-extension.test.ts against the extension view like the other subagent test
- update the test to current ToolRenderContext and ExtensionToolContext types
- use a decodable PNG in the image-only steering test; the fake data was omitted by image processing
This commit is contained in:
Mario Zechner 2026-11-04 10:34:45 +00:00
parent 3e4f872654
commit a3a1005053
4 changed files with 23 additions and 12 deletions

View file

@ -189,7 +189,8 @@
"include": [ "include": [
"./extensions/**/*", "./extensions/**/*",
"../../*/src/**/*.d.ts", "../../*/src/**/*.d.ts",
"../test/suite/regressions/8261-subagent-project-trust.test.ts" "../test/suite/regressions/8261-subagent-project-trust.test.ts",
"../test/subagent-extension.test.ts"
], ],
"exclude": [ "exclude": [
"**/node_modules/**", "**/node_modules/**",

View file

@ -7,7 +7,7 @@ import { fauxAssistantMessage, fauxToolCall, type Message } from "@earendil-work
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import subagentExtension from "../examples/extensions/subagent/index.ts"; import subagentExtension from "../examples/extensions/subagent/index.ts";
import { CONFIG_DIR_NAME } from "../src/config.ts"; import { CONFIG_DIR_NAME } from "../src/config.ts";
import type { ExtensionAPI, ExtensionContext, ToolDefinition } from "../src/core/extensions/index.ts"; import type { ExtensionAPI, ExtensionToolContext, ToolDefinition } from "../src/core/extensions/index.ts";
import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES } from "../src/core/tools/truncate.ts"; import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES } from "../src/core/tools/truncate.ts";
import { createHarness, type Harness } from "./suite/harness.ts"; import { createHarness, type Harness } from "./suite/harness.ts";
@ -111,22 +111,22 @@ function createTool(): ToolDefinition {
return registered; return registered;
} }
function createContext(cwd: string): ExtensionContext { function createContext(cwd: string): ExtensionToolContext {
return { return {
cwd, cwd,
hasUI: false, hasUI: false,
model: { provider: "test", id: "dispatch-alias" }, model: { provider: "test", id: "dispatch-alias" },
thinkingLevel: "high", thinkingLevel: "high",
isProjectTrusted: () => false, isProjectTrusted: () => false,
} as unknown as ExtensionContext; } as unknown as ExtensionToolContext;
} }
function createInteractiveContext( function createInteractiveContext(
base: ExtensionContext, base: ExtensionToolContext,
confirm: (title: string, message: string) => Promise<boolean>, confirm: (title: string, message: string) => Promise<boolean>,
trusted = false, trusted = false,
): ExtensionContext { ): ExtensionToolContext {
return { ...base, hasUI: true, ui: { confirm }, isProjectTrusted: () => trusted } as unknown as ExtensionContext; return { ...base, hasUI: true, ui: { confirm }, isProjectTrusted: () => trusted } as unknown as ExtensionToolContext;
} }
function singleTask(task: string, overrides: Record<string, unknown> = {}): Record<string, unknown> { function singleTask(task: string, overrides: Record<string, unknown> = {}): Record<string, unknown> {
@ -204,6 +204,10 @@ function renderText(tool: ToolDefinition, result: AgentToolResult<unknown>, expa
expanded, expanded,
showImages: false, showImages: false,
isError: false, isError: false,
durationMs: undefined,
outputPad: 0,
keyText: () => "",
keyHint: (_keybinding, description) => description,
}); });
return component.render(160).join("\n"); return component.render(160).join("\n");
} }
@ -211,7 +215,7 @@ function renderText(tool: ToolDefinition, result: AgentToolResult<unknown>, expa
describe("subagent example extension", () => { describe("subagent example extension", () => {
let tempDir: string; let tempDir: string;
let tool: ToolDefinition; let tool: ToolDefinition;
let ctx: ExtensionContext; let ctx: ExtensionToolContext;
const harnesses: Harness[] = []; const harnesses: Harness[] = [];
function executeSubagent( function executeSubagent(
@ -219,7 +223,7 @@ describe("subagent example extension", () => {
options: { options: {
signal?: AbortSignal; signal?: AbortSignal;
onUpdate?: (result: AgentToolResult<unknown>) => void; onUpdate?: (result: AgentToolResult<unknown>) => void;
context?: ExtensionContext; context?: ExtensionToolContext;
} = {}, } = {},
): Promise<AgentToolResult<unknown>> { ): Promise<AgentToolResult<unknown>> {
return withFixtureInvocation(() => return withFixtureInvocation(() =>
@ -419,7 +423,9 @@ describe("subagent example extension", () => {
expect(toolResult?.role).toBe("toolResult"); expect(toolResult?.role).toBe("toolResult");
if (toolResult?.role === "toolResult") { if (toolResult?.role === "toolResult") {
expect(toolResult.isError).toBe(true); expect(toolResult.isError).toBe(true);
expect((toolResult.details as TestSubagentDetails).results[0].errorMessage).toBe("provider evidence"); expect((toolResult.details as unknown as TestSubagentDetails).results[0].errorMessage).toBe(
"provider evidence",
);
} }
}); });

View file

@ -5,6 +5,10 @@ import { Type } from "typebox";
import { afterEach, describe, expect, it } from "vitest"; import { afterEach, describe, expect, it } from "vitest";
import { createHarness, getAssistantTexts, getMessageText, getUserTexts, type Harness } from "./harness.ts"; import { createHarness, getAssistantTexts, getMessageText, getUserTexts, type Harness } from "./harness.ts";
// Image processing omits images it cannot decode, so the fixture must be a real PNG.
const TINY_PNG_BASE64 =
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg==";
async function createWaitingHarness( async function createWaitingHarness(
options: { options: {
tools?: AgentTool[]; tools?: AgentTool[];
@ -456,7 +460,7 @@ describe("AgentSession queue characterization", () => {
]); ]);
await waitForToolStart; await waitForToolStart;
await harness.session.steer("", [{ type: "image", mimeType: "image/png", data: "ZmFrZQ==" }]); await harness.session.steer("", [{ type: "image", mimeType: "image/png", data: TINY_PNG_BASE64 }]);
expect(harness.session.pendingMessageCount).toBe(1); expect(harness.session.pendingMessageCount).toBe(1);
releaseToolExecution(); releaseToolExecution();
await promptPromise; await promptPromise;

View file

@ -68,5 +68,5 @@
} }
}, },
"include": ["scripts/model-catalog-protocol.ts", "scripts/model-catalog-protocol.test.ts", "packages/*/src/**/*", "packages/*/test/**/*", "packages/ai/catalog/**/*", "packages/durable/test-workers/**/*", "packages/coding-agent/examples/**/*", "packages/agent/examples/**/*"], "include": ["scripts/model-catalog-protocol.ts", "scripts/model-catalog-protocol.test.ts", "packages/*/src/**/*", "packages/*/test/**/*", "packages/ai/catalog/**/*", "packages/durable/test-workers/**/*", "packages/coding-agent/examples/**/*", "packages/agent/examples/**/*"],
"exclude": ["**/dist/**", "packages/coding-agent/examples/extensions/**", "packages/coding-agent/test/suite/regressions/8261-subagent-project-trust.test.ts"] "exclude": ["**/dist/**", "packages/coding-agent/examples/extensions/**", "packages/coding-agent/test/suite/regressions/8261-subagent-project-trust.test.ts", "packages/coding-agent/test/subagent-extension.test.ts"]
} }