eve
Version:
Filesystem-first framework for durable backend AI agents that run anywhere.
132 lines (131 loc) • 6.35 kB
TypeScript
import type { ModelMessage, TextStreamPart, ToolSet, TypedToolResult } from "ai";
type ToolResponsePart = Extract<ModelMessage, {
role: "tool";
}>["content"][number];
type InlineToolResultPart = Extract<ToolResponsePart, {
type: "tool-result";
}>;
import type { AssistantStepFinishReason, RuntimeIdentity } from "#protocol/message.js";
import type { RunMode } from "#shared/run-mode.js";
import type { JsonObject } from "#shared/json.js";
import type { HarnessEmitFn, HarnessSession, SessionStateMap, StepInput } from "#harness/types.js";
/**
* Tracks emission lifecycle state across harness step invocations.
*
* Persisted on `session.state` so the state survives when the durable
* workflow runtime recreates the harness at each `"use step"` boundary.
*/
export interface HarnessEmissionState {
readonly sessionStarted: boolean;
readonly sequence: number;
readonly stepIndex: number;
readonly turnId: string;
}
/** Reads the emission state, returning defaults when absent. */
export declare function getHarnessEmissionState(state: SessionStateMap | undefined): HarnessEmissionState;
/**
* Returns `true` when the harness is **between turns** — either no turn
* has started yet (initial state) or the previous turn has emitted its
* epilogue (or recoverable failure cascade) and reset.
*
* Returns `false` while a turn is in progress, including during
* tool-loop continuations and runtime-action resumes within the same
* turn. Callers that gate per-turn work (eg. lifecycle hook dispatch)
* use this predicate to distinguish a fresh delivery from a
* continuation of an in-flight turn.
*
* Implemented over the empty-`turnId` sentinel that `emitTurnEpilogue`
* and `emitRecoverableFailedTurn` write — clients should never read
* `state.turnId` directly to make this distinction.
*/
export declare function isHarnessBetweenTurns(session: HarnessSession): boolean;
/**
* Writes the emission state onto a new copy of the session.
*/
export declare function setHarnessEmissionState(session: HarnessSession, state: HarnessEmissionState): HarnessSession;
/**
* Emits `session.started` (once), `turn.started`, and `message.received` at the
* beginning of a new turn. Returns updated emission state.
*/
export declare function emitTurnPreamble(emitFn: HarnessEmitFn, input: StepInput, state: HarnessEmissionState, runtimeIdentity?: RuntimeIdentity): Promise<HarnessEmissionState>;
/**
* Emits `step.started` for one model call.
*/
export declare function emitStepStarted(emitFn: HarnessEmitFn, state: HarnessEmissionState, messages?: readonly import("ai").ModelMessage[]): Promise<void>;
interface FailedStepPayload {
readonly code: string;
readonly details?: JsonObject;
readonly message: string;
}
/**
* Emits the full terminal failure cascade: `step.failed` →
* `turn.failed` → `session.failed`.
*
* Use this when the session cannot be salvaged (structural config
* error, auth misconfig, non-recoverable provider response). The
* `session.failed` tail tells adapters the session is dead and no
* further follow-up is possible on the same continuation token.
*/
export declare function emitFailedStep(emitFn: HarnessEmitFn, state: HarnessEmissionState, input: FailedStepPayload & {
readonly sessionId: string;
}): Promise<void>;
/**
* Emits the recoverable failure cascade: `step.failed` →
* `turn.failed` → `session.waiting`.
*/
export declare function emitRecoverableFailedTurn(emitFn: HarnessEmitFn, state: HarnessEmissionState, input: FailedStepPayload): Promise<HarnessEmissionState>;
/**
* Returns updated emission state for the next step in the current turn.
*/
export declare function advanceStep(state: HarnessEmissionState): HarnessEmissionState;
/**
* Emits `turn.completed` and either `session.waiting` or `session.completed`.
* Returns updated emission state with an incremented sequence.
*/
export declare function emitTurnEpilogue(emitFn: HarnessEmitFn, state: HarnessEmissionState, mode: RunMode): Promise<HarnessEmissionState>;
/**
* Maps an AI SDK finish reason string to the eve-owned
* {@link AssistantStepFinishReason} union. Unknown values become `"other"`.
*/
export declare function normalizeAssistantStepFinishReason(value: string | undefined): AssistantStepFinishReason;
/**
* Result of consuming one step's `fullStream`.
*
* `handledInlineToolResultCallIds` lists approval-resume tool-result
* call ids the stream already handled inline — either emitted as
* `action.result` events or routed to the authorization park path.
* `emitStepActions` skips these to avoid double-emission.
*
* `inlineToolResultParts` holds the same tool-results in
* `ToolResultPart` shape. The AI SDK omits them from
* `stepResult.response.messages` on the approval-resume path, so the
* harness splices them into persisted history to keep the prior turn's
* `tool_use` block balanced with a matching `tool_result` on replay.
*
* `inlineAuthorizationResults` holds approval-resume tool-results whose
* output is an {@link AuthorizationSignal}. These are surfaced into
* `stepResult.toolResults` for the park detector instead of being emitted
* as plain `action.result` events.
*/
interface EmittedStreamContent {
readonly handledInlineToolResultCallIds: ReadonlySet<string>;
readonly inlineAuthorizationResults: readonly TypedToolResult<ToolSet>[];
readonly inlineToolResultParts: readonly InlineToolResultPart[];
}
/**
* Consumes the AI SDK `fullStream` and emits real-time text and reasoning
* events.
*
* `tool-result` parts that have no preceding `tool-call` in this stream
* are emitted inline as `action.result` events. This is the
* approval-resume path: when a previously-parked tool call is approved,
* the AI SDK enqueues the executed tool-result onto the same step's
* stream before the next LLM call. Emitting `action.result` inline keeps
* it ahead of the message events that depend on it.
*
* Tool-call, tool-approval-request, and non-resumed tool-result events
* are still emitted by `emitStepActions` from the `onStepFinish`
* callback so the existing single-step batch ordering is preserved.
*/
export declare function emitStreamContent(emitFn: HarnessEmitFn, state: HarnessEmissionState, fullStream: AsyncIterable<TextStreamPart<ToolSet>>): Promise<EmittedStreamContent>;
export {};