chore(vendor): import pi 1.3.0

Imported as a linear vendor commit so forge rebases do not replay the upstream root tree.

git-subtree-dir: third_party/pi

git-subtree-split: 71023aec1546d6d677a65669c5665c0927ce0dd0
This commit is contained in:
alexion committed 2026-10-15 17:58:48 -04:00
1 parent 719370c8f7
commit 6081e3e325
1743 files changed
+205459 -131704

No files matched your search

+62 -41
View File
@@ -1,11 +1,15 @@
import type {
ImageContent,
Message,
Model,
SimpleStreamOptions,
TextContent,
ThinkingBudgets,
Transport,
import {
createInitialSystemMessage,
getCurrentSystemMessage,
getCurrentSystemPrompt,
type ImageContent,
type Message,
type Model,
type SimpleStreamOptions,
type TextContent,
type ThinkingBudgets,
type Transport,
toToolDeclaration,
} from "@earendil-works/pi-ai";
import { runAgentLoop, runAgentLoopContinue } from "./agent-loop.ts";
import { getDefaultStreamFn } from "./stream-fn.ts";
@@ -21,9 +25,10 @@ import type {
AgentTool,
BeforeToolCallContext,
BeforeToolCallResult,
FinishTurn,
PrepareNextTurnContext,
PrepareRequest,
QueueMode,
ShouldStopAfterTurnContext,
StreamFn,
ToolExecutionMode,
} from "./types.ts";
@@ -32,7 +37,11 @@ export type { QueueMode } from "./types.ts";
function defaultConvertToLlm(messages: AgentMessage[]): Message[] {
return messages.filter(
(message) => message.role === "user" || message.role === "assistant" || message.role === "toolResult",
(message) =>
message.role === "system" ||
message.role === "user" ||
message.role === "assistant" ||
message.role === "toolResult",
);
}
@@ -65,14 +74,21 @@ type MutableAgentState = Omit<AgentState, "isStreaming" | "streamingMessage" | "
errorMessage?: string;
};
function createMutableAgentState(
initialState?: Partial<Omit<AgentState, "pendingToolCalls" | "isStreaming" | "streamingMessage" | "errorMessage">>,
): MutableAgentState {
/** Initial state for {@link Agent}. `systemPrompt` and `tools` become the leading system message unless `messages` already starts with one. */
export type AgentInitialState = Partial<
Omit<AgentState, "pendingToolCalls" | "isStreaming" | "streamingMessage" | "errorMessage">
>;
function createMutableAgentState(initialState?: AgentInitialState): MutableAgentState {
let tools = initialState?.tools?.slice() ?? [];
let messages = initialState?.messages?.slice() ?? [];
const initialMessage = createInitialSystemMessage(initialState?.systemPrompt, tools.map(toToolDeclaration));
if (messages[0]?.role !== "system" && initialMessage) messages.unshift(initialMessage);
return {
systemPrompt: initialState?.systemPrompt ?? "",
get systemPrompt() {
return getCurrentSystemPrompt(messages);
},
model: initialState?.model ?? DEFAULT_MODEL,
thinkingLevel: initialState?.thinkingLevel ?? "off",
get tools() {
@@ -96,16 +112,18 @@ function createMutableAgentState(
/** Options for constructing an {@link Agent}. */
export interface AgentOptions {
initialState?: Partial<Omit<AgentState, "pendingToolCalls" | "isStreaming" | "streamingMessage" | "errorMessage">>;
initialState?: AgentInitialState;
convertToLlm?: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;
transformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>;
streamFn: StreamFn;
getApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;
onPayload?: SimpleStreamOptions["onPayload"];
onResponse?: SimpleStreamOptions["onResponse"];
onProviderStreamEvent?: SimpleStreamOptions["onProviderStreamEvent"];
beforeToolCall?: (context: BeforeToolCallContext, signal?: AbortSignal) => Promise<BeforeToolCallResult | undefined>;
afterToolCall?: (context: AfterToolCallContext, signal?: AbortSignal) => Promise<AfterToolCallResult | undefined>;
shouldStopAfterTurn?: (context: ShouldStopAfterTurnContext, signal?: AbortSignal) => boolean | Promise<boolean>;
finishTurn?: FinishTurn;
prepareRequest?: PrepareRequest;
prepareNextTurn?: (
signal?: AbortSignal,
) => Promise<AgentLoopTurnUpdate | undefined> | AgentLoopTurnUpdate | undefined;
@@ -138,19 +156,16 @@ class PendingMessageQueue {
return this.messages.length > 0;
}
drain(): AgentMessage[] {
if (this.mode === "all") {
const drained = this.messages.slice();
this.messages = [];
return drained;
}
peek(): AgentMessage[] {
if (this.mode === "all") return this.messages.slice();
const first = this.messages[0];
if (!first) {
return [];
}
this.messages = this.messages.slice(1);
return [first];
return first ? [first] : [];
}
drain(): AgentMessage[] {
const drained = this.peek();
this.messages = this.messages.slice(drained.length);
return drained;
}
clear(): void {
@@ -182,6 +197,7 @@ export class Agent {
public getApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;
public onPayload?: SimpleStreamOptions["onPayload"];
public onResponse?: SimpleStreamOptions["onResponse"];
public onProviderStreamEvent?: SimpleStreamOptions["onProviderStreamEvent"];
public beforeToolCall?: (
context: BeforeToolCallContext,
signal?: AbortSignal,
@@ -190,10 +206,8 @@ export class Agent {
context: AfterToolCallContext,
signal?: AbortSignal,
) => Promise<AfterToolCallResult | undefined>;
public shouldStopAfterTurn?: (
context: ShouldStopAfterTurnContext,
signal?: AbortSignal,
) => boolean | Promise<boolean>;
public finishTurn?: FinishTurn;
public prepareRequest?: PrepareRequest;
public prepareNextTurn?: (
signal?: AbortSignal,
) => Promise<AgentLoopTurnUpdate | undefined> | AgentLoopTurnUpdate | undefined;
@@ -223,9 +237,11 @@ export class Agent {
this.getApiKey = runtimeOptions.getApiKey;
this.onPayload = runtimeOptions.onPayload;
this.onResponse = runtimeOptions.onResponse;
this.onProviderStreamEvent = runtimeOptions.onProviderStreamEvent;
this.beforeToolCall = runtimeOptions.beforeToolCall;
this.afterToolCall = runtimeOptions.afterToolCall;
this.shouldStopAfterTurn = runtimeOptions.shouldStopAfterTurn;
this.finishTurn = runtimeOptions.finishTurn;
this.prepareRequest = runtimeOptions.prepareRequest;
this.prepareNextTurn = runtimeOptions.prepareNextTurn;
this.prepareNextTurnWithContext = runtimeOptions.prepareNextTurnWithContext;
this.steeringQueue = new PendingMessageQueue(runtimeOptions.steeringMode ?? "one-at-a-time");
@@ -310,6 +326,12 @@ export class Agent {
return this.steeringQueue.hasItems() || this.followUpQueue.hasItems();
}
/** Preview the messages selected for the next turn without consuming them. */
peekQueuedMessages(): AgentMessage[] {
const steering = this.steeringQueue.peek();
return steering.length > 0 ? steering : this.followUpQueue.peek();
}
/** Active abort signal for the current run, if any. */
get signal(): AbortSignal | undefined {
return this.activeRun?.abortController.signal;
@@ -329,13 +351,14 @@ export class Agent {
return this.activeRun?.promise ?? Promise.resolve();
}
/** Clear transcript state, runtime state, and queued messages. */
/** Clear conversation state and queues while retaining the replayed prompt/tool baseline. */
reset(): void {
if (this.activeRun) {
throw new Error("Agent is already processing. Wait for completion before resetting.");
}
this._state.messages = [];
const baseline = getCurrentSystemMessage(this._state.messages);
this._state.messages = baseline ? [baseline] : [];
this._state.isStreaming = false;
this._state.streamingMessage = undefined;
this._state.pendingToolCalls = new Set<string>();
@@ -364,7 +387,7 @@ export class Agent {
}
const lastMessage = this._state.messages[this._state.messages.length - 1];
if (!lastMessage) {
if (!lastMessage || this._state.messages.every((message) => message.role === "system")) {
throw new Error("No messages to continue from");
}
@@ -436,7 +459,6 @@ export class Agent {
private createContextSnapshot(): AgentContext {
return {
systemPrompt: this._state.systemPrompt,
messages: this._state.messages.slice(),
tools: this._state.tools.slice(),
};
@@ -444,22 +466,21 @@ export class Agent {
private createLoopConfig(options: { skipInitialSteeringPoll?: boolean } = {}): AgentLoopConfig {
let skipInitialSteeringPoll = options.skipInitialSteeringPoll === true;
const shouldStopAfterTurn = this.shouldStopAfterTurn;
return {
model: this._state.model,
reasoning: this._state.thinkingLevel === "off" ? undefined : this._state.thinkingLevel,
sessionId: this.sessionId,
onPayload: this.onPayload,
onResponse: this.onResponse,
onProviderStreamEvent: this.onProviderStreamEvent,
transport: this.transport,
thinkingBudgets: this.thinkingBudgets,
maxRetryDelayMs: this.maxRetryDelayMs,
toolExecution: this.toolExecution,
beforeToolCall: this.beforeToolCall,
afterToolCall: this.afterToolCall,
shouldStopAfterTurn: shouldStopAfterTurn
? async (context) => await shouldStopAfterTurn(context, this.signal)
: undefined,
finishTurn: this.finishTurn,
prepareRequest: this.prepareRequest,
prepareNextTurn:
this.prepareNextTurnWithContext || this.prepareNextTurn
? async (context) => {