AgentHarness Types
Type definitions for the AgentHarnessContract interface, AgentConfig, and related types in Noetic.
AgentHarnessContract
The core execution engine interface. The exported AgentHarness class implements this contract. Use the contract type when you want to be agnostic about the concrete implementation (e.g., for tests or custom harnesses).
declare interface HarnessApi<TParams extends Record<string, unknown> = Record<string, unknown>> {
readonly config: AgentConfig<TParams>;
readonly fs: FsAdapter;
readonly shell: ShellAdapter;
readonly subprocess: SubprocessAdapter;
readonly rootCwdState: { cwd: string; previousCwd?: string };
callModel(request: CallModelRequest): Promise<LLMResponse>;
execute(input: ExecuteInput, options?: ExecuteOptions): Promise<void>;
getAgentResponse(scope?: SessionScope): Promise<HarnessResponse>;
getItemStream(scope?: SessionScope): AsyncIterable<StreamingItem>;
getTextStream(scope?: SessionScope): AsyncIterable<string>;
getReasoningStream(scope?: SessionScope): AsyncIterable<string>;
getFullStream(scope?: SessionScope): AsyncIterable<StreamEvent>;
abort(scope?: SessionScope & { reason?: string }): Promise<void>;
getStatus(scope?: SessionScope): HarnessStatus;
getQueueSize(scope?: SessionScope): number;
seedSessionHistory(threadId: string, items: ReadonlyArray<Item>): void;
setRootCwd(nextCwd: string): void;
run<I, O>(step: Step<ContextMemory, I, O>, input: I, ctx: Context): Promise<O>;
detachedSpawn<I, O>(
step: Step<ContextMemory, I, O>,
input: I,
parentCtx: Context,
overrides?: { threadId?: string; resourceId?: string; cwdInit?: string },
): DetachedHandle<O>;
createContext(opts?: {
parent?: Context;
items?: Item[];
state?: unknown;
threadId?: string;
resourceId?: string;
memory?: MemoryLayer[];
cwdInit?: string;
}): Context;
send<T>(channel: Channel<T>, value: T, ctx: Context): Promise<void>;
recv<T>(channel: Channel<T>, ctx: Context, opts?: { timeout?: number }): Promise<T>;
tryRecv<T>(channel: Channel<T>, ctx: Context): T | null;
getChannelHandle<T>(channel: ExternalChannel<T>, executionId: string): ChannelHandle<T>;
initLayers(layers: MemoryLayer[], ctx: Context, storage: StorageAdapter): Promise<void>;
recallLayers(layers: MemoryLayer[], input: string, ctx: Context): Promise<RecallLayerOutput[]>;
recallLayersAtomic(
layers: MemoryLayer[],
input: string,
ctx: Context,
budgets: Map<string, number>,
): Promise<RecallLayerOutput[]>;
recallLayersEventual(
layers: MemoryLayer[],
input: string,
ctx: Context,
budgets: Map<string, number>,
): Promise<RecallLayerOutput[]>;
projectHistory(
layers: MemoryLayer[],
items: ReadonlyArray<Item>,
ctx: Context,
): Promise<ReadonlyArray<Item>>;
runAppendPipeline(
layers: MemoryLayer[],
items: Item[],
ctx: Context,
): Promise<{ items: Item[]; rerenderRequests: RerenderRequest[] }>;
executeRerender(
requests: RerenderRequest[],
layers: MemoryLayer[],
ctx: Context,
budgets: Map<string, number>,
query?: string,
): Promise<RecallLayerOutput[]>;
previewRequestItems(scope?: SessionScope): Promise<ReadonlyArray<Item>>;
storeLayers(layers: MemoryLayer[], response: LLMResponse, ctx: Context): Promise<void>;
disposeLayers(layers: MemoryLayer[], ctx: Context): Promise<void>;
getLayerState<T>(executionId: string, layerId: string): T | undefined;
setLayerState<T>(executionId: string, layerId: string, state: T): void;
beforeToolCall(
layers: MemoryLayer[],
toolName: string,
toolArgs: unknown,
ctx: Context,
): Promise<unknown>;
afterModelCall(layers: MemoryLayer[], response: LLMResponse, ctx: Context): Promise<unknown>;
checkpoint(ctx: Context): Promise<void>;
restore(executionId: string): Promise<Context | null>;
cancel(ctx: Context, reason?: string): Promise<void>;
createSpan(name: string, parent: Span | null): Span;
}
declare interface RerenderRequest {
layerId: string;
slot: number;
timing: 'immediate' | 'batched';
scope: 'self' | 'slot-after' | 'all';
}AgentHarnessContract Methods
| Method | Description |
|---|---|
config | The AgentConfig<TParams> the harness was constructed with. |
fs | The FsAdapter the harness was constructed with (defaults to createLocalFsAdapter()). All filesystem operations — CLI tools, skill discovery, and memory layers — route through this adapter. |
shell | The ShellAdapter (defaults to createLocalShellAdapter()). Tools that need a real shell go through this. |
subprocess | The SubprocessAdapter used by step.run.subprocess requests and detached spawns. Defaults to an in-memory adapter. |
rootCwdState | Long-lived shared cwd state, seeded into every root context the harness creates so successive runs and the TUI observe each other's cds. |
callModel(request) | Issue a single LLM call, bypassing the session/queue runner. Used by plugins that need one-shot generation. |
execute(input, options?) | Enqueue a message on the session identified by options.threadId. Resolves once the message is accepted into the queue — not when the model responds. |
getAgentResponse(scope?) | Resolves with the accumulated response once the session drains. |
getItemStream(scope?) | Yields cumulative Item snapshots across every turn in the session. |
getTextStream(scope?) | Yields text deltas across every turn. |
getReasoningStream(scope?) | Yields reasoning-token deltas. |
getFullStream(scope?) | Yields all raw SDK + framework events. |
abort(scope?) | Cancel the in-flight turn. Queued messages are preserved. |
getStatus(scope?) | Session status snapshot. |
getQueueSize(scope?) | Messages currently queued on the session. |
seedSessionHistory(threadId, items) | Pre-populate a session's accumulated history with prior items. Used by resume flows. |
setRootCwd(nextCwd) | Update the harness root cwd (e.g., from a TUI ! cd). |
run(step, input, ctx) | Run a step with the given input and context. |
detachedSpawn(step, input, parentCtx, overrides?) | Launch a step concurrently, returning a DetachedHandle. Optional overrides.threadId / overrides.resourceId / overrides.cwdInit decouple the child's session-scoped state from the parent's. |
createContext(opts?) | Create a new execution context. Accepts optional memory to override harness-level defaults. |
send(channel, value, ctx) | Send a value to a channel. Returns a Promise<void> that resolves immediately unless the target queue channel is at capacity, in which case the send parks until a consumer frees a slot (back-pressure; default 30s timeout → channel_timeout, abort → cancelled). |
recv(channel, ctx, opts?) | Receive from a channel (blocking). Rejects with kind 'cancelled' when the context is aborted while waiting. |
tryRecv(channel, ctx) | Non-blocking receive. |
getChannelHandle(channel, executionId) | Get external channel handle. |
initLayers(layers, ctx, storage) | Initialize memory layers. |
recallLayers(layers, input, ctx) | Run recall on all layers. |
recallLayersAtomic(layers, input, ctx, budgets) | Run recall on the atomic-mode layers only, blocking until each completes within its budget. |
recallLayersEventual(layers, input, ctx, budgets) | Serve recall for eventual-mode layers from cache without blocking; the cache refreshes after store(). |
projectHistory(layers, items, ctx) | Run each layer's projectHistory hook in slot order to project (cap, transform) the history portion of the context window. Read-side only. |
runAppendPipeline(layers, items, ctx) | Run input items through each layer's onItemAppend hook in slot order, returning the transformed items plus any re-render requests. |
executeRerender(requests, layers, ctx, budgets, query?) | Re-run recall for the layers selected by the given re-render requests and return their fresh outputs. |
previewRequestItems(scope?) | Return the Item[] that would be sent on the next turn — accumulated history plus harness-level layer recall outputs. Read-mostly; no session is allocated for unknown thread ids. |
storeLayers(layers, response, ctx) | Run store on all layers. |
disposeLayers(layers, ctx) | Dispose all layers. |
getLayerState(executionId, layerId) | Read a layer's state for a given execution. |
setLayerState(executionId, layerId, state) | Overwrite a layer's state for a given execution. |
beforeToolCall(layers, toolName, toolArgs, ctx) | Invoke each layer's beforeToolCall steering hook and aggregate the decision. |
afterModelCall(layers, response, ctx) | Invoke each layer's afterModelCall steering hook. |
checkpoint(ctx) | Persist current execution state. |
restore(executionId) | Restore a previously checkpointed context. |
cancel(ctx, reason?) | Cancel an execution. |
createSpan(name, parent) | Create an observability span. |
DeliveryMode
type DeliveryMode = 'next-turn' | 'between-rounds' | 'interrupt';| Mode | Behaviour |
|---|---|
next-turn (default) | Queue and run after the current turn completes. |
between-rounds | Inject as a user item before the next tool-round LLM call within the active turn. |
interrupt | Abort the in-flight turn, place at head of queue, restart. |
ExecuteOptions
interface ExecuteOptions {
threadId?: string;
resourceId?: string;
state?: unknown;
memory?: MemoryLayer[];
deliveryMode?: DeliveryMode;
}
interface SessionScope {
threadId?: string;
}HarnessStatus
type HarnessStatus =
| { readonly kind: 'idle' }
| { readonly kind: 'generating'; readonly startedAt: number; readonly turnId: string }
| { readonly kind: 'aborting'; readonly turnId: string };HarnessResponse
interface HarnessResponse {
readonly items: ReadonlyArray<Item>;
readonly usage: { inputTokens: number; outputTokens: number; cachedTokens?: number };
readonly cost?: number;
readonly text: string;
readonly lastLayerUsage?: LastLayerUsage;
}StreamEvent
type StreamEvent = SdkStreamEvent | FrameworkStreamEvent;
interface SdkStreamEvent {
readonly source: 'sdk';
readonly type: string;
readonly data: Record<string, unknown>;
readonly outputIndex?: number;
readonly contentIndex?: number;
}
interface FrameworkStreamEvent {
readonly source: 'framework';
readonly type: `${string}:${string}`;
readonly data: Record<string, unknown>;
}StreamingItem
type StreamingItem = Item & { readonly isComplete: boolean };AgentConfig
Declarative configuration for an agent harness. Generic over TParams, an arbitrary key-value record accessible via ctx.harness.config.params. Filesystem and shell adapters are passed to the AgentHarness constructor and exposed as harness.fs / harness.shell; they are not part of AgentConfig itself.
declare interface AgentConfigShape<
TParams extends Record<string, unknown> = Record<string, unknown>,
> {
name: string;
storage?: StorageAdapter;
hooks?: AgentHooks;
params: TParams;
itemSchemas?: unknown;
strictItemSchemas?: boolean;
}| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Agent name. |
storage | StorageAdapter | no | Storage backend for memory persistence. Defaults to an in-memory adapter. |
hooks | AgentHooks | no | Before/after step hooks. |
params | TParams | yes | Arbitrary key-value parameters available to every step and tool via ctx.harness.config.params. |
itemSchemas | ItemSchemaExtensions | no | Harness-wide item-schema extensions used to validate emitted and returned items. Global by design; tool-contributed toolResults schemas are owner-scoped to each tool's own result items. Extension schemas are gates, not normalizers — the original item is returned on match. Mismatches raise NoeticError kind item_schema_mismatch. |
strictItemSchemas | boolean | no | Whether unknown extension item types must match a registered schema. Defaults to true. |
AgentHooks
interface AgentHooks {
beforeStep?: (step: Step, ctx: Context) => Promise<void>;
afterStep?: (step: Step, result: unknown, ctx: Context) => Promise<void>;
}| Hook | Parameters | Description |
|---|---|---|
beforeStep | (step, ctx) | Called before each step executes |
afterStep | (step, result, ctx) | Called after each step completes |
DetachedHandle
A handle returned by harness.detachedSpawn() to track a concurrently running child step.
interface DetachedHandle<O> {
readonly id: string;
readonly status: DetachedStatus;
readonly result: O | undefined;
readonly error: string | undefined;
await(timeout?: number): Promise<O>;
}| Property | Type | Description |
|---|---|---|
id | string | Unique handle identifier (child context ID) |
status | DetachedStatus | Current execution status |
result | O | undefined | Child output (set when completed) |
error | string | undefined | Error message (set when failed) |
await(timeout?) | Promise<O> | Wait for completion, optionally with timeout in ms |
DetachedStatus
const DetachedStatus = {
Running: 'running',
Completed: 'completed',
Failed: 'failed',
} as const;
type DetachedStatus = (typeof DetachedStatus)[keyof typeof DetachedStatus];| Value | Description |
|---|---|
'running' | Child step is still executing |
'completed' | Child step finished successfully |
'failed' | Child step threw an error |
RecallLayerOutput
The output from a single memory layer's recall hook.
interface RecallLayerOutput {
layerId: string;
items: Item[];
tokenCount: number;
}| Field | Type | Description |
|---|---|---|
layerId | string | Which layer produced this output |
items | Item[] | Items to inject into the prompt |
tokenCount | number | Token cost of the items |
LLMResponse
The response from an LLM call.
interface LLMResponse {
items: Item[];
usage: {
inputTokens: number;
outputTokens: number;
cachedTokens?: number;
};
cost?: number;
}| Field | Type | Description |
|---|---|---|
items | Item[] | Response items (messages, function calls, etc.) |
usage.inputTokens | number | Input tokens consumed |
usage.outputTokens | number | Output tokens generated |
usage.cachedTokens | number | Cached tokens used (optional) |
cost | number | Cost in USD (optional) |