Agent SDK · Reference

Recipes.

Six runnable examples, from a device-flow CLI agent to a multi-agent review pipeline.

Recipe 1: CLI agent with device-flow auth and streaming

A complete interactive CLI agent with device-flow auth, streaming, and multi-turn sessions.

TypeScript
import * as readline from 'node:readline/promises';
import { stdin, stdout } from 'node:process';
import { Runner, MatildaCore, createFileTokenStore, createSession } from '@maincode-ai/matilda-agent-sdk';
import { homedir } from 'node:os';
import { join } from 'node:path';

const { store, lock } = createFileTokenStore(join(homedir(), '.matilda', 'tokens.json'));

const runner = new Runner({
  core: new MatildaCore({ baseUrl: 'https://matilda.maincode.com/api' }),
});

// Try to restore persisted tokens, fall back to interactive login
const restored = await runner.auth.restore({ clientId: 'matilda-code', tokenStore: store, tokenLock: lock });
if (!restored) {
  console.log('Starting device flow authentication...');
  await runner.auth.loginWithDeviceFlow({ clientId: 'matilda-code', tokenStore: store, tokenLock: lock });
  console.log('Authenticated!');
}

const session = createSession({
  name: 'cli-assistant',
  purpose: 'general',
  instructions: 'Be helpful, concise, and friendly.',
});

const rl = readline.createInterface({ input: stdin, output: stdout });

while (true) {
  const input = await rl.question('\nYou: ');
  if (!input.trim() || input.toLowerCase() === 'exit') break;

  process.stdout.write('Agent: ');
  for await (const event of session.stream(input)) {
    if (event.type === 'message.delta') process.stdout.write(event.delta);
  }
  process.stdout.write('\n');
}

rl.close();

Recipe 2: Client tools (weather + calculator)

An agent that uses client tools to answer questions requiring external data.

TypeScript
import { Runner, MatildaCore, stream, type ToolHandlers } from '@maincode-ai/matilda-agent-sdk';

const runner = new Runner({
  core: new MatildaCore({ baseUrl: 'https://matilda.maincode.com/api' }),
});

if (!(await runner.auth.getTokens())) {
  await runner.auth.loginWithDeviceFlow({ clientId: 'matilda-code' });
}

const clientTools = [
  { name: 'get_weather', description: 'Get current weather for a city', parameters: { type: 'object' } },
  { name: 'calculate', description: 'Evaluate a math expression', parameters: { type: 'object' } },
];

const toolHandlers: ToolHandlers = {
  get_weather: async (args) => {
    const city = (args.city as string) ?? 'unknown';
    // In reality, call a weather API
    return { content: JSON.stringify({ city, temp: 22, condition: 'sunny' }) };
  },
  calculate: async (args) => {
    try {
      const result = Function(`return (${args.expression})`)();
      return { content: String(result) };
    } catch {
      return { content: 'Invalid expression', isError: true };
    }
  },
};

for await (const event of stream(
  { name: 'assistant', instructions: 'Use the available tools to answer.' },
  'What is the weather in Sydney, and what is 15 * 23?',
  { toolHandlers, clientTools },
)) {
  if (event.type === 'client.tool.requested') {
    console.log(`→ ${event.name}(${JSON.stringify(event.args)})`);
  }
  if (event.type === 'client.tool.result') {
    console.log(`← ${event.result}`);
  }
  if (event.type === 'message.delta') process.stdout.write(event.delta);
}

Recipe 3: Multi-agent code review pipeline

Sequential pipeline: security review → performance review → synthesis.

TypeScript
import { Agent, Runner, MatildaCore } from '@maincode-ai/matilda-agent-sdk';

const runner = new Runner({
  core: new MatildaCore({ baseUrl: 'https://matilda.maincode.com/api' }),
});

if (!(await runner.auth.getTokens())) {
  await runner.auth.loginWithDeviceFlow({ clientId: 'matilda-code' });
}

const security = new Agent({
  name: 'security',
  purpose: 'analysis',
  instructions: 'Review for vulnerabilities. Be specific.',
});

const performance = new Agent({
  name: 'performance',
  purpose: 'analysis',
  instructions: 'Review for efficiency. Be specific.',
});

const synthesiser = new Agent({
  name: 'synthesiser',
  purpose: 'analysis',
  instructions: 'Merge reviews into a prioritised action list. Use 🔴 🟡 🟢 priority.',
});

const code = 'function getUserData(userId, db) { var query = "SELECT * FROM users WHERE id = " + userId; }';

const [sec, perf] = await Promise.all([
  runner.run(security, `Review:\n\`\`\`javascript\n${code}\n\`\`\``),
  runner.run(performance, `Review:\n\`\`\`javascript\n${code}\n\`\`\``),
]);

const combined = `## Security\n${sec.finalOutput}\n\n## Performance\n${perf.finalOutput}`;
const result = await runner.run(synthesiser, combined);
console.log(result.finalOutput);

Recipe 4: Dynamic instructions with metadata

An agent whose instructions adapt based on runtime metadata.

TypeScript
import { Agent, Runner, MatildaCore, run } from '@maincode-ai/matilda-agent-sdk';

const runner = new Runner({
  core: new MatildaCore({ baseUrl: 'https://matilda.maincode.com/api' }),
});

if (!(await runner.auth.getTokens())) {
  await runner.auth.loginWithDeviceFlow({ clientId: 'matilda-code' });
}

const agent = new Agent({
  name: 'code-reviewer',
  purpose: 'code',
  instructions: ({ input, metadata }) => {
    const lang = (metadata.language as string) ?? 'auto-detect';
    const strictness = (metadata.strictness as string) ?? 'normal';
    return [
      'Review the following code.',
      `Language: ${lang}`,
      `Strictness: ${strictness}`,
      'Focus on: correctness, security, and readability.',
      'Cite line numbers when possible.',
    ].join('\n');
  },
});

const result = await run(agent, 'function add(a, b) { return a + b }', {
  metadata: { language: 'JavaScript', strictness: 'strict' },
});

console.log(result.finalOutput);

Recipe 5: Stream resume with disconnect recovery

Start a stream, simulate a disconnect, and resume from the last cursor.

TypeScript
import { stream, resumeAgentStream, configureClient } from '@maincode-ai/matilda-agent-sdk';

configureClient({ baseUrl: 'https://matilda.maincode.com/api' });

let streamId: string | null = null;
let lastEventId: string | undefined;
let receivedText = '';

console.log('Starting stream...');
try {
  for await (const event of stream({ name: 'writer' }, 'Write a very long essay about Australia.')) {
    if (event.type === 'stream.started') streamId = event.streamId;
    if (event.type === 'cursor') lastEventId = event.lastEventId;
    if (event.type === 'message.delta') {
      receivedText += event.delta;
      // Simulate disconnect after 500 chars
      if (receivedText.length > 500) {
        console.log('\n--- Simulated disconnect ---');
        break;
      }
    }
  }
} catch (err) {
  console.log('Disconnected:', err);
}

console.log(`Received ${receivedText.length} chars before disconnect.`);

// Resume from the last cursor
if (streamId) {
  console.log('\n--- Resuming ---');
  const result = await resumeAgentStream(streamId, lastEventId, {
    onEvent: (event) => {
      if (event.type === 'message.delta') process.stdout.write(event.delta);
    },
  });
  console.log(`\nTotal output: ${result.finalOutput.length} chars`);
}

Recipe 6: Custom Runner with file token store

A standalone Runner with persistent auth for CLI or long-running service use.

TypeScript
import {
  Runner,
  MatildaCore,
  createFileTokenStore,
  configureClient,
} from '@maincode-ai/matilda-agent-sdk';
import { homedir } from 'node:os';
import { join } from 'node:path';

const tokenPath = join(homedir(), '.matilda', 'tokens.json');
const { store, lock } = createFileTokenStore(tokenPath);

const runner = new Runner({
  core: new MatildaCore({ baseUrl: 'https://matilda.maincode.com/api' }),
});

// Restore persisted tokens or login interactively
const restored = await runner.auth.restore({ clientId: 'matilda-code', tokenStore: store, tokenLock: lock });
if (!restored) {
  await runner.auth.loginWithDeviceFlow({
    clientId: 'matilda-code',
    tokenStore: store,
    tokenLock: lock,
  });
}

// Runner is ready — tokens auto-refresh on 401
const result = await runner.run(
  { name: 'helper', instructions: 'Be concise.' },
  'What is the capital of Australia?',
);
console.log(result.finalOutput);

// Later: logout clears the token store
// await runner.auth.logout();