Maniac Docs
Agents

Running Agents

runAgent, chat, chatStream, thread scoping, budgets, structured output, and pause/resume.

The SDK exposes both stateless runners and Maniac chat APIs. All paths share the same runner loop and return an AgentResult.

Stateless runners

import { runAgent, runAgentStream, runAgentResume, runAgentResumeStream } from "@maniac-ai/agents";

// Single turn — caller owns history
const result = await runAgent(spec, "Hello", {
  prefixMessages: priorMessages,
  signal: abortController.signal
});

// Streaming — yields trace events, then AgentResult as final item
for await (const item of runAgentStream(spec, "Hello")) {
  if ("kind" in item) renderEvent(item);
  if ("final" in item) console.log(item.final);
}

// Resume after approval pause
const resumed = await runAgentResume(spec, checkpoint, approvalResponses);

RunOptions includes prefixMessages, threadId (for trace correlation), tracer, and signal (AbortSignal for cooperative cancel).

StreamEnvelope (resume)

runAgentResumeStream and Maniac.resumeStream emit tagged envelopes:

for await (const env of runAgentResumeStream(spec, checkpoint, responses)) {
  if (env.type === "event") render(env.event);
  else handleResult(env.result);
}

Maniac chat APIs

const result = await app.chat("support", "Hello", {
  threadId: "thread-1",
  resourceId: "user-42",
  memoryAgentId: "support",  // optional stable memory namespace
  signal: abortController.signal
});

for await (const envelope of app.chatStream("support", "Hello", { threadId: "thread-1" })) {
  if (envelope.type === "event") { /* token, tool, step, ... */ }
  else console.log(envelope.result.final);
}

threadId is required — it scopes ConversationStore load/save. resourceId activates cross-thread observational memory when observationalMemory.scope === "resource".

Partial persistence on error

By default (persistPartialOnError: true), chat / chatStream persist partial transcripts when a run errors or is interrupted. Unanswered tool_call IDs receive synthetic failure observations so the next turn stays well-formed for tool-calling providers. Set persistPartialOnError: false to restore v0.2 drop-on-error behavior.

Threads

Thread identity flows through three layers:

  1. threadId in ChatOptions — conversation store key (agent:<id>/threads/<threadId>/...)
  2. thread_id on trace events — propagated from RunOptions.threadId for UI correlation
  3. resourceId — optional user identity for cross-thread observational memory
// Same thread — history carries forward
await app.chat("support", "My order is ord_123", { threadId: "sess-a" });
await app.chat("support", "When does it arrive?", { threadId: "sess-a" });

// Different thread — fresh history
await app.chat("support", "New topic", { threadId: "sess-b" });

Budgets

Budgets enforce limits during the agent loop. Set on the agent spec or app-level budget:

const spec: Agent = {
  id: "bounded",
  instructions: "...",
  model,
  budget: {
    max_iterations: 16,
    max_tokens: 32_000,
    max_cost_usd: 0.50,
    max_wall_seconds: 60
  }
};

On overrun the runner emits a structured error event and returns status: "errored". Token and cost counters accumulate across all LM calls in the run (including sub-agents).

Structured output

Pass a Zod schema or JSON Schema as output_model. The runner validates the assistant's final text after the tool loop completes:

import { z } from "zod";
import { runAgent } from "@maniac-ai/agents";

const OutputSchema = z.object({
  answer: z.string(),
  count: z.number().int()
});

const result = await runAgent(
  {
    id: "assistant",
    instructions: "Respond with JSON matching the Output schema.",
    model,
    output_model: OutputSchema,
    output_repair_attempts: 1
  },
  "go"
);

expect(result.status).toBe("completed");
expect(result.output).toEqual({ answer: "ok", count: 3 });

Repair loop

When validation fails and output_repair_attempts > 0, the runner emits a step event with phase: "repair" and asks the model to correct its answer. Exhausting repairs yields status: "errored".

Fenced JSON (```json ... ```) is stripped before validation.

Pause and resume

Runs with pending approvals return status: "paused" and a checkpoint. Resume with runAgentResume / Maniac.resume after the operator supplies ApprovalResponse values. See Permissions for HITL flows.

On this page