agent harnness

This commit is contained in:
mkorwel
2026-02-11 22:31:43 -06:00
parent 67819bf5ae
commit 39fe31d2d3
4 changed files with 57 additions and 2 deletions
@@ -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);
});
+11 -2
View File
@@ -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<GeminiChat> {
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(
+1
View File
@@ -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'),
},
+1
View File
@@ -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;
}