From 3a43bafe3f3117c8012bd396c3198c0ab467f0e8 Mon Sep 17 00:00:00 2001 From: Carlos Ravelo Date: Wed, 4 Nov 2026 00:43:11 -0500 Subject: [PATCH] Convert delegated-agent tests to TypeScript --- .../automatic-session-context-resilience.md | 6 +- scripts/bun-test-files.json | 2 +- ...e-agent.test.js => delegate-agent.test.ts} | 101 ++++++++---------- src/tools/delegate-agent.ts | 2 +- 4 files changed, 48 insertions(+), 63 deletions(-) rename src/tools/__tests__/{delegate-agent.test.js => delegate-agent.test.ts} (89%) diff --git a/docs/plans/automatic-session-context-resilience.md b/docs/plans/automatic-session-context-resilience.md index 08f8d731d..358a3f7bb 100644 --- a/docs/plans/automatic-session-context-resilience.md +++ b/docs/plans/automatic-session-context-resilience.md @@ -27,7 +27,7 @@ affectedPaths: - "src/shared/session/request-dispatch.ts" - "src/shared/session/background-tasks.ts" - "src/tools/delegate-agent.ts" - - "src/tools/__tests__/delegate-agent.test.js" + - "src/tools/__tests__/delegate-agent.test.ts" - "src/cmd/compact/index.ts" - "src/cmd/compact/index.test.js" - "src/ui/tui/runtime-adapter.ts" @@ -334,7 +334,7 @@ shifts, migration or compatibility risk grows, or the Verification Plan no longe - `src/tools/delegate-agent.ts`, `src/shared/session/background-tasks.ts`, and background task tests — map foreground and background pause outcomes through their different existing settlement paths. Do not present partial output as successful work. -- `src/tools/__tests__/delegate-agent.test.js` — verify delegated Agent cancellation and context pause preserve tool +- `src/tools/__tests__/delegate-agent.test.ts` — verify delegated Agent cancellation and context pause preserve tool settlement, report `context_resilience_paused` deterministically, and respect Hosted Session compaction serialization. - `src/cmd/compact/index.ts` — handle Runtime's `compaction_in_progress` result without reading success-only compaction fields and display one concise retry-later message. @@ -462,7 +462,7 @@ unless the complete public AgentSession contract has been verified; a partial ga src/shared/session/agent-handler.test.ts src/shared/session/request-dispatch.test.ts src/shared/session/background-tasks.test.ts src/shared/session/background-tasks-extra.test.ts src/shared/session/segment-rollover.test.js src/shared/session/session-transcript-projection.test.js - src/tools/__tests__/delegate-agent.test.js src/cmd/compact/index.test.js + src/tools/__tests__/delegate-agent.test.ts src/cmd/compact/index.test.js src/ui/tui/runtime-adapter.test.js src/acp/server.test.js src/ui/workspace/workspace-session-ux.test.tsx`. Include new focused integration suites through the same runner. Never use direct `deno test`. - Existing coverage: preserve manual/resume compaction, automatic-disabled behavior, image and Named Invocation context, diff --git a/scripts/bun-test-files.json b/scripts/bun-test-files.json index d7830ec50..e9590e3e7 100644 --- a/scripts/bun-test-files.json +++ b/scripts/bun-test-files.json @@ -687,7 +687,7 @@ "src/testing/workflow-binary-fixtures.test.ts", "src/tools/__tests__/bash-fixture.test.ts", "src/tools/__tests__/bash.test.ts", - "src/tools/__tests__/delegate-agent.test.js", + "src/tools/__tests__/delegate-agent.test.ts", "src/tools/__tests__/delegate-bash-integration.test.ts", "src/tools/__tests__/docs-file-tools.test.ts", "src/tools/__tests__/edit.test.ts", diff --git a/src/tools/__tests__/delegate-agent.test.js b/src/tools/__tests__/delegate-agent.test.ts similarity index 89% rename from src/tools/__tests__/delegate-agent.test.js rename to src/tools/__tests__/delegate-agent.test.ts index ee5f90f88..45740ef52 100644 --- a/src/tools/__tests__/delegate-agent.test.js +++ b/src/tools/__tests__/delegate-agent.test.ts @@ -16,48 +16,43 @@ import { createDelegateAgentTool, diffDelegatedChangeSnapshot, resolveDelegatedT import { loadSubAgentDefinition } from "../../shared/session/subagent-definitions.ts"; import { SUBAGENTS } from "../../constants.js"; -/** - * @typedef {Object} DelegateToolDetails - * @property {boolean} ok - * @property {"read" | "write"} mode - * @property {string} [role] - * @property {"read" | "write"} [requestedAuthority] - * @property {"read" | "write"} [effectiveAuthority] - * @property {"read" | "write"} [roleAuthorityCeiling] - * @property {string[]} [validRoles] - * @property {string[]} [tools] - * @property {string[]} [changedPaths] - * @property {boolean} [changeAttributionComplete] - * @property {boolean} [committedChangesDetected] - * @property {string} [error] - */ - -/** - * @typedef {Object} DelegateToolResult - * @property {Array<{ type: string, text: string }>} content - * @property {DelegateToolDetails} details - * @property {boolean} [isError] - */ - -/** - * @typedef {Object} ExecutableTool - * @property {(toolCallId: string, params: Record, signal: AbortSignal, onUpdate: () => void, context: Record) => Promise} execute - */ - -/** - * @param {unknown} tool - * @param {Record} params - * @param {AbortSignal} [signal] - * @returns {Promise} - */ -async function execute(tool, params, signal = new AbortController().signal) { - const executable = /** @type {ExecutableTool} */ (tool); - return await executable.execute("delegate-call", params, signal, () => {}, {}); +import type { AgentMessage } from "@earendil-works/pi-agent-core"; +import type { AssistantMessage, TextContent } from "@earendil-works/pi-ai"; +import type { AgentToolResult, ExtensionToolContext } from "@earendil-works/pi-coding-agent"; +import type { DelegateAgentDetails, DelegatedAgentSessionOptions, DelegationMode } from "../delegate-agent.ts"; + +type DelegateTool = ReturnType; +interface DelegateToolResult extends Omit, "content"> { + content: TextContent[]; + isError?: boolean; } +type DelegatedLeaseState = ReturnType; -/** @returns {Promise} */ -function assistantDone() { - const message = /** @type {import('@earendil-works/pi-ai').AssistantMessage} */ ({ +// The invalid-role case deliberately exercises the tool's runtime validation boundary. +interface DelegateTestParameters { + mode: DelegationMode; + role?: string; + brief: string; +} + +async function execute( + tool: DelegateTool, + params: DelegateTestParameters, + signal = new AbortController().signal, +): Promise { + const executable = tool; + // These fixtures use no context fields and assert the tool's text-only results. + return (await executable.execute( + "delegate-call", + params, + signal, + () => {}, + {} as ExtensionToolContext, + )) as DelegateToolResult; +} + +function assistantDone(): Promise { + const message: AssistantMessage = { role: "assistant", api: "openai-completions", provider: "test", @@ -73,15 +68,11 @@ function assistantDone() { }, stopReason: "stop", timestamp: Date.now(), - }); + }; return Promise.resolve([message]); } -/** - * @param {string} cwd - * @param {string[]} args - */ -async function git(cwd, args) { +async function git(cwd: string, args: string[]) { const output = await captureForegroundProcess("git", { cwd, args }); if (!output.success) throw new Error(new TextDecoder().decode(output.stderr)); return new TextDecoder().decode(output.stdout); @@ -197,8 +188,7 @@ test("delegated agent prompt includes inherited repository context placeholders" }); test("delegate_agent returns child output without inheriting workflow tools", async () => { - /** @type {Array} */ - const calls = []; + const calls: DelegatedAgentSessionOptions[] = []; const hostedSession = new HostedSession({ id: "delegate-read", cwd: getCwd() }); const tool = createDelegateAgentTool({ hostedSession, @@ -235,11 +225,9 @@ test("delegate_agent returns child output without inheriting workflow tools", as }); test("delegate_agent applies verification-adversary read-only role ceiling", async () => { - /** @type {Array} */ - const calls = []; + const calls: DelegatedAgentSessionOptions[] = []; const hostedSession = new HostedSession({ id: "delegate-adversary", cwd: getCwd() }); - /** @type {Array<{ readers: number, writer: boolean }>} */ - const leaseStates = []; + const leaseStates: DelegatedLeaseState[] = []; const tool = createDelegateAgentTool({ hostedSession, cwd: getCwd(), @@ -366,8 +354,7 @@ test("delegate_agent limited to write mode refuses a role that reduces it to rea }); test("delegate_agent propagates parent model and thinking state", async () => { - /** @type {Array} */ - const calls = []; + const calls: DelegatedAgentSessionOptions[] = []; const hostedSession = new HostedSession({ id: "delegate-parent-state", cwd: getCwd() }); hostedSession.pushAgentInfo("Engineer", "anthropic/claude-sonnet-4", "anthropic", "engineer"); hostedSession.setThinkingLevel("high"); @@ -394,8 +381,7 @@ test("delegate_agent preserves failed writer changes and releases lease", async try { await fsWriteFile(join(executionCwd, "src", "pre-existing.js"), "modified\n"); const hostedSession = new HostedSession({ id: "delegate-write-fail", cwd: getCwd() }); - /** @type {Array} */ - const calls = []; + const calls: DelegatedAgentSessionOptions[] = []; const tool = createDelegateAgentTool({ hostedSession, cwd: executionCwd, @@ -473,8 +459,7 @@ test("delegate_agent returns structured failure when lease acquisition is reject test("delegate_agent forwards cancellation and releases its lease", async () => { const hostedSession = new HostedSession({ id: "delegate-cancel", cwd: getCwd() }); const controller = new AbortController(); - /** @type {AbortSignal | undefined} */ - let childSignal; + let childSignal: AbortSignal | undefined; const tool = createDelegateAgentTool({ hostedSession, cwd: getCwd(), diff --git a/src/tools/delegate-agent.ts b/src/tools/delegate-agent.ts index 8580cdff8..340f16139 100644 --- a/src/tools/delegate-agent.ts +++ b/src/tools/delegate-agent.ts @@ -123,7 +123,7 @@ interface PorcelainStatusEntry { path: string; } -interface DelegateAgentDetails { +export interface DelegateAgentDetails { ok: boolean; mode: DelegationMode; role: string;