Session
A session is one conversation with an agent, run a turn at a time. It is what callers hold:
Harness.createSession creates one over an agent session, and the session adds the host’s tools,
whole-turn results, and the record of the conversation that SessionState keeps.
SessionState is what suspendTurn, detach, and stop return for the caller to store. It
records the conversation as Effect AI Prompt messages, so that Harness.createSession can
resume the session from it on any agent: the agent that last ran it resumes from its own state,
and any other loads the conversation, and runs an unfinished turn again from its prompt.
suspendTurn, detach, stop, and destroy release the session. Each takes hold of it
before it asks the agent, so that no turn starts meanwhile, and one that fails leaves the session
open, to be tried again.
Since v0.1.0
Constructors
Section titled “Constructors”conversationAfter
Section titled “conversationAfter”The conversation a compaction leaves: its summary, as a user message, then the messages it kept.
Details
An agent that keeps its own conversation goes on from this one, so that it has what the session records.
Signature
declare const conversationAfter: (compaction: Pick<StreamPart.CompactionPart, "summary" | "kept">) => Prompt.PromptSince v0.1.0
Models
Section titled “Models”Session (interface)
Section titled “Session (interface)”One conversation with an agent, as callers hold it.
Details
Turns run one at a time, each as a stream of StreamParts or returned whole, and the session is
released once, with suspendTurn, detach, stop, or destroy.
Example (A turn with a host tool that needs approval)
import { Agent, Harness } from "@amends-dev/harness"import { Effect, Schema, Stream } from "effect"import { Tool, Toolkit } from "effect/ai"
const Delete = Tool.make("Delete", { description: "Deletes a file in the workspace", parameters: Schema.Struct({ path: Schema.String }), success: Schema.String, needsApproval: true})const Tools = Toolkit.make(Delete)
const program = Effect.gen(function* () { const harness = yield* Harness.Harness const toolkit = yield* Tools.pipe( Effect.provide(Tools.toLayer({ Delete: ({ path }) => Effect.succeed(`deleted ${path}`) })) ) const session = yield* harness.createSession({ id: Agent.SessionId.make("cleanup") }) // The session streams a request before it runs `Delete`; answering it lets the tool run. yield* session .stream({ prompt: "Delete the build output", toolkit }) .pipe( Stream.runForEach((part) => part.type === "tool-approval-request" ? session.approve({ approvalId: part.approvalId, approved: true }) : Effect.void ) ) return yield* session.detach})Signature
export interface Session { readonly id: Agent.SessionId
/** * Whether the agent has the unfinished turn the session resumed with, which the continue * operations then continue after the steps it finished. Otherwise they run the turn again from * its prompt, without them, for a caller that keeps the steps to set them aside. */ readonly hasUnfinishedTurn: boolean
/** * Runs a turn, streaming its parts until `finish`. Interrupting the stream aborts the turn, which * the conversation keeps as far as it got. A failure the agent reports arrives as an `error` * part, and the turn finishes with `error`. */ readonly stream: <Tools extends Record<string, Tool.Any> = {}>( options: StreamOptions<Tools> ) => Stream.Stream<StreamPart.StreamPart, TurnError<Tools>, TurnServices<Tools>>
/** * Runs a turn and returns it whole. Fails with `TurnFailedError` when the agent reports a * failure. Nothing can answer an approval request before the turn ends, so each is denied; * `stream` is the way to ask. */ readonly generate: <Tools extends Record<string, Tool.Any> = {}>( options: StreamOptions<Tools> ) => Effect.Effect<GenerateResult.GenerateResult, TurnError<Tools>, TurnServices<Tools>>
/** * Runs a turn that answers in JSON and decodes the answer with `schema`. An answer that does not * decode fails as Effect AI's `generateObject` fails, with an `AiError` whose reason is * `StructuredOutputError`; an agent that cannot answer in JSON fails with `UnsupportedError`. * Like `generate`, it denies every approval request. */ readonly generateObject: <S extends Schema.Top, Tools extends Record<string, Tool.Any> = {}>( options: GenerateObjectOptions<S, Tools> ) => Effect.Effect< GenerateResult.GenerateObjectResult<S["Type"]>, TurnError<Tools>, TurnServices<Tools> | S["DecodingServices"] >
/** * Streams the unfinished turn the session was started with, like `stream`, from the step that was * in flight when `suspendTurn` cut it short. That step starts over, so the parts of it delivered * before `suspendTurn` are replaced by the ones `continueStream` streams. An agent that does not * have the turn, such as another agent, runs it again from its prompt. Fails with `NoTurnError` * when the session has no unfinished turn, or when the turn answers in JSON, which * `continueGenerateObject` finishes. */ readonly continueStream: <Tools extends Record<string, Tool.Any> = {}>( options: ContinueOptions<Tools> ) => Stream.Stream<StreamPart.StreamPart, TurnError<Tools>, TurnServices<Tools>>
/** * Finishes the unfinished turn and returns what `continueStream` would stream whole, like * `generate`: from the step that was in flight, or the whole turn run again. The steps before are * in the `UnfinishedTurn` the session resumed with. Like `generate`, it denies every approval * request. */ readonly continueGenerate: <Tools extends Record<string, Tool.Any> = {}>( options: ContinueOptions<Tools> ) => Effect.Effect<GenerateResult.GenerateResult, TurnError<Tools>, TurnServices<Tools>>
/** * Finishes the unfinished turn that answers in JSON, like `continueGenerate`, and decodes the * answer with `schema`, as `generateObject` does. Fails with `NoTurnError` when the turn answers * in text, which `continueStream` or `continueGenerate` finishes. */ readonly continueGenerateObject: <S extends Schema.Top, Tools extends Record<string, Tool.Any> = {}>( options: ContinueObjectOptions<S, Tools> ) => Effect.Effect< GenerateResult.GenerateObjectResult<S["Type"]>, TurnError<Tools>, TurnServices<Tools> | S["DecodingServices"] >
/** * Answers a `tool-approval-request` of the running turn: one the session made for a host tool * that needs approval, which `approved` allows or refuses, or else one from the agent for its own * tools. A request the session made is answered once, and ends with the stream that made it. */ readonly approve: Agent.AgentSession["approve"]
/** * Asks the agent to replace the conversation so far with a summary, guided by `instructions`, and * returns the compaction, which the conversation then starts from. It runs between turns, and no * turn starts meanwhile. Fails with `UnsupportedError` when the agent compacts only by itself. */ readonly compact: Agent.AgentSession["compact"]
/** * Ends the running turn's stream, while the agent keeps working where it can, and returns the * session with the turn unfinished, for `continueStream`, `continueGenerate`, or * `continueGenerateObject` to finish once `Harness.createSession` resumes it. */ readonly suspendTurn: Effect.Effect<SessionState, HarnessError.HarnessError>
/** Releases the session between turns while the agent keeps running. */ readonly detach: Effect.Effect<SessionState, HarnessError.HarnessError>
/** * Stops the agent, aborting a running turn, whose stream ends with an error and which the * conversation keeps as far as it got, and returns the session to start again later. */ readonly stop: Effect.Effect<SessionState, HarnessError.HarnessError>
/** * Stops the agent for good, aborting a running turn. It also destroys a session already released, * and destroying it again succeeds. */ readonly destroy: Effect.Effect<void, HarnessError.HarnessError>}Since v0.1.0
Options
Section titled “Options”ContinueObjectOptions (interface)
Section titled “ContinueObjectOptions (interface)”What an unfinished turn that answers with an object takes to finish: the turn’s settings, given again, and the schema of the answer.
Signature
export interface ContinueObjectOptions< S extends Schema.Top, Tools extends Record<string, Tool.Any> = {}> extends ContinueOptions<Tools> { /** The schema the turn's answer decodes to, as JSON, as `generateObject` was given it. */ readonly schema: S}Since v0.1.0
ContinueOptions (type alias)
Section titled “ContinueOptions (type alias)”The settings the unfinished turn started with, given again for an agent that runs it again.
Signature
type ContinueOptions<Tools extends Record<string, Tool.Any> = {}> = Omit<StreamOptions<Tools>, "prompt">Since v0.1.0
GenerateObjectOptions (interface)
Section titled “GenerateObjectOptions (interface)”What a turn that answers with an object asks: a turn’s settings, and the schema of the answer.
Signature
export interface GenerateObjectOptions< S extends Schema.Top, Tools extends Record<string, Tool.Any> = {}> extends StreamOptions<Tools> { /** The schema the turn's answer decodes to, as JSON. */ readonly schema: S
/** Names the object for agents that name the JSON they answer with. Defaults to `output`. */ readonly objectName?: string}Since v0.1.0
StreamOptions (interface)
Section titled “StreamOptions (interface)”What a turn asks, and the tools the host gives it.
Signature
export interface StreamOptions<Tools extends Record<string, Tool.Any> = {}> { /** What the turn asks: text, or a user message that can also carry files. */ readonly prompt: string | Prompt.UserMessage
/** Instructions for this turn, as system or developer instructions where the agent has them. */ readonly instructions?: string
/** The model for this turn, as the agent names models. The agent's default when omitted. */ readonly model?: string
/** * The host's tools, with their handlers. When the agent calls one, the session runs its handler, * streams the result as a `tool-result` part, and gives the result back to the agent. The tools * of calls the agent makes together run side by side, and their results stream as they come; a * step ends only once the tools it called have answered. * * A tool that needs approval, by its `needsApproval`, runs only once `approve` allows it: the * turn streams a `tool-approval-request` part after the call and waits, while the agent waits for * the result. A call `approve` refuses is answered as `execution-denied`, a failure the agent * reads. `needsApproval` as a function reads the call's decoded params and the conversation as * the session has recorded it: the turns before, the prompt, and the steps the caller has read to * their end, not the step the call is in. */ readonly toolkit?: Toolkit.WithHandler<Tools>}Since v0.1.0
Schemas
Section titled “Schemas”SessionState
Section titled “SessionState”A session as suspendTurn, detach, and stop return it, for the caller to store and give
Harness.createSession back as resumeFrom. Schema.toCodecJson(SessionState) stores it as
JSON.
Signature
declare const SessionState: Schema.Struct<{ readonly id: Schema.brand<Schema.String, "SessionId"> readonly agent: Schema.String readonly history: Schema.Codec<Prompt.Prompt, Prompt.PromptEncoded, never, never> readonly agentState: Schema.optionalKey<Schema.Codec<Schema.Json, Schema.Json, never, never>> readonly unfinishedTurn: Schema.optional< Schema.Struct<{ readonly prompt: Schema.Struct<{ readonly role: Schema.Literal<"user"> readonly content: Schema.Union< readonly [ Schema.decodeTo< Schema.NonEmptyArray< Schema.toType< Schema.Struct<{ readonly type: Schema.Literal<"text"> readonly text: Schema.String readonly "~effect/ai/Prompt/Part": Schema.withDecodingDefaultKey< Schema.Literal<"~effect/ai/Prompt/Part"> > readonly options: Schema.withDecodingDefault< Schema.$Record<Schema.String, Schema.NullOr<Schema.Codec<Schema.Json>>> > }> > >, Schema.String, never, never >, Schema.$Array< Schema.Union< readonly [ Schema.Struct<{ readonly type: Schema.Literal<"text"> readonly text: Schema.String readonly "~effect/ai/Prompt/Part": Schema.withDecodingDefaultKey< Schema.Literal<"~effect/ai/Prompt/Part"> > readonly options: Schema.withDecodingDefault< Schema.$Record<Schema.String, Schema.NullOr<Schema.Codec<Schema.Json>>> > }>, Schema.Struct<{ readonly type: Schema.Literal<"file"> readonly mediaType: Schema.String readonly fileName: Schema.optional<Schema.String> readonly data: Schema.Union<readonly [Schema.String, Schema.Uint8Array, Schema.URL]> readonly "~effect/ai/Prompt/Part": Schema.withDecodingDefaultKey< Schema.Literal<"~effect/ai/Prompt/Part"> > readonly options: Schema.withDecodingDefault< Schema.$Record<Schema.String, Schema.NullOr<Schema.Codec<Schema.Json>>> > }> ] > > ] > readonly "~effect/ai/Prompt/Message": Schema.withDecodingDefaultKey<Schema.Literal<"~effect/ai/Prompt/Message">> readonly options: Schema.withDecodingDefault< Schema.$Record<Schema.String, Schema.NullOr<Schema.Codec<Schema.Json>>> > }> readonly responseFormat: Schema.Union< readonly [ Schema.Struct<{ readonly type: Schema.Literal<"text"> }>, Schema.Struct<{ readonly type: Schema.Literal<"json">; readonly objectName: Schema.String }> ] > readonly steps: Schema.$Array< Schema.Struct<{ readonly parts: Schema.$Array< Schema.Union< readonly [ Schema.toEncoded< Schema.Struct<{ readonly type: Schema.tag<"text"> readonly text: Schema.String readonly "~effect/ai/Response/Part": Schema.withDecodingDefaultKey< Schema.tag<"~effect/ai/Response/Part"> > readonly metadata: Schema.withDecodingDefault< Schema.$Record<Schema.String, Schema.Codec<Schema.Json>> > }> >, Schema.toEncoded< Schema.Struct<{ readonly type: Schema.tag<"reasoning"> readonly text: Schema.String readonly "~effect/ai/Response/Part": Schema.withDecodingDefaultKey< Schema.tag<"~effect/ai/Response/Part"> > readonly metadata: Schema.withDecodingDefault< Schema.$Record<Schema.String, Schema.Codec<Schema.Json>> > }> >, Schema.Struct<{ readonly type: Schema.tag<"tool-call"> readonly id: Schema.String readonly name: Schema.String readonly params: Schema.Unknown readonly providerExecuted: Schema.optional<Schema.Boolean> readonly metadata: Schema.optional< Schema.$Record<Schema.String, Schema.NullOr<Schema.Codec<Schema.Json, Schema.Json, never, never>>> > }>, Schema.Struct<{ readonly type: Schema.tag<"tool-result"> readonly id: Schema.String readonly name: Schema.String readonly result: Schema.Unknown readonly isFailure: Schema.Boolean readonly providerExecuted: Schema.optional<Schema.Boolean> readonly preliminary: Schema.optional<Schema.Boolean> readonly metadata: Schema.optional< Schema.$Record<Schema.String, Schema.NullOr<Schema.Codec<Schema.Json, Schema.Json, never, never>>> > }>, Schema.toEncoded< Schema.Struct<{ readonly type: Schema.tag<"tool-approval-request"> readonly approvalId: Schema.String readonly toolCallId: Schema.String readonly "~effect/ai/Response/Part": Schema.withDecodingDefaultKey< Schema.tag<"~effect/ai/Response/Part"> > readonly metadata: Schema.withDecodingDefault< Schema.$Record<Schema.String, Schema.Codec<Schema.Json>> > }> >, Schema.toEncoded< Schema.Struct<{ readonly type: Schema.tag<"file"> readonly mediaType: Schema.String readonly data: Schema.Uint8ArrayFromBase64 readonly "~effect/ai/Response/Part": Schema.withDecodingDefaultKey< Schema.tag<"~effect/ai/Response/Part"> > readonly metadata: Schema.withDecodingDefault< Schema.$Record<Schema.String, Schema.Codec<Schema.Json>> > }> >, Schema.toEncoded< Schema.Struct<{ readonly type: Schema.tag<"source"> readonly sourceType: Schema.tag<"document"> readonly id: Schema.String readonly mediaType: Schema.String readonly title: Schema.String readonly fileName: Schema.optionalKey<Schema.String> readonly "~effect/ai/Response/Part": Schema.withDecodingDefaultKey< Schema.tag<"~effect/ai/Response/Part"> > readonly metadata: Schema.withDecodingDefault< Schema.$Record<Schema.String, Schema.Codec<Schema.Json>> > }> >, Schema.toEncoded< Schema.Struct<{ readonly type: Schema.tag<"source"> readonly sourceType: Schema.tag<"url"> readonly id: Schema.String readonly url: Schema.URLFromString readonly title: Schema.String readonly "~effect/ai/Response/Part": Schema.withDecodingDefaultKey< Schema.tag<"~effect/ai/Response/Part"> > readonly metadata: Schema.withDecodingDefault< Schema.$Record<Schema.String, Schema.Codec<Schema.Json>> > }> >, Schema.toEncoded< Schema.Struct<{ readonly type: Schema.tag<"response-metadata"> readonly id: Schema.optional<Schema.String> readonly modelId: Schema.optional<Schema.String> readonly timestamp: Schema.optional<Schema.DateTimeUtcFromString> readonly request: Schema.optional<typeof HttpRequestDetails> readonly "~effect/ai/Response/Part": Schema.withDecodingDefaultKey< Schema.tag<"~effect/ai/Response/Part"> > readonly metadata: Schema.withDecodingDefault< Schema.$Record<Schema.String, Schema.Codec<Schema.Json>> > }> >, Schema.toEncoded< Schema.Struct<{ readonly type: Schema.tag<"error"> readonly error: Schema.Unknown readonly "~effect/ai/Response/Part": Schema.withDecodingDefaultKey< Schema.tag<"~effect/ai/Response/Part"> > readonly metadata: Schema.withDecodingDefault< Schema.$Record<Schema.String, Schema.Codec<Schema.Json>> > }> >, Schema.Struct<{ readonly type: Schema.tag<"file-change"> readonly event: Schema.Literals<readonly ["create", "modify", "delete"]> readonly path: Schema.String readonly metadata: Schema.optional< Schema.$Record<Schema.String, Schema.NullOr<Schema.Codec<Schema.Json, Schema.Json, never, never>>> > }>, Schema.Struct<{ readonly type: Schema.tag<"compaction"> readonly trigger: Schema.Literals<readonly ["manual", "auto"]> readonly summary: Schema.String readonly kept: Schema.optional< Schema.$Array<Schema.toEncoded<Schema.Codec<Prompt.Message, Prompt.MessageEncoded, never, never>>> > readonly tokensBefore: Schema.optional<Schema.Int> readonly tokensAfter: Schema.optional<Schema.Int> readonly metadata: Schema.optional< Schema.$Record<Schema.String, Schema.NullOr<Schema.Codec<Schema.Json, Schema.Json, never, never>>> > }> ] > > readonly finish: Schema.optional< Schema.Struct<{ readonly type: Schema.tag<"finish-step"> readonly reason: Schema.Literals< ["stop", "length", "content-filter", "tool-calls", "error", "pause", "other", "unknown"] > readonly usage: Schema.toEncoded<typeof Usage> readonly metadata: Schema.optional< Schema.$Record<Schema.String, Schema.NullOr<Schema.Codec<Schema.Json, Schema.Json, never, never>>> > }> > }> > readonly agentState: Schema.optionalKey<Schema.Codec<Schema.Json, Schema.Json, never, never>> }> >}>Since v0.1.0
SessionState (interface)
Section titled “SessionState (interface)”Signature
export interface SessionState extends Schema.Schema.Type<typeof SessionState> {}Since v0.1.0
UnfinishedTurn
Section titled “UnfinishedTurn”A turn that suspendTurn cut short, kept so that the session can finish it later.
Signature
declare const UnfinishedTurn: Schema.Struct<{ readonly prompt: Schema.Struct<{ readonly role: Schema.Literal<"user"> readonly content: Schema.Union< readonly [ Schema.decodeTo< Schema.NonEmptyArray< Schema.toType< Schema.Struct<{ readonly type: Schema.Literal<"text"> readonly text: Schema.String readonly "~effect/ai/Prompt/Part": Schema.withDecodingDefaultKey< Schema.Literal<"~effect/ai/Prompt/Part"> > readonly options: Schema.withDecodingDefault< Schema.$Record<Schema.String, Schema.NullOr<Schema.Codec<Schema.Json>>> > }> > >, Schema.String, never, never >, Schema.$Array< Schema.Union< readonly [ Schema.Struct<{ readonly type: Schema.Literal<"text"> readonly text: Schema.String readonly "~effect/ai/Prompt/Part": Schema.withDecodingDefaultKey< Schema.Literal<"~effect/ai/Prompt/Part"> > readonly options: Schema.withDecodingDefault< Schema.$Record<Schema.String, Schema.NullOr<Schema.Codec<Schema.Json>>> > }>, Schema.Struct<{ readonly type: Schema.Literal<"file"> readonly mediaType: Schema.String readonly fileName: Schema.optional<Schema.String> readonly data: Schema.Union<readonly [Schema.String, Schema.Uint8Array, Schema.URL]> readonly "~effect/ai/Prompt/Part": Schema.withDecodingDefaultKey< Schema.Literal<"~effect/ai/Prompt/Part"> > readonly options: Schema.withDecodingDefault< Schema.$Record<Schema.String, Schema.NullOr<Schema.Codec<Schema.Json>>> > }> ] > > ] > readonly "~effect/ai/Prompt/Message": Schema.withDecodingDefaultKey<Schema.Literal<"~effect/ai/Prompt/Message">> readonly options: Schema.withDecodingDefault< Schema.$Record<Schema.String, Schema.NullOr<Schema.Codec<Schema.Json>>> > }> readonly responseFormat: Schema.Union< readonly [ Schema.Struct<{ readonly type: Schema.Literal<"text"> }>, Schema.Struct<{ readonly type: Schema.Literal<"json">; readonly objectName: Schema.String }> ] > readonly steps: Schema.$Array< Schema.Struct<{ readonly parts: Schema.$Array< Schema.Union< readonly [ Schema.toEncoded< Schema.Struct<{ readonly type: Schema.tag<"text"> readonly text: Schema.String readonly "~effect/ai/Response/Part": Schema.withDecodingDefaultKey< Schema.tag<"~effect/ai/Response/Part"> > readonly metadata: Schema.withDecodingDefault<Schema.$Record<Schema.String, Schema.Codec<Schema.Json>>> }> >, Schema.toEncoded< Schema.Struct<{ readonly type: Schema.tag<"reasoning"> readonly text: Schema.String readonly "~effect/ai/Response/Part": Schema.withDecodingDefaultKey< Schema.tag<"~effect/ai/Response/Part"> > readonly metadata: Schema.withDecodingDefault<Schema.$Record<Schema.String, Schema.Codec<Schema.Json>>> }> >, Schema.Struct<{ readonly type: Schema.tag<"tool-call"> readonly id: Schema.String readonly name: Schema.String readonly params: Schema.Unknown readonly providerExecuted: Schema.optional<Schema.Boolean> readonly metadata: Schema.optional< Schema.$Record<Schema.String, Schema.NullOr<Schema.Codec<Schema.Json, Schema.Json, never, never>>> > }>, Schema.Struct<{ readonly type: Schema.tag<"tool-result"> readonly id: Schema.String readonly name: Schema.String readonly result: Schema.Unknown readonly isFailure: Schema.Boolean readonly providerExecuted: Schema.optional<Schema.Boolean> readonly preliminary: Schema.optional<Schema.Boolean> readonly metadata: Schema.optional< Schema.$Record<Schema.String, Schema.NullOr<Schema.Codec<Schema.Json, Schema.Json, never, never>>> > }>, Schema.toEncoded< Schema.Struct<{ readonly type: Schema.tag<"tool-approval-request"> readonly approvalId: Schema.String readonly toolCallId: Schema.String readonly "~effect/ai/Response/Part": Schema.withDecodingDefaultKey< Schema.tag<"~effect/ai/Response/Part"> > readonly metadata: Schema.withDecodingDefault<Schema.$Record<Schema.String, Schema.Codec<Schema.Json>>> }> >, Schema.toEncoded< Schema.Struct<{ readonly type: Schema.tag<"file"> readonly mediaType: Schema.String readonly data: Schema.Uint8ArrayFromBase64 readonly "~effect/ai/Response/Part": Schema.withDecodingDefaultKey< Schema.tag<"~effect/ai/Response/Part"> > readonly metadata: Schema.withDecodingDefault<Schema.$Record<Schema.String, Schema.Codec<Schema.Json>>> }> >, Schema.toEncoded< Schema.Struct<{ readonly type: Schema.tag<"source"> readonly sourceType: Schema.tag<"document"> readonly id: Schema.String readonly mediaType: Schema.String readonly title: Schema.String readonly fileName: Schema.optionalKey<Schema.String> readonly "~effect/ai/Response/Part": Schema.withDecodingDefaultKey< Schema.tag<"~effect/ai/Response/Part"> > readonly metadata: Schema.withDecodingDefault<Schema.$Record<Schema.String, Schema.Codec<Schema.Json>>> }> >, Schema.toEncoded< Schema.Struct<{ readonly type: Schema.tag<"source"> readonly sourceType: Schema.tag<"url"> readonly id: Schema.String readonly url: Schema.URLFromString readonly title: Schema.String readonly "~effect/ai/Response/Part": Schema.withDecodingDefaultKey< Schema.tag<"~effect/ai/Response/Part"> > readonly metadata: Schema.withDecodingDefault<Schema.$Record<Schema.String, Schema.Codec<Schema.Json>>> }> >, Schema.toEncoded< Schema.Struct<{ readonly type: Schema.tag<"response-metadata"> readonly id: Schema.optional<Schema.String> readonly modelId: Schema.optional<Schema.String> readonly timestamp: Schema.optional<Schema.DateTimeUtcFromString> readonly request: Schema.optional<typeof HttpRequestDetails> readonly "~effect/ai/Response/Part": Schema.withDecodingDefaultKey< Schema.tag<"~effect/ai/Response/Part"> > readonly metadata: Schema.withDecodingDefault<Schema.$Record<Schema.String, Schema.Codec<Schema.Json>>> }> >, Schema.toEncoded< Schema.Struct<{ readonly type: Schema.tag<"error"> readonly error: Schema.Unknown readonly "~effect/ai/Response/Part": Schema.withDecodingDefaultKey< Schema.tag<"~effect/ai/Response/Part"> > readonly metadata: Schema.withDecodingDefault<Schema.$Record<Schema.String, Schema.Codec<Schema.Json>>> }> >, Schema.Struct<{ readonly type: Schema.tag<"file-change"> readonly event: Schema.Literals<readonly ["create", "modify", "delete"]> readonly path: Schema.String readonly metadata: Schema.optional< Schema.$Record<Schema.String, Schema.NullOr<Schema.Codec<Schema.Json, Schema.Json, never, never>>> > }>, Schema.Struct<{ readonly type: Schema.tag<"compaction"> readonly trigger: Schema.Literals<readonly ["manual", "auto"]> readonly summary: Schema.String readonly kept: Schema.optional< Schema.$Array<Schema.toEncoded<Schema.Codec<Prompt.Message, Prompt.MessageEncoded, never, never>>> > readonly tokensBefore: Schema.optional<Schema.Int> readonly tokensAfter: Schema.optional<Schema.Int> readonly metadata: Schema.optional< Schema.$Record<Schema.String, Schema.NullOr<Schema.Codec<Schema.Json, Schema.Json, never, never>>> > }> ] > > readonly finish: Schema.optional< Schema.Struct<{ readonly type: Schema.tag<"finish-step"> readonly reason: Schema.Literals< ["stop", "length", "content-filter", "tool-calls", "error", "pause", "other", "unknown"] > readonly usage: Schema.toEncoded<typeof Usage> readonly metadata: Schema.optional< Schema.$Record<Schema.String, Schema.NullOr<Schema.Codec<Schema.Json, Schema.Json, never, never>>> > }> > }> > readonly agentState: Schema.optionalKey<Schema.Codec<Schema.Json, Schema.Json, never, never>>}>Since v0.1.0
UnfinishedTurn (interface)
Section titled “UnfinishedTurn (interface)”Signature
export interface UnfinishedTurn extends Schema.Schema.Type<typeof UnfinishedTurn> {}Since v0.1.0
Transforming
Section titled “Transforming”recordStep
Section titled “recordStep”conversation with step recorded after it, as SessionState.history records a conversation:
an assistant message with what the model wrote and the tools it called, followed by a tool
message with the results, and none for a step that wrote nothing and called none.
Details
A step that compacted the conversation replaces it, and what the step had before, with the conversation the compaction left, so that the conversation is the one the agent goes on with.
It is for a caller that keeps a session’s steps itself, as a log, and rebuilds the conversation
to give Harness.createSession as history, rather than storing it whole with every turn.
Example (Rebuilding a conversation from logged steps)
import { Session, StreamPart } from "@amends-dev/harness"import { Prompt } from "effect/ai"
declare const prompt: Prompt.UserMessagedeclare const steps: ReadonlyArray<StreamPart.Step> // as StreamPart.stepsOf adds a turn up
const history = steps.reduce(Session.recordStep, Prompt.fromMessages([prompt]))Signature
declare const recordStep: (conversation: Prompt.Prompt, step: StreamPart.Step) => Prompt.PromptSince v0.1.0
Utility types
Section titled “Utility types”TurnError (type alias)
Section titled “TurnError (type alias)”What a turn fails with: the harness’s errors, and those of the host’s tools the agent called.
Signature
type TurnError<Tools extends Record<string, Tool.Any>> = HarnessError.HarnessError | AiError.AiError | Tool.HandlerError<Tools[keyof Tools]>Since v0.1.0
TurnServices (type alias)
Section titled “TurnServices (type alias)”What the host’s tools that a turn can call need.
Signature
type TurnServices<Tools extends Record<string, Tool.Any>> = Tool.HandlerServices<Tools[keyof Tools]>Since v0.1.0