invoke()

In shortReference for the AgentFlowClient.invoke() method — send messages and receive the final state.

  • 5 min read
  • 11 sections
  • Updated
  • v0.9.2
  • Markdown

client.invoke() sends a list of messages to the agent graph and awaits the final response. It automatically handles the remote tool call loop — if the server responds with RemoteToolCallBlock entries, the client executes the registered handlers and re-invokes the graph until the agent returns a final text response or the recursion_limit is reached.

Use invoke() when you want a complete, final answer before updating the UI. For incremental updates use stream().

Endpoint: POST /v1/graph/invoke
Source: src/endpoints/invoke.ts


Signature

TypeScript
client.invoke(
  messages: Message[],
  options?: InvokeOptions
): Promise<InvokeResult>

Parameters

messages

An array of Message objects to send. The most recent message is typically the user’s input. You can include a system message to set the agent’s persona:

TypeScript
const systemMsg = Message.text_message('You are a concise assistant.', 'system');
const userMsg   = Message.text_message('What is 2 + 2?');

const result = await client.invoke([systemMsg, userMsg]);

The client serialises each Message before sending it. If message.message_id is null or undefined, the serialised payload uses "0" — the server replaces this with a real ID.

options (optional)

Field Type Default Description
initial_state Record<string, any> undefined Initial state values to seed the graph before execution. Keys must match the graph’s state schema.
config Record<string, any> undefined Run configuration. The server reads thread_id from the top level of this object: { thread_id: 'my-thread' }. When thread_id is set the server checkpoints state between calls; omitting it makes the server generate a fresh thread id for the run. A nested configurable key is not read and is silently ignored.
recursion_limit number 25 Maximum number of invoke loop iterations. Each iteration is one call to /v1/graph/invoke. When the limit is reached InvokeResult.recursion_limit_reached is true.
response_granularity 'full' | 'partial' | 'low' 'full' Controls how much data the server returns per call. See Response granularity below.
onPartialResult InvokeCallback undefined Callback invoked after each loop iteration with intermediate results. Useful for showing progress in the UI without full streaming.

Return value: InvokeResult

TypeScript
interface InvokeResult {
  messages: Message[];             // Final messages from the last iteration
  state?: AgentState;              // Final graph state
  context?: Message[];             // Relevant context messages (RAG results, etc.)
  summary?: string | null;         // Optional LLM-generated conversation summary
  meta: InvokeMetadata;            // Thread ID and is_new_thread flag
  all_messages: Message[];         // All messages across all iterations (including tool results)
  iterations: number;              // How many invoke loop iterations ran
  recursion_limit_reached: boolean; // true if iterations hit the recursion_limit
}

InvokeMetadata

TypeScript
interface InvokeMetadata {
  is_new_thread: boolean;  // true if this call created a new thread
  thread_id: string;       // The thread ID used for this invocation
}

Use meta.thread_id if you want to continue the conversation in a later call:

TypeScript
const first = await client.invoke([userMsg], {
  config: { thread_id: 'conv-001' },
});

const followUp = await client.invoke(
  [Message.text_message('Tell me more')],
  { config: { thread_id: first.meta.thread_id } }
);

Response granularity

The response_granularity option controls what the server includes in each response payload:

Value State Context Summary When to use
'full' ✅ ✅ ✅ When you need complete state inspection or debugging.
'partial' ✅ ✅ ❌ When you need state but not the summary. Slightly faster.
'low' ❌ ❌ ❌ When you only need the final messages. Best for production chat UIs.

InvokeCallback

The onPartialResult callback receives an InvokePartialResult after each loop iteration:

TypeScript
interface InvokePartialResult {
  iteration: number;
  messages: Message[];
  state?: AgentState;
  context?: Message[];
  summary?: string | null;
  meta: InvokeMetadata;
  has_tool_calls: boolean;  // true if this iteration returned remote tool calls
  is_final: boolean;        // true if this is the last iteration
}

type InvokeCallback = (partial: InvokePartialResult) => void | Promise<void>;

Example — show a “thinking…” indicator while waiting for the agent:

TypeScript
const result = await client.invoke([userMsg], {
  onPartialResult(partial) {
    if (partial.has_tool_calls) {
      console.log(`Iteration ${partial.iteration}: running tools…`);
    }
    if (partial.is_final) {
      console.log('Final answer received');
    }
  },
});

Remote tool call loop

When server-configured remote tools have matching client.registerToolHandler() handlers and the agent requests one, the loop works like this:

  1. Client sends the initial messages to POST /v1/graph/invoke.
  2. Server responds. If any RemoteToolCallBlock is in the response messages, the client intercepts them.
  3. Client maps each RemoteToolCallBlock.name to its registered ToolHandler and executes the handlers locally.
  4. Client wraps the handler results in ToolResultBlock objects and sends them back to /v1/graph/invoke as tool role messages.
  5. Steps 2–4 repeat until the server returns no more RemoteToolCallBlock entries or recursion_limit is reached.

See Register remote tools for how to register handlers.


InvokeRequest (advanced)

If you are calling the invoke endpoint directly (not through AgentFlowClient) the request body has this shape:

TypeScript
interface InvokeRequest {
  messages: any[];                           // Serialised Message objects
  initial_state?: Record<string, any>;
  config?: Record<string, any>;
  recursion_limit?: number;                  // default 25
  response_granularity?: 'full' | 'partial' | 'low';
}

Code examples

Minimal invoke

TypeScript
import { AgentFlowClient, Message } from '@10xscale/agentflow-client';

const client = new AgentFlowClient({ baseUrl: 'http://localhost:8000' });

const result = await client.invoke([
  Message.text_message('What is the capital of Japan?'),
]);

console.log(result.messages[0].content[0]);
// { type: 'text', text: 'The capital of Japan is Tokyo.' }

Persistent thread

TypeScript
const THREAD_ID = 'user-123-session-456';

async function chat(userInput: string) {
  const result = await client.invoke(
    [Message.text_message(userInput)],
    {
      config: { thread_id: THREAD_ID },
      response_granularity: 'low',
    }
  );

  // Extract the assistant's text response
  const assistantMsg = result.messages.find(m => m.role === 'assistant');
  const textBlock = assistantMsg?.content.find(b => b.type === 'text');
  return (textBlock as any)?.text ?? '';
}

await chat('Hello!');
await chat('Tell me a joke.');  // Remembers the previous message

With initial state

TypeScript
const result = await client.invoke(
  [Message.text_message('Continue where we left off')],
  {
    initial_state: {
      user_preferences: { language: 'en', theme: 'dark' },
    },
    config: { thread_id: 'thread-789' },
  }
);

Common errors

Error Cause Fix
AgentFlowError status 401 Missing or invalid auth token. Set auth in AgentFlowConfig.
AgentFlowError status 422 Invalid request payload (e.g. empty messages array, bad config shape). Check that messages is non-empty and all required fields are correct.
AgentFlowError status 500 Server-side error during graph execution. Check the server logs with agentflow api --verbose.
Request timeout The graph took longer than config.timeout. Increase timeout in AgentFlowConfig or optimise the graph.
recursion_limit_reached: true The tool call loop ran more than recursion_limit iterations. Increase recursion_limit in options, or check that tools are completing without infinite loops.

What you learned

  • invoke() sends messages and returns when the agent produces a final response.
  • The config.thread_id option enables persistent conversations with checkpointing.
  • response_granularity: 'low' is the most efficient setting for chat UIs.
  • The onPartialResult callback lets you react to each iteration without requiring full streaming.
  • The remote tool call loop is handled automatically — register matching handlers with registerToolHandler().

Next step

See reference/client/stream to learn the stream() method for token-by-token streaming.

Last updated for v0.9.2Edit this page on GitHubReport an issue