Something went wrong. Try again.
fork of prime agent with changes i want but are not upstreamable probqbly (and some fixes)
Something went wrong. Try again.
4.6 kB · 137 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138import type { AgentMessage } from "@earendil-works/pi-agent-core";import type { AssistantMessage } from "@earendil-works/pi-ai";import type { AgentSession } from "../core/agent-session.js";import { type AgentAutonomousStatus, autonomousLimitReason, buildAutonomousGateFailureContinuation,} from "../core/autonomous.js";import { COMPACTION_OUTCOME_CUSTOM_TYPE, type CompactionOutcomeMessage, HARNESS_DIGEST_CUSTOM_TYPE, isCompactionOutcomeMessage, isSessionSlashCommandResultMessage, REFINEMENT_NOTICE_CUSTOM_TYPE, REFINEMENT_OUTCOME_CUSTOM_TYPE, type SessionSlashCommandResultMessage,} from "../core/messages.js";
export function latestAutonomousGateAttempt(status: AgentAutonomousStatus): number { return Math.max(status.lastGateFailure?.attempt ?? 0, 0, ...Object.values(status.gateAttempts));}
export type HeadlessTerminalResultMessage = AssistantMessage | SessionSlashCommandResultMessage;
export interface HeadlessTerminalResult { primary?: HeadlessTerminalResultMessage; compactionOutcomes: CompactionOutcomeMessage[];}
export function selectHeadlessTerminalResult(messages: readonly AgentMessage[]): HeadlessTerminalResult { let index = messages.length - 1; const compactionOutcomes: CompactionOutcomeMessage[] = []; while (index >= 0) { const message = messages[index]; if (isCompactionOutcomeMessage(message)) { compactionOutcomes.unshift(message); index--; continue; } // A corrupt outcome is still part of the terminal outcome suffix. Skip it // without letting it hide earlier valid outcomes or their failure status. if ( message.role === "custom" && (message.customType === COMPACTION_OUTCOME_CUSTOM_TYPE || message.customType === REFINEMENT_OUTCOME_CUSTOM_TYPE || message.customType === REFINEMENT_NOTICE_CUSTOM_TYPE || message.customType === HARNESS_DIGEST_CUSTOM_TYPE) ) { index--; continue; } break; } const precedingMessage = messages[index]; const primary = precedingMessage?.role === "assistant" || isSessionSlashCommandResultMessage(precedingMessage) ? precedingMessage : undefined; return { primary, compactionOutcomes };}
function shouldContinueAutonomousGates(status: AgentAutonomousStatus): boolean { return ( status.enabled && status.gates.commands.length > 0 && !!status.lastGateFailure && latestAutonomousGateAttempt(status) <= status.gates.maxRetries && !autonomousLimitReason(status) );}
function autonomousProgressKey(status: AgentAutonomousStatus): string { return [ latestAutonomousGateAttempt(status), status.continuationsUsed, status.turnsUsed, status.tokensUsed, status.lastGateFailure?.exitText ?? "", ].join(":");}
export interface HeadlessCompletionOptions { /** Include descendant settlement and the parent turns caused by their results. */ waitForRlmQuiescence?: boolean;}
export async function waitForHeadlessCompletion( session: AgentSession, options: HeadlessCompletionOptions = {},): Promise<AgentAutonomousStatus> { let lastPromptedProgressKey: string | undefined; let repeatedProgressPrompts = 0; while (true) { if (options.waitForRlmQuiescence) await session.waitForRlmQuiescence(); else await session.waitForHeadlessIdle(); const status = session.getAutonomousStatus(); if (!shouldContinueAutonomousGates(status) || !status.lastGateFailure) { return status; } const progressKey = autonomousProgressKey(status); if (progressKey === lastPromptedProgressKey) { repeatedProgressPrompts++; } else { repeatedProgressPrompts = 0; lastPromptedProgressKey = progressKey; } if (repeatedProgressPrompts > 0) { await new Promise((resolve) => setTimeout(resolve, Math.min(1000, repeatedProgressPrompts * 50))); } session.recordHostAutonomousContinuation(); await session.prompt( buildAutonomousGateFailureContinuation( { ...status.lastGateFailure, attempt: latestAutonomousGateAttempt(status) }, status.gates.maxRetries, ), { streamingBehavior: "followUp", internalPrompt: true, suppressAutonomousContinuation: true, }, ); await session.waitForIdle(); await session.refreshAutonomousGates(); const { primary } = selectHeadlessTerminalResult(session.state.messages); if (primary?.role === "assistant") { if (primary.stopReason === "error" || primary.stopReason === "aborted") { const postErrorStatus = session.getAutonomousStatus(); if (shouldContinueAutonomousGates(postErrorStatus) && postErrorStatus.lastGateFailure) { continue; } if (options.waitForRlmQuiescence) continue; return postErrorStatus; } } }}