From 39fe31d2d31301163efdeee173eb5a5aa874dea5 Mon Sep 17 00:00:00 2001 From: mkorwel Date: Wed, 11 Feb 2026 22:31:43 -0600 Subject: [PATCH] agent harnness --- integration-tests/agent_harness_e2e.test.ts | 44 +++++++++++++++++++++ packages/core/src/agents/harness.ts | 13 +++++- packages/core/src/core/client.test.ts | 1 + packages/core/src/core/client.ts | 1 + 4 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 integration-tests/agent_harness_e2e.test.ts diff --git a/integration-tests/agent_harness_e2e.test.ts b/integration-tests/agent_harness_e2e.test.ts new file mode 100644 index 0000000000..ea37006acc --- /dev/null +++ b/integration-tests/agent_harness_e2e.test.ts @@ -0,0 +1,44 @@ +/** + * @license + * Copyright 2025 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { TestRig } from './test-helper.js'; + +describe('Agent Harness E2E', () => { + let rig: TestRig; + + beforeEach(() => { + rig = new TestRig(); + }); + + afterEach(async () => await rig.cleanup()); + + it('should execute a simple prompt using the agent harness', async () => { + await rig.setup('agent-harness-simple'); + + // Run with the harness enabled via env var + // Turn 1 + const result1 = await rig.run({ + args: ['chat', 'My name is GeminiUser'], + env: { + ...process.env, + GEMINI_ENABLE_AGENT_HARNESS: 'true', + }, + }); + expect(result1).toBeDefined(); + + // Turn 2 + const result2 = await rig.run({ + args: ['chat', 'What is my name?', '--resume', 'latest'], + env: { + ...process.env, + GEMINI_ENABLE_AGENT_HARNESS: 'true', + }, + }); + + expect(result2).toContain('GeminiUser'); + }, 30000); +}); diff --git a/packages/core/src/agents/harness.ts b/packages/core/src/agents/harness.ts index 3967bdf5bd..0645586e58 100644 --- a/packages/core/src/agents/harness.ts +++ b/packages/core/src/agents/harness.ts @@ -4,7 +4,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { type Part, type FunctionDeclaration } from '@google/genai'; +import { + type Part, + type FunctionDeclaration, + type Content, +} from '@google/genai'; import { type Config } from '../config/config.js'; import { GeminiChat } from '../core/geminiChat.js'; import { @@ -45,6 +49,8 @@ export interface AgentHarnessOptions { inputs?: AgentInputs; /** If provided, this prompt_id will be used as a prefix. */ parentPromptId?: string; + /** Existing chat history to initialize with (e.g. for main agent turns). */ + history?: Content[]; } /** @@ -60,6 +66,7 @@ export class AgentHarness { private readonly compressionService: ChatCompressionService; private readonly toolOutputMaskingService: ToolOutputMaskingService; private readonly toolRegistry: ToolRegistry; + private readonly initialHistory?: Content[]; private chat?: GeminiChat; private currentSequenceModel: string | null = null; @@ -68,6 +75,7 @@ export class AgentHarness { constructor(options: AgentHarnessOptions) { this.config = options.config; this.behavior = options.behavior; + this.initialHistory = options.history; this.loopDetector = new LoopDetectionService(this.config); this.compressionService = new ChatCompressionService(); @@ -89,7 +97,8 @@ export class AgentHarness { private async createChat(): Promise { const systemInstruction = await this.behavior.getSystemInstruction(); - const history = await this.behavior.getInitialHistory(); + const history = + this.initialHistory ?? (await this.behavior.getInitialHistory()); const tools = this.prepareToolsList(); return new GeminiChat( diff --git a/packages/core/src/core/client.test.ts b/packages/core/src/core/client.test.ts index 900abac591..a3849a52de 100644 --- a/packages/core/src/core/client.test.ts +++ b/packages/core/src/core/client.test.ts @@ -243,6 +243,7 @@ describe('Gemini Client (client.ts)', () => { getShowModelInfoInChat: vi.fn().mockReturnValue(false), getContinueOnFailedApiCall: vi.fn(), getProjectRoot: vi.fn().mockReturnValue('/test/project/root'), + isAgentHarnessEnabled: vi.fn().mockReturnValue(false), storage: { getProjectTempDir: vi.fn().mockReturnValue('/test/temp'), }, diff --git a/packages/core/src/core/client.ts b/packages/core/src/core/client.ts index 257ad2fb3e..bdec2e4311 100644 --- a/packages/core/src/core/client.ts +++ b/packages/core/src/core/client.ts @@ -812,6 +812,7 @@ export class GeminiClient { if (!this.harness || this.lastPromptId !== prompt_id) { this.harness = AgentFactory.createHarness(this.config, undefined, { parentPromptId: prompt_id, + history: this.getChat().getHistory(), }); this.lastPromptId = prompt_id; }