Cortex · guide

An agent as a tool

One agent hands part of its work to another by calling it, the way it calls any tool.

Install it
pnpm add @niscorp/cortex @niscorp/signal zod openai
Step 1 of 4

The model

createSignal makes the client that calls the model. Cortex is handed this client, here for Groq, and Signal reads the key from GROQ_API_KEY.

The model
import { z } from 'zod';
import { createSignal } from '@niscorp/signal';
import { asTool, defineAgent, defineTool } from '@niscorp/cortex';

const llm = createSignal('groq');
Step 2 of 4

An agent with one tool

This agent has the tool for seats, and nothing else. Its description is what another agent’s model will read to decide when to call it.

An agent with one tool
const seatsLeft = defineTool({
  id: 'seatsLeft',
  name: 'seatsLeft',
  description: 'How many seats are left on a night',
  input: z.object({ night: z.string().describe('The night, as YYYY-MM-DD') }),
  execute: ({ night }) => ({ night, left: night === '2026-10-24' ? 113 : 120 }),
});

const programme = defineAgent({
  id: 'programme',
  description: 'Knows how many seats are left on a night.',
  instructions: 'Answer from the seatsLeft tool, in one sentence.',
  tools: [seatsLeft],
});
Step 3 of 4

Wrap it as a tool

asTool turns an agent into an ordinary tool. When the desk’s model calls it, the programme agent runs its own loop, with its own tool, and its answer comes back as the tool’s result.

Wrap it as a tool
const desk = defineAgent({
  id: 'desk',
  instructions: 'Help the customer. Ask the programme agent about seats.',
  tools: [asTool(programme, { llm, deps: undefined })],
});
Step 4 of 4

One stream for both

The inner agent’s events arrive in the outer run’s stream. agentPath says which agent an event came from.

One stream for both
const run = desk.run('Are there seats left on 2026-10-24?', { llm });
run.onEvent((event) => {
  if (event.type === 'tool-start') console.log(event.agentPath.join(' > '), 'calls', event.call.toolId);
});

const result = await run.result;
console.log(result.ok ? result.output.response : result.error.message);
Then

Run it.

This file asks a model, so it is not run on this site. When the desk’s model hands the question on, it prints desk calls programme, then desk > programme calls seatsLeft, and the desk’s sentence with the 113 seats in it.

Run it
GROQ_API_KEY=your-key npx tsx delegate.mts