/** * pi SDK adapter boundary * Zones: pi agent sdk boundary, shared adapters * Owns direct pi SDK imports and exposes narrow bridge-facing helpers/types for the extension composition layer */ import type { AssistantMessageEvent } from "@earendil-works/pi-ai"; import { type AgentEndEvent, type AgentSettledEvent, type AgentStartEvent, type BeforeAgentStartEvent, type ExtensionAPI, type ExtensionCommandContext, type ExtensionContext, type InputEvent, type SessionBeforeCompactEvent, type SessionCompactEvent, type SessionShutdownEvent, type SessionStartEvent, type SlashCommandInfo, SettingsManager, } from "@earendil-works/pi-coding-agent"; export type { AgentEndEvent, AgentSettledEvent, AgentStartEvent, AssistantMessageEvent, BeforeAgentStartEvent, ExtensionAPI, ExtensionCommandContext, ExtensionContext, InputEvent, SessionBeforeCompactEvent, SessionCompactEvent, SessionShutdownEvent, SessionStartEvent, SlashCommandInfo, }; export interface ToolExecutionStartEvent { type: "tool_execution_start"; toolCallId: string; toolName: string; args: unknown; } export interface ToolExecutionUpdateEvent { type: "tool_execution_update"; toolCallId: string; toolName: string; args: unknown; partialResult: unknown; } export interface ToolExecutionEndEvent { type: "tool_execution_end"; toolCallId: string; toolName: string; result: unknown; isError: boolean; } export interface PiSettingsManager { reload: () => Promise; flush: () => Promise; getEnabledModels: () => string[] | undefined; setEnabledModels: (patterns: string[] | undefined) => void; } export type PiSlashCommandInfo = SlashCommandInfo; export type PiRunMode = "tui" | "rpc" | "json" | "print"; function isPiRunMode(value: unknown): value is PiRunMode { return ( value === "tui" || value === "rpc" || value === "json" || value === "print" ); } export function getExtensionContextMode(ctx: unknown): PiRunMode | undefined { const mode = typeof ctx === "object" && ctx !== null ? (ctx as { mode?: unknown }).mode : undefined; return isPiRunMode(mode) ? mode : undefined; } export function isExtensionContextPassiveRunMode(ctx: unknown): boolean { const mode = getExtensionContextMode(ctx); return mode === "print" || mode === "json"; } export function canStartPollingInExtensionContext(ctx: unknown): boolean { return !isExtensionContextPassiveRunMode(ctx); } export function formatPollingStartBlockedByRunMode(ctx: unknown): string { const mode = getExtensionContextMode(ctx); return mode ? `Telegram polling is unavailable in Pi ${mode} mode. Use /telegram-connect from a long-lived Pi session.` : "Telegram polling is unavailable in this Pi run mode."; } export function getSessionCompactionReason( event: unknown, ): "manual" | "threshold" | "overflow" | "unknown" { const reason = event && typeof event === "object" && "reason" in event ? (event as { reason?: unknown }).reason : undefined; return reason === "manual" || reason === "threshold" || reason === "overflow" ? reason : "unknown"; } export type PiSendUserMessageOptions = NonNullable< Parameters[1] >; export interface PiExtensionApiRuntimePorts { sendUserMessage: ExtensionAPI["sendUserMessage"]; exec: ExtensionAPI["exec"]; getCommands: ExtensionAPI["getCommands"]; getThinkingLevel: ExtensionAPI["getThinkingLevel"]; setThinkingLevel: ExtensionAPI["setThinkingLevel"]; setModel: ExtensionAPI["setModel"]; } export function createExtensionApiRuntimePorts( api: Pick< ExtensionAPI, | "sendUserMessage" | "exec" | "getCommands" | "getThinkingLevel" | "setThinkingLevel" | "setModel" >, ): PiExtensionApiRuntimePorts { return { sendUserMessage: (content, options) => api.sendUserMessage(content, options), exec: (command, args, options) => api.exec(command, args, options), getCommands: () => api.getCommands(), getThinkingLevel: () => api.getThinkingLevel(), setThinkingLevel: (level) => api.setThinkingLevel(level), setModel: (model) => api.setModel(model), }; } export function createSettingsManager(cwd: string): PiSettingsManager { return SettingsManager.create(cwd); } export function createScopedModelPatternPersister(deps: { createSettingsManager: (cwd: string) => PiSettingsManager; clearCachedModelMenuInputs: () => void; }): (patterns: string[], ctx: ExtensionContext) => Promise { return async (patterns, ctx) => { const settingsManager = deps.createSettingsManager(ctx.cwd); settingsManager.setEnabledModels( patterns.length > 0 ? patterns : undefined, ); await settingsManager.flush(); deps.clearCachedModelMenuInputs(); }; } export function getExtensionContextModel( ctx: ExtensionContext, ): ExtensionContext["model"] { return ctx.model; } export function getExtensionContextCwd(ctx: ExtensionContext): string { return ctx.cwd; } export function isExtensionContextIdle(ctx: ExtensionContext): boolean { return ctx.isIdle(); } export function hasExtensionContextPendingMessages( ctx: ExtensionContext, ): boolean { return ctx.hasPendingMessages(); } export function compactExtensionContext( ctx: ExtensionContext, callbacks: Parameters[0], ): ReturnType { return ctx.compact(callbacks); }