multica/src/agent/runner.ts

77 lines
2.6 KiB
TypeScript

import { Agent as PiAgentCore, type AgentEvent, type AgentMessage } from "@mariozechner/pi-agent-core";
import type { AgentOptions, AgentRunResult } from "./types.js";
import { createAgentOutput } from "./output.js";
import { resolveModel, resolveTools } from "./tools.js";
import { SessionManager } from "./session/session-manager.js";
export class Agent {
private readonly agent: PiAgentCore;
private readonly output;
private readonly session: SessionManager;
constructor(options: AgentOptions = {}) {
const stdout = options.logger?.stdout ?? process.stdout;
const stderr = options.logger?.stderr ?? process.stderr;
this.output = createAgentOutput({ stdout, stderr });
this.agent = new PiAgentCore();
if (options.systemPrompt) this.agent.setSystemPrompt(options.systemPrompt);
const sessionId = options.sessionId ?? "default";
this.session = new SessionManager({ sessionId });
const storedMeta = this.session.getMeta();
if (!options.thinkingLevel && storedMeta?.thinkingLevel) {
this.agent.setThinkingLevel(storedMeta.thinkingLevel as any);
} else if (options.thinkingLevel) {
this.agent.setThinkingLevel(options.thinkingLevel);
}
const model = options.provider && options.model ? resolveModel(options) : resolveModel({
...options,
provider: storedMeta?.provider,
model: storedMeta?.model,
});
this.agent.setModel(model);
this.agent.setTools(resolveTools(options));
const restoredMessages = this.session.loadMessages();
if (restoredMessages.length > 0) {
this.agent.replaceMessages(restoredMessages);
}
this.session.saveMeta({
provider: this.agent.state.model?.provider,
model: this.agent.state.model?.id,
thinkingLevel: this.agent.state.thinkingLevel,
});
this.agent.subscribe((event: AgentEvent) => {
this.output.handleEvent(event);
this.handleSessionEvent(event);
});
}
async run(prompt: string): Promise<AgentRunResult> {
this.output.state.lastAssistantText = "";
await this.agent.prompt(prompt);
return { text: this.output.state.lastAssistantText, error: this.agent.state.error };
}
private handleSessionEvent(event: AgentEvent) {
if (event.type === "message_end") {
const message = event.message as AgentMessage;
this.session.saveMessage(message);
if (message.role === "assistant") {
void this.maybeCompact();
}
}
}
private async maybeCompact() {
const messages = this.agent.state.messages.slice();
const result = await this.session.maybeCompact(messages);
if (result?.kept) {
this.agent.replaceMessages(result.kept);
}
}
}