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 };
};
}| Property | Type | Default | Description |
|---|---|---|---|
defaultProvider | AIProvider | undefined | Preferred provider ("anthropic", "google", "mistral", "local") |
context | ProjectContext | undefined | Project context injected into all requests |
governance | GovernanceConfig | undefined | Governance policy configuration |
providers | object | undefined | Custom 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 unavailableContext 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
| Event | Fired When |
|---|---|
request_started | A completion request begins |
request_completed | A completion request succeeds |
request_failed | A completion request fails |
stream_started | A streaming request begins |
stream_chunk | A streaming chunk is received |
stream_ended | A streaming request completes |
tool_called | A tool call is made |
tool_result | A tool call returns a result |
context_updated | Project context is changed |
governance_decision | A governance check produces a decision |
rate_limit_reached | Rate limit is exceeded |
approval_required | An 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>;
}