chore(vendor): import pi 0.85.1

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: d981de1229ef899957bbe968bc8dcda02a21f477
This commit is contained in:
2026-09-14 10:48:47 -04:00
parent 5ebc7018c3
commit 4def357d09
1669 changed files with 397627 additions and 0 deletions

View File

@@ -0,0 +1,175 @@
import type {
Context as AiContext,
Api,
AssistantMessage,
AssistantMessageEvent,
AssistantMessageEventStream,
Message,
Model,
SimpleStreamOptions,
Tool,
} from "@earendil-works/pi-ai";
import type { AgentMessage, ThinkingLevel } from "../../types.ts";
import { type Context, getTelemetryContext } from "../context.ts";
import type { SettledAssistantMessage } from "../session/types.ts";
import type { AgentHarnessStreamOptions } from "../types.ts";
import { AbortRequested } from "./effect-gate.ts";
/** HTTP response metadata captured before the provider response body is consumed. */
export interface AssistantResponseMetadata {
status?: number;
headers?: Record<string, string>;
}
/** Process-local lifecycle observer for one assistant stream. */
export interface AssistantStreamObserver {
start(
message: AssistantMessage,
event: Extract<AssistantMessageEvent, { type: "start" }>,
context: Context,
): void | Promise<void>;
update(message: AssistantMessage, event: AssistantMessageEvent, context: Context): void | Promise<void>;
end(message: SettledAssistantMessage, context: Context): void | Promise<void>;
}
/** Executable inputs for one already-approved assistant provider request. */
export interface HarnessAssistantStreamConfig {
model: Model<Api>;
systemPrompt: string;
tools?: Tool[];
thinkingLevel: ThinkingLevel;
streamOptions: AgentHarnessStreamOptions;
transformContext?: (
requestContext: { messages: AgentMessage[]; systemPrompt: string },
context: Context,
) => Promise<{ messages: AgentMessage[]; systemPrompt: string }>;
toProviderMessages: (messages: AgentMessage[], context: Context) => Message[] | Promise<Message[]>;
beforePayload?: (
payload: unknown,
model: Model<Api>,
context: Context,
) => unknown | undefined | Promise<unknown | undefined>;
afterResponse?: (
message: SettledAssistantMessage,
metadata: AssistantResponseMetadata,
context: Context,
) => Promise<SettledAssistantMessage>;
request(
aiContext: AiContext,
options: SimpleStreamOptions,
context: Context,
): AssistantMessageEventStream | Promise<AssistantMessageEventStream>;
observer: AssistantStreamObserver;
}
function createRequestOptions(
config: HarnessAssistantStreamConfig,
captureMetadata: (metadata: AssistantResponseMetadata) => void,
context: Context,
): SimpleStreamOptions {
const options = config.streamOptions;
return {
transport: options.transport,
timeoutMs: options.timeoutMs,
maxRetries: options.maxRetries,
maxRetryDelayMs: options.maxRetryDelayMs,
headers: options.headers,
metadata: options.metadata,
cacheRetention: options.cacheRetention,
deferred: options.deferred,
...(config.thinkingLevel === "off" ? {} : { reasoning: config.thinkingLevel }),
signal: context.abortSignal,
telemetryContext: getTelemetryContext(context),
onPayload:
config.beforePayload === undefined
? undefined
: (payload, model) => config.beforePayload?.(payload, model, context),
onResponse: (response) => {
captureMetadata({ status: response.status, headers: response.headers });
},
};
}
function isUpdateEvent(
event: AssistantMessageEvent,
): event is Exclude<AssistantMessageEvent, { type: "start" | "done" | "error" }> {
return event.type !== "start" && event.type !== "done" && event.type !== "error";
}
export async function consumeAssistantStream(
stream: AssistantMessageEventStream,
observer: AssistantStreamObserver,
afterResponse:
| ((message: SettledAssistantMessage, context: Context) => Promise<SettledAssistantMessage>)
| undefined,
context: Context,
): Promise<SettledAssistantMessage> {
let started = false;
for await (const event of stream) {
if (event.type === "start") {
if (started) throw new Error("Assistant message stream emitted more than one start event");
started = true;
await observer.start({ ...event.partial }, event, context);
} else if (isUpdateEvent(event)) {
if (!started) throw new Error(`Assistant message stream emitted ${event.type} before start`);
await observer.update({ ...event.partial }, event, context);
} else if (event.type === "done" && !started) {
throw new Error("Assistant message stream emitted done before start");
}
}
const settled = (await stream.result()) as SettledAssistantMessage;
let finalMessage = settled;
if (afterResponse !== undefined) {
try {
finalMessage = await afterResponse(settled, context);
} catch (error) {
if (!(error instanceof AbortRequested)) throw error;
await error.cancellation;
}
}
await observer.end(finalMessage, context);
return finalMessage;
}
/** Stream one assistant response without mutating the caller's message list. */
export async function streamHarnessAssistant(
messages: AgentMessage[],
config: HarnessAssistantStreamConfig,
context: Context,
): Promise<SettledAssistantMessage> {
let requestContext = { messages: messages.slice(), systemPrompt: config.systemPrompt };
if (config.transformContext) {
requestContext = await config.transformContext(requestContext, context);
}
const providerMessages = await config.toProviderMessages(requestContext.messages, context);
const aiContext: AiContext = {
systemPrompt: requestContext.systemPrompt,
messages: providerMessages,
tools: config.tools,
};
let metadata: AssistantResponseMetadata = {};
const stream = await config.request(
aiContext,
createRequestOptions(
config,
(nextMetadata) => {
metadata = nextMetadata;
},
context,
),
context,
);
const afterResponse = config.afterResponse;
return consumeAssistantStream(
stream,
config.observer,
afterResponse === undefined
? undefined
: (message, afterContext) => afterResponse(message, metadata, afterContext),
context,
);
}

View File

@@ -0,0 +1,64 @@
/** Expected internal control flow when cancellation wins effect admission. */
export class AbortRequested extends Error {
readonly cancellation: Promise<void>;
constructor(cancellation: Promise<void>) {
super("Abort requested");
this.name = "AbortRequested";
this.cancellation = cancellation;
}
}
/** Procedure-facing synchronous admission capability for one drive pass. */
export interface Gate {
readonly signal: AbortSignal;
admit<T>(invoke: () => T): T;
}
/** Owner-facing lifecycle controls for one drive pass. */
export interface GateControl {
beginAbort(cancellation: Promise<void>): void;
signalAbort(): void;
close(error: Error): void;
}
type GateState =
| { status: "open" }
| { status: "aborting"; cancellation: Promise<void> }
| { status: "closed"; error: Error };
/** Create separate procedure-facing and owner-facing views of one effect gate. */
export function createGate(): { gate: Gate; control: GateControl } {
let state: GateState = { status: "open" };
const controller = new AbortController();
const check = (): void => {
if (state.status === "aborting") throw new AbortRequested(state.cancellation);
if (state.status === "closed") throw state.error;
};
return {
gate: {
admit<T>(invoke: () => T): T {
check();
return invoke();
},
signal: controller.signal,
},
control: {
beginAbort(cancellation) {
if (state.status !== "open") return;
state = { status: "aborting", cancellation };
},
signalAbort() {
if (state.status !== "aborting" || controller.signal.aborted) return;
controller.abort(new AbortRequested(state.cancellation));
},
close(error) {
if (state.status === "closed") return;
state = { status: "closed", error };
if (!controller.signal.aborted) controller.abort(error);
},
},
};
}

View File

@@ -0,0 +1,210 @@
import { type ToolResultMessage, validateToolArguments } from "@earendil-works/pi-ai";
import type { AgentToolCall, AgentToolResult } from "../../types.ts";
import { type Context, withAbortSignal } from "../context.ts";
import type { JsonValue } from "../session/types.ts";
import type { AgentHarnessTool, AgentHarnessToolInvocation, AgentHarnessToolUpdateCallback } from "../types.ts";
import type { Gate } from "./effect-gate.ts";
/** A tool call whose tool exists and whose prepared arguments passed validation. */
export interface PreparedToolCall<TContext extends object | undefined> {
toolCall: AgentToolCall;
tool: AgentHarnessTool<TContext>;
args: Record<string, JsonValue>;
}
/** Synthetic result produced without crossing the external tool-effect boundary. */
export interface ImmediateToolOutcome {
kind: "immediate";
toolCall: AgentToolCall;
result: AgentToolResult<unknown>;
isError: true;
terminate: boolean;
}
/** Aggregated decision from the before-tool hook pipeline. */
export interface BeforeToolDecision {
args?: Record<string, JsonValue>;
block?: { reason: string; terminate?: boolean };
}
/** A prepared call cleared for durable intent publication and execution. */
export interface ClearedToolCall<TContext extends object | undefined> {
toolCall: AgentToolCall;
tool: AgentHarnessTool<TContext>;
args: Record<string, JsonValue>;
}
/** Raw phase-two tool output before after-tool patching. */
export interface ExecutedToolCall {
result: AgentToolResult<unknown>;
isError: boolean;
}
/** Aggregated patch from the after-tool hook pipeline. */
export interface AfterToolPatch {
content?: AgentToolResult<unknown>["content"];
details?: JsonValue;
isError?: boolean;
usage?: AgentToolResult<unknown>["usage"];
terminate?: boolean;
}
/** Final tool output ready to become a durable tool-result message. */
export interface FinalizedToolCall {
toolCall: AgentToolCall;
result: AgentToolResult<unknown>;
isError: boolean;
terminate: boolean;
}
function createErrorToolResult(message: string): AgentToolResult<unknown> {
return {
content: [{ type: "text", text: message }],
details: undefined,
};
}
function immediateError(toolCall: AgentToolCall, message: string, terminate = false): ImmediateToolOutcome {
return {
kind: "immediate",
toolCall,
result: createErrorToolResult(message),
isError: true,
terminate,
};
}
/** Resolve a tool, apply its deterministic argument preparation, and validate the result. */
export function prepareToolCall<TContext extends object | undefined>(
call: AgentToolCall,
tools: AgentHarnessTool<TContext>[],
): PreparedToolCall<TContext> | ImmediateToolOutcome {
const tool = tools.find((candidate) => candidate.name === call.name);
if (!tool) {
return immediateError(call, `Tool ${JSON.stringify(call.name)} is unavailable`);
}
try {
const preparedArguments = tool.prepareArguments ? tool.prepareArguments(call.arguments) : call.arguments;
const preparedCall: AgentToolCall =
preparedArguments === call.arguments
? call
: { ...call, arguments: preparedArguments as Record<string, JsonValue> };
const args = validateToolArguments(tool, preparedCall) as Record<string, JsonValue>;
return { toolCall: call, tool, args };
} catch (error) {
return immediateError(call, error instanceof Error ? error.message : String(error));
}
}
/** Apply an explicit hook decision and revalidate replacement arguments. */
export function applyBeforeToolDecision<TContext extends object | undefined>(
prepared: PreparedToolCall<TContext>,
decision: BeforeToolDecision | undefined,
): ClearedToolCall<TContext> | ImmediateToolOutcome {
if (decision?.block) {
return immediateError(prepared.toolCall, decision.block.reason, decision.block.terminate === true);
}
if (!decision?.args) {
return { toolCall: prepared.toolCall, tool: prepared.tool, args: prepared.args };
}
try {
const validatedArgs = validateToolArguments(prepared.tool, {
...prepared.toolCall,
arguments: decision.args,
}) as Record<string, JsonValue>;
return { toolCall: prepared.toolCall, tool: prepared.tool, args: validatedArgs };
} catch (error) {
return immediateError(prepared.toolCall, error instanceof Error ? error.message : String(error));
}
}
/** Execute one cleared external tool effect, converting expected tool throws to error output. */
export function executeToolCall<TContext extends object | undefined>(
call: ClearedToolCall<TContext>,
gate: Gate,
onUpdate: AgentHarnessToolUpdateCallback<unknown>,
toolContext: TContext,
invocation: AgentHarnessToolInvocation,
context: Context,
): Promise<ExecutedToolCall> {
let acceptingUpdates = true;
return gate.admit(async () => {
const admittedContext = withAbortSignal(gate.signal, context);
admittedContext.abortSignal?.throwIfAborted();
try {
const result = await call.tool.execute(
call.toolCall.id,
call.args,
(partial, options) => {
if (acceptingUpdates) onUpdate(partial, options);
},
toolContext,
invocation,
admittedContext,
);
return { result, isError: false };
} catch (error) {
return {
result: createErrorToolResult(error instanceof Error ? error.message : String(error)),
isError: true,
};
} finally {
acceptingUpdates = false;
}
});
}
/** Apply an after-tool patch field by field. */
export function finalizeToolCall<TContext extends object | undefined>(
call: ClearedToolCall<TContext>,
executed: ExecutedToolCall,
patch: AfterToolPatch | undefined,
): FinalizedToolCall {
const result: AgentToolResult<unknown> = patch
? {
...executed.result,
content: patch.content === undefined ? executed.result.content : patch.content,
details: patch.details === undefined ? executed.result.details : patch.details,
usage: patch.usage === undefined ? executed.result.usage : patch.usage,
terminate: patch.terminate === undefined ? executed.result.terminate : patch.terminate,
}
: executed.result;
return {
toolCall: call.toolCall,
result,
isError: patch?.isError ?? executed.isError,
terminate: result.terminate === true,
};
}
/** Reconstruct the canonical tool result represented by a staged transcript message. */
export function toolResultFromMessage(
message: ToolResultMessage<unknown>,
terminate: boolean,
): AgentToolResult<unknown> {
return {
content: message.content,
details: message.details,
...(message.usage === undefined ? {} : { usage: message.usage }),
...(message.addedToolNames === undefined ? {} : { addedToolNames: message.addedToolNames }),
...(terminate ? { terminate: true } : {}),
};
}
/** Convert finalized tool output to the provider-facing transcript message. */
export function createToolResultMessage(call: FinalizedToolCall): ToolResultMessage {
return {
role: "toolResult",
toolCallId: call.toolCall.id,
toolName: call.toolCall.name,
content: call.result.content ?? [],
...(call.result.details === undefined ? {} : { details: call.result.details }),
...(call.result.usage === undefined ? {} : { usage: call.result.usage }),
...(call.result.addedToolNames?.length ? { addedToolNames: call.result.addedToolNames } : {}),
isError: call.isError,
timestamp: Date.now(),
};
}