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

@ritwikranjan/copilot-agent-framework

v0.1.0

Published

Framework-agnostic wrapper for GitHub Copilot SDK with stateless session management, audit logging, and pluggable persistence — built for horizontal scaling

Readme

@ritwikranjan/copilot-agent-framework

Framework-agnostic library for building stateless agents on top of the GitHub Copilot SDK. Provides session management, audit logging, streaming, and pluggable persistence — with zero Azure or Teams dependencies. Built for horizontal scaling.

Installation

npm install @ritwikranjan/copilot-agent-framework @github/copilot-sdk

Or as a workspace dependency (within this monorepo):

{
  "dependencies": {
    "@ritwikranjan/copilot-agent-framework": "*"
  }
}

Quick Start

import {
  CopilotService,
  SessionManager,
  InMemorySessionStore,
  loadSystemPrompt,
} from '@ritwikranjan/copilot-agent-framework';

// 1. Create a session store (in-memory for dev, or your own DB implementation)
const sessionStore = new InMemorySessionStore();

// 2. Create a session manager
const sessionManager = new SessionManager({ store: sessionStore });

// 3. Create the Copilot service
const copilot = new CopilotService(
  {
    cliUrl: 'localhost:3000',
    model: 'gpt-5.2',
    agentName: 'my-agent',
    systemPrompt: loadSystemPrompt(),
  },
  sessionManager,
);

// 4. Send a message
const response = await copilot.sendMessage(
  'Hello!',
  { username: '[email protected]', hostname: 'my-app' },
  { conversationId: 'conv-1' },
);

console.log(response.response);

Streaming

import type { IStreamHandler } from '@ritwikranjan/copilot-agent-framework';

// Implement the generic stream handler for your framework
const streamHandler: IStreamHandler = {
  emit: (content) => process.stdout.write(content),
  update: (status) => console.log(`Status: ${status}`),
};

await copilot.sendMessageStreaming(
  'Write a poem',
  { username: '[email protected]', hostname: 'my-app' },
  streamHandler,
  { conversationId: 'conv-1' },
);

With Audit Logging

import {
  AuditManager,
  InMemoryAuditStore,
} from '@ritwikranjan/copilot-agent-framework';

const auditStore = new InMemoryAuditStore();
const auditManager = new AuditManager({ store: auditStore });

const copilot = new CopilotService(
  {
    cliUrl: 'localhost:3000',
    model: 'gpt-5.2',
    agentName: 'my-agent',
    enableAudit: true,
  },
  sessionManager,
  auditManager, // optional third argument
);

Logging

The library is silent by default — no console.log output. Logs use the debug package under the hood.

Enable via environment variable:

DEBUG=copilot:*              # All logs
DEBUG=copilot:session        # Session manager only
DEBUG=copilot:audit          # Audit manager only
DEBUG=copilot:service        # CopilotService only

Or inject a custom logger:

import { setLogger } from '@ritwikranjan/copilot-agent-framework';

// Route all SDK logs through your app's logger (e.g., winston, pino)
setLogger({
  debug: (msg, ...args) => myLogger.debug(msg, ...args),
  info:  (msg, ...args) => myLogger.info(msg, ...args),
  warn:  (msg, ...args) => myLogger.warn(msg, ...args),
  error: (msg, ...args) => myLogger.error(msg, ...args),
});

You can also pass a logger per-component:

const sessionManager = new SessionManager({ store: myStore, logger: myLogger });
const auditManager = new AuditManager({ store: myStore, logger: myLogger });
const copilot = new CopilotService({ ...config, logger: myLogger }, sessionManager);

API Reference

CopilotService

The main class for interacting with the Copilot SDK.

new CopilotService(config, sessionManager, auditManager?)

| Method | Description | | --- | --- | | getConfig() | Returns current service configuration | | sendMessage(message, userInfo, options?) | Send a synchronous message, returns CopilotResponse | | sendMessageStreaming(message, userInfo, streamHandler, options?) | Send a message with streaming via IStreamHandler | | getConversationSessionStatus(conversationId, username) | Check session status | | endConversationSession(conversationId, username) | End a session | | resumeConversationSession(conversationId, userInfo) | Resume an expired session |

CopilotServiceConfig

interface CopilotServiceConfig {
  cliUrl: string;           // CLI server URL (e.g., 'localhost:3000')
  model: string;            // Model name (e.g., 'gpt-5.2')
  agentName: string;        // Agent identifier
  systemPrompt?: string;    // System prompt content
  mcpServers?: MCPServerConfig[];  // MCP server configurations
  enableAudit?: boolean;    // Enable audit logging (default: true)
  logger?: ILogger;         // Custom logger (default: debug-based, silent)
}

SessionManager

Manages session lifecycle with pluggable persistence. Sessions expire after 12 hours by default.

new SessionManager({ store: ISessionStore })

Configure a custom session expiration:

const sessionManager = new SessionManager({
    store: sessionStore,
    sessionExpirationMs: 4 * 60 * 60 * 1000, // 4 hours (default: 12 hours)
});

| Method | Description | | --- | --- | | resolveSession(userInfo, options?) | Find or create a session | | getSessionStatus(username, conversationId) | Check session expiration status | | updateCopilotSessionId(username, sessionId, copilotSessionId) | Store SDK session ID for resume | | touchSession(username, sessionId) | Update last activity timestamp | | renameSession(username, sessionId, newName) | Rename a session | | endSession(username, sessionId, status?) | End a session | | endSessionByConversationId(username, conversationId, status?) | End by conversation ID | | getSession(username, sessionId) | Get session by ID | | getUserSessions(username) | List all user sessions |

AuditManager

Logs interactions and tool executions within a session.

new AuditManager({ store: IAuditStore })

| Method | Description | | --- | --- | | setSession(sessionId) | Set active session for logging | | startInteraction(userQuery) | Log start of user turn | | completeInteraction(response?, reasoning?) | Log agent response | | logToolStart(toolName, args?) | Log tool execution start | | logToolComplete(toolId, result?, error?) | Log tool execution completion | | logToolExecution(toolName, args?, result?, error?) | Log full tool execution in one call | | getSessionInteractions() | Query interactions for current session | | getSessionToolExecutions() | Query tool executions for current session |

Interfaces

ISessionStore

Implement this to use your own database for session persistence:

interface ISessionStore {
  initialize(): Promise<void>;
  createSession(sessionData: SessionInfo): Promise<SessionInfo>;
  updateSession(sessionId: string, partitionKey: string, sessionData: SessionInfo): Promise<SessionInfo>;
  getSession(sessionId: string, partitionKey: string): Promise<SessionInfo | null>;
  getSessionByName(username: string, sessionName: string): Promise<SessionInfo | null>;
  getSessionsByUser(username: string): Promise<SessionInfo[]>;
  getSessionByConversationId(username: string, conversationId: string): Promise<SessionInfo | null>;
}

IAuditStore

Implement this to use your own database for audit logging:

interface IAuditStore {
  initialize(): Promise<void>;
  createInteraction(data: Interaction): Promise<Interaction>;
  updateInteraction(id: string, partitionKey: string, data: Interaction): Promise<Interaction>;
  getInteractionsBySession(sessionId: string): Promise<Interaction[]>;
  createToolExecution(data: ToolExecution): Promise<ToolExecution>;
  updateToolExecution(id: string, partitionKey: string, data: ToolExecution): Promise<ToolExecution>;
  getToolExecutionsBySession(sessionId: string): Promise<ToolExecution[]>;
  getToolExecutionsByInteraction(sessionId: string, interactionId: string): Promise<ToolExecution[]>;
}

IStreamHandler

Implement this to adapt streaming to your framework:

interface IStreamHandler {
  emit(content: string): void;      // Emit content chunk
  update?(status: string): void;    // Optional status update
}

Built-in Stores

| Store | Description | | --- | --- | | InMemorySessionStore | In-memory session store for development and testing | | InMemoryAuditStore | In-memory audit store for development and testing |

Both stores provide clear() and getCounts() helpers for test setup/teardown.

Note: IAuditDataStore (combined ISessionStore & IAuditStore) is deprecated. Always implement the interfaces separately. The Teams Copilot Agent uses SessionCosmosStore and AuditCosmosStore as separate classes.

Utility Functions

| Function | Description | | --- | --- | | loadSystemPrompt(options?) | Load system prompt from env var or file paths | | loadToolsConfig(options?) | Load MCP tools configuration from JSON file | | buildMcpServersConfig(toolsConfig, extraEnv?) | Convert tools config to SDK format | | formatRemainingTime(remainingMs) | Format milliseconds as "Xh Ym" |

Models

| Type | Description | | --- | --- | | UserInfo | User identity (username, hostname) | | SessionInfo | Session metadata with expiration, conversation ID, SDK session ID | | Interaction | User query + agent response within a session | | ToolExecution | Tool call metadata (name, args, result, timing) | | CopilotResponse | Standard response envelope with success/error/session info | | SessionStatus | Enum: ACTIVE, COMPLETED, ERROR | | ToolExecutionStatus | Enum: STARTED, COMPLETED, ERROR |

Example: Building an HTTP API

import express from 'express';
import {
  CopilotService,
  SessionManager,
  AuditManager,
  InMemorySessionStore,
  InMemoryAuditStore,
  loadSystemPrompt,
} from '@ritwikranjan/copilot-agent-framework';

const app = express();
app.use(express.json());

const sessionStore = new InMemorySessionStore();
const auditStore = new InMemoryAuditStore();
const sessionManager = new SessionManager({ store: sessionStore });
const auditManager = new AuditManager({ store: auditStore });

const copilot = new CopilotService(
  {
    cliUrl: process.env.CLI_URL || 'localhost:3000',
    model: 'gpt-5.2',
    agentName: 'http-agent',
    systemPrompt: loadSystemPrompt(),
  },
  sessionManager,
  auditManager,
);

// POST /chat — synchronous
app.post('/chat', async (req, res) => {
  const { message, conversationId } = req.body;
  const userInfo = { username: 'api-user', hostname: 'http' };
  const response = await copilot.sendMessage(message, userInfo, { conversationId });
  res.json(response);
});

// POST /chat/stream — streaming via SSE
app.post('/chat/stream', async (req, res) => {
  const { message, conversationId } = req.body;
  const userInfo = { username: 'api-user', hostname: 'http' };

  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');

  const streamHandler = {
    emit: (content: string) => res.write(`data: ${JSON.stringify({ content })}\n\n`),
    update: (status: string) => res.write(`data: ${JSON.stringify({ status })}\n\n`),
  };

  const response = await copilot.sendMessageStreaming(message, userInfo, streamHandler, { conversationId });
  res.write(`data: ${JSON.stringify({ done: true, ...response })}\n\n`);
  res.end();
});

app.listen(3000);

Testing

npm test           # Run tests
npm run test:watch # Watch mode
npm run build      # Build the package

Publishing to npm

The package is structured for direct publishing:

npm run build
npm publish --access public

License

MIT