npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@aeye/core

v0.5.4

Published

@aeye Core - Core primitives for AI components (agents, tools, prompts)

Readme

@aeye/core

Core primitives for building AI agents, tools, and prompts with TypeScript. Provides a type-safe, composable framework for creating sophisticated AI applications with structured inputs/outputs, tool calling, and context management.

Features

  • 🎯 Type-Safe Components - Prompts, Tools, and Agents with full TypeScript support
  • 🔧 Tool Calling - Native support for function/tool calling with schema validation
  • 📝 Template-Based Prompts - Handlebars templates for dynamic prompt generation
  • ✅ Schema Validation - Zod integration for structured inputs and outputs
  • 🌊 Streaming Support - First-class streaming for real-time AI responses
  • 🔄 Composable Architecture - Tools can use other tools, prompts can use tools, agents orchestrate everything
  • 📊 Context Management - Type-safe context threading and automatic token window management
  • 🎛️ Flexible Execution - Sequential, parallel, or immediate tool execution modes

Installation

npm install @aeye/core zod handlebars

Note: @aeye/core defines the primitives — you need an executor/streamer to run prompts. These are provided by @aeye/ai via ai.buildCoreContext(), or you can supply your own execute / stream functions.

Core Concepts

Components

All AI primitives implement the Component interface:

  • Prompt - Generates AI responses with optional tool usage and structured outputs
  • Tool - Extends AI capabilities with custom functions and external integrations
  • Agent - Orchestrates complex workflows combining prompts and tools

Context & Metadata

  • Context (TContext) - Application-specific data threaded through operations (user, db, etc.)
  • Metadata (TMetadata) - Execution settings for AI requests (model, temperature, etc.)

Quick Start

Basic Prompt

import { Prompt } from '@aeye/core';
import z from 'zod';

const summarizer = new Prompt({
  name: 'summarize',
  description: 'Summarizes text concisely',
  content: 'Summarize the following text:\n\n{{text}}',

  // Transform raw input into template variables
  input: (input: { text: string }) => ({ text: input.text }),

  // Define output schema (structured JSON)
  schema: z.object({
    summary: z.string().describe('A concise summary'),
    keyPoints: z.array(z.string()).describe('Main points'),
  }),
});

// Execute — ctx must provide execute or stream (from @aeye/ai or custom)
const result = await summarizer.get(
  'result',
  { text: 'Long article text...' },
  {
    execute: yourAIExecutor,  // (request, ctx, metadata?, signal?) => Promise<Response>
    messages: [],
  }
);

console.log(result?.summary);
console.log(result?.keyPoints);

Creating Tools

import { Tool } from '@aeye/core';
import z from 'zod';

const getWeather = new Tool({
  name: 'getWeather',
  description: 'Get current weather for a location',
  instructions: 'Use this tool to get weather data for {{location}}.',

  schema: z.object({
    location: z.string().describe('City name or coordinates'),
    units: z.enum(['celsius', 'fahrenheit']).default('celsius'),
  }),

  call: async (input, _refs, _ctx) => {
    const response = await fetch(
      `https://api.weather.com/v1/${encodeURIComponent(input.location)}`
    );
    const data = await response.json();
    return {
      temperature: data.temp,
      condition: data.condition,
      humidity: data.humidity,
    };
  },
});

Prompts with Tools

import { Prompt } from '@aeye/core';
import z from 'zod';

const travelAdvisor = new Prompt({
  name: 'travelAdvisor',
  description: 'Provides travel advice based on weather',
  content: `You are a travel advisor. Help plan a trip to {{destination}}.

Use the getWeather tool to check current conditions, then provide:
- What to pack
- Recommended activities
- Best times to visit`,

  input: (input: { destination: string }) => ({
    destination: input.destination,
  }),

  tools: [getWeather],

  schema: z.object({
    recommendations: z.array(z.string()),
    packingList: z.array(z.string()),
    weatherNotes: z.string(),
  }),
});

// The AI will automatically call getWeather if needed
const advice = await travelAdvisor.get(
  'result',
  { destination: 'Paris' },
  { execute: yourAIExecutor, messages: [] }
);

console.log(advice?.recommendations);

Streaming Responses

// Stream text content only
for await (const chunk of summarizer.get(
  'streamContent',
  { text: 'Long text...' },
  { stream: yourAIStreamer, messages: [] }
)) {
  process.stdout.write(chunk);
}

// Stream all events (including tool calls)
for await (const event of summarizer.get(
  'stream',
  { text: 'Long text...' },
  { stream: yourAIStreamer, messages: [] }
)) {
  if (event.type === 'textPartial') {
    process.stdout.write(event.content);
  } else if (event.type === 'toolStart') {
    console.log('Tool started:', event.tool.name);
  } else if (event.type === 'toolOutput') {
    console.log('Tool result:', event.result);
  } else if (event.type === 'complete') {
    console.log('Final output:', event.output);
  }
}

Building Agents

import { Agent } from '@aeye/core';
import z from 'zod';

// Assume searchTool, summarizePrompt, and analyzePrompt are defined elsewhere
const researchAgent = new Agent({
  name: 'researcher',
  description: 'Conducts research on a topic',

  refs: [searchTool, summarizePrompt, analyzePrompt] as const,

  call: async (
    input: { topic: string },
    [search, summarize, analyze],
    ctx
  ) => {
    // Step 1: Search for information
    const searchResults = await search.run({ query: input.topic, limit: 5 }, ctx);

    // Step 2: Summarize each result
    const summaries: string[] = [];
    for (const result of searchResults.items) {
      const summary = await summarize.get('result', { text: result.content }, ctx);
      summaries.push(summary?.summary ?? '');
    }

    // Step 3: Synthesize into a final analysis
    const analysis = await analyze.get(
      'result',
      { topic: input.topic, sources: summaries },
      ctx
    );

    return analysis;
  },
});

const research = await researchAgent.run(
  { topic: 'Quantum Computing' },
  { execute: yourAIExecutor, messages: [] }
);

Prompt Modes

The get(mode, input?, ctx?) method supports different execution modes:

| Mode | Description | Returns | |------|-------------|---------| | 'result' | Await the final structured output | Promise<TOutput \| undefined> | | 'tools' | Await all tool call results | Promise<PromptToolOutput[] \| undefined> | | 'stream' | Stream all events | AsyncGenerator<PromptEvent<TOutput, TTools>, TOutput \| undefined> | | 'streamTools' | Stream tool output events | AsyncGenerator<PromptToolOutput<TTools>, TOutput \| undefined> | | 'streamContent' | Stream text content only | AsyncGenerator<string, TOutput \| undefined> |

// Get structured result
const result = await prompt.get('result', input, ctx);

// Get tool outputs only
const tools = await prompt.get('tools', input, ctx);

// Stream everything
for await (const event of prompt.get('stream', input, ctx)) {
  // event.type is one of: 'request', 'textPartial', 'text', 'toolStart',
  //   'toolOutput', 'toolError', 'message', 'complete', 'usage', ...
}

// Stream text only
for await (const text of prompt.get('streamContent', input, ctx)) {
  process.stdout.write(text);
}

Tool Execution Modes

Control how tools are executed by the prompt:

const prompt = new Prompt({
  name: 'multi-tool',
  description: 'Uses multiple tools',
  content: 'Analyze this data using the available tools.',
  tools: [tool1, tool2, tool3],

  // 'sequential' | 'parallel' | 'immediate' (default: 'immediate')
  toolExecution: 'parallel',

  // Number of times to retry a failed tool call (default: 2)
  toolRetries: 2,
  // Max tool-calling iterations per request (default: 3)
  toolIterations: 3,
  // Max total successful tool calls before stopping (no strict limit by default)
  toolsMax: 5,
});
  • sequential - Wait for each tool to complete before starting the next
  • parallel - Start all pending tools at once and wait for all to complete
  • immediate - Start each tool as soon as its call is received (default)

Advanced Features

Context-Aware Tools

Tool schemas and applicability can depend on the current context:

interface MyContext {
  userRole: 'admin' | 'viewer';
  isAuthenticated: boolean;
}

const contextTool = new Tool({
  name: 'manageData',
  description: 'Manage data records',
  instructions: 'Use this tool to read or write data.',

  // Schema varies based on the caller's role
  schema: (ctx: Context<MyContext, {}>) => {
    if (ctx.userRole === 'admin') {
      return z.object({ action: z.enum(['read', 'write', 'delete']) });
    }
    return z.object({ action: z.enum(['read']) });
  },

  // Tool is only available to authenticated users
  applicable: (ctx: Context<MyContext, {}>) => ctx.isAuthenticated,

  call: async (input, _refs, ctx) => {
    return { action: input.action, executedBy: ctx.userRole };
  },
});

Custom Validation

Add business-logic validation beyond Zod schema checks:

const placeOrder = new Tool({
  name: 'placeOrder',
  description: 'Place a product order',

  schema: z.object({
    itemId: z.string(),
    quantity: z.number().int().min(1),
  }),

  // Runs after schema parsing succeeds; throw to trigger re-prompting
  validate: async (input, ctx) => {
    const inventory = await checkInventory(input.itemId);
    if (inventory < input.quantity) {
      throw new Error(`Only ${inventory} units of ${input.itemId} are available.`);
    }
  },

  call: async (input, _refs, _ctx) => {
    return { orderId: crypto.randomUUID(), ...input };
  },
});

Event Tracking with withEvents

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

const runner = withEvents({
  onStatus: (instance) => {
    console.log(`${instance.component.name}: ${instance.status}`);
    if (instance.status === 'completed' && instance.completed && instance.running) {
      console.log(`  Took ${instance.completed - instance.running}ms`);
    }
  },

  onPromptEvent: (_instance, event) => {
    if (event.type === 'usage') {
      console.log('Tokens used:', event.usage);
    }
  },
});

const result = await prompt.get(
  'result',
  { text: 'Hello' },
  { execute: yourAIExecutor, messages: [], runner }
);

Token Management

The prompt automatically trims old messages when the context window is full:

const result = await prompt.get(
  'result',
  { text: 'Query' },
  {
    execute: yourAIExecutor,
    messages: conversationHistory,  // Will be trimmed if needed

    // Reserve this many tokens for the model's response
    maxOutputTokens: 2048,

    // Custom per-message token estimator
    estimateUsage: (message) => ({
      text: { input: Math.ceil((message.content as string).length / 4) }
    }),
  }
);

Dynamic Reconfiguration

Use reconfig to adapt the prompt based on runtime statistics:

const adaptivePrompt = new Prompt({
  name: 'adaptive',
  description: 'Adapts based on runtime stats',
  content: 'Solve: {{problem}}',
  schema: z.object({ solution: z.string() }),

  reconfig: (stats, _ctx) => {
    // If many tool errors occurred, switch to one-at-a-time mode
    if (stats.toolCallErrors > 3) {
      return { config: { toolsOneAtATime: true } };
    }
    return {};
  },
});

Model Capabilities

@aeye uses a capability system for automatic model selection. Models advertise their capabilities and the library selects the best match for each request:

| Capability | Description | |------------|-------------| | chat | Basic text completion | | streaming | Real-time response streaming | | image | Image generation | | vision | Image understanding | | audio | Text-to-speech | | hearing | Speech-to-text | | embedding | Text embeddings | | tools | Function/tool calling | | json | JSON output mode | | structured | Structured outputs with schemas | | toolsStrict | Strict-mode tool input schemas (auto-derived; see Strict Mode) | | reasoning | Extended chain-of-thought reasoning | | zdr | Zero data retention |

Strict Mode

Each LLM provider that supports strict mode does so with its own JSON Schema dialect. @aeye/core reconciles them under a single FormatDescriptor so a Zod schema authored once works against any of them.

strict on Tool / Prompt

Tool.strict and Prompt.strict are boolean | number:

| Value | Meaning | |---|---| | true | Require strict. @aeye/ai filters selection down to models that declare strict support; the request fails if none qualify. | | false | Force lenient. Standard JSON Schema; no strict: true on the wire. | | number > 0 (default 1) | Prefer strict, accept fallback. Higher numbers win when there are more strict-requesting items than the chosen model's per-request budget allows. | | omitted | Treated as 1. |

new Tool({ name: 'must',     /* default 1 */                schema: z.object({...}) }); // best-effort
new Tool({ name: 'critical', strict: true,                  schema: z.object({...}) }); // hard require
new Tool({ name: 'meh',      strict: false,                 schema: z.object({...}) }); // never strict
new Tool({ name: 'top',      strict: 100,                   schema: z.object({...}) }); // high priority

Default 1 keeps "it just works" against unannotated models — strict where the chosen model supports it, lenient elsewhere. Set true only when strict is non-negotiable.

Native format descriptors

Seven descriptors ship out of the box:

| Descriptor | Family / Strict | Notes | |---|---|---| | OPENAI_STRICT | openai / strict | Records → array-of-pairs, tuples → numeric-key objects, optional → T \| null, closed objects, restricted format whitelist. | | ANTHROPIC_STRICT | anthropic / strict | Closed objects, no recursion, no length / numeric range constraints, per-request slot budgets (20 strict tools / 24 optional params / 16 union types). | | GOOGLE_STRICT | google / strict | prefixItems, $ref: '#' recursion, propertyOrdering emitted, restricted format whitelist. | | LENIENT | lenient / non-strict | No rewrites, all features pass through. Default for unannotated models. | | OPENAI_NON_STRICT, ANTHROPIC_NON_STRICT, GOOGLE_NON_STRICT | family / non-strict | Aliased to LENIENT but tagged with the family for diagnostics. |

import { OPENAI_STRICT, ANTHROPIC_STRICT, getDescriptor, toJSONSchema } from '@aeye/core';

const schema = z.object({
  name: z.string(),
  tags: z.record(z.string(), z.string()),
});

// Same Zod, different wire shapes:
toJSONSchema(schema, OPENAI_STRICT);                    // tags → array of {key, value}
toJSONSchema(schema, ANTHROPIC_STRICT);                 // tags → array of {key, value} (open records unsupported)
toJSONSchema(schema, getDescriptor('google', true));    // tags → standard additionalProperties

// Backward-compat overloads still work:
toJSONSchema(schema, true);   // === OPENAI_STRICT
toJSONSchema(schema, false);  // === LENIENT

Registering a custom descriptor

You can ship your own descriptor for a provider or dialect that isn't supported natively. Define it as a FormatDescriptor value and register it with registerDescriptor(descriptor):

import { registerDescriptor, type FormatDescriptor, OPENAI_STRICT } from '@aeye/core';

const MY_PROVIDER_STRICT: FormatDescriptor = {
  ...OPENAI_STRICT,                   // start from a known good baseline
  id: 'my-provider-strict',
  family: 'openai',                   // or 'anthropic' | 'google' | 'lenient'
  // Override only the fields that differ from the baseline:
  recordEncoding: 'open-record',      // your provider supports additionalProperties: <schema>
  tupleEncoding: 'prefix-items',
  supportsRecursion: true,
};

registerDescriptor(MY_PROVIDER_STRICT);

Once registered:

  • Provider code can pass the descriptor to toJSONSchema(schema, MY_PROVIDER_STRICT) and strictify(schema, MY_PROVIDER_STRICT).
  • The Prompt's validation roundtrip (which looks up descriptors by id from the request) will resolve 'my-provider-strict' correctly.
  • A custom Provider implementation can return this descriptor from its getStrictFormat-equivalent helper.

The descriptor's full field list is exported as the FormatDescriptor interface — every flag is documented inline. Common adjustments:

| Field | Controls | |---|---| | objectAllFieldsRequired | Whether every property must appear in required[]. | | objectClosedByDefault | Whether every object emits additionalProperties: false. | | recordEncoding | 'array-of-pairs' vs 'open-record'. | | tupleEncoding | 'object-numeric-keys' vs 'prefix-items' vs 'items-union'. | | allowAllOf / allowAnyOf / allowOneOf | JSON-Schema combinator support. | | supportedStringFormats | 'all' or a Set<string> whitelist ('date-time', 'email', etc.). | | optionalAsNullable | Whether optional fields are emitted as T \| null (OpenAI strict) vs omitted from required[]. | | supportsRecursion | Whether $ref self-reference works under this dialect. | | maxStrictTools / maxStrictOptionalParams / maxStrictUnionTypes | Per-request budget caps; undefined means no documented limit. | | schemaSizeLimits | Per-SCHEMA size ceilings (properties, nesting, total string characters, enum values); undefined means no documented limit. An over-size schema degrades on its own, even when it asked for strict: true. |

You don't need to set every field — spread an existing descriptor and override deltas. Tested via analyzeSchema + the schema test suite; see the strict-mode guide in @aeye/docs for end-to-end examples.

API Reference

Prompt

class Prompt<TContext, TMetadata, TName extends string, TInput extends object, TOutput extends object | string, TTools extends Tuple<ToolCompatible<TContext, TMetadata>>, TDecoded extends unknown = TOutput>

TDecoded is the type a custom parse produces from the model's structured output (e.g. a built AST/class instance); it types validate and the decoded output. It defaults to TOutput (the wire type) when no custom parse is supplied — schema always stays the wire type.

Constructor Options (PromptInput):

  • name: string - Unique identifier
  • description: string - Purpose description
  • content: string - Handlebars template for the system/user message
  • input?: (input: TInput, ctx: Context<TContext, TMetadata>) => Record<string, any> - Maps raw input to template variables
  • schema?: ZodType<TOutput> | ((input: TInput | undefined, ctx: Context<TContext, TMetadata>) => ZodType<TOutput> | false) - Output schema; false disables structured output
  • strict?: boolean - Enforce strict Zod schema (default: true)
  • config?: Partial<Request> | ((input: TInput | undefined, ctx: Context<TContext, TMetadata>) => Partial<Request> | false) - AI request overrides
  • tools?: TTools - Tools the prompt may call
  • toolExecution?: 'sequential' | 'parallel' | 'immediate' - Tool execution mode
  • toolRetries?: number - Retry count for failed tools (default: 2)
  • toolIterations?: number - Max tool-calling iterations (default: 3)
  • toolsMax?: number - Max successful tool calls
  • outputRetries?: number - Retry count for invalid structured outputs
  • metadata?: TMetadata - Static metadata for this prompt
  • validate?: (output: TOutput, ctx: Context<TContext, TMetadata>) => void | Promise<void> - Post-parse validation
  • applicable?: (ctx: Context<TContext, TMetadata>) => boolean | Promise<boolean> - Availability check

Methods:

  • get(mode: PromptGetType, input?: TInput, ctx?: Context<TContext, TMetadata>): PromptGet<...> - Execute and return output in the specified mode
  • run(input?: TInput, ctx?: Context<TContext, TMetadata>): AsyncGenerator<PromptEvent<TOutput, TTools>, TOutput | undefined> - Full streaming generator
  • applicable(ctx?: Context<TContext, TMetadata>): Promise<boolean> - Check if prompt is usable

Tool

class Tool<TContext, TMetadata, TName extends string, TParams extends object, TOutput, TRefs extends Tuple<ComponentCompatible<TContext, TMetadata>>, TDecoded extends unknown = TParams>

TDecoded is the type a custom parse produces from the raw wire args (e.g. a built class instance); it types call, validate, and metadataFn. It defaults to TParams (the wire type) when no custom parse is supplied — schema always stays the wire type.

Constructor Options (ToolInput):

  • name: string - Unique identifier (shown to the AI model)
  • description: string - What the tool does (shown to the AI model)
  • instructions?: string - Handlebars template for usage instructions
  • schema: ZodType<TParams> | ((ctx: Context<TContext, TMetadata>) => ZodType<TParams> | undefined) - Input parameter schema
  • strict?: boolean - Enforce strict Zod schema (default: true)
  • refs?: TRefs - Referenced components
  • call: (input: TParams, refs: TRefs, ctx: Context<TContext, TMetadata>) => TOutput - Implementation
  • validate?: (input: TParams, ctx: Context<TContext, TMetadata>) => void | Promise<void> - Post-parse validation
  • applicable?: (ctx: Context<TContext, TMetadata>) => boolean | Promise<boolean> - Availability check

Methods:

  • run(input?: TParams, ctx?: Context<TContext, TMetadata>): TOutput - Execute the tool directly
  • compile(ctx: Context<TContext, TMetadata>): Promise<readonly [string, ToolDefinition] | undefined> - Generate AI tool definition
  • applicable(ctx?: Context<TContext, TMetadata>): Promise<boolean> - Check if tool is usable

Agent

class Agent<TContext, TMetadata, TName extends string, TInput extends object, TOutput, TRefs extends Tuple<ComponentCompatible<TContext, TMetadata>>>

Constructor Options (AgentInput):

  • name: string - Unique identifier
  • description: string - Purpose description
  • refs: TRefs - Components this agent uses
  • call: (input: TInput, refs: TRefs, ctx: Context<TContext, TMetadata>) => TOutput - Implementation
  • applicable?: (ctx: Context<TContext, TMetadata>) => boolean | Promise<boolean> - Availability check

Methods:

  • run(input?: TInput, ctx?: Context<TContext, TMetadata>): TOutput - Execute the agent
  • applicable(ctx?: Context<TContext, TMetadata>): Promise<boolean> - Check if agent is usable

Context Structure

The Context<TContext, TMetadata> type combines your custom context with the core execution context:

// Core fields available in every context
interface CoreContextFields {
  // AI executor for non-streaming requests
  execute?: (request: Request, ctx: TContext, metadata?: TMetadata, signal?: AbortSignal) => Promise<Response>;
  // AI streamer for streaming requests
  stream?: (request: Request, ctx: TContext, metadata?: TMetadata, signal?: AbortSignal) => AsyncGenerator<Chunk, Response>;

  // Conversation history passed to the model
  messages?: Message[];

  // Reserve N tokens for the model's output (used for context trimming)
  maxOutputTokens?: number;
  // Override context window size (used for context trimming)
  contextWindow?: number;

  // Custom per-message usage estimator
  estimateUsage?: (message: Message) => Usage | undefined;

  // Number of output-format retries (default: 2)
  outputRetries?: number;
  // Number of context-trimming retries (default: 1)
  forgetRetries?: number;

  // Abort signal for cancellation
  signal?: AbortSignal;
  // Event runner for tracking component lifecycle
  runner?: Events;
}

Request Configuration

interface Request {
  messages: Message[];
  temperature?: number;       // 0.0–2.0
  topP?: number;              // 0.0–1.0
  maxTokens?: number;
  stop?: string | string[];
  tools?: ToolDefinition[];
  toolChoice?: 'auto' | 'required' | 'none' | { type: 'tool'; name: string };
  responseFormat?: 'text' | 'json' | { type: 'json_schema'; schema: object; name: string; strict?: boolean };
  reason?: { effort?: 'low' | 'medium' | 'high'; maxTokens?: number };
  frequencyPenalty?: number;
  presencePenalty?: number;
  seed?: number;
  // ... more options
}

Best Practices

  1. Type Safety - Use TypeScript generics for context and metadata
  2. Schema Validation - Use Zod for robust input/output validation
  3. Token Management - Supply estimateUsage for accurate context trimming
  4. Tool Organization - Group related tools and use agents to orchestrate them
  5. Testing - Unit test tools and prompts independently
  6. Context Minimization - Pass only necessary data in context
  7. Streaming - Use streaming for better UX with long responses
  8. Validation - Use validate hooks for business-logic checks after schema parsing

Examples

See the src/__tests__ directory for comprehensive examples:

  • prompt-core-features.test.ts - Basic prompt usage
  • prompt-streaming-tool-events.test.ts - Streaming and events
  • tool.test.ts - Tool creation and usage
  • agent.test.ts - Agent orchestration
  • context-propagation.test.ts - Context handling

TypeScript Support

Full type inference across component hierarchies:

import { Prompt, Tool } from '@aeye/core';
import z from 'zod';

// Output type is inferred from the schema
const prompt = new Prompt({
  name: 'example',
  description: 'Example prompt',
  content: 'Say hello to {{name}}.',
  input: (input: { name: string }) => ({ name: input.name }),
  schema: z.object({ greeting: z.string() }),
});

// result is typed as { greeting: string } | undefined
const result = await prompt.get('result', { name: 'World' }, ctx);
console.log(result?.greeting);

// Tool call parameters are type-safe
const mathTool = new Tool({
  name: 'add',
  description: 'Adds two numbers',
  schema: z.object({ a: z.number(), b: z.number() }),
  call: (input, _refs, _ctx) => {
    // input is typed as { a: number; b: number }
    return input.a + input.b;
  },
});

Contributing

Contributions are welcome! See the main @aeye repository for contribution guidelines.

License

GPL-3.0 © ClickerMonkey

Links