| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- /**
- * AgentModelEvaluator - Logs agent and model information for test transparency
- *
- * This evaluator provides visibility into which agent and model were used during test execution.
- * It extracts actual agent metadata from eval-runner.md and logs it prominently.
- *
- * Features:
- * - Reads eval-runner.md to extract actual agent metadata (id, name, description)
- * - Shows agent prompt snippet (first 200 chars) to confirm correct agent loaded
- * - Logs expected vs actual agent/model for comparison
- * - Always passes (informational only, not validation)
- *
- * Note: This is INFORMATIONAL ONLY. Actual agent/model configuration happens at test setup time
- * via test-runner.ts setupEvalRunner(). Session data doesn't store agent/model metadata.
- */
- import { BaseEvaluator } from './base-evaluator.js';
- import {
- TimelineEvent,
- SessionInfo,
- EvaluationResult,
- Evidence,
- Violation
- } from '../types/index.js';
- import { readFileSync, existsSync } from 'fs';
- import { join } from 'path';
- import { homedir } from 'os';
- export interface AgentModelExpectations {
- /** Expected agent name (e.g., "openagent", "opencoder", "coder-agent") */
- expectedAgent?: string;
- /** Expected model (e.g., "opencode/grok-code", "anthropic/claude-3-5-sonnet-20241022") */
- expectedModel?: string;
- /** Project path to find eval-runner.md */
- projectPath?: string;
- }
- interface AgentMetadata {
- id?: string;
- name?: string;
- description?: string;
- category?: string;
- type?: string;
- version?: string;
- mode?: string;
- promptSnippet?: string;
- }
- export class AgentModelEvaluator extends BaseEvaluator {
- name = 'agent-model';
- description = 'Logs agent and model information for test transparency';
- private expectations: AgentModelExpectations;
- constructor(expectations: AgentModelExpectations = {}) {
- super();
- this.expectations = expectations;
- }
- /**
- * Extract agent metadata from eval-runner.md frontmatter
- */
- private extractAgentMetadata(projectPath: string): AgentMetadata {
- const metadata: AgentMetadata = {};
-
- try {
- const evalRunnerPath = join(projectPath, '.opencode', 'agent', 'eval-runner.md');
-
- if (!existsSync(evalRunnerPath)) {
- return metadata;
- }
- const content = readFileSync(evalRunnerPath, 'utf-8');
-
- // Extract frontmatter (between --- markers)
- const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
- if (!frontmatterMatch) {
- return metadata;
- }
- const frontmatter = frontmatterMatch[1];
-
- // Extract key fields
- const idMatch = frontmatter.match(/^id:\s*(.+)$/m);
- const nameMatch = frontmatter.match(/^name:\s*(.+)$/m);
- const descMatch = frontmatter.match(/^description:\s*["'](.+)["']$/m);
- const categoryMatch = frontmatter.match(/^category:\s*(.+)$/m);
- const typeMatch = frontmatter.match(/^type:\s*(.+)$/m);
- const versionMatch = frontmatter.match(/^version:\s*(.+)$/m);
- const modeMatch = frontmatter.match(/^mode:\s*(.+)$/m);
- if (idMatch) metadata.id = idMatch[1].trim();
- if (nameMatch) metadata.name = nameMatch[1].trim();
- if (descMatch) metadata.description = descMatch[1].trim();
- if (categoryMatch) metadata.category = categoryMatch[1].trim();
- if (typeMatch) metadata.type = typeMatch[1].trim();
- if (versionMatch) metadata.version = versionMatch[1].trim();
- if (modeMatch) metadata.mode = modeMatch[1].trim();
- // Extract prompt snippet (first 200 chars after frontmatter)
- const promptStart = content.indexOf('---', 4) + 3; // Skip first ---
- const promptContent = content.substring(promptStart).trim();
- const snippet = promptContent.substring(0, 200).replace(/\n/g, ' ').trim();
- metadata.promptSnippet = snippet + (promptContent.length > 200 ? '...' : '');
- } catch (error) {
- // Silently fail - this is informational only
- }
- return metadata;
- }
- /**
- * Extract model from timeline events
- * Model is passed in API calls but not stored in session metadata
- */
- private extractModelFromTimeline(timeline: TimelineEvent[]): string | undefined {
- // Look for session.created or message events that might contain model info
- // In practice, model isn't in timeline either, but we try
- return undefined; // Model not available in timeline
- }
- async evaluate(timeline: TimelineEvent[], sessionInfo: SessionInfo): Promise<EvaluationResult> {
- const violations: Violation[] = [];
- const evidence: Evidence[] = [];
- // Extract actual agent metadata from eval-runner.md
- const projectPath = this.expectations.projectPath || process.cwd();
- const actualAgent = this.extractAgentMetadata(projectPath);
- const actualModel = this.extractModelFromTimeline(timeline);
- // Log actual agent information
- if (actualAgent.id || actualAgent.name) {
- evidence.push(this.createEvidence(
- 'actual-agent-info',
- `Actual agent loaded: ${actualAgent.name || actualAgent.id || 'unknown'}`,
- {
- id: actualAgent.id,
- name: actualAgent.name,
- description: actualAgent.description,
- category: actualAgent.category,
- type: actualAgent.type,
- version: actualAgent.version,
- mode: actualAgent.mode,
- }
- ));
- }
- // Log agent prompt snippet for verification
- if (actualAgent.promptSnippet) {
- evidence.push(this.createEvidence(
- 'agent-prompt-snippet',
- `Agent prompt snippet: "${actualAgent.promptSnippet}"`,
- { snippet: actualAgent.promptSnippet }
- ));
- }
- // Log expected agent/model if set
- if (this.expectations.expectedAgent) {
- evidence.push(this.createEvidence(
- 'expected-agent',
- `Expected agent: ${this.expectations.expectedAgent}`,
- { expectedAgent: this.expectations.expectedAgent }
- ));
- }
- if (this.expectations.expectedModel) {
- evidence.push(this.createEvidence(
- 'expected-model',
- `Expected model: ${this.expectations.expectedModel}`,
- { expectedModel: this.expectations.expectedModel }
- ));
- }
- // Log informational note
- evidence.push(this.createEvidence(
- 'info-note',
- 'ℹ️ This evaluator is INFORMATIONAL ONLY - it logs agent/model info but does not validate',
- {
- note: 'Agent/model configuration happens at test setup time (test-runner.ts)',
- actualAgentId: actualAgent.id,
- expectedAgent: this.expectations.expectedAgent,
- expectedModel: this.expectations.expectedModel,
- }
- ));
- // Always pass - this is informational only
- const passed = true;
- const score = 100;
- return {
- evaluator: this.name,
- passed,
- score,
- violations,
- evidence,
- metadata: {
- actualAgent: {
- id: actualAgent.id,
- name: actualAgent.name,
- description: actualAgent.description,
- category: actualAgent.category,
- type: actualAgent.type,
- version: actualAgent.version,
- mode: actualAgent.mode,
- promptSnippet: actualAgent.promptSnippet,
- },
- expectedAgent: this.expectations.expectedAgent,
- expectedModel: this.expectations.expectedModel,
- mode: 'informational',
- }
- };
- }
- }
|