Skip to content

Agents

Agents orchestrate multiple tools and prompts to accomplish complex, multi-step goals. They're the highest-level component in @aeye.

Two ways to create components

Most examples use ai.agent(), which binds the agent to an AI instance:

typescript
const myAgent = ai.agent({ /* ... */ });

You can also create standalone agents from @aeye/core:

typescript
import { Agent } from '@aeye/core';
const myAgent = new Agent({ /* ... */ });

See AI Instance for ai setup.

Creating an Agent

typescript
import z from 'zod';

const searchFiles = ai.tool({
  name: 'searchFiles',
  description: 'Search for files',
  schema: z.object({ pattern: z.string() }),
  call: async ({ pattern }) => ({ files: ['src/index.ts', 'src/app.ts'] }),
});

const readFile = ai.tool({
  name: 'readFile',
  description: 'Read file contents',
  schema: z.object({ path: z.string() }),
  call: async ({ path }) => ({ content: `// ${path} contents` }),
});

const summarize = ai.prompt({
  name: 'summarize',
  description: 'Summarize code',
  content: 'Summarize this code:\n\n{{code}}',
  input: (input: { code: string }) => input,
  schema: z.object({
    summary: z.string(),
    exports: z.array(z.string()),
  }),
});

// Agent composes all three
const codeReviewer = ai.agent({
  name: 'codeReviewer',
  description: 'Reviews TypeScript files',
  refs: [searchFiles, readFile, summarize] as const,
  call: async ({ pattern }: { pattern: string }, [search, read, summarize], ctx) => {
    const { files } = await search.run({ pattern }, ctx);
    const results = [];

    for (const file of files) {
      const { content } = await read.run({ path: file }, ctx);
      const result = await summarize.get('result', { code: content }, ctx);
      results.push({ file, ...result });
    }

    return results;
  },
});

// Run the agent
const reviews = await codeReviewer.run({ pattern: 'src/**/*.ts' });

Agent Configuration

PropertyTypeDescription
namestringUnique identifier
descriptionstringWhat the agent does
refsComponent[] as constRequired child components (typed tuple)
call(input, refs, ctx) => outputOrchestration function
applicable(ctx) => booleanAvailability check
metadata / metadataFnStatic/dynamic metadata

Typed Refs

The refs tuple is fully typed. Using as const ensures each ref maintains its specific type:

typescript
const myAgent = ai.agent({
  name: 'myAgent',
  refs: [toolA, toolB, promptC] as const,
  call: async (input, [a, b, c], ctx) => {
    // a is typed as typeof toolA
    // b is typed as typeof toolB
    // c is typed as typeof promptC
    const resultA = await a.run({ /* typed input */ }, ctx);
    const resultC = await c.get('result', { /* typed input */ }, ctx);
    return { resultA, resultC };
  },
});

Agents vs Prompts

PromptAgent
AI callsMakes AI API calls directlyOrchestrates other components that make calls
ToolsAI model decides which tools to callYour code decides the workflow
OutputAI-generated text/structured dataWhatever your call() function returns
ControlAI-drivenDeveloper-driven
Use caseSingle AI interaction with toolsMulti-step workflows

Nested Agents

Agents can reference other agents:

typescript
const researcher = ai.agent({
  name: 'researcher',
  refs: [webSearch, readPage] as const,
  call: async (input, [search, read], ctx) => {
    const results = await search.run(input, ctx);
    // ... process results
    return { findings: [] };
  },
});

const writer = ai.agent({
  name: 'writer',
  refs: [researcher, summarize] as const,
  call: async (input, [research, summarize], ctx) => {
    const { findings } = await research.run(input, ctx);
    const summary = await summarize.get('result', { text: findings.join('\n') }, ctx);
    return summary;
  },
});

Event Tracking

Track agent execution with the instance event system:

typescript
import { withEvents } from '@aeye/core';

const runner = withEvents({
  onStatus: (instance) => {
    console.log(`[${instance.component.kind}] ${instance.component.name}: ${instance.status}`);
  },
  onChild: (parent, child) => {
    console.log(`${parent.component.name} → ${child.component.name}`);
  },
});

const result = await codeReviewer.run(
  { pattern: 'src/**/*.ts' },
  { runner }
);

Output:

[agent] codeReviewer: running
codeReviewer → searchFiles
[tool] searchFiles: running
[tool] searchFiles: completed
codeReviewer → readFile
[tool] readFile: running
[tool] readFile: completed
codeReviewer → summarize
[prompt] summarize: running
[prompt] summarize: completed
[agent] codeReviewer: completed

Released under the GPL-3.0 License.