refactor(agent): finalize harness resource config
This commit is contained in:
@@ -17,7 +17,9 @@ import type {
|
||||
ExecutionEnv,
|
||||
NavigateTreeResult,
|
||||
PendingSessionWrite,
|
||||
PromptTemplate,
|
||||
Session,
|
||||
Skill,
|
||||
} from "./types.js";
|
||||
|
||||
function createUserMessage(text: string, images?: ImageContent[]): UserMessage {
|
||||
@@ -26,7 +28,11 @@ function createUserMessage(text: string, images?: ImageContent[]): UserMessage {
|
||||
return { role: "user", content, timestamp: Date.now() };
|
||||
}
|
||||
|
||||
export class AgentHarness {
|
||||
export class AgentHarness<
|
||||
TSkill extends Skill = Skill,
|
||||
TPromptTemplate extends PromptTemplate = PromptTemplate,
|
||||
TTool extends AgentTool = AgentTool,
|
||||
> {
|
||||
readonly agent: Agent;
|
||||
readonly env: ExecutionEnv;
|
||||
private session: Session;
|
||||
@@ -38,14 +44,16 @@ export class AgentHarness {
|
||||
private steerQueue: UserMessage[] = [];
|
||||
private followUpQueue: UserMessage[] = [];
|
||||
private pendingSessionWrites: PendingSessionWrite[] = [];
|
||||
private resources: AgentHarnessResources;
|
||||
private systemPrompt: AgentHarnessOptions["systemPrompt"];
|
||||
private resources: AgentHarnessResources<TSkill, TPromptTemplate>;
|
||||
private systemPrompt: AgentHarnessOptions<TSkill, TPromptTemplate, TTool>["systemPrompt"];
|
||||
private getApiKeyAndHeaders?: AgentHarnessOptions["getApiKeyAndHeaders"];
|
||||
private tools = new Map<string, AgentTool>();
|
||||
private listeners = new Set<(event: AgentHarnessEvent, signal?: AbortSignal) => Promise<void> | void>();
|
||||
private tools = new Map<string, TTool>();
|
||||
private listeners = new Set<
|
||||
(event: AgentHarnessEvent<TSkill, TPromptTemplate>, signal?: AbortSignal) => Promise<void> | void
|
||||
>();
|
||||
private hooks = new Map<keyof AgentHarnessEventResultMap, Set<(event: any) => Promise<any> | any>>();
|
||||
|
||||
constructor(options: AgentHarnessOptions) {
|
||||
constructor(options: AgentHarnessOptions<TSkill, TPromptTemplate, TTool>) {
|
||||
this.agent = new Agent({
|
||||
initialState: {
|
||||
model: options.model,
|
||||
@@ -60,12 +68,12 @@ export class AgentHarness {
|
||||
this.resources = options.resources ?? {};
|
||||
this.systemPrompt = options.systemPrompt;
|
||||
this.getApiKeyAndHeaders = options.getApiKeyAndHeaders;
|
||||
for (const tool of this.agent.state.tools) {
|
||||
for (const tool of options.tools ?? []) {
|
||||
this.tools.set(tool.name, tool);
|
||||
}
|
||||
this.model = options.model;
|
||||
this.thinkingLevel = options.thinkingLevel ?? this.agent.state.thinkingLevel;
|
||||
this.activeToolNames = options.activeToolNames ?? this.agent.state.tools.map((tool) => tool.name);
|
||||
this.activeToolNames = options.activeToolNames ?? (options.tools ?? []).map((tool) => tool.name);
|
||||
this.agent.state.model = this.model;
|
||||
this.agent.state.thinkingLevel = this.thinkingLevel;
|
||||
this.agent.getApiKey = async (provider) => {
|
||||
@@ -127,13 +135,13 @@ export class AgentHarness {
|
||||
});
|
||||
}
|
||||
|
||||
private async emitOwn(event: AgentHarnessOwnEvent, signal?: AbortSignal): Promise<void> {
|
||||
private async emitOwn(event: AgentHarnessOwnEvent<TSkill, TPromptTemplate>, signal?: AbortSignal): Promise<void> {
|
||||
for (const listener of this.listeners) {
|
||||
await listener(event, signal);
|
||||
}
|
||||
}
|
||||
|
||||
private async emitAny(event: AgentHarnessEvent, signal?: AbortSignal): Promise<void> {
|
||||
private async emitAny(event: AgentHarnessEvent<TSkill, TPromptTemplate>, signal?: AbortSignal): Promise<void> {
|
||||
for (const listener of this.listeners) {
|
||||
await listener(event, signal);
|
||||
}
|
||||
@@ -163,13 +171,13 @@ export class AgentHarness {
|
||||
});
|
||||
}
|
||||
|
||||
private async createTurnState(): Promise<AgentHarnessTurnState> {
|
||||
private async createTurnState(): Promise<AgentHarnessTurnState<TSkill, TPromptTemplate, TTool>> {
|
||||
const context = await this.session.buildContext();
|
||||
const resources = this.getResources();
|
||||
const tools = [...this.tools.values()];
|
||||
const activeTools = this.activeToolNames
|
||||
.map((name) => this.tools.get(name))
|
||||
.filter((tool): tool is AgentTool => tool !== undefined);
|
||||
.filter((tool): tool is TTool => tool !== undefined);
|
||||
let systemPrompt = "You are a helpful assistant.";
|
||||
if (typeof this.systemPrompt === "string") {
|
||||
systemPrompt = this.systemPrompt;
|
||||
@@ -194,7 +202,7 @@ export class AgentHarness {
|
||||
};
|
||||
}
|
||||
|
||||
private applyTurnState(turnState: AgentHarnessTurnState): void {
|
||||
private applyTurnState(turnState: AgentHarnessTurnState<TSkill, TPromptTemplate, TTool>): void {
|
||||
this.agent.state.messages = turnState.messages;
|
||||
this.agent.state.systemPrompt = turnState.systemPrompt;
|
||||
this.agent.state.model = turnState.model;
|
||||
@@ -263,7 +271,7 @@ export class AgentHarness {
|
||||
}
|
||||
|
||||
private async executeTurn(
|
||||
turnState: AgentHarnessTurnState,
|
||||
turnState: AgentHarnessTurnState<TSkill, TPromptTemplate, TTool>,
|
||||
text: string,
|
||||
options?: { images?: ImageContent[] },
|
||||
): Promise<AssistantMessage> {
|
||||
@@ -578,22 +586,23 @@ export class AgentHarness {
|
||||
this.agent.followUpMode = mode;
|
||||
}
|
||||
|
||||
getResources(): AgentHarnessResources {
|
||||
getResources(): AgentHarnessResources<TSkill, TPromptTemplate> {
|
||||
return {
|
||||
skills: this.resources.skills?.slice(),
|
||||
promptTemplates: this.resources.promptTemplates?.slice(),
|
||||
};
|
||||
}
|
||||
|
||||
async setResources(resources: AgentHarnessResources): Promise<void> {
|
||||
async setResources(resources: AgentHarnessResources<TSkill, TPromptTemplate>): Promise<void> {
|
||||
const previousResources = this.getResources();
|
||||
this.resources = {
|
||||
skills: resources.skills?.slice(),
|
||||
promptTemplates: resources.promptTemplates?.slice(),
|
||||
};
|
||||
await this.emitOwn({ type: "resources_update", resources: this.getResources() });
|
||||
await this.emitOwn({ type: "resources_update", resources: this.getResources(), previousResources });
|
||||
}
|
||||
|
||||
async setTools(tools: AgentTool[], activeToolNames?: string[]): Promise<void> {
|
||||
async setTools(tools: TTool[], activeToolNames?: string[]): Promise<void> {
|
||||
this.tools = new Map(tools.map((tool) => [tool.name, tool]));
|
||||
if (activeToolNames) {
|
||||
this.validateToolNames(activeToolNames);
|
||||
@@ -623,7 +632,9 @@ export class AgentHarness {
|
||||
await this.agent.waitForIdle();
|
||||
}
|
||||
|
||||
subscribe(listener: (event: AgentHarnessEvent, signal?: AbortSignal) => Promise<void> | void): () => void {
|
||||
subscribe(
|
||||
listener: (event: AgentHarnessEvent<TSkill, TPromptTemplate>, signal?: AbortSignal) => Promise<void> | void,
|
||||
): () => void {
|
||||
this.listeners.add(listener);
|
||||
return () => this.listeners.delete(listener);
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import { AgentHarness } from "./agent-harness.js";
|
||||
import { Session } from "./session/session.js";
|
||||
import type { AgentHarnessOptions, SessionMetadata, SessionStorage } from "./types.js";
|
||||
|
||||
export function createSession<TMetadata extends SessionMetadata>(
|
||||
storage: SessionStorage<TMetadata>,
|
||||
): Session<TMetadata> {
|
||||
return new Session(storage);
|
||||
}
|
||||
|
||||
export function createAgentHarness(options: AgentHarnessOptions): AgentHarness {
|
||||
return new AgentHarness(options);
|
||||
}
|
||||
@@ -52,19 +52,26 @@ export async function loadPromptTemplates(
|
||||
* Source values are preserved exactly and attached to every loaded prompt template and diagnostic. The agent package does
|
||||
* not interpret source values; applications define their own provenance shape.
|
||||
*/
|
||||
export async function loadSourcedPromptTemplates<TSource>(
|
||||
export async function loadSourcedPromptTemplates<TSource, TPromptTemplate extends PromptTemplate = PromptTemplate>(
|
||||
env: ExecutionEnv,
|
||||
inputs: Array<{ path: string; source: TSource }>,
|
||||
mapPromptTemplate?: (promptTemplate: PromptTemplate, source: TSource) => TPromptTemplate,
|
||||
): Promise<{
|
||||
promptTemplates: Array<{ promptTemplate: PromptTemplate; source: TSource }>;
|
||||
promptTemplates: Array<{ promptTemplate: TPromptTemplate; source: TSource }>;
|
||||
diagnostics: Array<PromptTemplateDiagnostic & { source: TSource }>;
|
||||
}> {
|
||||
const promptTemplates: Array<{ promptTemplate: PromptTemplate; source: TSource }> = [];
|
||||
const promptTemplates: Array<{ promptTemplate: TPromptTemplate; source: TSource }> = [];
|
||||
const diagnostics: Array<PromptTemplateDiagnostic & { source: TSource }> = [];
|
||||
for (const input of inputs) {
|
||||
const result = await loadPromptTemplates(env, input.path);
|
||||
for (const promptTemplate of result.promptTemplates)
|
||||
promptTemplates.push({ promptTemplate, source: input.source });
|
||||
for (const promptTemplate of result.promptTemplates) {
|
||||
promptTemplates.push({
|
||||
promptTemplate: mapPromptTemplate
|
||||
? mapPromptTemplate(promptTemplate, input.source)
|
||||
: (promptTemplate as TPromptTemplate),
|
||||
source: input.source,
|
||||
});
|
||||
}
|
||||
for (const diagnostic of result.diagnostics) diagnostics.push({ ...diagnostic, source: input.source });
|
||||
}
|
||||
return { promptTemplates, diagnostics };
|
||||
|
||||
@@ -59,18 +59,21 @@ export async function loadSkills(
|
||||
* Source values are preserved exactly and attached to every loaded skill and diagnostic. The agent package does not
|
||||
* interpret source values; applications define their own provenance shape.
|
||||
*/
|
||||
export async function loadSourcedSkills<TSource>(
|
||||
export async function loadSourcedSkills<TSource, TSkill extends Skill = Skill>(
|
||||
env: ExecutionEnv,
|
||||
inputs: Array<{ path: string; source: TSource }>,
|
||||
mapSkill?: (skill: Skill, source: TSource) => TSkill,
|
||||
): Promise<{
|
||||
skills: Array<{ skill: Skill; source: TSource }>;
|
||||
skills: Array<{ skill: TSkill; source: TSource }>;
|
||||
diagnostics: Array<SkillDiagnostic & { source: TSource }>;
|
||||
}> {
|
||||
const skills: Array<{ skill: Skill; source: TSource }> = [];
|
||||
const skills: Array<{ skill: TSkill; source: TSource }> = [];
|
||||
const diagnostics: Array<SkillDiagnostic & { source: TSource }> = [];
|
||||
for (const input of inputs) {
|
||||
const result = await loadSkills(env, input.path);
|
||||
for (const skill of result.skills) skills.push({ skill, source: input.source });
|
||||
for (const skill of result.skills) {
|
||||
skills.push({ skill: mapSkill ? mapSkill(skill, input.source) : (skill as TSkill), source: input.source });
|
||||
}
|
||||
for (const diagnostic of result.diagnostics) diagnostics.push({ ...diagnostic, source: input.source });
|
||||
}
|
||||
return { skills, diagnostics };
|
||||
|
||||
@@ -33,11 +33,14 @@ export interface PromptTemplate {
|
||||
}
|
||||
|
||||
/** Resources made available to explicit invocation methods and system-prompt callbacks. */
|
||||
export interface AgentHarnessResources {
|
||||
export interface AgentHarnessResources<
|
||||
TSkill extends Skill = Skill,
|
||||
TPromptTemplate extends PromptTemplate = PromptTemplate,
|
||||
> {
|
||||
/** Prompt templates available for explicit invocation. */
|
||||
promptTemplates?: PromptTemplate[];
|
||||
promptTemplates?: TPromptTemplate[];
|
||||
/** Skills available to the model and explicit skill invocation. */
|
||||
skills?: Skill[];
|
||||
skills?: TSkill[];
|
||||
}
|
||||
|
||||
/** Kind of filesystem object as addressed by an {@link ExecutionEnv}. Symlinks are not followed automatically. */
|
||||
@@ -295,14 +298,18 @@ export type PendingSessionWrite = SessionTreeEntry extends infer TEntry
|
||||
: never
|
||||
: never;
|
||||
|
||||
export interface AgentHarnessTurnState {
|
||||
export interface AgentHarnessTurnState<
|
||||
TSkill extends Skill = Skill,
|
||||
TPromptTemplate extends PromptTemplate = PromptTemplate,
|
||||
TTool extends AgentTool = AgentTool,
|
||||
> {
|
||||
messages: AgentMessage[];
|
||||
resources: AgentHarnessResources;
|
||||
resources: AgentHarnessResources<TSkill, TPromptTemplate>;
|
||||
systemPrompt: string;
|
||||
model: Model<any>;
|
||||
thinkingLevel: ThinkingLevel;
|
||||
tools: AgentTool[];
|
||||
activeTools: AgentTool[];
|
||||
tools: TTool[];
|
||||
activeTools: TTool[];
|
||||
}
|
||||
|
||||
export interface QueueUpdateEvent {
|
||||
@@ -328,12 +335,15 @@ export interface SettledEvent {
|
||||
nextTurnCount: number;
|
||||
}
|
||||
|
||||
export interface BeforeAgentStartEvent {
|
||||
export interface BeforeAgentStartEvent<
|
||||
TSkill extends Skill = Skill,
|
||||
TPromptTemplate extends PromptTemplate = PromptTemplate,
|
||||
> {
|
||||
type: "before_agent_start";
|
||||
prompt: string;
|
||||
images?: ImageContent[];
|
||||
systemPrompt: string;
|
||||
resources: AgentHarnessResources;
|
||||
resources: AgentHarnessResources<TSkill, TPromptTemplate>;
|
||||
}
|
||||
|
||||
export interface ContextEvent {
|
||||
@@ -410,17 +420,24 @@ export interface ThinkingLevelSelectEvent {
|
||||
previousLevel: ThinkingLevel;
|
||||
}
|
||||
|
||||
export interface ResourcesUpdateEvent {
|
||||
export interface ResourcesUpdateEvent<
|
||||
TSkill extends Skill = Skill,
|
||||
TPromptTemplate extends PromptTemplate = PromptTemplate,
|
||||
> {
|
||||
type: "resources_update";
|
||||
resources: AgentHarnessResources;
|
||||
resources: AgentHarnessResources<TSkill, TPromptTemplate>;
|
||||
previousResources: AgentHarnessResources<TSkill, TPromptTemplate>;
|
||||
}
|
||||
|
||||
export type AgentHarnessOwnEvent =
|
||||
export type AgentHarnessOwnEvent<
|
||||
TSkill extends Skill = Skill,
|
||||
TPromptTemplate extends PromptTemplate = PromptTemplate,
|
||||
> =
|
||||
| QueueUpdateEvent
|
||||
| SavePointEvent
|
||||
| AbortEvent
|
||||
| SettledEvent
|
||||
| BeforeAgentStartEvent
|
||||
| BeforeAgentStartEvent<TSkill, TPromptTemplate>
|
||||
| ContextEvent
|
||||
| BeforeProviderRequestEvent
|
||||
| AfterProviderResponseEvent
|
||||
@@ -432,9 +449,11 @@ export type AgentHarnessOwnEvent =
|
||||
| SessionTreeEvent
|
||||
| ModelSelectEvent
|
||||
| ThinkingLevelSelectEvent
|
||||
| ResourcesUpdateEvent;
|
||||
| ResourcesUpdateEvent<TSkill, TPromptTemplate>;
|
||||
|
||||
export type AgentHarnessEvent = AgentEvent | AgentHarnessOwnEvent;
|
||||
export type AgentHarnessEvent<TSkill extends Skill = Skill, TPromptTemplate extends PromptTemplate = PromptTemplate> =
|
||||
| AgentEvent
|
||||
| AgentHarnessOwnEvent<TSkill, TPromptTemplate>;
|
||||
|
||||
export interface BeforeAgentStartResult {
|
||||
messages?: AgentMessage[];
|
||||
@@ -568,15 +587,19 @@ export interface BranchSummaryResult {
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface AgentHarnessOptions {
|
||||
export interface AgentHarnessOptions<
|
||||
TSkill extends Skill = Skill,
|
||||
TPromptTemplate extends PromptTemplate = PromptTemplate,
|
||||
TTool extends AgentTool = AgentTool,
|
||||
> {
|
||||
env: ExecutionEnv;
|
||||
session: Session;
|
||||
tools?: AgentTool[];
|
||||
tools?: TTool[];
|
||||
/**
|
||||
* Concrete resources available to explicit invocation methods and system-prompt callbacks.
|
||||
* Applications own loading/reloading resources and should call `setResources()` with new values.
|
||||
*/
|
||||
resources?: AgentHarnessResources;
|
||||
resources?: AgentHarnessResources<TSkill, TPromptTemplate>;
|
||||
systemPrompt?:
|
||||
| string
|
||||
| ((context: {
|
||||
@@ -584,8 +607,8 @@ export interface AgentHarnessOptions {
|
||||
session: Session;
|
||||
model: Model<any>;
|
||||
thinkingLevel: ThinkingLevel;
|
||||
activeTools: AgentTool[];
|
||||
resources: AgentHarnessResources;
|
||||
activeTools: TTool[];
|
||||
resources: AgentHarnessResources<TSkill, TPromptTemplate>;
|
||||
}) => string | Promise<string>);
|
||||
getApiKeyAndHeaders?: (
|
||||
model: Model<any>,
|
||||
|
||||
@@ -23,7 +23,6 @@ export {
|
||||
shouldCompact,
|
||||
} from "./harness/compaction/compaction.js";
|
||||
export * from "./harness/execution-env.js";
|
||||
export * from "./harness/factory.js";
|
||||
export * from "./harness/messages.js";
|
||||
export * from "./harness/prompt-templates.js";
|
||||
export * from "./harness/session/repo/jsonl.js";
|
||||
|
||||
Reference in New Issue
Block a user