My App

Fragola Class

Fragola API Reference

Constructor

constructor(private clientOptions: ClientOptions, private globalStore: Store<TGlobalStore> | undefined = undefined, sdkClass: typeof OpenAI = OpenAI)

Properties

sdkClass

Returns the OpenAI SDK constructor used by this Fragola instance.

get sdkClass()

sdk

Returns the OpenAI client instance created from this Fragola configuration.

get sdk()

options

Returns the client options configured on this Fragola instance.

get options()

store

Acess the instance default global store.

get store(): Store<TGlobalStore> | undefined

Methods

setSdkOpts

Set the options for the underlying sdk. Can be used to refresh the options. Will re-instanciate the sdk

setSdkOpts(clientOptions: OpenaiClientOptions)

agent

Create a new agent attached to this Fragola instance.

The returned agent uses this instance's client configuration and can access its global store.

  • opts: Agent configuration. Returns: A new agent instance.

Example

```ts
const fragola = new Fragola({
  model: "gpt-5.4"
});

const agent = fragola.agent({
  name: "assistant",
  description: "Minimal assistant",
  instructions: "You are a helpful assistant."
});

```typescript
agent<TMetaData extends DefineMetaData<any> = {}, TStore = {}>(opts: CreateAgentOptions<TStore>): Agent<TMetaData, TGlobalStore, TStore>

getStore

Returns the instance (global) store or a namespaced store casted as T. Recommended when accessing the store from outside an agent store.

  • namespace: The namespace of the store to access (optional).
getStore<T extends StoreLike<any> = {}>(namespace?: string): Store<T> | undefined

addStore

Add a namespaced store to the Fragola instance so agents can access it via getStore(namespace).

  • store: The store to add (must have a namespace defined).
addStore(store: Store<any>): void

removeStore

Remove a namespaced store from the Fragola instance.

  • namespace: The namespace of the store to remove
removeStore(namespace: string): void

json

Runs a one-off JSON extraction with a temporary agent.

If options is omitted, Fragola creates a minimal extraction agent for this call and returns only the schema parse result.

  • query: The extraction prompt and Zod schema.
  • options: Optional agent options to use instead of the default extraction agent. Returns: The Zod safe-parse result for the assistant response.

Example

```ts
const result = await fragola.json({
  content: "my name is Ada",
  name: "extract_person",
  schema,
});

if (result.success) {
  console.log(result.data);
}

```typescript
async json<S extends ZodSchema = ZodSchema>(query: JsonQuery<S>, options: CreateAgentOptions | undefined = undefined): Promise<SafeParseResult<unknown, Infer<S>>>

Agent Class

Agent API Reference

Constructor

constructor(
        private opts: CreateAgentOptions<TStore>,
        private globalStore: Store<TGlobalStore> | undefined = undefined,
        openai: OpenAI,
        forkOf: string | undefined = undefined,
        instance: Fragola,
        state = Agent.defaultAgentState as AgentState<TMetaData>,
    )

Properties

defaultAgentState

public static defaultAgentState: AgentState = {
        messages: [],
        stepCount: 0,
        status: "idle"
    }

forkOf

Returns the parent agent id when this agent was created with fork().

get forkOf()

context

public context = (() => {
        const _this = this;
        return new class extends AgentContext<TMetaData, TGlobalStore, TStore> {
            get state() { return _this.state; }
            get options() { return _this.options; }
            get raw() {
                return {
                    updateMessages: (...args: Parameters<typeof _this.updateMessages>) => _this.updateMessages(...args)
                };
            }
            get store() { return _this.opts.store as Store<TStore>; }
            get messagesParser() { return messagesUtils<TMetaData>(() => _this.state.messages); }
            get instance() { return _this.#instance }
            getStore<T extends StoreLike<any>>(namespace?: string): Store<T> | undefined {
                let context = namespace ? _this.#namespaceStore.get(namespace) : _this.options.store;
                if (context)
                    return context as unknown as Store<T>;
                return undefined;
            }
            addStore(store: Store<any>): void {
                _this.addStore(store);
            }
            removeStore(namespace: string): void {
                _this.removeStore(namespace);
            }
            get systemPrompt(): string {
                return _this.mergedInstructions;
            }
            setInstructions(instructions: string, scope?: string): void {
                // If a scope is provided, context scoped instructions, otherwise update the default instructions
                if (scope) {
                    if (scope == '*')
                        throw new BadUsage("'*' is a reserved instructions scope and cannot be used.");
                    _this.instructionScopes.set(scope, instructions);
                } else {
                    _this.opts.instructions = instructions;
                }
                // Refresh cached merged instructions
                _this.updateMergedInstructionsCache();
            }
            instructions(scope?: string): string | undefined {
                if (scope) {
                    if (scope == '*')
                        return _this.mergedInstructions;
                    return _this.instructionScopes.get(scope);
                }
                return _this.opts.instructions ?? undefined;
            }
            removeInstructions(scope: string): boolean {
                if (scope == '*')
                    throw new BadUsage("'*' is a reserved instructions scope and cannot be removed.");
                const existed = _this.instructionScopes.delete(scope);
                if (existed)
                    _this.updateMergedInstructionsCache();
                return existed;
            }
            setOptions(options: SetOptionsParams): void {
                _this.setOptions(options);
            }
            async stop(): Promise<{ [STOP]: true }> {
                await _this.stop();
                return {
                    [STOP]: true
                }
            }
            stopSync(): { [STOP]: true } {
                _this.stopSync();
                return {
                    [STOP]: true
                }
            }
            updateTools(callback: (prev: Tool[]) => Tool[]): void {

                const updatedTools = callback(_this.opts.tools ?? []);
                _this.opts.tools = updatedTools;
                _this.toolsToModelSettingsTools();
            }
        }
    })()

[FORK_FRIEND]

[FORK_FRIEND] = {
        setRegisteredEvents: this.setRegisteredEvents,
        getRegisteredEvents: () => this.registeredEvents
    }

id

Returns the unique id of this agent instance.

get id()

state

Returns the current in-memory state for this agent.

get state()

options

Returns the current configuration options for this agent.

get options()

Methods

fork

Creates a new agent from the current one.

The fork gets a new id, copies the current options, state, hooks, and registered events, and sets forkOf to this agent's id. The OpenAI client and global context are shared. If this agent has a local context, the fork receives a new context instance seeded with the same value.

Returns: A new agent initialized from the current agent.

fork()

setOptions

Updates the agent's options. Note: Can only be called when agent status is "idle". The name and messages properties are omitted.

  • options: The new options to set, as a SetOptionsParams object. Throws: BadUsage When called while agent is not idle (generating or waiting).
setOptions(options: SetOptionsParams)

init

Waits for all pending hook setup to finish without executing a step.

async init()

step

Continues execution from the current message history for one or more steps.

Use this when messages were seeded manually or when you want to continue an in-progress model/tool loop without appending a new user message first.

  • stepParams: Optional per-call limits and step overrides. Returns: The updated agent state after execution completes.

Example

```ts
const agent = fragola.agent({
  name: "assistant",
  description: "Minimal assistant",
  instructions: "You are a helpful assistant",
  messages: [{ role: "user", content: "Say hello once." }],
});

await agent.step();

```typescript
async step(stepParams?: StepParams)

resetStepCount

Resets the internal step counter to 0 without changing messages or status.

resetStepCount()

reset

Resets the agent to an idle state and replaces its message history.

  • params: Optional replacement messages for the new state. Throws: BadUsage When called while the agent is not idle.
reset(params: ResetParams = { messages: [] })

stop

Stops the current agent execution. This will abort any ongoing API calls and prevent further tool execution.

async stop()

stopSync

Requests cancellation of the current run without awaiting completion.

stopSync()

json

Appends a user message, requests structured JSON output, and validates it against a Zod schema.

  • query: The user prompt, schema, and optional per-call step overrides. Returns: The schema validation result together with the final agent state.

Example

```ts
const result = await agent.json({
  content: "my name is Ada",
  name: "extract_person",
  schema,
});

if (result.success) {
  console.log(result.data);
}

```typescript
async json<S extends ZodSchema = ZodSchema>(query: JsonQuery<S>): Promise<JsonResult<S, TMetaData>>

userMessage

Appends a user message to the messages and executes the agent for one or more steps. Parameters:

  • query: The user message and optional per-call step controls. See UserMessageQuery

Returns: Promise<AgentState> - The updated agent state after processing the message and any model/tool steps. Example

// 1) Minimal text message
await agent.userMessage({ content: "Say hello" });

// 2) Multi-part content (text + image)
await agent.userMessage({
  content: [
    { type: "text", text: "What's in this image?" },
    { type: "image_url", image_url: { url: "https://example.com/cat.png" } }
  ]
});

// 3) Limit the number of steps for this turn
await agent.userMessage({
  content: "Compute 2+2, then stop.",
  step: { by: 1 } // will stop execution after 1 turn (1 llm response maximum)
});

// 4) Override model settings for this call (without changing agent defaults)
await agent.userMessage({
  content: "Answer concisely.",
  step: { modelSettings: { temperature: 0 } }
});
     
async userMessage(query: UserMessageQuery<TMetaData>): Promise<AgentState>

watch

Register a state watcher for a given watch event id. Returns an unsubscribe function that removes the registered watcher.

Example

const off = agent.watch('state', ({ context }) => {
  console.log('State updated:', context.state);
});
// later
off();
     
watch<TEventId extends AgentEventWatchId>(eventId: TEventId, callback: eventIdToCallback<TEventId, TMetaData, TGlobalStore, TStore>)

watchState

Register a handler that watches agent state updates.

State watchers do not return a value and cannot intercept or mutate state. Use these for side-effects such as metrics, logging, or UI updates.

Example

agent.watchState(({ context }) => {
  console.log('stepCount', context.state.stepCount);
});
     
watchState(callback: EventWatchState<TMetaData, TGlobalStore, TStore>)

on

Register a handler for a given event id. Returns an unsubscribe function that removes the registered handler.

Example

// listen to userMessage events
const off = agent.on('userMessage', (message, context) => {
  // inspect or transform the message
  return { ...message, content: message.content.trim() };
});
// later
off();
     
on<TEventId extends AgentOnEventId>(eventId: TEventId, callback: eventIdToCallback<TEventId, TMetaData, TGlobalStore, TStore>)

onToolCall

Register a tool result handler.

This event runs after before:toolCall resolves the current config and after the tool handler (or an injected result) produces a payload. Each callback receives the current payload and may transform it before it is exposed to later toolCall handlers, after:toolCall, and the tool message appended to state.

Example

agent.onToolCall(({ result, params, tool }) => {
  if (tool.name !== "getWeather") return result;
  if (!result.success) return result;
  return {
    success: true,
    data: {
      requestedLocation: params.location,
      data: result.data,
    },
  };
});
     
onToolCall(callback: EventToolCall<TMetaData, TGlobalStore, TStore>)

onAiMessage

Register an assistant message handler.

This event runs for streamed partial assistant messages and for the final assistant message. During streaming, finish_reason is null until the stream completes.

Return a new assistant message to replace the current one.

Example

agent.onAiMessage(({ message, finish_reason }) => {
  if (finish_reason === null) return message;
  if (typeof message.content !== "string") return message;
  return {
    ...message,
    content: message.content.trim() + "\n\n(checked)",
  };
});
     
onAiMessage(callback: EventAiMessage<TMetaData, TGlobalStore, TStore>)

onUserMessage

Register a user message event handler.

Called when a user message is appended to the messages. Handlers may return a modified user message which will be used instead of the original.

Example

agent.onUserMessage(({ message, context }) => {
  // enrich user message with metadata
  return { ...message, content: message.content.trim() };
});
     
onUserMessage(callback: EventUserMessage<TMetaData, TGlobalStore, TStore>)

onBeforeStep

Register a before step event handler.

Called before a step is executed.

Example

agent.onBeforeStep(({ options, context }) => {
  console.log('Before step', options);
});
     
onBeforeStep(callback: EventBeforeStep<TMetaData, TGlobalStore, TStore>)

onAfterStep

Register an after step event handler.

Called after a step is executed.

Example

agent.onAfterStep(({ options, newMessages, stepsTaken, context }) => {
  console.log('After step', options, newMessages, stepsTaken);
});
     
onAfterStep(callback: EventAfterStep<TMetaData, TGlobalStore, TStore>)

onBeforeModelInvocation

Register a handler that can alter model invocation config before the request is made.

The callback receives the current invocation config and may:

  • return { modelSettings, clientOptions } to override the request settings
  • return { injectMessage } to bypass the API call with a final assistant message
  • return { injectResponse } to provide a custom SDK response

Example

agent.onBeforeModelInvocation(() => ({
  injectMessage: { content: "hello from cache" },
}));
     
onBeforeModelInvocation(callback: EventBeforeModelInvocation<TMetaData, TGlobalStore, TStore>)

onAfterModelInvocation

Register an after model invocation event handler.

Called after the model is invoked.

Example

agent.onAfterModelInvocation(({ message, context }) => {
  console.log('After model invocation', message);
});
     
onAfterModelInvocation(callback: EventAfterModelInvocation<TMetaData, TGlobalStore, TStore>)

onBeforeToolCall

Register a handler that can alter a tool call before execution.

The callback receives { params } before the tool handler runs. It may return a new { params } object to rewrite validated arguments or { injectConfig } with a full ToolCallPayload to bypass the handler entirely.

Example

agent.onBeforeToolCall(({ config, tool }) => {
  if (tool.name !== "search" || !("params" in config)) return config;
  return { params: { ...config.params, limit: 5 } };
});
     
onBeforeToolCall(callback: EventBeforeToolCall<TMetaData, TGlobalStore, TStore>)

onAfterToolCall

Register an after tool call event handler.

Called after a tool payload is finalized.

Example

agent.onAfterToolCall(({ result, params, tool, context }) => {
  console.log('After tool call', tool.name, result);
});
     
onAfterToolCall(callback: EventAfterToolCall<TMetaData, TGlobalStore, TStore>)

onModelInvocation

Register a model invocation handler.

Use this event to inspect or transform data produced by the model before it is turned into the assistant message stored in state.

Handle the payload as a discriminated union by checking invocation.kind:

  • "chunk": streamed delta payload. The callback receives { kind, chunk, primaryChoice, delta } before that chunk is merged into the partial assistant message.
  • "completion": full assistant message payload. The callback receives { kind: "completion", data }, where data is the complete assistant message.

For kind === "chunk", you may:

  • return the raw chunk to replace it directly
  • return { injectChunk, merge?: true } to update the whole chunk
  • return { injectPrimary, merge?: true } to update choices[0]
  • return { injectDelta, merge?: true } to update choices[0].delta
  • set merge: false on any inject* object to replace that target instead of merge-patching it

For kind === "completion", return an updated message object to replace it.

Example

agent.onModelInvocation((payload) => {
  if (payload.kind === "completion") {
    // payload.data.content can be in some cases an array
    if (typeof payload.data.content !== "string") return payload.data;
    return {
      ...payload.data,
      content: payload.data.content.trim(),
    };
  }

  if (!payload.delta?.content) return payload.chunk;

  return {
    injectDelta: {
      content: invocation.delta.content.replace("[DEBUG]", ""),
    },
  };
});
     
onModelInvocation(callback: EventModelInvocation<TMetaData, TGlobalStore, TStore>)

use

Attach a hook to this agent.

Hooks receive the agent instance and may register event handlers or otherwise augment the agent's behavior.

  • hook: A FragolaHook to attach to the agent Returns: The agent instance (chainable)

Example

```ts
import { Hook } from "@fragola-ai/agent/hook";

const loggingHook = Hook((agent) => {
  agent.watchState(({ context }) => {
    console.log(context.state.status);
  });
});

const agent = fragola.agent({...}).use(loggingHook, "logging");
// agent is returned so additional configuration/calls can be chained

```typescript
use(hook: FragolaHook, name?: string)

hasHook

Returns whether a named hook is registered. Pending hooks count as registered.

hasHook(name: string): boolean

removeHook

Removes a named hook, waits for pending setup to finish, and runs its disposer.

async removeHook(name: string): Promise<void>

dispose

Removes every hook currently registered on the agent.

async dispose(): Promise<void>

AgentContext Class

AgentContext API Reference

Properties

state

The current state of the agent.

abstract get state(): AgentState<TMetaData>

options

The configuration options for the agent.

abstract get options(): AgentOptions

raw

Raw methods for advanced context manipulation

abstract get raw(): ContextRaw<TMetaData>

store

Acess the agent's default local store.

abstract get store(): Store<TStore>

messagesParser

Live parser helpers bound to the current state.messages.

abstract get messagesParser(): MessagesParser<TMetaData>

instance

Return the Fragola instance which created this agent

abstract get instance(): Fragola<TGlobalStore>

systemPrompt

Returns the agent system prompt exactly as sent to the llm. The system prompt is the result of all the instructions scopes merged

abstract get systemPrompt(): string

Methods

addStore

Add a store that has a namespace. Can be accessed with getStore method.

  • store: The store to add
abstract addStore(store: Store<any>): void

updateTools

Updates the agent's tool list using a callback.

  • callback: Function that receives the current tools and returns the updated list.

Example

agent.context.updateTools(prev => [...prev, newTool]);
agent.context.updateTools(prev => prev.filter(tool => tool.name !== "oldTool"));
     
abstract updateTools(callback: (prev: Tool[]) => Tool[]): void

removeStore

Remove a store by its namespace.

  • namespace: The namespace of the store to remove
abstract removeStore(namespace: string): void

getStore

Returns the agent's local store or namespace store casted as T. Recommended when accessing the store from a hook.

  • namespace: The namespace of the store to access (optional).
abstract getStore<T extends StoreLike<any> = {}>(namespace?: string): Store<T> | undefined

setInstructions

Sets the current instructions for the agent.

  • instructions: The new instructions as a string.
abstract setInstructions(instructions: string, scope?: string): void

instructions

Returns the instructions for a given scope.

  • scope: The instructions scope, leave empty to get the default scope (optional)
abstract instructions(scope?: string): string | undefined

removeInstructions

Remove the instructions for a given scope.

  • scope: The instructions scope to remove Returns: a boolean, true = removed, false = scope do not exist
abstract removeInstructions(scope: string): boolean

setOptions

Updates the agent's options. note: the name, fork and messages properties are ommited

  • options: The new options to set, as a SetOptionsParams object.
abstract setOptions(options: SetOptionsParams): void

stop

Stop the agent execution

abstract stop(): Promise<{[STOP]: true}>

stopSync

Stop the agent execution - Sync

abstract stopSync(): {[STOP]: true}

Events & Payloads

Events & Payloads

Coming soon.

Store & Utility Functions

On this page