NOIR
Get started

Authoring modes

Choose between the concise provider-native loop, an existing framework handler, and the low-level model adapter.

Noir supports three authoring modes. They share the same channel, store, approval, checkpoint, and outbox semantics. Choose based on who should own model execution.

Provider-native with noir()

Use this for a new agent. Pass a current AI SDK language model directly and organize external systems in the connector tree.

export default noir({
  id: 'operator',
  instructions: 'Use tools for facts. Be concise.',
  model: openrouter('openai/gpt-5-mini'),
  channels: { slack: slack() },
  connectors: { posthog: posthog() },
})

Noir owns the assistant loop, tool dispatch, result projection, failure repair, memory recall, and final output.

Existing framework with handle

Use this when another library, service, or workflow already produces the answer. Noir remains the message and reliability layer.

export default defineAgent({
  id: 'wrapped-agent',
  channels: { slack: slack() },
  async handle(message, noir) {
    const answer = await existingAgent.run(message.text)
    const saved = await noir.run('save-answer', () =>
      saveAnswer(answer, { idempotencyKey: `${noir.executionId}:save-answer` }),
    )
    return `Saved ${saved.id}`
  },
})

The handler receives a normalized Message and a Noir context. Returning text is shorthand for durable delivery. Use noir.ask for a requester-bound pause and noir.run for a replayable JSON checkpoint.

Low-level ModelAdapter

Use this when a provider does not implement the AI SDK or you need exact control over message conversion.

const model: ModelAdapter = {
  id: 'internal-model',
  async generate({ messages, tools, signal }) {
    return callInternalModel({ messages, tools, signal })
  },
}

The adapter returns assistant content and optional tool calls. Noir validates tool inputs and owns the rest of the loop.

Selection guide

SituationMode
new agent using an AI SDK providernoir()
existing LangGraph, Mastra, custom loop, or HTTP servicedefineAgent({ handle })
proprietary or unsupported model protocoldefineAgent({ model })

Do not wrap a provider-native model in a custom adapter merely for style. Do not move existing orchestration into Noir unless the simpler built-in loop serves the same behavior.

On this page