generateHarness()
Run a Little Harness agent turn and return a completed result.
Little Harness: published on npm as little-harness@0.2.0-alpha.0 (2026-10-05). Sections labeled UNRELEASED describe source-only changes. Release status
generateHarness() runs the same harness as streamHarness(), but waits for the
turn to complete and returns the final result directly.
Use it for workers, cron jobs, tests, server actions, queue consumers, and Little Workflow nodes.
Import
import { generateHarness } from "@little-stack/harness";Basic Usage
import { Output } from "ai";
import { generateHarness } from "@little-stack/harness";
const result = await generateHarness({
harness,
type: "support.ticket_triage",
input: ticket,
output: Output.object({ schema: triageSchema }),
session: ticket.id,
});
// Compiles against published 0.2.0-alpha.0 and against the unreleased source.
if (result.status === "completed") {
console.log(result.output.priority);
}A turn can park when a tool returns a parked result. The published
0.2.0-alpha.0 types do not say so: there, generateHarness() returns
GenerateHarnessResult, which names the completed result only, even though the
runtime can still return a parked one. Guard on status === "completed" before
reading completed-only fields.
UNRELEASED: in the current source,
generateHarness()returnsHarnessTurnResult, thestatus-discriminated union, so the parked branch type-checks. TheGenerateHarnessResultalias is removed. No npm version includes this yet. See Typed parked results.
// UNRELEASED: needs the unreleased source SDK.
if (result.status === "parked") {
// The agent is waiting in await_tasks, for example for a person's answer.
// resumeParked() continues the turn once the wait is over.
return;
}
console.log(result.output.priority);API Signature
async function generateHarness<
TOutput = string,
TInput = unknown,
TExtraBody = unknown,
>(
options: GenerateHarnessOptions<TInput, TExtraBody, TOutput>,
): Promise<HarnessTurnResult<TOutput>>;Parameters
generateHarness() accepts the same core input options as streamHarness().
harness
Harness
Reusable harness returned by createHarness().
messages
UIMessage[]
Raw chat messages. Use this when a non-streaming caller still wants to run a chat transcript to completion.
const result = await generateHarness({
harness,
messages,
session: chatId,
});prompt
string
One user message, as plain text. The quickest way to run a turn from a script or a worker.
const result = await generateHarness({
harness,
session: "loan_APP-1001",
prompt: "Pre-approve APP-1001.",
});Pass prompt or messages, not both, and not with type or input.
type
string
Open string that names a custom Input Type.
type: "account.renewal_brief"input
TInput
App-defined payload for the custom Input Type.
input: {
accountId: "acct_123",
includeOpenTickets: true,
}session
string | HarnessSession
Stable continuation key or explicit session handle.
session: `account:${accountId}:renewal`extraBody
TExtraBody
App-defined request context passed to tools, file hooks, Persistent Dirs, and events.
model
LanguageModel
Per-call model override. createHarness() still requires a default model in
the TypeScript API; use this when a worker, route, or remote configuration
temporarily overrides that default.
await generateHarness({
harness,
type: "support.ticket_triage",
input: ticket,
model: requestModel,
});system
string | SystemModelMessage | SystemModelMessage[]
Per-call system override. When provided, it replaces the harness-level system prompt for this turn. Input Type instructions, skills, memory, and runtime hints are still added around the selected system prompt.
runtime
HarnessRuntimeOptions
Per-call runtime policy override. It merges with the harness-level runtime policy.
await generateHarness({
harness,
type: "support.ticket_triage",
input: ticket,
runtime: { bash: false },
});temperature
number
Per-call AI SDK temperature setting. Little Harness includes it in durable model request identity, so replay does not reuse a response generated with a different temperature.
output
Output
AI SDK output specification for parsing the completed result.
output: Output.object({ schema: renewalBriefSchema })abortSignal
AbortSignal
Cancels the run when possible.
runId
string
Durable identity of this run for kill-and-resume. Re-invoking with the same
runId (and the same inputs) against the same session log replays the
recorded run instead of re-calling models and re-executing tool side effects.
Defaults to a fresh id per call.
const result = await generateHarness({
harness,
messages,
session: chatId,
runId: "run_123",
sessionLog: remoteSessionLog({ baseUrl, authToken }),
});sessionLog
HarnessSessionLog (type from @little-stack/harness/events)
Per-call session-log override (the durable event log this run appends to and
replays from). When omitted, the log configured on createHarness() is used.
UNRELEASED: the current source removes the
durabilityalias. Published0.2.0-alpha.0acceptsdurabilityas an alias ofsessionLog(sessionLogwins when both are set). See Alpha Migrations.
See Execution Environments for the remote session-log adapter.
onEvent
Function that receives a HarnessEvent.
Per-call trace callback.
onPersistenceError
Function that receives a PersistenceError.
Per-call Persistent Dir failure callback.
toolResultSpooling
false | { maxInlineBytes?: number; previewBytes?: number; outputDir?: string }
Per-call override for automatic oversized tool-result spooling. When omitted,
the setting from createHarness() is used. If neither is configured, Little
Harness spools tool results larger than 16 KB into /artifacts/tool-results.
The compact result includes summary metadata with text counts and, for JSON
content, a bounded structure sketch.
trace
false | HarnessTraceOptions
Per-call override for trace capture. When omitted, the setting from
createHarness() is used.
await generateHarness({
harness,
type: "support.ticket_triage",
input: ticket,
trace: {
content: {
maxInlineBytes: 4096,
},
fileDiffs: {
enabled: true,
},
},
});Set trace: false on a single call for requests that should not persist a
Local Host trace.
Agent Loop Options
generateHarness() forwards the common AI SDK loop controls:
await generateHarness({
harness,
messages,
stopWhen,
prepareStep,
toolChoice,
activeTools,
temperature,
providerOptions,
headers,
maxRetries,
});stopWhen takes AI SDK 7 stop conditions such as isStepCount(8) (imported
from ai). When it is omitted, Little Harness uses
isStepCount(workflowBudgets.maxModelSteps) โ 20 by default โ so a model can
continue after executed tool calls instead of stopping after the first step.
Returns
A turn ends in one of two states:
status: "completed": the turn finished. Every field below is present.status: "parked": a tool parked the turn until pending work settles. The result carriescontinuationIdandpending, plusartifacts,traceandwarnings. It has nooutput,persistenceorcommitManual, so checkstatusbefore reading them. When the turn parked inawait_tasks,resumeParked()continues it once the wait is over; see Asking A Person.
How the type says so depends on the version:
| Version | Declared return type |
|---|---|
Published 0.2.0-alpha.0 | GenerateHarnessResult<TOutput>, an alias of HarnessCompletedResult<TOutput> only. The runtime can still return a parked result; the type does not include it. |
| UNRELEASED source (no npm version yet) | HarnessTurnResult<TOutput>, the union of the completed and parked results. GenerateHarnessResult no longer exists. |
HarnessTurnResult already declares both states in 0.2.0-alpha.0; only the
declared return type changes. A status === "completed" guard compiles with both. See
Typed parked results.
The fields below describe a completed result.
text
string
The completed text response.
output
TOutput
The parsed structured output when output is provided.
session
HarnessSession
The session that ran the turn.
artifacts
ArtifactRef[]
Artifacts created during the run.
trace
TraceRef
Host-facing trace reference.
persistence
PersistenceStatus
Persistent Dir commit status.
commitManual
() => Promise<PersistenceStatus>
Commits Persistent Dirs configured with commit: "manual" for the completed
turn. The method is idempotent for a result object; repeated calls return the
same commit promise.
warnings
HarnessWarning[]
Warnings that should be visible to the host app.
if (result.warnings.some((warning) => warning.code === "undefined_input_type")) {
logger.warn("Missing Input Type definition", result.warnings);
}Examples
From A Script (No HTTP Request)
In a plain script or worker there is no req.json() to read messages from.
Pass the request as prompt:
import { generateHarness } from "@little-stack/harness";
const result = await generateHarness({ harness, prompt: "Summarize the latest incident." });
if (result.status === "completed") console.log(result.text);For more than one message, build the UIMessage[] array yourself. Each
UIMessage needs an id and parts; the AI SDK's { role, content } model
messages are refused with an error that says so.
import type { UIMessage } from "ai";
const messages: UIMessage[] = [
{ id: "u1", role: "user", parts: [{ type: "text", text: "Summarize the latest incident." }] },
];
const result = await generateHarness({ harness, messages });Generate A Structured Brief
const result = await generateHarness({
harness,
type: "account.renewal_brief",
input: {
accountId: "acct_123",
quarter: "2026-Q3",
},
output: Output.object({ schema: renewalBriefSchema }),
session: "acct_123-renewal",
});
if (result.status === "completed") {
await reports.create({
accountId: "acct_123",
body: result.output,
traceId: result.trace.id,
});
}Run A Chat Transcript To Completion
const result = await generateHarness({
harness,
messages,
session: chatId,
output: Output.text(),
});
if (result.status !== "completed") {
return Response.json({ status: result.status }, { status: 202 });
}
return Response.json({
text: result.text,
artifacts: result.artifacts,
});Handle Persistence Failure
const result = await generateHarness({
harness,
type: "project.update_notes",
input,
session: projectId,
});
if (result.status === "completed" && result.persistence.status === "failed") {
await retryQueue.enqueue(result.persistence.failedCommits);
}The model should not automatically receive persistence failures in its normal context. The host app can retry, notify the user, or inspect the trace.