Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@copass/langchain

Package Overview
Dependencies
Maintainers
1
Versions
29
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@copass/langchain - npm Package Compare versions

Comparing version
0.5.9
to
0.5.10
+30
-1
dist/index.cjs

@@ -108,3 +108,32 @@ "use strict";

);
return { discover, interpret, search };
const get_origin = (0, import_tools.tool)(
async ({
canonical_ids,
limit_per_canonical
}) => {
const response = await client.retrieval.getOrigin(sandbox_id, {
canonical_ids,
...limit_per_canonical !== void 0 ? { limit_per_canonical } : {}
});
return {
sandbox_id: response.sandbox_id,
origins: response.origins.map((entry) => ({
canonical_id: entry.canonical_id,
files: entry.files.map((f) => ({
file_path: f.file_path,
extraction_count: f.extraction_count
}))
}))
};
},
{
name: "get_origin",
description: import_config.GET_ORIGIN_DESCRIPTION,
schema: import_zod.z.object({
canonical_ids: import_zod.z.array(import_zod.z.string()).min(1).max(100).describe(import_config.ORIGIN_CANONICAL_IDS_PARAM),
limit_per_canonical: import_zod.z.number().int().min(1).max(50).optional().describe(import_config.ORIGIN_LIMIT_PARAM)
})
}
);
return { discover, interpret, search, get_origin };
}

@@ -111,0 +140,0 @@

+1
-1

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/index.ts","../src/agent.ts","../src/tools.ts","../src/callback.ts"],"sourcesContent":["export { createCopassAgent } from './agent.js';\nexport type { CreateCopassAgentOptions } from './agent.js';\nexport { copassTools } from './tools.js';\nexport type { CopassToolsOptions } from './tools.js';\nexport { CopassWindowCallback } from './callback.js';\nexport type { CopassWindowCallbackOptions } from './callback.js';\n","import { createReactAgent } from '@langchain/langgraph/prebuilt';\nimport type { BaseChatModel } from '@langchain/core/language_models/chat_models';\nimport type { StructuredToolInterface } from '@langchain/core/tools';\nimport type { Runnable, RunnableConfig } from '@langchain/core/runnables';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport type { CopassClient, ContextWindow, SearchPreset } from '@copass/core';\nimport { copassTools } from './tools.js';\nimport { CopassWindowCallback } from './callback.js';\n\ntype ReactAgentExtraOptions = Omit<\n Parameters<typeof createReactAgent>[0],\n 'llm' | 'tools'\n>;\n\n/**\n * Output type of {@link createCopassAgent}. A standard LangChain `Runnable`\n * producing `{ messages: BaseMessage[] }` on `.invoke()` / `.stream()`.\n *\n * The concrete inferred type (a LangGraph `CompiledStateGraph`) can't be\n * serialized into a portable declaration, so we expose the narrower\n * `Runnable` surface that's sufficient for every public method a caller\n * needs — `.invoke`, `.stream`, `.streamEvents`, `.batch`, `.pipe`,\n * `.withConfig`, etc. Escape hatch if you need the raw graph: cast the\n * result.\n */\nexport type CopassAgent = Runnable<\n { messages: BaseMessage[] } | BaseMessage[] | string,\n { messages: BaseMessage[] },\n RunnableConfig\n>;\n\nexport interface CreateCopassAgentOptions extends ReactAgentExtraOptions {\n /** An authenticated `@copass/core` client. */\n client: CopassClient;\n /** Sandbox all retrieval runs against. */\n sandbox_id: string;\n /**\n * Context Window for this conversation thread. Turn history is mirrored\n * into it automatically across every chat model invocation — retrieval\n * becomes window-aware with no per-call wiring.\n */\n window: ContextWindow;\n /** The chat model (e.g. `new ChatAnthropic(...)`, `new ChatOpenAI(...)`). */\n llm: BaseChatModel;\n /** Additional tools to mix in alongside `discover` / `interpret` / `search`. */\n tools?: StructuredToolInterface[];\n /** Optional project scoping for retrieval. */\n project_id?: string;\n /** Preset for `interpret` / `search`. Default: `\"copass/1.0\"`. */\n preset?: SearchPreset;\n /** Include LangChain `ToolMessage`s in the Context Window. Default: `false`. */\n includeToolMessages?: boolean;\n}\n\n/**\n * Create a LangChain agent pre-wired with Copass retrieval tools and\n * window-aware memory.\n *\n * Returns a standard LangChain Runnable — call `.invoke()`, `.stream()`,\n * `.streamEvents()`, `.batch()` as you would with the output of\n * `createReactAgent`. The Copass Context Window is mirrored from the\n * conversation history automatically; you don't pass callbacks, trackers,\n * or lifecycle hooks.\n *\n * @example\n * ```ts\n * import { ChatAnthropic } from '@langchain/anthropic';\n * import { CopassClient } from '@copass/core';\n * import { createCopassAgent } from '@copass/langchain';\n *\n * const copass = new CopassClient({ auth: { type: 'bearer', token: process.env.COPASS_API_KEY! } });\n * const window = await copass.contextWindow.create({ sandbox_id });\n *\n * const agent = createCopassAgent({\n * client: copass,\n * sandbox_id,\n * window,\n * llm: new ChatAnthropic({ model: 'claude-opus-4-7' }),\n * });\n *\n * const result = await agent.invoke({\n * messages: [{ role: 'user', content: 'why is checkout flaky?' }],\n * });\n * ```\n *\n * ### How the wiring works\n *\n * Under the hood this composes three primitives also exported from this\n * package — in case you want to build your own variant:\n *\n * - `copassTools({ client, sandbox_id, window, ... })` — the three retrieval\n * tools, window-aware at the server call level.\n * - `createReactAgent({ llm, tools, ... })` — the standard LangGraph ReAct agent.\n * - `CopassWindowCallback({ window })` — bound via `Runnable.withConfig` so\n * every chat model invocation auto-mirrors messages into the window.\n */\nexport function createCopassAgent(options: CreateCopassAgentOptions): CopassAgent {\n const {\n client,\n sandbox_id,\n window,\n llm,\n tools: extraTools = [],\n project_id,\n preset,\n includeToolMessages,\n ...reactAgentOptions\n } = options;\n\n const copass = copassTools({ client, sandbox_id, window, project_id, preset });\n const callback = new CopassWindowCallback({ window, includeToolMessages });\n\n const agent = createReactAgent({\n llm,\n tools: [copass.discover, copass.interpret, copass.search, ...extraTools],\n ...reactAgentOptions,\n });\n\n // `withConfig` returns a RunnableBinding that propagates these callbacks\n // to every child Runnable invocation — including the chat model calls\n // that trigger `CopassWindowCallback.handleChatModelStart`. The cast\n // narrows LangGraph's concrete graph type to the portable Runnable\n // surface we want to expose.\n return agent.withConfig({ callbacks: [callback] }) as unknown as CopassAgent;\n}\n","import { tool } from '@langchain/core/tools';\nimport { z } from 'zod';\nimport {\n DISCOVER_DESCRIPTION,\n DISCOVER_QUERY_PARAM,\n INTERPRET_DESCRIPTION,\n INTERPRET_ITEMS_PARAM,\n INTERPRET_QUERY_PARAM,\n SEARCH_DESCRIPTION,\n SEARCH_QUERY_PARAM,\n} from '@copass/config';\nimport type { CopassClient, SearchPreset, WindowLike } from '@copass/core';\n\nexport interface CopassToolsOptions {\n /** An authenticated `@copass/core` client. */\n client: CopassClient;\n /** Sandbox all retrieval runs against. */\n sandbox_id: string;\n /** Optional project scoping for retrieval calls. */\n project_id?: string;\n /**\n * Optional conversation window — a {@link WindowLike} (typically a\n * `ContextWindow` from `client.contextWindow.create()`). When provided,\n * every retrieval call is automatically window-aware.\n */\n window?: WindowLike;\n /**\n * Preset for `discover`, `interpret`, and `search`. Defaults to\n * `\"copass/copass_1.0\"`. Under `\"copass/copass_2.0\"` discover items\n * carry `subgraph` (pre-rendered ASCII tree) and `matched_query_nodes`\n * fields. Append `\":thinking\"` (e.g. `\"copass/copass_2.0:thinking\"`)\n * to enable task decomposition before retrieval on `search`.\n */\n preset?: SearchPreset;\n}\n\n/**\n * Return Copass retrieval as a set of LangChain tool objects (built with\n * `tool()` from `@langchain/core/tools`).\n *\n * Agent-framework-neutral shape: the LLM picks `discover` (menu of\n * relevant items) or `search` (synthesized answer). `interpret` stays\n * registered for back-compat but is legacy — prefer `search` for drill-in.\n *\n * @example\n * ```ts\n * import { ChatAnthropic } from '@langchain/anthropic';\n * import { createReactAgent } from '@langchain/langgraph/prebuilt';\n * import { copassTools } from '@copass/langchain';\n *\n * const window = await copass.contextWindow.create({ sandbox_id });\n * const tools = copassTools({ client: copass, sandbox_id, window });\n *\n * const agent = createReactAgent({\n * llm: new ChatAnthropic({ model: 'claude-opus-4-7' }),\n * tools: [tools.discover, tools.interpret, tools.search],\n * });\n *\n * const result = await agent.invoke({\n * messages: [{ role: 'user', content: 'why is checkout flaky?' }],\n * });\n * ```\n */\nexport function copassTools(options: CopassToolsOptions) {\n const { client, sandbox_id, project_id, window, preset = 'copass/copass_1.0' } = options;\n\n const discover = tool(\n async ({ query }: { query: string }) => {\n const response = await client.retrieval.discover(sandbox_id, {\n query,\n project_id,\n window,\n preset,\n });\n return {\n header: response.header,\n // Project the v2 fields (`subgraph` + `matched_query_nodes`)\n // alongside the v1 fields. Populated only under\n // `copass/copass_2.0` (or its `copass/2.0` alias); `null` under\n // v1 — agents can ignore them when not present.\n items: response.items.map((item) => ({\n score: item.score,\n summary: item.summary,\n canonical_ids: item.canonical_ids,\n subgraph: item.subgraph ?? null,\n matched_query_nodes: item.matched_query_nodes ?? null,\n })),\n next_steps: response.next_steps,\n };\n },\n {\n name: 'discover',\n description: DISCOVER_DESCRIPTION,\n schema: z.object({\n query: z.string().describe(DISCOVER_QUERY_PARAM),\n }),\n },\n );\n\n const interpret = tool(\n async ({ query, items }: { query: string; items: string[][] }) => {\n const response = await client.retrieval.interpret(sandbox_id, {\n query,\n items,\n project_id,\n window,\n preset,\n });\n return { brief: response.brief };\n },\n {\n name: 'interpret',\n description: INTERPRET_DESCRIPTION,\n schema: z.object({\n query: z.string().describe(INTERPRET_QUERY_PARAM),\n items: z.array(z.array(z.string())).min(1).describe(INTERPRET_ITEMS_PARAM),\n }),\n },\n );\n\n const search = tool(\n async ({ query }: { query: string }) => {\n const response = await client.retrieval.search(sandbox_id, {\n query,\n project_id,\n window,\n preset,\n });\n return { answer: response.answer };\n },\n {\n name: 'search',\n description: SEARCH_DESCRIPTION,\n schema: z.object({\n query: z.string().describe(SEARCH_QUERY_PARAM),\n }),\n },\n );\n\n return { discover, interpret, search };\n}\n","import { BaseCallbackHandler, type BaseCallbackHandlerInput } from '@langchain/core/callbacks/base';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport type { ChatMessage, ChatRole, ContextWindow } from '@copass/core';\n\nexport interface CopassWindowCallbackOptions extends BaseCallbackHandlerInput {\n /** The Context Window to mirror messages into. */\n window: ContextWindow;\n /**\n * Include LangChain `ToolMessage`s (tool-call results) as turns. Default: false.\n *\n * Tool results tend to be noisy — the underlying graph already has the\n * retrieved content indexed — so they're skipped by default. Enable only if\n * your agent's tool results carry conceptual content you want retrieval to\n * dedupe against.\n */\n includeToolMessages?: boolean;\n}\n\n/**\n * LangChain callback that auto-mirrors the chat model's conversation history\n * into a Copass {@link ContextWindow}.\n *\n * Hooks `handleChatModelStart`, which fires before every chat model invocation\n * with the full message history. We walk that history and call\n * {@link ContextWindow.addTurn} for any message we haven't seen — so retrieval\n * tools called inside the same agent step get a window that reflects the\n * actual conversation, not an empty buffer.\n *\n * @example\n * ```ts\n * import { CopassWindowCallback, copassTools } from '@copass/langchain';\n *\n * const tools = copassTools({ client, sandbox_id, window });\n * const agent = createReactAgent({ llm, tools: Object.values(tools) });\n *\n * await agent.invoke(\n * { messages: [{ role: 'user', content: 'why is checkout flaky?' }] },\n * { callbacks: [new CopassWindowCallback({ window })] },\n * );\n * ```\n */\nexport class CopassWindowCallback extends BaseCallbackHandler {\n readonly name = 'copass-window';\n private readonly window: ContextWindow;\n private readonly includeToolMessages: boolean;\n private readonly seen = new Set<string>();\n\n constructor(options: CopassWindowCallbackOptions) {\n super(options);\n this.window = options.window;\n this.includeToolMessages = options.includeToolMessages ?? false;\n // Seed the dedup set with turns already in the window so we don't re-add.\n for (const turn of options.window.getTurns()) {\n this.seen.add(hashTurn(turn));\n }\n }\n\n async handleChatModelStart(\n _llm: unknown,\n messages: BaseMessage[][],\n ): Promise<void> {\n // `messages` is `BaseMessage[][]` because chat models support batched calls.\n // Flatten — in agent loops this is always a single conversation.\n for (const msg of messages.flat()) {\n const turn = toTurn(msg, this.includeToolMessages);\n if (!turn) continue;\n\n const key = hashTurn(turn);\n if (this.seen.has(key)) continue;\n this.seen.add(key);\n\n // Fire-and-forget — window.addTurn pushes into the graph. We don't block\n // the model call on ingestion latency; we swallow errors because missing\n // a turn is recoverable (retrieval still sees already-added turns).\n this.window.addTurn(turn).catch(() => {\n /* intentionally empty */\n });\n }\n }\n}\n\nfunction toTurn(msg: BaseMessage, includeToolMessages: boolean): ChatMessage | null {\n const role = roleFromMessage(msg, includeToolMessages);\n if (!role) return null;\n\n const content = contentToString(msg.content);\n if (!content.trim()) return null;\n\n return { role, content };\n}\n\nfunction roleFromMessage(msg: BaseMessage, includeToolMessages: boolean): ChatRole | null {\n const type = msg.getType();\n switch (type) {\n case 'human':\n return 'user';\n case 'ai':\n return 'assistant';\n case 'system':\n return 'system';\n case 'tool':\n return includeToolMessages ? 'system' : null;\n default:\n return null;\n }\n}\n\nfunction contentToString(content: unknown): string {\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return String(content ?? '');\n return content\n .map((part) => {\n if (typeof part === 'string') return part;\n if (part && typeof part === 'object' && 'text' in (part as Record<string, unknown>)) {\n return String((part as Record<string, unknown>).text ?? '');\n }\n return '';\n })\n .filter(Boolean)\n .join('\\n');\n}\n\nfunction hashTurn(turn: ChatMessage): string {\n // Stable-ish hash: role + first 500 chars of content.\n // Collisions across different long messages starting identically are accepted\n // as benign — worst case, we skip adding one turn.\n return `${turn.role}:${turn.content.slice(0, 500)}`;\n}\n"],"mappings":"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","names":[]}
{"version":3,"sources":["../src/index.ts","../src/agent.ts","../src/tools.ts","../src/callback.ts"],"sourcesContent":["export { createCopassAgent } from './agent.js';\nexport type { CreateCopassAgentOptions } from './agent.js';\nexport { copassTools } from './tools.js';\nexport type { CopassToolsOptions } from './tools.js';\nexport { CopassWindowCallback } from './callback.js';\nexport type { CopassWindowCallbackOptions } from './callback.js';\n","import { createReactAgent } from '@langchain/langgraph/prebuilt';\nimport type { BaseChatModel } from '@langchain/core/language_models/chat_models';\nimport type { StructuredToolInterface } from '@langchain/core/tools';\nimport type { Runnable, RunnableConfig } from '@langchain/core/runnables';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport type { CopassClient, ContextWindow, SearchPreset } from '@copass/core';\nimport { copassTools } from './tools.js';\nimport { CopassWindowCallback } from './callback.js';\n\ntype ReactAgentExtraOptions = Omit<\n Parameters<typeof createReactAgent>[0],\n 'llm' | 'tools'\n>;\n\n/**\n * Output type of {@link createCopassAgent}. A standard LangChain `Runnable`\n * producing `{ messages: BaseMessage[] }` on `.invoke()` / `.stream()`.\n *\n * The concrete inferred type (a LangGraph `CompiledStateGraph`) can't be\n * serialized into a portable declaration, so we expose the narrower\n * `Runnable` surface that's sufficient for every public method a caller\n * needs — `.invoke`, `.stream`, `.streamEvents`, `.batch`, `.pipe`,\n * `.withConfig`, etc. Escape hatch if you need the raw graph: cast the\n * result.\n */\nexport type CopassAgent = Runnable<\n { messages: BaseMessage[] } | BaseMessage[] | string,\n { messages: BaseMessage[] },\n RunnableConfig\n>;\n\nexport interface CreateCopassAgentOptions extends ReactAgentExtraOptions {\n /** An authenticated `@copass/core` client. */\n client: CopassClient;\n /** Sandbox all retrieval runs against. */\n sandbox_id: string;\n /**\n * Context Window for this conversation thread. Turn history is mirrored\n * into it automatically across every chat model invocation — retrieval\n * becomes window-aware with no per-call wiring.\n */\n window: ContextWindow;\n /** The chat model (e.g. `new ChatAnthropic(...)`, `new ChatOpenAI(...)`). */\n llm: BaseChatModel;\n /** Additional tools to mix in alongside `discover` / `interpret` / `search`. */\n tools?: StructuredToolInterface[];\n /** Optional project scoping for retrieval. */\n project_id?: string;\n /** Preset for `interpret` / `search`. Default: `\"copass/1.0\"`. */\n preset?: SearchPreset;\n /** Include LangChain `ToolMessage`s in the Context Window. Default: `false`. */\n includeToolMessages?: boolean;\n}\n\n/**\n * Create a LangChain agent pre-wired with Copass retrieval tools and\n * window-aware memory.\n *\n * Returns a standard LangChain Runnable — call `.invoke()`, `.stream()`,\n * `.streamEvents()`, `.batch()` as you would with the output of\n * `createReactAgent`. The Copass Context Window is mirrored from the\n * conversation history automatically; you don't pass callbacks, trackers,\n * or lifecycle hooks.\n *\n * @example\n * ```ts\n * import { ChatAnthropic } from '@langchain/anthropic';\n * import { CopassClient } from '@copass/core';\n * import { createCopassAgent } from '@copass/langchain';\n *\n * const copass = new CopassClient({ auth: { type: 'bearer', token: process.env.COPASS_API_KEY! } });\n * const window = await copass.contextWindow.create({ sandbox_id });\n *\n * const agent = createCopassAgent({\n * client: copass,\n * sandbox_id,\n * window,\n * llm: new ChatAnthropic({ model: 'claude-opus-4-7' }),\n * });\n *\n * const result = await agent.invoke({\n * messages: [{ role: 'user', content: 'why is checkout flaky?' }],\n * });\n * ```\n *\n * ### How the wiring works\n *\n * Under the hood this composes three primitives also exported from this\n * package — in case you want to build your own variant:\n *\n * - `copassTools({ client, sandbox_id, window, ... })` — the three retrieval\n * tools, window-aware at the server call level.\n * - `createReactAgent({ llm, tools, ... })` — the standard LangGraph ReAct agent.\n * - `CopassWindowCallback({ window })` — bound via `Runnable.withConfig` so\n * every chat model invocation auto-mirrors messages into the window.\n */\nexport function createCopassAgent(options: CreateCopassAgentOptions): CopassAgent {\n const {\n client,\n sandbox_id,\n window,\n llm,\n tools: extraTools = [],\n project_id,\n preset,\n includeToolMessages,\n ...reactAgentOptions\n } = options;\n\n const copass = copassTools({ client, sandbox_id, window, project_id, preset });\n const callback = new CopassWindowCallback({ window, includeToolMessages });\n\n const agent = createReactAgent({\n llm,\n tools: [copass.discover, copass.interpret, copass.search, ...extraTools],\n ...reactAgentOptions,\n });\n\n // `withConfig` returns a RunnableBinding that propagates these callbacks\n // to every child Runnable invocation — including the chat model calls\n // that trigger `CopassWindowCallback.handleChatModelStart`. The cast\n // narrows LangGraph's concrete graph type to the portable Runnable\n // surface we want to expose.\n return agent.withConfig({ callbacks: [callback] }) as unknown as CopassAgent;\n}\n","import { tool } from '@langchain/core/tools';\nimport { z } from 'zod';\nimport {\n DISCOVER_DESCRIPTION,\n DISCOVER_QUERY_PARAM,\n GET_ORIGIN_DESCRIPTION,\n INTERPRET_DESCRIPTION,\n INTERPRET_ITEMS_PARAM,\n INTERPRET_QUERY_PARAM,\n ORIGIN_CANONICAL_IDS_PARAM,\n ORIGIN_LIMIT_PARAM,\n SEARCH_DESCRIPTION,\n SEARCH_QUERY_PARAM,\n} from '@copass/config';\nimport type {\n CopassClient,\n OriginEntry,\n OriginFile,\n SearchPreset,\n WindowLike,\n} from '@copass/core';\n\nexport interface CopassToolsOptions {\n /** An authenticated `@copass/core` client. */\n client: CopassClient;\n /** Sandbox all retrieval runs against. */\n sandbox_id: string;\n /** Optional project scoping for retrieval calls. */\n project_id?: string;\n /**\n * Optional conversation window — a {@link WindowLike} (typically a\n * `ContextWindow` from `client.contextWindow.create()`). When provided,\n * every retrieval call is automatically window-aware.\n */\n window?: WindowLike;\n /**\n * Preset for `discover`, `interpret`, and `search`. Defaults to\n * `\"copass/copass_1.0\"`. Under `\"copass/copass_2.0\"` discover items\n * carry `subgraph` (pre-rendered ASCII tree) and `matched_query_nodes`\n * fields. Append `\":thinking\"` (e.g. `\"copass/copass_2.0:thinking\"`)\n * to enable task decomposition before retrieval on `search`.\n */\n preset?: SearchPreset;\n}\n\n/**\n * Return Copass retrieval as a set of LangChain tool objects (built with\n * `tool()` from `@langchain/core/tools`).\n *\n * Agent-framework-neutral shape: the LLM picks `discover` (menu of\n * relevant items) or `search` (synthesized answer). `interpret` stays\n * registered for back-compat but is legacy — prefer `search` for drill-in.\n *\n * @example\n * ```ts\n * import { ChatAnthropic } from '@langchain/anthropic';\n * import { createReactAgent } from '@langchain/langgraph/prebuilt';\n * import { copassTools } from '@copass/langchain';\n *\n * const window = await copass.contextWindow.create({ sandbox_id });\n * const tools = copassTools({ client: copass, sandbox_id, window });\n *\n * const agent = createReactAgent({\n * llm: new ChatAnthropic({ model: 'claude-opus-4-7' }),\n * tools: [tools.discover, tools.interpret, tools.search],\n * });\n *\n * const result = await agent.invoke({\n * messages: [{ role: 'user', content: 'why is checkout flaky?' }],\n * });\n * ```\n */\nexport function copassTools(options: CopassToolsOptions) {\n const { client, sandbox_id, project_id, window, preset = 'copass/copass_1.0' } = options;\n\n const discover = tool(\n async ({ query }: { query: string }) => {\n const response = await client.retrieval.discover(sandbox_id, {\n query,\n project_id,\n window,\n preset,\n });\n return {\n header: response.header,\n // Project the v2 fields (`subgraph` + `matched_query_nodes`)\n // alongside the v1 fields. Populated only under\n // `copass/copass_2.0` (or its `copass/2.0` alias); `null` under\n // v1 — agents can ignore them when not present.\n items: response.items.map((item) => ({\n score: item.score,\n summary: item.summary,\n canonical_ids: item.canonical_ids,\n subgraph: item.subgraph ?? null,\n matched_query_nodes: item.matched_query_nodes ?? null,\n })),\n next_steps: response.next_steps,\n };\n },\n {\n name: 'discover',\n description: DISCOVER_DESCRIPTION,\n schema: z.object({\n query: z.string().describe(DISCOVER_QUERY_PARAM),\n }),\n },\n );\n\n const interpret = tool(\n async ({ query, items }: { query: string; items: string[][] }) => {\n const response = await client.retrieval.interpret(sandbox_id, {\n query,\n items,\n project_id,\n window,\n preset,\n });\n return { brief: response.brief };\n },\n {\n name: 'interpret',\n description: INTERPRET_DESCRIPTION,\n schema: z.object({\n query: z.string().describe(INTERPRET_QUERY_PARAM),\n items: z.array(z.array(z.string())).min(1).describe(INTERPRET_ITEMS_PARAM),\n }),\n },\n );\n\n const search = tool(\n async ({ query }: { query: string }) => {\n const response = await client.retrieval.search(sandbox_id, {\n query,\n project_id,\n window,\n preset,\n });\n return { answer: response.answer };\n },\n {\n name: 'search',\n description: SEARCH_DESCRIPTION,\n schema: z.object({\n query: z.string().describe(SEARCH_QUERY_PARAM),\n }),\n },\n );\n\n const get_origin = tool(\n async ({\n canonical_ids,\n limit_per_canonical,\n }: {\n canonical_ids: string[];\n limit_per_canonical?: number;\n }) => {\n const response = await client.retrieval.getOrigin(sandbox_id, {\n canonical_ids,\n ...(limit_per_canonical !== undefined ? { limit_per_canonical } : {}),\n });\n return {\n sandbox_id: response.sandbox_id,\n origins: response.origins.map((entry: OriginEntry) => ({\n canonical_id: entry.canonical_id,\n files: entry.files.map((f: OriginFile) => ({\n file_path: f.file_path,\n extraction_count: f.extraction_count,\n })),\n })),\n };\n },\n {\n name: 'get_origin',\n description: GET_ORIGIN_DESCRIPTION,\n schema: z.object({\n canonical_ids: z\n .array(z.string())\n .min(1)\n .max(100)\n .describe(ORIGIN_CANONICAL_IDS_PARAM),\n limit_per_canonical: z\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .describe(ORIGIN_LIMIT_PARAM),\n }),\n },\n );\n\n return { discover, interpret, search, get_origin };\n}\n","import { BaseCallbackHandler, type BaseCallbackHandlerInput } from '@langchain/core/callbacks/base';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport type { ChatMessage, ChatRole, ContextWindow } from '@copass/core';\n\nexport interface CopassWindowCallbackOptions extends BaseCallbackHandlerInput {\n /** The Context Window to mirror messages into. */\n window: ContextWindow;\n /**\n * Include LangChain `ToolMessage`s (tool-call results) as turns. Default: false.\n *\n * Tool results tend to be noisy — the underlying graph already has the\n * retrieved content indexed — so they're skipped by default. Enable only if\n * your agent's tool results carry conceptual content you want retrieval to\n * dedupe against.\n */\n includeToolMessages?: boolean;\n}\n\n/**\n * LangChain callback that auto-mirrors the chat model's conversation history\n * into a Copass {@link ContextWindow}.\n *\n * Hooks `handleChatModelStart`, which fires before every chat model invocation\n * with the full message history. We walk that history and call\n * {@link ContextWindow.addTurn} for any message we haven't seen — so retrieval\n * tools called inside the same agent step get a window that reflects the\n * actual conversation, not an empty buffer.\n *\n * @example\n * ```ts\n * import { CopassWindowCallback, copassTools } from '@copass/langchain';\n *\n * const tools = copassTools({ client, sandbox_id, window });\n * const agent = createReactAgent({ llm, tools: Object.values(tools) });\n *\n * await agent.invoke(\n * { messages: [{ role: 'user', content: 'why is checkout flaky?' }] },\n * { callbacks: [new CopassWindowCallback({ window })] },\n * );\n * ```\n */\nexport class CopassWindowCallback extends BaseCallbackHandler {\n readonly name = 'copass-window';\n private readonly window: ContextWindow;\n private readonly includeToolMessages: boolean;\n private readonly seen = new Set<string>();\n\n constructor(options: CopassWindowCallbackOptions) {\n super(options);\n this.window = options.window;\n this.includeToolMessages = options.includeToolMessages ?? false;\n // Seed the dedup set with turns already in the window so we don't re-add.\n for (const turn of options.window.getTurns()) {\n this.seen.add(hashTurn(turn));\n }\n }\n\n async handleChatModelStart(\n _llm: unknown,\n messages: BaseMessage[][],\n ): Promise<void> {\n // `messages` is `BaseMessage[][]` because chat models support batched calls.\n // Flatten — in agent loops this is always a single conversation.\n for (const msg of messages.flat()) {\n const turn = toTurn(msg, this.includeToolMessages);\n if (!turn) continue;\n\n const key = hashTurn(turn);\n if (this.seen.has(key)) continue;\n this.seen.add(key);\n\n // Fire-and-forget — window.addTurn pushes into the graph. We don't block\n // the model call on ingestion latency; we swallow errors because missing\n // a turn is recoverable (retrieval still sees already-added turns).\n this.window.addTurn(turn).catch(() => {\n /* intentionally empty */\n });\n }\n }\n}\n\nfunction toTurn(msg: BaseMessage, includeToolMessages: boolean): ChatMessage | null {\n const role = roleFromMessage(msg, includeToolMessages);\n if (!role) return null;\n\n const content = contentToString(msg.content);\n if (!content.trim()) return null;\n\n return { role, content };\n}\n\nfunction roleFromMessage(msg: BaseMessage, includeToolMessages: boolean): ChatRole | null {\n const type = msg.getType();\n switch (type) {\n case 'human':\n return 'user';\n case 'ai':\n return 'assistant';\n case 'system':\n return 'system';\n case 'tool':\n return includeToolMessages ? 'system' : null;\n default:\n return null;\n }\n}\n\nfunction contentToString(content: unknown): string {\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return String(content ?? '');\n return content\n .map((part) => {\n if (typeof part === 'string') return part;\n if (part && typeof part === 'object' && 'text' in (part as Record<string, unknown>)) {\n return String((part as Record<string, unknown>).text ?? '');\n }\n return '';\n })\n .filter(Boolean)\n .join('\\n');\n}\n\nfunction hashTurn(turn: ChatMessage): string {\n // Stable-ish hash: role + first 500 chars of content.\n // Collisions across different long messages starting identically are accepted\n // as benign — worst case, we skip adding one turn.\n return `${turn.role}:${turn.content.slice(0, 500)}`;\n}\n"],"mappings":"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","names":[]}

@@ -182,2 +182,21 @@ import { createReactAgent } from '@langchain/langgraph/prebuilt';

}, unknown, "search">;
get_origin: _langchain_core_tools.DynamicStructuredTool<z.ZodObject<{
canonical_ids: z.ZodArray<z.ZodString>;
limit_per_canonical: z.ZodOptional<z.ZodNumber>;
}, z.core.$strip>, {
canonical_ids: string[];
limit_per_canonical?: number | undefined;
}, {
canonical_ids: string[];
limit_per_canonical?: number | undefined;
}, {
sandbox_id: string;
origins: {
canonical_id: string;
files: {
file_path: string;
extraction_count: number;
}[];
}[];
}, unknown, "get_origin">;
};

@@ -184,0 +203,0 @@

@@ -182,2 +182,21 @@ import { createReactAgent } from '@langchain/langgraph/prebuilt';

}, unknown, "search">;
get_origin: _langchain_core_tools.DynamicStructuredTool<z.ZodObject<{
canonical_ids: z.ZodArray<z.ZodString>;
limit_per_canonical: z.ZodOptional<z.ZodNumber>;
}, z.core.$strip>, {
canonical_ids: string[];
limit_per_canonical?: number | undefined;
}, {
canonical_ids: string[];
limit_per_canonical?: number | undefined;
}, {
sandbox_id: string;
origins: {
canonical_id: string;
files: {
file_path: string;
extraction_count: number;
}[];
}[];
}, unknown, "get_origin">;
};

@@ -184,0 +203,0 @@

@@ -10,5 +10,8 @@ // src/agent.ts

DISCOVER_QUERY_PARAM,
GET_ORIGIN_DESCRIPTION,
INTERPRET_DESCRIPTION,
INTERPRET_ITEMS_PARAM,
INTERPRET_QUERY_PARAM,
ORIGIN_CANONICAL_IDS_PARAM,
ORIGIN_LIMIT_PARAM,
SEARCH_DESCRIPTION,

@@ -89,3 +92,32 @@ SEARCH_QUERY_PARAM

);
return { discover, interpret, search };
const get_origin = tool(
async ({
canonical_ids,
limit_per_canonical
}) => {
const response = await client.retrieval.getOrigin(sandbox_id, {
canonical_ids,
...limit_per_canonical !== void 0 ? { limit_per_canonical } : {}
});
return {
sandbox_id: response.sandbox_id,
origins: response.origins.map((entry) => ({
canonical_id: entry.canonical_id,
files: entry.files.map((f) => ({
file_path: f.file_path,
extraction_count: f.extraction_count
}))
}))
};
},
{
name: "get_origin",
description: GET_ORIGIN_DESCRIPTION,
schema: z.object({
canonical_ids: z.array(z.string()).min(1).max(100).describe(ORIGIN_CANONICAL_IDS_PARAM),
limit_per_canonical: z.number().int().min(1).max(50).optional().describe(ORIGIN_LIMIT_PARAM)
})
}
);
return { discover, interpret, search, get_origin };
}

@@ -92,0 +124,0 @@

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/agent.ts","../src/tools.ts","../src/callback.ts"],"sourcesContent":["import { createReactAgent } from '@langchain/langgraph/prebuilt';\nimport type { BaseChatModel } from '@langchain/core/language_models/chat_models';\nimport type { StructuredToolInterface } from '@langchain/core/tools';\nimport type { Runnable, RunnableConfig } from '@langchain/core/runnables';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport type { CopassClient, ContextWindow, SearchPreset } from '@copass/core';\nimport { copassTools } from './tools.js';\nimport { CopassWindowCallback } from './callback.js';\n\ntype ReactAgentExtraOptions = Omit<\n Parameters<typeof createReactAgent>[0],\n 'llm' | 'tools'\n>;\n\n/**\n * Output type of {@link createCopassAgent}. A standard LangChain `Runnable`\n * producing `{ messages: BaseMessage[] }` on `.invoke()` / `.stream()`.\n *\n * The concrete inferred type (a LangGraph `CompiledStateGraph`) can't be\n * serialized into a portable declaration, so we expose the narrower\n * `Runnable` surface that's sufficient for every public method a caller\n * needs — `.invoke`, `.stream`, `.streamEvents`, `.batch`, `.pipe`,\n * `.withConfig`, etc. Escape hatch if you need the raw graph: cast the\n * result.\n */\nexport type CopassAgent = Runnable<\n { messages: BaseMessage[] } | BaseMessage[] | string,\n { messages: BaseMessage[] },\n RunnableConfig\n>;\n\nexport interface CreateCopassAgentOptions extends ReactAgentExtraOptions {\n /** An authenticated `@copass/core` client. */\n client: CopassClient;\n /** Sandbox all retrieval runs against. */\n sandbox_id: string;\n /**\n * Context Window for this conversation thread. Turn history is mirrored\n * into it automatically across every chat model invocation — retrieval\n * becomes window-aware with no per-call wiring.\n */\n window: ContextWindow;\n /** The chat model (e.g. `new ChatAnthropic(...)`, `new ChatOpenAI(...)`). */\n llm: BaseChatModel;\n /** Additional tools to mix in alongside `discover` / `interpret` / `search`. */\n tools?: StructuredToolInterface[];\n /** Optional project scoping for retrieval. */\n project_id?: string;\n /** Preset for `interpret` / `search`. Default: `\"copass/1.0\"`. */\n preset?: SearchPreset;\n /** Include LangChain `ToolMessage`s in the Context Window. Default: `false`. */\n includeToolMessages?: boolean;\n}\n\n/**\n * Create a LangChain agent pre-wired with Copass retrieval tools and\n * window-aware memory.\n *\n * Returns a standard LangChain Runnable — call `.invoke()`, `.stream()`,\n * `.streamEvents()`, `.batch()` as you would with the output of\n * `createReactAgent`. The Copass Context Window is mirrored from the\n * conversation history automatically; you don't pass callbacks, trackers,\n * or lifecycle hooks.\n *\n * @example\n * ```ts\n * import { ChatAnthropic } from '@langchain/anthropic';\n * import { CopassClient } from '@copass/core';\n * import { createCopassAgent } from '@copass/langchain';\n *\n * const copass = new CopassClient({ auth: { type: 'bearer', token: process.env.COPASS_API_KEY! } });\n * const window = await copass.contextWindow.create({ sandbox_id });\n *\n * const agent = createCopassAgent({\n * client: copass,\n * sandbox_id,\n * window,\n * llm: new ChatAnthropic({ model: 'claude-opus-4-7' }),\n * });\n *\n * const result = await agent.invoke({\n * messages: [{ role: 'user', content: 'why is checkout flaky?' }],\n * });\n * ```\n *\n * ### How the wiring works\n *\n * Under the hood this composes three primitives also exported from this\n * package — in case you want to build your own variant:\n *\n * - `copassTools({ client, sandbox_id, window, ... })` — the three retrieval\n * tools, window-aware at the server call level.\n * - `createReactAgent({ llm, tools, ... })` — the standard LangGraph ReAct agent.\n * - `CopassWindowCallback({ window })` — bound via `Runnable.withConfig` so\n * every chat model invocation auto-mirrors messages into the window.\n */\nexport function createCopassAgent(options: CreateCopassAgentOptions): CopassAgent {\n const {\n client,\n sandbox_id,\n window,\n llm,\n tools: extraTools = [],\n project_id,\n preset,\n includeToolMessages,\n ...reactAgentOptions\n } = options;\n\n const copass = copassTools({ client, sandbox_id, window, project_id, preset });\n const callback = new CopassWindowCallback({ window, includeToolMessages });\n\n const agent = createReactAgent({\n llm,\n tools: [copass.discover, copass.interpret, copass.search, ...extraTools],\n ...reactAgentOptions,\n });\n\n // `withConfig` returns a RunnableBinding that propagates these callbacks\n // to every child Runnable invocation — including the chat model calls\n // that trigger `CopassWindowCallback.handleChatModelStart`. The cast\n // narrows LangGraph's concrete graph type to the portable Runnable\n // surface we want to expose.\n return agent.withConfig({ callbacks: [callback] }) as unknown as CopassAgent;\n}\n","import { tool } from '@langchain/core/tools';\nimport { z } from 'zod';\nimport {\n DISCOVER_DESCRIPTION,\n DISCOVER_QUERY_PARAM,\n INTERPRET_DESCRIPTION,\n INTERPRET_ITEMS_PARAM,\n INTERPRET_QUERY_PARAM,\n SEARCH_DESCRIPTION,\n SEARCH_QUERY_PARAM,\n} from '@copass/config';\nimport type { CopassClient, SearchPreset, WindowLike } from '@copass/core';\n\nexport interface CopassToolsOptions {\n /** An authenticated `@copass/core` client. */\n client: CopassClient;\n /** Sandbox all retrieval runs against. */\n sandbox_id: string;\n /** Optional project scoping for retrieval calls. */\n project_id?: string;\n /**\n * Optional conversation window — a {@link WindowLike} (typically a\n * `ContextWindow` from `client.contextWindow.create()`). When provided,\n * every retrieval call is automatically window-aware.\n */\n window?: WindowLike;\n /**\n * Preset for `discover`, `interpret`, and `search`. Defaults to\n * `\"copass/copass_1.0\"`. Under `\"copass/copass_2.0\"` discover items\n * carry `subgraph` (pre-rendered ASCII tree) and `matched_query_nodes`\n * fields. Append `\":thinking\"` (e.g. `\"copass/copass_2.0:thinking\"`)\n * to enable task decomposition before retrieval on `search`.\n */\n preset?: SearchPreset;\n}\n\n/**\n * Return Copass retrieval as a set of LangChain tool objects (built with\n * `tool()` from `@langchain/core/tools`).\n *\n * Agent-framework-neutral shape: the LLM picks `discover` (menu of\n * relevant items) or `search` (synthesized answer). `interpret` stays\n * registered for back-compat but is legacy — prefer `search` for drill-in.\n *\n * @example\n * ```ts\n * import { ChatAnthropic } from '@langchain/anthropic';\n * import { createReactAgent } from '@langchain/langgraph/prebuilt';\n * import { copassTools } from '@copass/langchain';\n *\n * const window = await copass.contextWindow.create({ sandbox_id });\n * const tools = copassTools({ client: copass, sandbox_id, window });\n *\n * const agent = createReactAgent({\n * llm: new ChatAnthropic({ model: 'claude-opus-4-7' }),\n * tools: [tools.discover, tools.interpret, tools.search],\n * });\n *\n * const result = await agent.invoke({\n * messages: [{ role: 'user', content: 'why is checkout flaky?' }],\n * });\n * ```\n */\nexport function copassTools(options: CopassToolsOptions) {\n const { client, sandbox_id, project_id, window, preset = 'copass/copass_1.0' } = options;\n\n const discover = tool(\n async ({ query }: { query: string }) => {\n const response = await client.retrieval.discover(sandbox_id, {\n query,\n project_id,\n window,\n preset,\n });\n return {\n header: response.header,\n // Project the v2 fields (`subgraph` + `matched_query_nodes`)\n // alongside the v1 fields. Populated only under\n // `copass/copass_2.0` (or its `copass/2.0` alias); `null` under\n // v1 — agents can ignore them when not present.\n items: response.items.map((item) => ({\n score: item.score,\n summary: item.summary,\n canonical_ids: item.canonical_ids,\n subgraph: item.subgraph ?? null,\n matched_query_nodes: item.matched_query_nodes ?? null,\n })),\n next_steps: response.next_steps,\n };\n },\n {\n name: 'discover',\n description: DISCOVER_DESCRIPTION,\n schema: z.object({\n query: z.string().describe(DISCOVER_QUERY_PARAM),\n }),\n },\n );\n\n const interpret = tool(\n async ({ query, items }: { query: string; items: string[][] }) => {\n const response = await client.retrieval.interpret(sandbox_id, {\n query,\n items,\n project_id,\n window,\n preset,\n });\n return { brief: response.brief };\n },\n {\n name: 'interpret',\n description: INTERPRET_DESCRIPTION,\n schema: z.object({\n query: z.string().describe(INTERPRET_QUERY_PARAM),\n items: z.array(z.array(z.string())).min(1).describe(INTERPRET_ITEMS_PARAM),\n }),\n },\n );\n\n const search = tool(\n async ({ query }: { query: string }) => {\n const response = await client.retrieval.search(sandbox_id, {\n query,\n project_id,\n window,\n preset,\n });\n return { answer: response.answer };\n },\n {\n name: 'search',\n description: SEARCH_DESCRIPTION,\n schema: z.object({\n query: z.string().describe(SEARCH_QUERY_PARAM),\n }),\n },\n );\n\n return { discover, interpret, search };\n}\n","import { BaseCallbackHandler, type BaseCallbackHandlerInput } from '@langchain/core/callbacks/base';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport type { ChatMessage, ChatRole, ContextWindow } from '@copass/core';\n\nexport interface CopassWindowCallbackOptions extends BaseCallbackHandlerInput {\n /** The Context Window to mirror messages into. */\n window: ContextWindow;\n /**\n * Include LangChain `ToolMessage`s (tool-call results) as turns. Default: false.\n *\n * Tool results tend to be noisy — the underlying graph already has the\n * retrieved content indexed — so they're skipped by default. Enable only if\n * your agent's tool results carry conceptual content you want retrieval to\n * dedupe against.\n */\n includeToolMessages?: boolean;\n}\n\n/**\n * LangChain callback that auto-mirrors the chat model's conversation history\n * into a Copass {@link ContextWindow}.\n *\n * Hooks `handleChatModelStart`, which fires before every chat model invocation\n * with the full message history. We walk that history and call\n * {@link ContextWindow.addTurn} for any message we haven't seen — so retrieval\n * tools called inside the same agent step get a window that reflects the\n * actual conversation, not an empty buffer.\n *\n * @example\n * ```ts\n * import { CopassWindowCallback, copassTools } from '@copass/langchain';\n *\n * const tools = copassTools({ client, sandbox_id, window });\n * const agent = createReactAgent({ llm, tools: Object.values(tools) });\n *\n * await agent.invoke(\n * { messages: [{ role: 'user', content: 'why is checkout flaky?' }] },\n * { callbacks: [new CopassWindowCallback({ window })] },\n * );\n * ```\n */\nexport class CopassWindowCallback extends BaseCallbackHandler {\n readonly name = 'copass-window';\n private readonly window: ContextWindow;\n private readonly includeToolMessages: boolean;\n private readonly seen = new Set<string>();\n\n constructor(options: CopassWindowCallbackOptions) {\n super(options);\n this.window = options.window;\n this.includeToolMessages = options.includeToolMessages ?? false;\n // Seed the dedup set with turns already in the window so we don't re-add.\n for (const turn of options.window.getTurns()) {\n this.seen.add(hashTurn(turn));\n }\n }\n\n async handleChatModelStart(\n _llm: unknown,\n messages: BaseMessage[][],\n ): Promise<void> {\n // `messages` is `BaseMessage[][]` because chat models support batched calls.\n // Flatten — in agent loops this is always a single conversation.\n for (const msg of messages.flat()) {\n const turn = toTurn(msg, this.includeToolMessages);\n if (!turn) continue;\n\n const key = hashTurn(turn);\n if (this.seen.has(key)) continue;\n this.seen.add(key);\n\n // Fire-and-forget — window.addTurn pushes into the graph. We don't block\n // the model call on ingestion latency; we swallow errors because missing\n // a turn is recoverable (retrieval still sees already-added turns).\n this.window.addTurn(turn).catch(() => {\n /* intentionally empty */\n });\n }\n }\n}\n\nfunction toTurn(msg: BaseMessage, includeToolMessages: boolean): ChatMessage | null {\n const role = roleFromMessage(msg, includeToolMessages);\n if (!role) return null;\n\n const content = contentToString(msg.content);\n if (!content.trim()) return null;\n\n return { role, content };\n}\n\nfunction roleFromMessage(msg: BaseMessage, includeToolMessages: boolean): ChatRole | null {\n const type = msg.getType();\n switch (type) {\n case 'human':\n return 'user';\n case 'ai':\n return 'assistant';\n case 'system':\n return 'system';\n case 'tool':\n return includeToolMessages ? 'system' : null;\n default:\n return null;\n }\n}\n\nfunction contentToString(content: unknown): string {\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return String(content ?? '');\n return content\n .map((part) => {\n if (typeof part === 'string') return part;\n if (part && typeof part === 'object' && 'text' in (part as Record<string, unknown>)) {\n return String((part as Record<string, unknown>).text ?? '');\n }\n return '';\n })\n .filter(Boolean)\n .join('\\n');\n}\n\nfunction hashTurn(turn: ChatMessage): string {\n // Stable-ish hash: role + first 500 chars of content.\n // Collisions across different long messages starting identically are accepted\n // as benign — worst case, we skip adding one turn.\n return `${turn.role}:${turn.content.slice(0, 500)}`;\n}\n"],"mappings":"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","names":[]}
{"version":3,"sources":["../src/agent.ts","../src/tools.ts","../src/callback.ts"],"sourcesContent":["import { createReactAgent } from '@langchain/langgraph/prebuilt';\nimport type { BaseChatModel } from '@langchain/core/language_models/chat_models';\nimport type { StructuredToolInterface } from '@langchain/core/tools';\nimport type { Runnable, RunnableConfig } from '@langchain/core/runnables';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport type { CopassClient, ContextWindow, SearchPreset } from '@copass/core';\nimport { copassTools } from './tools.js';\nimport { CopassWindowCallback } from './callback.js';\n\ntype ReactAgentExtraOptions = Omit<\n Parameters<typeof createReactAgent>[0],\n 'llm' | 'tools'\n>;\n\n/**\n * Output type of {@link createCopassAgent}. A standard LangChain `Runnable`\n * producing `{ messages: BaseMessage[] }` on `.invoke()` / `.stream()`.\n *\n * The concrete inferred type (a LangGraph `CompiledStateGraph`) can't be\n * serialized into a portable declaration, so we expose the narrower\n * `Runnable` surface that's sufficient for every public method a caller\n * needs — `.invoke`, `.stream`, `.streamEvents`, `.batch`, `.pipe`,\n * `.withConfig`, etc. Escape hatch if you need the raw graph: cast the\n * result.\n */\nexport type CopassAgent = Runnable<\n { messages: BaseMessage[] } | BaseMessage[] | string,\n { messages: BaseMessage[] },\n RunnableConfig\n>;\n\nexport interface CreateCopassAgentOptions extends ReactAgentExtraOptions {\n /** An authenticated `@copass/core` client. */\n client: CopassClient;\n /** Sandbox all retrieval runs against. */\n sandbox_id: string;\n /**\n * Context Window for this conversation thread. Turn history is mirrored\n * into it automatically across every chat model invocation — retrieval\n * becomes window-aware with no per-call wiring.\n */\n window: ContextWindow;\n /** The chat model (e.g. `new ChatAnthropic(...)`, `new ChatOpenAI(...)`). */\n llm: BaseChatModel;\n /** Additional tools to mix in alongside `discover` / `interpret` / `search`. */\n tools?: StructuredToolInterface[];\n /** Optional project scoping for retrieval. */\n project_id?: string;\n /** Preset for `interpret` / `search`. Default: `\"copass/1.0\"`. */\n preset?: SearchPreset;\n /** Include LangChain `ToolMessage`s in the Context Window. Default: `false`. */\n includeToolMessages?: boolean;\n}\n\n/**\n * Create a LangChain agent pre-wired with Copass retrieval tools and\n * window-aware memory.\n *\n * Returns a standard LangChain Runnable — call `.invoke()`, `.stream()`,\n * `.streamEvents()`, `.batch()` as you would with the output of\n * `createReactAgent`. The Copass Context Window is mirrored from the\n * conversation history automatically; you don't pass callbacks, trackers,\n * or lifecycle hooks.\n *\n * @example\n * ```ts\n * import { ChatAnthropic } from '@langchain/anthropic';\n * import { CopassClient } from '@copass/core';\n * import { createCopassAgent } from '@copass/langchain';\n *\n * const copass = new CopassClient({ auth: { type: 'bearer', token: process.env.COPASS_API_KEY! } });\n * const window = await copass.contextWindow.create({ sandbox_id });\n *\n * const agent = createCopassAgent({\n * client: copass,\n * sandbox_id,\n * window,\n * llm: new ChatAnthropic({ model: 'claude-opus-4-7' }),\n * });\n *\n * const result = await agent.invoke({\n * messages: [{ role: 'user', content: 'why is checkout flaky?' }],\n * });\n * ```\n *\n * ### How the wiring works\n *\n * Under the hood this composes three primitives also exported from this\n * package — in case you want to build your own variant:\n *\n * - `copassTools({ client, sandbox_id, window, ... })` — the three retrieval\n * tools, window-aware at the server call level.\n * - `createReactAgent({ llm, tools, ... })` — the standard LangGraph ReAct agent.\n * - `CopassWindowCallback({ window })` — bound via `Runnable.withConfig` so\n * every chat model invocation auto-mirrors messages into the window.\n */\nexport function createCopassAgent(options: CreateCopassAgentOptions): CopassAgent {\n const {\n client,\n sandbox_id,\n window,\n llm,\n tools: extraTools = [],\n project_id,\n preset,\n includeToolMessages,\n ...reactAgentOptions\n } = options;\n\n const copass = copassTools({ client, sandbox_id, window, project_id, preset });\n const callback = new CopassWindowCallback({ window, includeToolMessages });\n\n const agent = createReactAgent({\n llm,\n tools: [copass.discover, copass.interpret, copass.search, ...extraTools],\n ...reactAgentOptions,\n });\n\n // `withConfig` returns a RunnableBinding that propagates these callbacks\n // to every child Runnable invocation — including the chat model calls\n // that trigger `CopassWindowCallback.handleChatModelStart`. The cast\n // narrows LangGraph's concrete graph type to the portable Runnable\n // surface we want to expose.\n return agent.withConfig({ callbacks: [callback] }) as unknown as CopassAgent;\n}\n","import { tool } from '@langchain/core/tools';\nimport { z } from 'zod';\nimport {\n DISCOVER_DESCRIPTION,\n DISCOVER_QUERY_PARAM,\n GET_ORIGIN_DESCRIPTION,\n INTERPRET_DESCRIPTION,\n INTERPRET_ITEMS_PARAM,\n INTERPRET_QUERY_PARAM,\n ORIGIN_CANONICAL_IDS_PARAM,\n ORIGIN_LIMIT_PARAM,\n SEARCH_DESCRIPTION,\n SEARCH_QUERY_PARAM,\n} from '@copass/config';\nimport type {\n CopassClient,\n OriginEntry,\n OriginFile,\n SearchPreset,\n WindowLike,\n} from '@copass/core';\n\nexport interface CopassToolsOptions {\n /** An authenticated `@copass/core` client. */\n client: CopassClient;\n /** Sandbox all retrieval runs against. */\n sandbox_id: string;\n /** Optional project scoping for retrieval calls. */\n project_id?: string;\n /**\n * Optional conversation window — a {@link WindowLike} (typically a\n * `ContextWindow` from `client.contextWindow.create()`). When provided,\n * every retrieval call is automatically window-aware.\n */\n window?: WindowLike;\n /**\n * Preset for `discover`, `interpret`, and `search`. Defaults to\n * `\"copass/copass_1.0\"`. Under `\"copass/copass_2.0\"` discover items\n * carry `subgraph` (pre-rendered ASCII tree) and `matched_query_nodes`\n * fields. Append `\":thinking\"` (e.g. `\"copass/copass_2.0:thinking\"`)\n * to enable task decomposition before retrieval on `search`.\n */\n preset?: SearchPreset;\n}\n\n/**\n * Return Copass retrieval as a set of LangChain tool objects (built with\n * `tool()` from `@langchain/core/tools`).\n *\n * Agent-framework-neutral shape: the LLM picks `discover` (menu of\n * relevant items) or `search` (synthesized answer). `interpret` stays\n * registered for back-compat but is legacy — prefer `search` for drill-in.\n *\n * @example\n * ```ts\n * import { ChatAnthropic } from '@langchain/anthropic';\n * import { createReactAgent } from '@langchain/langgraph/prebuilt';\n * import { copassTools } from '@copass/langchain';\n *\n * const window = await copass.contextWindow.create({ sandbox_id });\n * const tools = copassTools({ client: copass, sandbox_id, window });\n *\n * const agent = createReactAgent({\n * llm: new ChatAnthropic({ model: 'claude-opus-4-7' }),\n * tools: [tools.discover, tools.interpret, tools.search],\n * });\n *\n * const result = await agent.invoke({\n * messages: [{ role: 'user', content: 'why is checkout flaky?' }],\n * });\n * ```\n */\nexport function copassTools(options: CopassToolsOptions) {\n const { client, sandbox_id, project_id, window, preset = 'copass/copass_1.0' } = options;\n\n const discover = tool(\n async ({ query }: { query: string }) => {\n const response = await client.retrieval.discover(sandbox_id, {\n query,\n project_id,\n window,\n preset,\n });\n return {\n header: response.header,\n // Project the v2 fields (`subgraph` + `matched_query_nodes`)\n // alongside the v1 fields. Populated only under\n // `copass/copass_2.0` (or its `copass/2.0` alias); `null` under\n // v1 — agents can ignore them when not present.\n items: response.items.map((item) => ({\n score: item.score,\n summary: item.summary,\n canonical_ids: item.canonical_ids,\n subgraph: item.subgraph ?? null,\n matched_query_nodes: item.matched_query_nodes ?? null,\n })),\n next_steps: response.next_steps,\n };\n },\n {\n name: 'discover',\n description: DISCOVER_DESCRIPTION,\n schema: z.object({\n query: z.string().describe(DISCOVER_QUERY_PARAM),\n }),\n },\n );\n\n const interpret = tool(\n async ({ query, items }: { query: string; items: string[][] }) => {\n const response = await client.retrieval.interpret(sandbox_id, {\n query,\n items,\n project_id,\n window,\n preset,\n });\n return { brief: response.brief };\n },\n {\n name: 'interpret',\n description: INTERPRET_DESCRIPTION,\n schema: z.object({\n query: z.string().describe(INTERPRET_QUERY_PARAM),\n items: z.array(z.array(z.string())).min(1).describe(INTERPRET_ITEMS_PARAM),\n }),\n },\n );\n\n const search = tool(\n async ({ query }: { query: string }) => {\n const response = await client.retrieval.search(sandbox_id, {\n query,\n project_id,\n window,\n preset,\n });\n return { answer: response.answer };\n },\n {\n name: 'search',\n description: SEARCH_DESCRIPTION,\n schema: z.object({\n query: z.string().describe(SEARCH_QUERY_PARAM),\n }),\n },\n );\n\n const get_origin = tool(\n async ({\n canonical_ids,\n limit_per_canonical,\n }: {\n canonical_ids: string[];\n limit_per_canonical?: number;\n }) => {\n const response = await client.retrieval.getOrigin(sandbox_id, {\n canonical_ids,\n ...(limit_per_canonical !== undefined ? { limit_per_canonical } : {}),\n });\n return {\n sandbox_id: response.sandbox_id,\n origins: response.origins.map((entry: OriginEntry) => ({\n canonical_id: entry.canonical_id,\n files: entry.files.map((f: OriginFile) => ({\n file_path: f.file_path,\n extraction_count: f.extraction_count,\n })),\n })),\n };\n },\n {\n name: 'get_origin',\n description: GET_ORIGIN_DESCRIPTION,\n schema: z.object({\n canonical_ids: z\n .array(z.string())\n .min(1)\n .max(100)\n .describe(ORIGIN_CANONICAL_IDS_PARAM),\n limit_per_canonical: z\n .number()\n .int()\n .min(1)\n .max(50)\n .optional()\n .describe(ORIGIN_LIMIT_PARAM),\n }),\n },\n );\n\n return { discover, interpret, search, get_origin };\n}\n","import { BaseCallbackHandler, type BaseCallbackHandlerInput } from '@langchain/core/callbacks/base';\nimport type { BaseMessage } from '@langchain/core/messages';\nimport type { ChatMessage, ChatRole, ContextWindow } from '@copass/core';\n\nexport interface CopassWindowCallbackOptions extends BaseCallbackHandlerInput {\n /** The Context Window to mirror messages into. */\n window: ContextWindow;\n /**\n * Include LangChain `ToolMessage`s (tool-call results) as turns. Default: false.\n *\n * Tool results tend to be noisy — the underlying graph already has the\n * retrieved content indexed — so they're skipped by default. Enable only if\n * your agent's tool results carry conceptual content you want retrieval to\n * dedupe against.\n */\n includeToolMessages?: boolean;\n}\n\n/**\n * LangChain callback that auto-mirrors the chat model's conversation history\n * into a Copass {@link ContextWindow}.\n *\n * Hooks `handleChatModelStart`, which fires before every chat model invocation\n * with the full message history. We walk that history and call\n * {@link ContextWindow.addTurn} for any message we haven't seen — so retrieval\n * tools called inside the same agent step get a window that reflects the\n * actual conversation, not an empty buffer.\n *\n * @example\n * ```ts\n * import { CopassWindowCallback, copassTools } from '@copass/langchain';\n *\n * const tools = copassTools({ client, sandbox_id, window });\n * const agent = createReactAgent({ llm, tools: Object.values(tools) });\n *\n * await agent.invoke(\n * { messages: [{ role: 'user', content: 'why is checkout flaky?' }] },\n * { callbacks: [new CopassWindowCallback({ window })] },\n * );\n * ```\n */\nexport class CopassWindowCallback extends BaseCallbackHandler {\n readonly name = 'copass-window';\n private readonly window: ContextWindow;\n private readonly includeToolMessages: boolean;\n private readonly seen = new Set<string>();\n\n constructor(options: CopassWindowCallbackOptions) {\n super(options);\n this.window = options.window;\n this.includeToolMessages = options.includeToolMessages ?? false;\n // Seed the dedup set with turns already in the window so we don't re-add.\n for (const turn of options.window.getTurns()) {\n this.seen.add(hashTurn(turn));\n }\n }\n\n async handleChatModelStart(\n _llm: unknown,\n messages: BaseMessage[][],\n ): Promise<void> {\n // `messages` is `BaseMessage[][]` because chat models support batched calls.\n // Flatten — in agent loops this is always a single conversation.\n for (const msg of messages.flat()) {\n const turn = toTurn(msg, this.includeToolMessages);\n if (!turn) continue;\n\n const key = hashTurn(turn);\n if (this.seen.has(key)) continue;\n this.seen.add(key);\n\n // Fire-and-forget — window.addTurn pushes into the graph. We don't block\n // the model call on ingestion latency; we swallow errors because missing\n // a turn is recoverable (retrieval still sees already-added turns).\n this.window.addTurn(turn).catch(() => {\n /* intentionally empty */\n });\n }\n }\n}\n\nfunction toTurn(msg: BaseMessage, includeToolMessages: boolean): ChatMessage | null {\n const role = roleFromMessage(msg, includeToolMessages);\n if (!role) return null;\n\n const content = contentToString(msg.content);\n if (!content.trim()) return null;\n\n return { role, content };\n}\n\nfunction roleFromMessage(msg: BaseMessage, includeToolMessages: boolean): ChatRole | null {\n const type = msg.getType();\n switch (type) {\n case 'human':\n return 'user';\n case 'ai':\n return 'assistant';\n case 'system':\n return 'system';\n case 'tool':\n return includeToolMessages ? 'system' : null;\n default:\n return null;\n }\n}\n\nfunction contentToString(content: unknown): string {\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return String(content ?? '');\n return content\n .map((part) => {\n if (typeof part === 'string') return part;\n if (part && typeof part === 'object' && 'text' in (part as Record<string, unknown>)) {\n return String((part as Record<string, unknown>).text ?? '');\n }\n return '';\n })\n .filter(Boolean)\n .join('\\n');\n}\n\nfunction hashTurn(turn: ChatMessage): string {\n // Stable-ish hash: role + first 500 chars of content.\n // Collisions across different long messages starting identically are accepted\n // as benign — worst case, we skip adding one turn.\n return `${turn.role}:${turn.content.slice(0, 500)}`;\n}\n"],"mappings":"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","names":[]}
{
"name": "@copass/langchain",
"version": "0.5.9",
"version": "0.5.10",
"description": "LangChain tool adapters for Copass — drop-in discover/interpret/search tools for LangChain agents",

@@ -58,3 +58,3 @@ "publishConfig": {

"dependencies": {
"@copass/config": "^0.5.2"
"@copass/config": "^0.5.3"
},

@@ -73,3 +73,3 @@ "peerDependencies": {

"devDependencies": {
"@copass/core": "^0.8.1",
"@copass/core": "^0.11.1",
"@langchain/core": "^1.1.0",

@@ -82,3 +82,3 @@ "@langchain/langgraph": "^1.2.0",

},
"gitHead": "778b1411e89f721857fc9c3ca7c41b5903cd0056"
"gitHead": "647e1abcdbdc52dae62913df5cf86aeaf68dc2ff"
}
# @copass/langchain
**Copass retrieval as LangChain tools.** The LLM picks `discover` (menu of relevant items) or `search` (synthesized answer) — you don't write the tool-calling loop. `interpret` is exposed for back-compat but legacy; prefer `search` for drill-in.
**Copass retrieval as LangChain tools.** The LLM picks `discover` (menu of relevant items), `search` (synthesized answer), or `get_origin` (map canonical_ids to source files) — you don't write the tool-calling loop. `interpret` is exposed for back-compat but legacy; prefer `search` for drill-in.

@@ -74,5 +74,6 @@ ## Prerequisites

| `search` | "Tell me about X" / "Answer this." — synthesized answer (canonical drill-in) |
| `get_origin` | "Where does this live?" — maps canonical_ids from `discover` to source files. Cheap, no LLM. |
| `interpret` | Legacy — brief pinned to canonical_ids. Prefer `search` for drill-in. |
Add your own tools via the `tools` option — they'll be mixed in alongside the Copass three:
Add your own tools via the `tools` option — they'll be mixed in alongside the Copass four:

@@ -79,0 +80,0 @@ ```ts