eve
Version:
Filesystem-first framework for durable backend AI agents that run anywhere.
218 lines (217 loc) • 8.51 kB
TypeScript
import type { SessionStateMap } from "#harness/types.js";
import { AGENT_HANDLES_STATE_KEY } from "./state-key.js";
export { AGENT_HANDLES_STATE_KEY };
/**
* Stable identity of one delegated child, minted before its start side
* effect runs. The model-visible `id` derives from the first start
* operation, never from the child session id, so it exists before the
* child does and cannot collide on externally supplied session suffixes.
*/
export interface AgentIdentity {
/** Model-visible identifier: `ag_<name>:<operation-hash>`. */
readonly id: string;
/** Subagent tool name. */
readonly name: string;
/** Agent-graph node used to re-resolve delivery configuration. */
readonly nodeId: string;
}
/**
* One dispatch the parent intends to perform or has performed. Repeating
* the same operation is a replay; a different operation against a
* starting/running handle is a busy conflict.
*/
export interface StartOperation {
readonly kind: "start";
/** Derived via {@link deriveAgentOperationId}; stable across replays. */
readonly id: string;
readonly callId: string;
readonly parentTurnId: string;
}
/** A continuation delivery against a parked handle. */
export interface ContinueOperation {
readonly kind: "continue";
/** Derived via {@link deriveAgentOperationId}; stable across replays. */
readonly id: string;
readonly callId: string;
readonly parentTurnId: string;
/**
* Status the handle showed before this delivery, restored when the
* delivery is rejected as retryable so the handle returns to `parked`
* without optional state.
*/
readonly previousStatus: string;
}
/** Where a fresh child will be started. No session exists yet. */
export type AgentStartTarget = {
readonly kind: "agent/local";
/** Deterministic child continuation token chosen at dispatch. */
readonly continuationToken: string;
} | {
readonly kind: "agent/self";
readonly continuationToken: string;
} | {
readonly kind: "agent/remote";
/** Deliver target base URL; never model-visible. */
readonly url: string;
/** Callback base URL stub captured at dispatch; never model-visible. */
readonly callbackBaseUrl: string;
/** Auth/header resolver selected when this child was created; `{}` means none. */
readonly credentialResolver?: {
readonly resolverId?: string;
};
};
/** Confirmed delivery coordinates of a started child. */
export type AgentAddress = {
readonly kind: "agent/local";
readonly sessionId: string;
readonly continuationToken: string;
} | {
readonly kind: "agent/self";
readonly sessionId: string;
readonly continuationToken: string;
} | {
readonly kind: "agent/remote";
readonly sessionId: string;
readonly url: string;
readonly callbackBaseUrl: string;
/** Auth/header resolver selected when this child was created; `{}` means none. */
readonly credentialResolver?: {
readonly resolverId?: string;
};
};
/**
* Parent-turn-owned lifecycle: `starting → running → parked ↔ running`.
*
* `starting` owns a start intent before the child has an address, `running`
* owns one outstanding child turn, and `parked` retains an idle, resumable
* child. A terminal child or dead dispatch leaves this union entirely.
*/
export type TurnOwnedAgentHandle = {
readonly phase: "starting";
readonly identity: AgentIdentity;
readonly operation: StartOperation;
readonly target: AgentStartTarget;
} | {
readonly phase: "running";
readonly identity: AgentIdentity;
readonly operation: StartOperation | ContinueOperation;
readonly address: AgentAddress;
} | {
readonly phase: "parked";
readonly identity: AgentIdentity;
readonly address: AgentAddress;
readonly lastStatus: string;
};
/**
* Workflow-owner lifecycle: `reserved → claimed → available ↔ claimed`.
*
* `reserved` leases a fresh identity before start, `claimed` leases an
* addressed child turn to one task or workflow-tool run, and `available`
* retains the idle address between invocations. A terminal child leaves this
* union entirely.
*/
export type TaskOwnedAgentHandle = {
/** Fresh identity leased to one owner before the child's address is confirmed. */
readonly phase: "reserved";
readonly identity: AgentIdentity;
readonly operationId: string;
readonly callId?: string;
readonly ownerId: string;
} | {
/** Addressed child turn leased until its owner releases it. */
readonly phase: "claimed";
readonly identity: AgentIdentity;
readonly operationId: string;
readonly callId?: string;
readonly address: AgentAddress;
readonly ownerId: string;
} | {
/** Idle addressed child with no owner lease, ready for a later invocation to claim. */
readonly phase: "available";
readonly identity: AgentIdentity;
readonly address: AgentAddress;
};
/**
* Durable ownership record for one delegated child.
*
* The two execution policies share an identity namespace and serialized store,
* but their lifecycle states and transitions are disjoint. A terminal child has
* no handle: settlement deletes it.
*/
export type AgentHandle = TurnOwnedAgentHandle | TaskOwnedAgentHandle;
/** Lifecycle phase of a delegated agent handle. */
export type AgentHandlePhase = AgentHandle["phase"];
/** Session-state collection of delegated agent handles. */
export interface AgentHandleStore {
readonly handles: readonly AgentHandle[];
}
export declare const EMPTY_AGENT_HANDLE_STORE: AgentHandleStore;
/** One serialized owner-lease mutation against the shared agent handle store. */
export type AgentHandleStoreCommand = {
readonly kind: "read";
} | {
readonly identity: AgentIdentity;
readonly kind: "reserve";
readonly operationId: string;
readonly callId?: string;
readonly ownerId: string;
} | {
readonly address: AgentAddress;
readonly kind: "confirm";
readonly operationId: string;
readonly ownerId: string;
} | {
readonly agentId: string;
readonly expectedTarget: "local" | "remote";
readonly callId?: string;
readonly invokedName: string;
readonly kind: "claim";
readonly operationId: string;
readonly ownerId: string;
} | {
readonly agentId: string;
readonly kind: "remove";
readonly ownerId: string;
} | {
readonly kind: "release-owner";
readonly ownerId: string;
};
export type AgentHandleStoreCommandResult = {
readonly kind: "ready";
readonly handle?: TaskOwnedAgentHandle;
} | {
readonly kind: "busy";
readonly handle: AgentHandle;
} | {
readonly kind: "mismatch";
readonly handle: AgentHandle;
} | {
readonly kind: "unknown";
};
/** Derives the model-visible agent id from the first start operation. */
export declare function deriveAgentId(name: string, startOperationId: string): string;
/** Collapses whitespace and truncates output into a handle status line. */
export declare function formatAgentStatus(output: unknown): string;
/**
* Validates one agent handle store about to be persisted, returning the
* parsed value. Throws instead of writing an invalid store: transitions run
* this on every write, which is the invariant that lets the schema-free
* owner-side reader (`query.ts`) trust stored values without revalidating.
*/
export declare function assertPersistableAgentHandleStore(store: AgentHandleStore): AgentHandleStore;
/** Parses one complete command before the session inbox routes it to the handle store. */
export declare function parseAgentHandleStoreCommand(value: unknown): AgentHandleStoreCommand | undefined;
/**
* Reads and validates the agent handle store from session state.
*
* Returns `undefined` only when no store has been written. A present but
* invalid store throws: treating corruption as absence would let the next
* transition silently replace every delegated child's delivery coordinates.
*/
export declare function getAgentHandleStore(state: SessionStateMap | undefined): AgentHandleStore | undefined;
/** Writes the validated agent handle store under its single session-state key. */
export declare function setAgentHandleStore(state: SessionStateMap | undefined, store: AgentHandleStore): SessionStateMap;
/** Writes a validated handle list to a session-shaped value. */
export declare function writeHandles<Session extends {
readonly state?: SessionStateMap;
}>(session: Session, handles: readonly AgentHandle[]): Session;