From a1651fd94ad9642fc6a3734a81a418087777946c Mon Sep 17 00:00:00 2001 From: Ed Burns Date: Thu, 18 Jun 2026 15:57:01 -0400 Subject: [PATCH 1/2] Add Node.js low-level tool-definition E2E test Related to issue #1682 but does not fix #1682. Align low_level_tool_definition coverage with PR #1721 snapshot behavior by only defining tools exercised by the shared snapshot. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- nodejs/test/e2e/session_lifecycle.e2e.test.ts | 2 +- nodejs/test/e2e/tools.e2e.test.ts | 48 ++++++++++++++++++- ...ated_sessions_after_sending_a_message.yaml | 4 +- 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/nodejs/test/e2e/session_lifecycle.e2e.test.ts b/nodejs/test/e2e/session_lifecycle.e2e.test.ts index fae878273..0cb570ceb 100644 --- a/nodejs/test/e2e/session_lifecycle.e2e.test.ts +++ b/nodejs/test/e2e/session_lifecycle.e2e.test.ts @@ -32,7 +32,7 @@ describe("Session Lifecycle", async () => { // Sessions must have activity to be persisted to disk await session1.sendAndWait({ prompt: "Say hello" }); - await session2.sendAndWait({ prompt: "Say world" }); + await session2.sendAndWait({ prompt: "Say hi" }); // Poll until both sessions are visible on disk instead of a hard 500ms wait. await waitFor(async () => { diff --git a/nodejs/test/e2e/tools.e2e.test.ts b/nodejs/test/e2e/tools.e2e.test.ts index 29e2ccf21..1faf57b51 100644 --- a/nodejs/test/e2e/tools.e2e.test.ts +++ b/nodejs/test/e2e/tools.e2e.test.ts @@ -6,7 +6,7 @@ import { writeFile } from "fs/promises"; import { join } from "path"; import { assert, describe, expect, it } from "vitest"; import { z } from "zod"; -import { defineTool, approveAll } from "../../src/index.js"; +import { defineTool, approveAll, ToolSet } from "../../src/index.js"; import type { PermissionRequest } from "../../src/index.js"; import { createSdkTestContext } from "./harness/sdkTestContext"; @@ -45,6 +45,52 @@ describe("Custom tools", async () => { expect(assistantMessage?.data.content).toContain("HELLO"); }); + it("low_level_tool_definition", async () => { + let currentPhase = ""; + const session = await client.createSession({ + onPermissionRequest: approveAll, + availableTools: new ToolSet().addCustom("*").addBuiltIn("web_fetch"), + tools: [ + defineTool("set_current_phase", { + description: "Sets the current phase of the agent", + parameters: z.object({ + phase: z.enum(["searching", "analyzing", "done"]), + }), + handler: ({ phase }) => { + currentPhase = phase; + return `Phase set to ${phase}`; + }, + }), + defineTool("search_items", { + description: "Search for items by keyword", + parameters: z.object({ + keyword: z.string(), + }), + handler: (_args, invocation) => { + const args = invocation.arguments as Record; + if (args.keyword !== "copilot") { + throw new Error(`Expected keyword to be 'copilot', got: ${String(args.keyword)}`); + } + return "Found: item_alpha, item_beta"; + }, + }), + ], + }); + + const assistantMessage = await session.sendAndWait({ + prompt: "First, set the current phase to 'analyzing'. Then search for items with keyword 'copilot'. Report the phase and search results.", + }); + + const content = assistantMessage?.data.content ?? ""; + expect(content.length).toBeGreaterThan(0); + expect(content.toLowerCase()).toContain("analyzing"); + expect( + content.toLowerCase().includes("item_alpha") || + content.toLowerCase().includes("item_beta") + ).toBe(true); + expect(currentPhase).toBe("analyzing"); + }); + it("handles tool calling errors", async () => { const session = await client.createSession({ onPermissionRequest: approveAll, diff --git a/test/snapshots/session_lifecycle/should_list_created_sessions_after_sending_a_message.yaml b/test/snapshots/session_lifecycle/should_list_created_sessions_after_sending_a_message.yaml index 274ab7d2f..779cffb03 100644 --- a/test/snapshots/session_lifecycle/should_list_created_sessions_after_sending_a_message.yaml +++ b/test/snapshots/session_lifecycle/should_list_created_sessions_after_sending_a_message.yaml @@ -13,6 +13,6 @@ conversations: - role: system content: ${system} - role: user - content: Say world + content: Say hi - role: assistant - content: world + content: Hi! I'm GitHub Copilot CLI, ready to help with your software engineering tasks. What would you like to work on? From a6a403d323f97ae9d031b91330c9413e3077eefc Mon Sep 17 00:00:00 2001 From: Ed Burns Date: Thu, 18 Jun 2026 16:02:30 -0400 Subject: [PATCH 2/2] Fix Node.js PR formatting and scope - Apply Prettier formatting to tools.e2e.test.ts so Node ubuntu format check passes. - Drop session lifecycle carryover from this PR by restoring Node session lifecycle files to upstream/main content, keeping this PR focused on low-level tool-definition coverage. Related to issue #1682 but does not fix #1682. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- nodejs/test/e2e/session_lifecycle.e2e.test.ts | 2 +- nodejs/test/e2e/tools.e2e.test.ts | 4 +++- .../should_list_created_sessions_after_sending_a_message.yaml | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/nodejs/test/e2e/session_lifecycle.e2e.test.ts b/nodejs/test/e2e/session_lifecycle.e2e.test.ts index 0cb570ceb..fae878273 100644 --- a/nodejs/test/e2e/session_lifecycle.e2e.test.ts +++ b/nodejs/test/e2e/session_lifecycle.e2e.test.ts @@ -32,7 +32,7 @@ describe("Session Lifecycle", async () => { // Sessions must have activity to be persisted to disk await session1.sendAndWait({ prompt: "Say hello" }); - await session2.sendAndWait({ prompt: "Say hi" }); + await session2.sendAndWait({ prompt: "Say world" }); // Poll until both sessions are visible on disk instead of a hard 500ms wait. await waitFor(async () => { diff --git a/nodejs/test/e2e/tools.e2e.test.ts b/nodejs/test/e2e/tools.e2e.test.ts index 1faf57b51..c505f8aa8 100644 --- a/nodejs/test/e2e/tools.e2e.test.ts +++ b/nodejs/test/e2e/tools.e2e.test.ts @@ -69,7 +69,9 @@ describe("Custom tools", async () => { handler: (_args, invocation) => { const args = invocation.arguments as Record; if (args.keyword !== "copilot") { - throw new Error(`Expected keyword to be 'copilot', got: ${String(args.keyword)}`); + throw new Error( + `Expected keyword to be 'copilot', got: ${String(args.keyword)}` + ); } return "Found: item_alpha, item_beta"; }, diff --git a/test/snapshots/session_lifecycle/should_list_created_sessions_after_sending_a_message.yaml b/test/snapshots/session_lifecycle/should_list_created_sessions_after_sending_a_message.yaml index 779cffb03..274ab7d2f 100644 --- a/test/snapshots/session_lifecycle/should_list_created_sessions_after_sending_a_message.yaml +++ b/test/snapshots/session_lifecycle/should_list_created_sessions_after_sending_a_message.yaml @@ -13,6 +13,6 @@ conversations: - role: system content: ${system} - role: user - content: Say hi + content: Say world - role: assistant - content: Hi! I'm GitHub Copilot CLI, ready to help with your software engineering tasks. What would you like to work on? + content: world