Multi-provider AI client with project context, governance, and event monitoring

CodemirAI

CodemirAI is the main AI client for the @codmir/ai package. It abstracts multiple AI providers behind a unified interface with project context awareness, governance policy enforcement, and event monitoring.

Installation

import { CodemirAI, createCodemirAI } from "@codmir/ai";

Quick Start

import { createCodemirAI } from "@codmir/ai";

const ai = createCodemirAI({
  defaultProvider: "anthropic",
  context: {
    projectId: "my-project",
    name: "My Project",
    techStack: ["TypeScript", "NestJS", "React"],
    description: "A SaaS platform for project management",
  },
  governance: {
    enabled: true,
    rateLimitPerMinute: 60,
    autoApprove: ["code_generation"],
    requireApproval: ["file_deletion", "database_operation"],
  },
});

// Simple chat
const answer = await ai.chat("Explain the auth flow in this codebase");

// Stream response
for await (const chunk of ai.stream({
  messages: [
    { role: "system", content: "You are a code reviewer." },
    { role: "user", content: "Review this function..." },
  ],
})) {
  process.stdout.write(chunk.content);
}

// Listen to events
ai.on("request_completed", (event) => {
  console.log("Usage:", event.data.usage);
});

createCodemirAI(config?): CodemirAI

Factory function that creates a new CodemirAI instance.

CodemirAIConfig

interface CodemirAIConfig {
  defaultProvider?: AIProvider;
  context?: ProjectContext;
  governance?: GovernanceConfig;
  providers?: {
    anthropic?: { apiKey?: string; baseUrl?: string };
  };
}
PropertyTypeDefaultDescription
defaultProviderAIProviderundefinedPreferred provider ("anthropic", "google", "mistral", "local")
contextProjectContextundefinedProject context injected into all requests
governanceGovernanceConfigundefinedGovernance policy configuration
providersobjectundefinedCustom provider options

Provider Management

getProviderStatus(): ProviderStatus[]

Returns the status and capabilities of all configured providers.

const statuses = ai.getProviderStatus();
// [{ provider: "anthropic", available: true, model: "claude-opus-4-20250514", capabilities: {...} }]

isProviderAvailable(provider): boolean

Checks if a specific provider is configured and available.

if (ai.isProviderAvailable("anthropic")) {
  // Safe to use Anthropic
}

getBestProvider(preference?): AIProvider

Returns the best available provider, optionally preferring a specific one.

const provider = ai.getBestProvider("anthropic");
// Falls back to another provider if Anthropic is unavailable

Context Management

setContext(context): void

Sets or updates the project context. The context is automatically prepended to all AI requests as a system message.

ai.setContext({
  projectId: "codmir",
  name: "Codmir",
  techStack: ["TypeScript", "Next.js", "NestJS", "Prisma"],
  description: "AI-native project management platform",
  instructions: "Always use Zod schemas for validation. Follow type-first development.",
  keyFiles: [
    { path: "CLAUDE.md", content: "..." },
  ],
  knowledge: [
    { id: "auth", type: "pattern", title: "Auth Flow", content: "Desktop uses opaque tokens..." },
  ],
});

getContext(): ProjectContext | undefined

Returns the current project context.

addKnowledge(item): void

Adds a knowledge item to the existing context. Throws if no context is set.

ai.addKnowledge({
  id: "deploy-pattern",
  type: "pattern",
  title: "Deployment Strategy",
  content: "Always deploy to staging first, then promote to production.",
  tags: ["devops", "deployment"],
});

Governance

setGovernance(config): void

Sets the governance configuration for policy enforcement.

ai.setGovernance({
  enabled: true,
  rateLimitPerMinute: 30,
  autoApprove: ["code_generation"],
  requireApproval: ["file_deletion", "command_execution"],
  maxTokensPerRequest: 8192,
  notifyOn: ["task_failed", "rate_limit_reached"],
});

Governance checks run automatically before every complete() and stream() call. If a request is denied (rate limit exceeded, action not approved), an error is thrown.

AI Operations

chat(message, options?): Promise<string>

Simple single-turn chat interface. Returns the AI response as a string.

chat(
  message: string,
  options?: {
    system?: string;
    provider?: AIProvider;
    model?: string;
  }
): Promise<string>
const answer = await ai.chat("What does this error mean?", {
  system: "You are a debugging assistant.",
  provider: "anthropic",
  model: "claude-sonnet-4-20250514",
});

complete(request): Promise<CompletionResponse>

Full completion request with all options.

complete(request: CompletionRequest): Promise<CompletionResponse>
const response = await ai.complete({
  messages: [
    { role: "system", content: "You are a code generator." },
    { role: "user", content: "Write a React hook for debouncing." },
  ],
  provider: "anthropic",
  model: "claude-opus-4-20250514",
  temperature: 0.3,
  maxTokens: 4096,
  tools: [
    {
      name: "write_file",
      description: "Write content to a file",
      parameters: {
        type: "object",
        properties: {
          path: { type: "string" },
          content: { type: "string" },
        },
      },
    },
  ],
});

console.log(response.content);
console.log(response.usage); // { promptTokens, completionTokens, totalTokens }
console.log(response.provider); // "anthropic"
console.log(response.finishReason); // "stop" | "length" | "tool_calls" | "error"

stream(request): AsyncIterable<StreamChunk>

Streams a completion response. Yields chunks as they arrive.

stream(request: CompletionRequest): AsyncIterable<StreamChunk>
for await (const chunk of ai.stream({
  messages: [{ role: "user", content: "Explain monorepo structure" }],
})) {
  process.stdout.write(chunk.content);
  if (chunk.done) break;
}

embed(text, options?): Promise<number[][]>

Creates embeddings for text or an array of texts.

embed(
  text: string | string[],
  options?: { provider?: AIProvider; model?: string }
): Promise<number[][]>
const embeddings = await ai.embed(["hello world", "foo bar"]);
// [[0.123, -0.456, ...], [0.789, -0.012, ...]]

Note: Anthropic does not support embeddings. Use a provider that supports them (e.g., "google").

Events

on(event, handler): () => void

Subscribe to AI events. Returns an unsubscribe function.

on(event: AIEventType | "*", handler: (event: AIEvent) => void | Promise<void>): () => void
// Listen to specific events
const unsub = ai.on("request_completed", (event) => {
  console.log(`Provider: ${event.data.provider}`);
  console.log(`Tokens: ${JSON.stringify(event.data.usage)}`);
});

// Listen to all events
ai.on("*", (event) => {
  overseerBridge.reportEvent(event);
});

// Unsubscribe
unsub();

Event Types

EventFired When
request_startedA completion request begins
request_completedA completion request succeeds
request_failedA completion request fails
stream_startedA streaming request begins
stream_chunkA streaming chunk is received
stream_endedA streaming request completes
tool_calledA tool call is made
tool_resultA tool call returns a result
context_updatedProject context is changed
governance_decisionA governance check produces a decision
rate_limit_reachedRate limit is exceeded
approval_requiredAn action requires approval

Types

AIProvider

type AIProvider = "anthropic" | "google" | "mistral" | "local";

CompletionRequest

interface CompletionRequest {
  messages: Message[];
  provider?: AIProvider;
  model?: string;
  temperature?: number;
  maxTokens?: number;
  tools?: ToolDefinition[];
  stream?: boolean;
  context?: ProjectContext;
}

CompletionResponse

interface CompletionResponse {
  content: string;
  role: MessageRole;
  toolCalls?: ToolCall[];
  usage: {
    promptTokens: number;
    completionTokens: number;
    totalTokens: number;
  };
  provider: AIProvider;
  model: string;
  finishReason: "stop" | "length" | "tool_calls" | "error";
}

StreamChunk

interface StreamChunk {
  content: string;
  done: boolean;
  toolCalls?: ToolCall[];
}

Message

type MessageRole = "system" | "user" | "assistant" | "tool";

interface Message {
  role: MessageRole;
  content: string;
  name?: string;
  toolCallId?: string;
  toolCalls?: ToolCall[];
}

ProjectContext

interface ProjectContext {
  projectId: string;
  name: string;
  description?: string;
  techStack: string[];
  fileStructure?: FileNode[];
  keyFiles?: FileContent[];
  instructions?: string;
  knowledge?: KnowledgeItem[];
}

GovernanceConfig

interface GovernanceConfig {
  enabled: boolean;
  requireApproval?: ActionType[];
  autoApprove?: ActionType[];
  maxTokensPerRequest?: number;
  rateLimitPerMinute?: number;
  notifyOn?: GovernanceEvent[];
}

type ActionType =
  | "code_generation"
  | "code_modification"
  | "file_creation"
  | "file_deletion"
  | "command_execution"
  | "api_call"
  | "database_operation";

AIEvent

interface AIEvent {
  type: AIEventType;
  timestamp: Date;
  agentId?: string;
  data: Record<string, unknown>;
}