multica/src/agent/runner.ts
Jiayuan 200b2cefda feat(agent): add profile system and improve tools
- Add Agent Profile module for managing agent identity, soul, tools,
  memory, and bootstrap configuration
- Add profile CLI (pnpm agent:profile) for creating/listing/showing profiles
- Default sessionId to UUIDv7 instead of "default"
- Expose Agent.sessionId as public readonly property
- Improve exec/process tools error handling (no more crashes on spawn errors)
- Add 'output' action to process tool for reading stdout/stderr
- Better tool descriptions to guide agent in choosing exec vs process

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 02:47:30 +08:00

97 lines
3.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Agent as PiAgentCore, type AgentEvent, type AgentMessage } from "@mariozechner/pi-agent-core";
import { v7 as uuidv7 } from "uuid";
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";
import { ProfileManager } from "./profile/index.js";
export class Agent {
private readonly agent: PiAgentCore;
private readonly output;
private readonly session: SessionManager;
private readonly profile?: ProfileManager;
/** 当前会话 ID */
readonly sessionId: string;
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();
// 加载 Agent Profile如果指定了 profileId
if (options.profileId) {
this.profile = new ProfileManager({
profileId: options.profileId,
baseDir: options.profileBaseDir,
});
const systemPrompt = this.profile.buildSystemPrompt();
if (systemPrompt) {
this.agent.setSystemPrompt(systemPrompt);
}
} else if (options.systemPrompt) {
// 直接使用传入的 systemPrompt
this.agent.setSystemPrompt(options.systemPrompt);
}
this.sessionId = options.sessionId ?? uuidv7();
this.session = new SessionManager({ sessionId: this.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);
}
}
}