| "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/middleware/context-compaction/prompt.ts | ||
| var NO_TOOLS_PREAMBLE = `CRITICAL: Respond with TEXT ONLY. Do NOT call any tools. | ||
| - Do NOT use any tools or functions. | ||
| - You already have all the context you need in the conversation below. | ||
| - Tool calls will be REJECTED and your response will be discarded. | ||
| - Your entire response must be plain text containing the summary. | ||
| `; | ||
| var SUMMARY_INSTRUCTION = `Your task is to create a detailed summary of the conversation history provided below. This summary will replace the older portion of the conversation so that work can continue without losing important context. | ||
| Your summary should include the following sections: | ||
| 1. Primary Request and Intent: Capture all of the user's explicit requests and intents in detail. | ||
| 2. Key Technical Concepts: List all important technical concepts, technologies, and frameworks discussed. | ||
| 3. Files and Code Sections: Enumerate specific files and code sections examined, modified, or created. Include code snippets where applicable and a summary of why each file is important. | ||
| 4. Errors and Fixes: List all errors encountered and how they were fixed. Include any user feedback on corrections. | ||
| 5. Problem Solving: Document problems solved and any ongoing troubleshooting efforts. | ||
| 6. All User Messages: List ALL user messages that are not tool results. These are critical for understanding user feedback and changing intent. | ||
| 7. Pending Tasks: Outline any pending tasks that were explicitly requested. | ||
| 8. Current Work: Describe precisely what was being worked on most recently, including file names and code snippets where applicable. | ||
| 9. Optional Next Step: List the next step that should be taken, directly in line with the most recent user requests. If the last task was concluded, only list next steps that are explicitly requested. | ||
| Be thorough and precise. Technical details, file paths, and code patterns are essential for continuing work without losing context.`; | ||
| var NO_TOOLS_TRAILER = "\n\nREMINDER: Do NOT call any tools. Respond with plain text only containing the summary."; | ||
| function buildCompactionPrompt(serializedHistory, targetTokens) { | ||
| const systemContent = NO_TOOLS_PREAMBLE + SUMMARY_INSTRUCTION + ` | ||
| Target summary length: approximately ${targetTokens} tokens. Be concise but do not omit important details.` + NO_TOOLS_TRAILER; | ||
| return [ | ||
| { role: "system", content: systemContent }, | ||
| { | ||
| role: "user", | ||
| content: [ | ||
| { | ||
| type: "text", | ||
| text: `Here is the conversation history to summarize: | ||
| ${serializedHistory}` | ||
| } | ||
| ] | ||
| } | ||
| ]; | ||
| } | ||
| // src/middleware/context-compaction/serialize.ts | ||
| function estimateTokens(prompt) { | ||
| let chars = 0; | ||
| for (const msg of prompt) { | ||
| chars += messageCharCount(msg); | ||
| } | ||
| return Math.ceil(chars / 4); | ||
| } | ||
| function messageCharCount(msg) { | ||
| if (msg.role === "system") { | ||
| return msg.content.length; | ||
| } | ||
| let chars = 0; | ||
| for (const part of msg.content) { | ||
| switch (part.type) { | ||
| case "text": | ||
| chars += part.text.length; | ||
| break; | ||
| case "reasoning": | ||
| chars += part.text.length; | ||
| break; | ||
| case "tool-call": | ||
| chars += part.toolName.length + JSON.stringify(part.input).length; | ||
| break; | ||
| case "tool-result": | ||
| chars += part.toolName.length + toolResultLength(part.output); | ||
| break; | ||
| case "file": | ||
| chars += 20; | ||
| break; | ||
| default: | ||
| chars += 20; | ||
| break; | ||
| } | ||
| } | ||
| return chars; | ||
| } | ||
| function toolResultLength(output) { | ||
| if (output.type === "text") return String(_nullishCoalesce(output.value, () => ( ""))).length; | ||
| if (output.type === "json") | ||
| return JSON.stringify(_nullishCoalesce(output.value, () => ( ""))).length; | ||
| if (output.type === "execution-denied") | ||
| return (_nullishCoalesce(output.reason, () => ( ""))).length + 20; | ||
| return 20; | ||
| } | ||
| function serializePrompt(messages) { | ||
| return messages.map(serializeMessage).join("\n\n"); | ||
| } | ||
| function serializeMessage(msg) { | ||
| const label = msg.role.toUpperCase(); | ||
| if (msg.role === "system") { | ||
| return `[${label}] | ||
| ${msg.content}`; | ||
| } | ||
| const parts = []; | ||
| for (const part of msg.content) { | ||
| const s = serializePart(part); | ||
| if (s) parts.push(s); | ||
| } | ||
| return `[${label}] | ||
| ${parts.join("\n")}`; | ||
| } | ||
| function serializePart(part) { | ||
| switch (part.type) { | ||
| case "text": | ||
| return part.text; | ||
| case "reasoning": | ||
| return `[reasoning: ${part.text}]`; | ||
| case "file": | ||
| return `[file${part.filename ? `: ${part.filename}` : ""}]`; | ||
| case "tool-call": | ||
| return `[tool-call: ${part.toolName}(${JSON.stringify(part.input)})]`; | ||
| case "tool-result": | ||
| return `[tool-result: ${part.toolName} \u2192 ${serializeToolOutput(part.output)}]`; | ||
| case "tool-approval-response": | ||
| return `[tool-approval: ${part.approved ? "approved" : "denied"}]`; | ||
| default: | ||
| return ""; | ||
| } | ||
| } | ||
| function serializeToolOutput(output) { | ||
| if (output.type === "text") return String(_nullishCoalesce(output.value, () => ( ""))); | ||
| if (output.type === "json") return JSON.stringify(_nullishCoalesce(output.value, () => ( ""))); | ||
| if (output.type === "execution-denied") | ||
| return `denied: ${_nullishCoalesce(output.reason, () => ( ""))}`; | ||
| return ""; | ||
| } | ||
| function extractSummaryText(result) { | ||
| const text = result.content.filter( | ||
| (c) => c.type === "text" | ||
| ).map((c) => c.text).join("\n").trim(); | ||
| return text || null; | ||
| } | ||
| function splitPrompt(prompt, recentTokenBudget, tokenEstimator) { | ||
| const systemMessages = []; | ||
| const nonSystem = []; | ||
| for (const msg of prompt) { | ||
| if (msg.role === "system") { | ||
| systemMessages.push(msg); | ||
| } else { | ||
| nonSystem.push(msg); | ||
| } | ||
| } | ||
| let recentTokens = 0; | ||
| let splitIndex = nonSystem.length; | ||
| for (let i = nonSystem.length - 1; i >= 0; i--) { | ||
| const msgTokens = tokenEstimator([nonSystem[i]]); | ||
| if (recentTokens + msgTokens > recentTokenBudget && i < nonSystem.length - 1) { | ||
| splitIndex = i + 1; | ||
| break; | ||
| } | ||
| recentTokens += msgTokens; | ||
| if (i === 0) { | ||
| splitIndex = 0; | ||
| } | ||
| } | ||
| while (splitIndex > 0 && _optionalChain([nonSystem, 'access', _ => _[splitIndex], 'optionalAccess', _2 => _2.role]) === "tool") { | ||
| splitIndex--; | ||
| } | ||
| return { | ||
| systemMessages, | ||
| olderHistory: nonSystem.slice(0, splitIndex), | ||
| recentWindow: nonSystem.slice(splitIndex) | ||
| }; | ||
| } | ||
| // src/middleware/context-compaction/index.ts | ||
| var DEFAULT_THRESHOLD_PCT = 0.8; | ||
| var DEFAULT_SUMMARY_TARGET_PCT = 0.05; | ||
| var DEFAULT_RESERVED_OUTPUT = 16384; | ||
| var RECENT_WINDOW_PCT = 0.2; | ||
| function createContextCompaction(config) { | ||
| if (!config.maxContextTokens || config.maxContextTokens <= 0) { | ||
| throw new Error( | ||
| "[context-compaction] maxContextTokens must be a positive number" | ||
| ); | ||
| } | ||
| const thresholdPct = clamp( | ||
| _nullishCoalesce(config.autoCompactThresholdPct, () => ( DEFAULT_THRESHOLD_PCT)), | ||
| 0, | ||
| 1 | ||
| ); | ||
| const summaryTargetPct = clamp( | ||
| _nullishCoalesce(config.summaryTargetPct, () => ( DEFAULT_SUMMARY_TARGET_PCT)), | ||
| 0, | ||
| 1 | ||
| ); | ||
| const reservedOutput = _nullishCoalesce(config.reservedOutputTokens, () => ( DEFAULT_RESERVED_OUTPUT)); | ||
| const tokenEstimator = _nullishCoalesce(config.estimateTokens, () => ( estimateTokens)); | ||
| const onFailure = _nullishCoalesce(config.onCompactionFailure, () => ( "passthrough")); | ||
| const threshold = config.maxContextTokens * thresholdPct - reservedOutput; | ||
| const summaryTargetTokens = Math.floor( | ||
| config.maxContextTokens * summaryTargetPct | ||
| ); | ||
| const recentWindowBudget = Math.floor( | ||
| config.maxContextTokens * RECENT_WINDOW_PCT | ||
| ); | ||
| return { | ||
| specificationVersion: "v3", | ||
| wrapGenerate: async ({ | ||
| doGenerate, | ||
| model, | ||
| params | ||
| }) => { | ||
| const compacted = await compactIfNeeded( | ||
| model, | ||
| params, | ||
| threshold, | ||
| summaryTargetTokens, | ||
| recentWindowBudget, | ||
| tokenEstimator, | ||
| config.summarize, | ||
| onFailure | ||
| ); | ||
| if (!compacted) { | ||
| return doGenerate(); | ||
| } | ||
| return model.doGenerate(compacted); | ||
| }, | ||
| wrapStream: async ({ | ||
| doStream, | ||
| model, | ||
| params | ||
| }) => { | ||
| const compacted = await compactIfNeeded( | ||
| model, | ||
| params, | ||
| threshold, | ||
| summaryTargetTokens, | ||
| recentWindowBudget, | ||
| tokenEstimator, | ||
| config.summarize, | ||
| onFailure | ||
| ); | ||
| if (!compacted) { | ||
| return doStream(); | ||
| } | ||
| return model.doStream(compacted); | ||
| } | ||
| }; | ||
| } | ||
| async function compactIfNeeded(model, params, threshold, summaryTargetTokens, recentWindowBudget, tokenEstimator, customSummarize, onFailure) { | ||
| const estimatedTokens = tokenEstimator(params.prompt); | ||
| if (estimatedTokens <= threshold) { | ||
| return null; | ||
| } | ||
| const { systemMessages, olderHistory, recentWindow } = splitPrompt( | ||
| params.prompt, | ||
| recentWindowBudget, | ||
| tokenEstimator | ||
| ); | ||
| if (olderHistory.length === 0) { | ||
| return null; | ||
| } | ||
| let summaryText; | ||
| try { | ||
| if (customSummarize) { | ||
| summaryText = await customSummarize( | ||
| olderHistory, | ||
| summaryTargetTokens | ||
| ); | ||
| } else { | ||
| summaryText = await defaultSummarize( | ||
| model, | ||
| olderHistory, | ||
| summaryTargetTokens | ||
| ); | ||
| } | ||
| } catch (error) { | ||
| if (onFailure === "throw") { | ||
| throw error; | ||
| } | ||
| return null; | ||
| } | ||
| if (!summaryText) { | ||
| if (onFailure === "throw") { | ||
| throw new Error( | ||
| "[context-compaction] Summarization produced no text output" | ||
| ); | ||
| } | ||
| return null; | ||
| } | ||
| const compactedPrompt = [ | ||
| ...systemMessages, | ||
| { | ||
| role: "user", | ||
| content: [{ type: "text", text: summaryText }] | ||
| }, | ||
| { | ||
| role: "assistant", | ||
| content: [{ type: "text", text: "Understood." }] | ||
| }, | ||
| ...recentWindow | ||
| ]; | ||
| return { ...params, prompt: compactedPrompt }; | ||
| } | ||
| async function defaultSummarize(model, olderHistory, targetTokens) { | ||
| const serialized = serializePrompt(olderHistory); | ||
| const summaryPrompt = buildCompactionPrompt(serialized, targetTokens); | ||
| const result = await model.doGenerate({ | ||
| prompt: summaryPrompt, | ||
| maxOutputTokens: targetTokens, | ||
| // Strip everything that could cause tool calls or structured output | ||
| tools: void 0, | ||
| toolChoice: void 0, | ||
| responseFormat: void 0, | ||
| inputFormat: "messages", | ||
| mode: { type: "regular" } | ||
| }); | ||
| const text = extractSummaryText(result); | ||
| if (!text) { | ||
| throw new Error( | ||
| "[context-compaction] Model returned no text content during summarization" | ||
| ); | ||
| } | ||
| return text; | ||
| } | ||
| function clamp(value, min, max) { | ||
| return Math.min(max, Math.max(min, value)); | ||
| } | ||
| exports.createContextCompaction = createContextCompaction; |
| // src/middleware/context-compaction/prompt.ts | ||
| var NO_TOOLS_PREAMBLE = `CRITICAL: Respond with TEXT ONLY. Do NOT call any tools. | ||
| - Do NOT use any tools or functions. | ||
| - You already have all the context you need in the conversation below. | ||
| - Tool calls will be REJECTED and your response will be discarded. | ||
| - Your entire response must be plain text containing the summary. | ||
| `; | ||
| var SUMMARY_INSTRUCTION = `Your task is to create a detailed summary of the conversation history provided below. This summary will replace the older portion of the conversation so that work can continue without losing important context. | ||
| Your summary should include the following sections: | ||
| 1. Primary Request and Intent: Capture all of the user's explicit requests and intents in detail. | ||
| 2. Key Technical Concepts: List all important technical concepts, technologies, and frameworks discussed. | ||
| 3. Files and Code Sections: Enumerate specific files and code sections examined, modified, or created. Include code snippets where applicable and a summary of why each file is important. | ||
| 4. Errors and Fixes: List all errors encountered and how they were fixed. Include any user feedback on corrections. | ||
| 5. Problem Solving: Document problems solved and any ongoing troubleshooting efforts. | ||
| 6. All User Messages: List ALL user messages that are not tool results. These are critical for understanding user feedback and changing intent. | ||
| 7. Pending Tasks: Outline any pending tasks that were explicitly requested. | ||
| 8. Current Work: Describe precisely what was being worked on most recently, including file names and code snippets where applicable. | ||
| 9. Optional Next Step: List the next step that should be taken, directly in line with the most recent user requests. If the last task was concluded, only list next steps that are explicitly requested. | ||
| Be thorough and precise. Technical details, file paths, and code patterns are essential for continuing work without losing context.`; | ||
| var NO_TOOLS_TRAILER = "\n\nREMINDER: Do NOT call any tools. Respond with plain text only containing the summary."; | ||
| function buildCompactionPrompt(serializedHistory, targetTokens) { | ||
| const systemContent = NO_TOOLS_PREAMBLE + SUMMARY_INSTRUCTION + ` | ||
| Target summary length: approximately ${targetTokens} tokens. Be concise but do not omit important details.` + NO_TOOLS_TRAILER; | ||
| return [ | ||
| { role: "system", content: systemContent }, | ||
| { | ||
| role: "user", | ||
| content: [ | ||
| { | ||
| type: "text", | ||
| text: `Here is the conversation history to summarize: | ||
| ${serializedHistory}` | ||
| } | ||
| ] | ||
| } | ||
| ]; | ||
| } | ||
| // src/middleware/context-compaction/serialize.ts | ||
| function estimateTokens(prompt) { | ||
| let chars = 0; | ||
| for (const msg of prompt) { | ||
| chars += messageCharCount(msg); | ||
| } | ||
| return Math.ceil(chars / 4); | ||
| } | ||
| function messageCharCount(msg) { | ||
| if (msg.role === "system") { | ||
| return msg.content.length; | ||
| } | ||
| let chars = 0; | ||
| for (const part of msg.content) { | ||
| switch (part.type) { | ||
| case "text": | ||
| chars += part.text.length; | ||
| break; | ||
| case "reasoning": | ||
| chars += part.text.length; | ||
| break; | ||
| case "tool-call": | ||
| chars += part.toolName.length + JSON.stringify(part.input).length; | ||
| break; | ||
| case "tool-result": | ||
| chars += part.toolName.length + toolResultLength(part.output); | ||
| break; | ||
| case "file": | ||
| chars += 20; | ||
| break; | ||
| default: | ||
| chars += 20; | ||
| break; | ||
| } | ||
| } | ||
| return chars; | ||
| } | ||
| function toolResultLength(output) { | ||
| if (output.type === "text") return String(output.value ?? "").length; | ||
| if (output.type === "json") | ||
| return JSON.stringify(output.value ?? "").length; | ||
| if (output.type === "execution-denied") | ||
| return (output.reason ?? "").length + 20; | ||
| return 20; | ||
| } | ||
| function serializePrompt(messages) { | ||
| return messages.map(serializeMessage).join("\n\n"); | ||
| } | ||
| function serializeMessage(msg) { | ||
| const label = msg.role.toUpperCase(); | ||
| if (msg.role === "system") { | ||
| return `[${label}] | ||
| ${msg.content}`; | ||
| } | ||
| const parts = []; | ||
| for (const part of msg.content) { | ||
| const s = serializePart(part); | ||
| if (s) parts.push(s); | ||
| } | ||
| return `[${label}] | ||
| ${parts.join("\n")}`; | ||
| } | ||
| function serializePart(part) { | ||
| switch (part.type) { | ||
| case "text": | ||
| return part.text; | ||
| case "reasoning": | ||
| return `[reasoning: ${part.text}]`; | ||
| case "file": | ||
| return `[file${part.filename ? `: ${part.filename}` : ""}]`; | ||
| case "tool-call": | ||
| return `[tool-call: ${part.toolName}(${JSON.stringify(part.input)})]`; | ||
| case "tool-result": | ||
| return `[tool-result: ${part.toolName} \u2192 ${serializeToolOutput(part.output)}]`; | ||
| case "tool-approval-response": | ||
| return `[tool-approval: ${part.approved ? "approved" : "denied"}]`; | ||
| default: | ||
| return ""; | ||
| } | ||
| } | ||
| function serializeToolOutput(output) { | ||
| if (output.type === "text") return String(output.value ?? ""); | ||
| if (output.type === "json") return JSON.stringify(output.value ?? ""); | ||
| if (output.type === "execution-denied") | ||
| return `denied: ${output.reason ?? ""}`; | ||
| return ""; | ||
| } | ||
| function extractSummaryText(result) { | ||
| const text = result.content.filter( | ||
| (c) => c.type === "text" | ||
| ).map((c) => c.text).join("\n").trim(); | ||
| return text || null; | ||
| } | ||
| function splitPrompt(prompt, recentTokenBudget, tokenEstimator) { | ||
| const systemMessages = []; | ||
| const nonSystem = []; | ||
| for (const msg of prompt) { | ||
| if (msg.role === "system") { | ||
| systemMessages.push(msg); | ||
| } else { | ||
| nonSystem.push(msg); | ||
| } | ||
| } | ||
| let recentTokens = 0; | ||
| let splitIndex = nonSystem.length; | ||
| for (let i = nonSystem.length - 1; i >= 0; i--) { | ||
| const msgTokens = tokenEstimator([nonSystem[i]]); | ||
| if (recentTokens + msgTokens > recentTokenBudget && i < nonSystem.length - 1) { | ||
| splitIndex = i + 1; | ||
| break; | ||
| } | ||
| recentTokens += msgTokens; | ||
| if (i === 0) { | ||
| splitIndex = 0; | ||
| } | ||
| } | ||
| while (splitIndex > 0 && nonSystem[splitIndex]?.role === "tool") { | ||
| splitIndex--; | ||
| } | ||
| return { | ||
| systemMessages, | ||
| olderHistory: nonSystem.slice(0, splitIndex), | ||
| recentWindow: nonSystem.slice(splitIndex) | ||
| }; | ||
| } | ||
| // src/middleware/context-compaction/index.ts | ||
| var DEFAULT_THRESHOLD_PCT = 0.8; | ||
| var DEFAULT_SUMMARY_TARGET_PCT = 0.05; | ||
| var DEFAULT_RESERVED_OUTPUT = 16384; | ||
| var RECENT_WINDOW_PCT = 0.2; | ||
| function createContextCompaction(config) { | ||
| if (!config.maxContextTokens || config.maxContextTokens <= 0) { | ||
| throw new Error( | ||
| "[context-compaction] maxContextTokens must be a positive number" | ||
| ); | ||
| } | ||
| const thresholdPct = clamp( | ||
| config.autoCompactThresholdPct ?? DEFAULT_THRESHOLD_PCT, | ||
| 0, | ||
| 1 | ||
| ); | ||
| const summaryTargetPct = clamp( | ||
| config.summaryTargetPct ?? DEFAULT_SUMMARY_TARGET_PCT, | ||
| 0, | ||
| 1 | ||
| ); | ||
| const reservedOutput = config.reservedOutputTokens ?? DEFAULT_RESERVED_OUTPUT; | ||
| const tokenEstimator = config.estimateTokens ?? estimateTokens; | ||
| const onFailure = config.onCompactionFailure ?? "passthrough"; | ||
| const threshold = config.maxContextTokens * thresholdPct - reservedOutput; | ||
| const summaryTargetTokens = Math.floor( | ||
| config.maxContextTokens * summaryTargetPct | ||
| ); | ||
| const recentWindowBudget = Math.floor( | ||
| config.maxContextTokens * RECENT_WINDOW_PCT | ||
| ); | ||
| return { | ||
| specificationVersion: "v3", | ||
| wrapGenerate: async ({ | ||
| doGenerate, | ||
| model, | ||
| params | ||
| }) => { | ||
| const compacted = await compactIfNeeded( | ||
| model, | ||
| params, | ||
| threshold, | ||
| summaryTargetTokens, | ||
| recentWindowBudget, | ||
| tokenEstimator, | ||
| config.summarize, | ||
| onFailure | ||
| ); | ||
| if (!compacted) { | ||
| return doGenerate(); | ||
| } | ||
| return model.doGenerate(compacted); | ||
| }, | ||
| wrapStream: async ({ | ||
| doStream, | ||
| model, | ||
| params | ||
| }) => { | ||
| const compacted = await compactIfNeeded( | ||
| model, | ||
| params, | ||
| threshold, | ||
| summaryTargetTokens, | ||
| recentWindowBudget, | ||
| tokenEstimator, | ||
| config.summarize, | ||
| onFailure | ||
| ); | ||
| if (!compacted) { | ||
| return doStream(); | ||
| } | ||
| return model.doStream(compacted); | ||
| } | ||
| }; | ||
| } | ||
| async function compactIfNeeded(model, params, threshold, summaryTargetTokens, recentWindowBudget, tokenEstimator, customSummarize, onFailure) { | ||
| const estimatedTokens = tokenEstimator(params.prompt); | ||
| if (estimatedTokens <= threshold) { | ||
| return null; | ||
| } | ||
| const { systemMessages, olderHistory, recentWindow } = splitPrompt( | ||
| params.prompt, | ||
| recentWindowBudget, | ||
| tokenEstimator | ||
| ); | ||
| if (olderHistory.length === 0) { | ||
| return null; | ||
| } | ||
| let summaryText; | ||
| try { | ||
| if (customSummarize) { | ||
| summaryText = await customSummarize( | ||
| olderHistory, | ||
| summaryTargetTokens | ||
| ); | ||
| } else { | ||
| summaryText = await defaultSummarize( | ||
| model, | ||
| olderHistory, | ||
| summaryTargetTokens | ||
| ); | ||
| } | ||
| } catch (error) { | ||
| if (onFailure === "throw") { | ||
| throw error; | ||
| } | ||
| return null; | ||
| } | ||
| if (!summaryText) { | ||
| if (onFailure === "throw") { | ||
| throw new Error( | ||
| "[context-compaction] Summarization produced no text output" | ||
| ); | ||
| } | ||
| return null; | ||
| } | ||
| const compactedPrompt = [ | ||
| ...systemMessages, | ||
| { | ||
| role: "user", | ||
| content: [{ type: "text", text: summaryText }] | ||
| }, | ||
| { | ||
| role: "assistant", | ||
| content: [{ type: "text", text: "Understood." }] | ||
| }, | ||
| ...recentWindow | ||
| ]; | ||
| return { ...params, prompt: compactedPrompt }; | ||
| } | ||
| async function defaultSummarize(model, olderHistory, targetTokens) { | ||
| const serialized = serializePrompt(olderHistory); | ||
| const summaryPrompt = buildCompactionPrompt(serialized, targetTokens); | ||
| const result = await model.doGenerate({ | ||
| prompt: summaryPrompt, | ||
| maxOutputTokens: targetTokens, | ||
| // Strip everything that could cause tool calls or structured output | ||
| tools: void 0, | ||
| toolChoice: void 0, | ||
| responseFormat: void 0, | ||
| inputFormat: "messages", | ||
| mode: { type: "regular" } | ||
| }); | ||
| const text = extractSummaryText(result); | ||
| if (!text) { | ||
| throw new Error( | ||
| "[context-compaction] Model returned no text content during summarization" | ||
| ); | ||
| } | ||
| return text; | ||
| } | ||
| function clamp(value, min, max) { | ||
| return Math.min(max, Math.max(min, value)); | ||
| } | ||
| export { | ||
| createContextCompaction | ||
| }; |
| "use strict";Object.defineProperty(exports, "__esModule", {value: true}); | ||
| var _chunkN5EFHCHEcjs = require('../chunk-N5EFHCHE.cjs'); | ||
| var _chunkVPRUYL4Tcjs = require('../chunk-VPRUYL4T.cjs'); | ||
| require('../chunk-KONXT2SF.cjs'); | ||
| exports.contextCompaction = _chunkVPRUYL4Tcjs.contextCompaction; exports.contextCompactionPrompt = _chunkVPRUYL4Tcjs.getPrompt; exports.createContextCompaction = _chunkVPRUYL4Tcjs.createContextCompaction; | ||
| exports.createContextCompaction = _chunkN5EFHCHEcjs.createContextCompaction; |
@@ -1,87 +0,63 @@ | ||
| import * as ai from 'ai'; | ||
| import { B as BaseToolConfig } from '../types-3QPDuCXN.cjs'; | ||
| import { LanguageModelV3Prompt, LanguageModelV3Middleware } from '@ai-sdk/provider'; | ||
| /** | ||
| * Generate the description prompt for the context-compaction tool. | ||
| * Configuration for the context compaction middleware. | ||
| * | ||
| * @param config - The same config passed to {@link createContextCompaction}. | ||
| * @returns The full description string for the context-compaction tool. | ||
| */ | ||
| declare function getPrompt(config?: Pick<ContextCompactionConfig, 'maxTokens'>): string; | ||
| /** | ||
| * Configuration for the context compaction tool. | ||
| * Extends {@link BaseToolConfig} with summarization options. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { createContextCompaction } from 'agentool/context-compaction'; | ||
| * import { wrapLanguageModel } from 'ai'; | ||
| * | ||
| * const compactor = createContextCompaction({ | ||
| * summarize: async (msgs) => `Summary of ${msgs.length} messages`, | ||
| * maxTokens: 2048, | ||
| * const model = wrapLanguageModel({ | ||
| * model: anthropic('claude-sonnet-4-20250514'), | ||
| * middleware: createContextCompaction({ | ||
| * maxContextTokens: 200_000, | ||
| * }), | ||
| * }); | ||
| * ``` | ||
| */ | ||
| interface ContextCompactionConfig extends BaseToolConfig { | ||
| interface ContextCompactionConfig { | ||
| /** Model's max context window in tokens. Required. */ | ||
| maxContextTokens: number; | ||
| /** | ||
| * Function that summarizes messages into a shorter form. | ||
| * Consumer must provide this for compaction to work. | ||
| * Trigger compaction when estimated usage exceeds this fraction of | ||
| * the context window (0–1). Default: `0.80`. | ||
| */ | ||
| summarize?: (messages: Array<{ | ||
| role: string; | ||
| content: string; | ||
| }>) => Promise<string>; | ||
| /** Maximum tokens target. Defaults to 4096. */ | ||
| maxTokens?: number; | ||
| /** Override the default tool description. */ | ||
| description?: string; | ||
| autoCompactThresholdPct?: number; | ||
| /** | ||
| * Target summary size as a fraction of `maxContextTokens` (0–1). | ||
| * Default: `0.05`. | ||
| */ | ||
| summaryTargetPct?: number; | ||
| /** Tokens reserved for model output. Default: `16384`. */ | ||
| reservedOutputTokens?: number; | ||
| /** | ||
| * Custom token estimator. Receives the full prompt array and must | ||
| * return an estimated token count. | ||
| * Default: character-count / 4 heuristic. | ||
| */ | ||
| estimateTokens?: (prompt: LanguageModelV3Prompt) => number; | ||
| /** | ||
| * Custom summarizer. When provided, the middleware calls this | ||
| * instead of using the underlying model for summarization. | ||
| */ | ||
| summarize?: (messages: LanguageModelV3Prompt, targetTokens: number) => Promise<string>; | ||
| /** | ||
| * What to do when summarization fails. | ||
| * - `'passthrough'` (default): proceed with the original, uncompacted prompt. | ||
| * - `'throw'`: throw the summarization error. | ||
| */ | ||
| onCompactionFailure?: 'passthrough' | 'throw'; | ||
| } | ||
| /** | ||
| * Create a context compaction tool with the given configuration. | ||
| * Summarizes conversation history to reduce context size when it | ||
| * exceeds the token budget (estimated as maxTokens * 4 characters). | ||
| * Create a context-compaction middleware for the Vercel AI SDK. | ||
| * | ||
| * @param config - Configuration including the summarize callback and token budget | ||
| * @returns An AI SDK tool that compacts conversation messages | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { createContextCompaction } from 'agentool/context-compaction'; | ||
| * | ||
| * const compactor = createContextCompaction({ | ||
| * summarize: async (msgs) => { | ||
| * // Call your LLM to summarize | ||
| * return 'Condensed summary of the conversation'; | ||
| * }, | ||
| * maxTokens: 4096, | ||
| * }); | ||
| * ``` | ||
| * Wrap any language model with this middleware via | ||
| * `wrapLanguageModel({ model, middleware })`. When the prompt | ||
| * exceeds `maxContextTokens * autoCompactThresholdPct`, the | ||
| * middleware transparently summarizes older history while | ||
| * preserving system messages and the most recent turns. | ||
| */ | ||
| declare function createContextCompaction(config?: ContextCompactionConfig): ai.Tool<{ | ||
| messages: { | ||
| content: string; | ||
| role: string; | ||
| }[]; | ||
| maxTokens?: number | undefined; | ||
| }, string>; | ||
| /** | ||
| * Default context compaction tool instance with no summarize function. | ||
| * Configure with {@link createContextCompaction} for full functionality. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { contextCompaction } from 'agentool/context-compaction'; | ||
| * // Use directly — will return error if messages exceed budget | ||
| * // since no summarize function is configured. | ||
| * ``` | ||
| */ | ||
| declare const contextCompaction: ai.Tool<{ | ||
| messages: { | ||
| content: string; | ||
| role: string; | ||
| }[]; | ||
| maxTokens?: number | undefined; | ||
| }, string>; | ||
| declare function createContextCompaction(config: ContextCompactionConfig): LanguageModelV3Middleware; | ||
| export { type ContextCompactionConfig, contextCompaction, getPrompt as contextCompactionPrompt, createContextCompaction }; | ||
| export { type ContextCompactionConfig, createContextCompaction }; |
@@ -1,87 +0,63 @@ | ||
| import * as ai from 'ai'; | ||
| import { B as BaseToolConfig } from '../types-3QPDuCXN.js'; | ||
| import { LanguageModelV3Prompt, LanguageModelV3Middleware } from '@ai-sdk/provider'; | ||
| /** | ||
| * Generate the description prompt for the context-compaction tool. | ||
| * Configuration for the context compaction middleware. | ||
| * | ||
| * @param config - The same config passed to {@link createContextCompaction}. | ||
| * @returns The full description string for the context-compaction tool. | ||
| */ | ||
| declare function getPrompt(config?: Pick<ContextCompactionConfig, 'maxTokens'>): string; | ||
| /** | ||
| * Configuration for the context compaction tool. | ||
| * Extends {@link BaseToolConfig} with summarization options. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { createContextCompaction } from 'agentool/context-compaction'; | ||
| * import { wrapLanguageModel } from 'ai'; | ||
| * | ||
| * const compactor = createContextCompaction({ | ||
| * summarize: async (msgs) => `Summary of ${msgs.length} messages`, | ||
| * maxTokens: 2048, | ||
| * const model = wrapLanguageModel({ | ||
| * model: anthropic('claude-sonnet-4-20250514'), | ||
| * middleware: createContextCompaction({ | ||
| * maxContextTokens: 200_000, | ||
| * }), | ||
| * }); | ||
| * ``` | ||
| */ | ||
| interface ContextCompactionConfig extends BaseToolConfig { | ||
| interface ContextCompactionConfig { | ||
| /** Model's max context window in tokens. Required. */ | ||
| maxContextTokens: number; | ||
| /** | ||
| * Function that summarizes messages into a shorter form. | ||
| * Consumer must provide this for compaction to work. | ||
| * Trigger compaction when estimated usage exceeds this fraction of | ||
| * the context window (0–1). Default: `0.80`. | ||
| */ | ||
| summarize?: (messages: Array<{ | ||
| role: string; | ||
| content: string; | ||
| }>) => Promise<string>; | ||
| /** Maximum tokens target. Defaults to 4096. */ | ||
| maxTokens?: number; | ||
| /** Override the default tool description. */ | ||
| description?: string; | ||
| autoCompactThresholdPct?: number; | ||
| /** | ||
| * Target summary size as a fraction of `maxContextTokens` (0–1). | ||
| * Default: `0.05`. | ||
| */ | ||
| summaryTargetPct?: number; | ||
| /** Tokens reserved for model output. Default: `16384`. */ | ||
| reservedOutputTokens?: number; | ||
| /** | ||
| * Custom token estimator. Receives the full prompt array and must | ||
| * return an estimated token count. | ||
| * Default: character-count / 4 heuristic. | ||
| */ | ||
| estimateTokens?: (prompt: LanguageModelV3Prompt) => number; | ||
| /** | ||
| * Custom summarizer. When provided, the middleware calls this | ||
| * instead of using the underlying model for summarization. | ||
| */ | ||
| summarize?: (messages: LanguageModelV3Prompt, targetTokens: number) => Promise<string>; | ||
| /** | ||
| * What to do when summarization fails. | ||
| * - `'passthrough'` (default): proceed with the original, uncompacted prompt. | ||
| * - `'throw'`: throw the summarization error. | ||
| */ | ||
| onCompactionFailure?: 'passthrough' | 'throw'; | ||
| } | ||
| /** | ||
| * Create a context compaction tool with the given configuration. | ||
| * Summarizes conversation history to reduce context size when it | ||
| * exceeds the token budget (estimated as maxTokens * 4 characters). | ||
| * Create a context-compaction middleware for the Vercel AI SDK. | ||
| * | ||
| * @param config - Configuration including the summarize callback and token budget | ||
| * @returns An AI SDK tool that compacts conversation messages | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { createContextCompaction } from 'agentool/context-compaction'; | ||
| * | ||
| * const compactor = createContextCompaction({ | ||
| * summarize: async (msgs) => { | ||
| * // Call your LLM to summarize | ||
| * return 'Condensed summary of the conversation'; | ||
| * }, | ||
| * maxTokens: 4096, | ||
| * }); | ||
| * ``` | ||
| * Wrap any language model with this middleware via | ||
| * `wrapLanguageModel({ model, middleware })`. When the prompt | ||
| * exceeds `maxContextTokens * autoCompactThresholdPct`, the | ||
| * middleware transparently summarizes older history while | ||
| * preserving system messages and the most recent turns. | ||
| */ | ||
| declare function createContextCompaction(config?: ContextCompactionConfig): ai.Tool<{ | ||
| messages: { | ||
| content: string; | ||
| role: string; | ||
| }[]; | ||
| maxTokens?: number | undefined; | ||
| }, string>; | ||
| /** | ||
| * Default context compaction tool instance with no summarize function. | ||
| * Configure with {@link createContextCompaction} for full functionality. | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * import { contextCompaction } from 'agentool/context-compaction'; | ||
| * // Use directly — will return error if messages exceed budget | ||
| * // since no summarize function is configured. | ||
| * ``` | ||
| */ | ||
| declare const contextCompaction: ai.Tool<{ | ||
| messages: { | ||
| content: string; | ||
| role: string; | ||
| }[]; | ||
| maxTokens?: number | undefined; | ||
| }, string>; | ||
| declare function createContextCompaction(config: ContextCompactionConfig): LanguageModelV3Middleware; | ||
| export { type ContextCompactionConfig, contextCompaction, getPrompt as contextCompactionPrompt, createContextCompaction }; | ||
| export { type ContextCompactionConfig, createContextCompaction }; |
| import { | ||
| contextCompaction, | ||
| createContextCompaction, | ||
| getPrompt | ||
| } from "../chunk-2JF3ZF2J.js"; | ||
| import "../chunk-X6ZY2KFU.js"; | ||
| createContextCompaction | ||
| } from "../chunk-VCP53KEZ.js"; | ||
| export { | ||
| contextCompaction, | ||
| getPrompt as contextCompactionPrompt, | ||
| createContextCompaction | ||
| }; |
+2
-6
@@ -18,9 +18,7 @@ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); | ||
| var _chunkN5EFHCHEcjs = require('./chunk-N5EFHCHE.cjs'); | ||
| var _chunkVPRUYL4Tcjs = require('./chunk-VPRUYL4T.cjs'); | ||
| var _chunkKUFZFNPTcjs = require('./chunk-KUFZFNPT.cjs'); | ||
@@ -175,4 +173,2 @@ | ||
| exports.askUser = _chunkKUFZFNPTcjs.askUser; exports.askUserPrompt = _chunkKUFZFNPTcjs.getPrompt; exports.bash = _chunkCXBWF5ONcjs.bash; exports.bashPrompt = _chunkCXBWF5ONcjs.getPrompt; exports.contextCompaction = _chunkVPRUYL4Tcjs.contextCompaction; exports.contextCompactionPrompt = _chunkVPRUYL4Tcjs.getPrompt; exports.createAskUser = _chunkKUFZFNPTcjs.createAskUser; exports.createBash = _chunkCXBWF5ONcjs.createBash; exports.createContextCompaction = _chunkVPRUYL4Tcjs.createContextCompaction; exports.createDiff = _chunkOYLTQJXTcjs.createDiff; exports.createEdit = _chunk6ULQG2W2cjs.createEdit; exports.createGlob = _chunkYCWJVQYOcjs.createGlob; exports.createGrep = _chunkRIGL3JTScjs.createGrep; exports.createHttpRequest = _chunk5T3SQYI4cjs.createHttpRequest; exports.createLsp = _chunkNQIV6LBHcjs.createLsp; exports.createMemory = _chunkLNAR3NJQcjs.createMemory; exports.createMultiEdit = _chunkVPV6WG5Vcjs.createMultiEdit; exports.createRead = _chunkHG5T47NAcjs.createRead; exports.createSleep = _chunkJYTOARJVcjs.createSleep; exports.createTaskCreate = _chunkSFDZRLSXcjs.createTaskCreate; exports.createTaskGet = _chunkXGDE7S2Dcjs.createTaskGet; exports.createTaskList = _chunk3FT4ZPB2cjs.createTaskList; exports.createTaskUpdate = _chunkT6STO7PScjs.createTaskUpdate; exports.createToolSearch = _chunk2JBLVFB7cjs.createToolSearch; exports.createWebFetch = _chunkG6ZVJA4Vcjs.createWebFetch; exports.createWebSearch = _chunkCM3VRCNXcjs.createWebSearch; exports.createWrite = _chunkABXTBB2Ncjs.createWrite; exports.diff = _chunkOYLTQJXTcjs.diff; exports.diffPrompt = _chunkOYLTQJXTcjs.getPrompt; exports.edit = _chunk6ULQG2W2cjs.edit; exports.editPrompt = _chunk6ULQG2W2cjs.getPrompt; exports.glob = _chunkYCWJVQYOcjs.glob; exports.globPrompt = _chunkYCWJVQYOcjs.getPrompt; exports.grep = _chunkRIGL3JTScjs.grep; exports.grepPrompt = _chunkRIGL3JTScjs.getPrompt; exports.httpRequest = _chunk5T3SQYI4cjs.httpRequest; exports.httpRequestPrompt = _chunk5T3SQYI4cjs.getPrompt; exports.lsp = _chunkNQIV6LBHcjs.lsp; exports.lspPrompt = _chunkNQIV6LBHcjs.getPrompt; exports.memory = _chunkLNAR3NJQcjs.memory; exports.memoryPrompt = _chunkLNAR3NJQcjs.getPrompt; exports.multiEdit = _chunkVPV6WG5Vcjs.multiEdit; exports.multiEditPrompt = _chunkVPV6WG5Vcjs.getPrompt; exports.read = _chunkHG5T47NAcjs.read; exports.readPrompt = _chunkHG5T47NAcjs.getPrompt; exports.sleep = _chunkJYTOARJVcjs.sleep; exports.sleepPrompt = _chunkJYTOARJVcjs.getPrompt; exports.taskCreate = _chunkSFDZRLSXcjs.taskCreate; exports.taskCreatePrompt = _chunkSFDZRLSXcjs.getPrompt; exports.taskGet = _chunkXGDE7S2Dcjs.taskGet; exports.taskGetPrompt = _chunkXGDE7S2Dcjs.getPrompt; exports.taskList = _chunk3FT4ZPB2cjs.taskList; exports.taskListPrompt = _chunk3FT4ZPB2cjs.getPrompt; exports.taskUpdate = _chunkT6STO7PScjs.taskUpdate; exports.taskUpdatePrompt = _chunkT6STO7PScjs.getPrompt; exports.toolSearch = _chunk2JBLVFB7cjs.toolSearch; exports.toolSearchPrompt = _chunk2JBLVFB7cjs.getPrompt; exports.webFetch = _chunkG6ZVJA4Vcjs.webFetch; exports.webFetchPrompt = _chunkG6ZVJA4Vcjs.getPrompt; exports.webSearch = _chunkCM3VRCNXcjs.webSearch; exports.webSearchPrompt = _chunkCM3VRCNXcjs.getPrompt; exports.write = _chunkABXTBB2Ncjs.write; exports.writePrompt = _chunkABXTBB2Ncjs.getPrompt; | ||
| exports.askUser = _chunkKUFZFNPTcjs.askUser; exports.askUserPrompt = _chunkKUFZFNPTcjs.getPrompt; exports.bash = _chunkCXBWF5ONcjs.bash; exports.bashPrompt = _chunkCXBWF5ONcjs.getPrompt; exports.createAskUser = _chunkKUFZFNPTcjs.createAskUser; exports.createBash = _chunkCXBWF5ONcjs.createBash; exports.createContextCompaction = _chunkN5EFHCHEcjs.createContextCompaction; exports.createDiff = _chunkOYLTQJXTcjs.createDiff; exports.createEdit = _chunk6ULQG2W2cjs.createEdit; exports.createGlob = _chunkYCWJVQYOcjs.createGlob; exports.createGrep = _chunkRIGL3JTScjs.createGrep; exports.createHttpRequest = _chunk5T3SQYI4cjs.createHttpRequest; exports.createLsp = _chunkNQIV6LBHcjs.createLsp; exports.createMemory = _chunkLNAR3NJQcjs.createMemory; exports.createMultiEdit = _chunkVPV6WG5Vcjs.createMultiEdit; exports.createRead = _chunkHG5T47NAcjs.createRead; exports.createSleep = _chunkJYTOARJVcjs.createSleep; exports.createTaskCreate = _chunkSFDZRLSXcjs.createTaskCreate; exports.createTaskGet = _chunkXGDE7S2Dcjs.createTaskGet; exports.createTaskList = _chunk3FT4ZPB2cjs.createTaskList; exports.createTaskUpdate = _chunkT6STO7PScjs.createTaskUpdate; exports.createToolSearch = _chunk2JBLVFB7cjs.createToolSearch; exports.createWebFetch = _chunkG6ZVJA4Vcjs.createWebFetch; exports.createWebSearch = _chunkCM3VRCNXcjs.createWebSearch; exports.createWrite = _chunkABXTBB2Ncjs.createWrite; exports.diff = _chunkOYLTQJXTcjs.diff; exports.diffPrompt = _chunkOYLTQJXTcjs.getPrompt; exports.edit = _chunk6ULQG2W2cjs.edit; exports.editPrompt = _chunk6ULQG2W2cjs.getPrompt; exports.glob = _chunkYCWJVQYOcjs.glob; exports.globPrompt = _chunkYCWJVQYOcjs.getPrompt; exports.grep = _chunkRIGL3JTScjs.grep; exports.grepPrompt = _chunkRIGL3JTScjs.getPrompt; exports.httpRequest = _chunk5T3SQYI4cjs.httpRequest; exports.httpRequestPrompt = _chunk5T3SQYI4cjs.getPrompt; exports.lsp = _chunkNQIV6LBHcjs.lsp; exports.lspPrompt = _chunkNQIV6LBHcjs.getPrompt; exports.memory = _chunkLNAR3NJQcjs.memory; exports.memoryPrompt = _chunkLNAR3NJQcjs.getPrompt; exports.multiEdit = _chunkVPV6WG5Vcjs.multiEdit; exports.multiEditPrompt = _chunkVPV6WG5Vcjs.getPrompt; exports.read = _chunkHG5T47NAcjs.read; exports.readPrompt = _chunkHG5T47NAcjs.getPrompt; exports.sleep = _chunkJYTOARJVcjs.sleep; exports.sleepPrompt = _chunkJYTOARJVcjs.getPrompt; exports.taskCreate = _chunkSFDZRLSXcjs.taskCreate; exports.taskCreatePrompt = _chunkSFDZRLSXcjs.getPrompt; exports.taskGet = _chunkXGDE7S2Dcjs.taskGet; exports.taskGetPrompt = _chunkXGDE7S2Dcjs.getPrompt; exports.taskList = _chunk3FT4ZPB2cjs.taskList; exports.taskListPrompt = _chunk3FT4ZPB2cjs.getPrompt; exports.taskUpdate = _chunkT6STO7PScjs.taskUpdate; exports.taskUpdatePrompt = _chunkT6STO7PScjs.getPrompt; exports.toolSearch = _chunk2JBLVFB7cjs.toolSearch; exports.toolSearchPrompt = _chunk2JBLVFB7cjs.getPrompt; exports.webFetch = _chunkG6ZVJA4Vcjs.webFetch; exports.webFetchPrompt = _chunkG6ZVJA4Vcjs.getPrompt; exports.webSearch = _chunkCM3VRCNXcjs.webSearch; exports.webSearchPrompt = _chunkCM3VRCNXcjs.getPrompt; exports.write = _chunkABXTBB2Ncjs.write; exports.writePrompt = _chunkABXTBB2Ncjs.getPrompt; |
+2
-1
@@ -19,3 +19,3 @@ export { BashConfig, bash, bashPrompt, createBash } from './bash/index.cjs'; | ||
| export { HttpRequestConfig, createHttpRequest, httpRequest, httpRequestPrompt } from './http-request/index.cjs'; | ||
| export { ContextCompactionConfig, contextCompaction, contextCompactionPrompt, createContextCompaction } from './context-compaction/index.cjs'; | ||
| export { ContextCompactionConfig, createContextCompaction } from './context-compaction/index.cjs'; | ||
| export { AskUserConfig, askUser, askUserPrompt, createAskUser } from './ask-user/index.cjs'; | ||
@@ -25,1 +25,2 @@ export { SleepConfig, createSleep, sleep, sleepPrompt } from './sleep/index.cjs'; | ||
| import 'ai'; | ||
| import '@ai-sdk/provider'; |
+2
-1
@@ -19,3 +19,3 @@ export { BashConfig, bash, bashPrompt, createBash } from './bash/index.js'; | ||
| export { HttpRequestConfig, createHttpRequest, httpRequest, httpRequestPrompt } from './http-request/index.js'; | ||
| export { ContextCompactionConfig, contextCompaction, contextCompactionPrompt, createContextCompaction } from './context-compaction/index.js'; | ||
| export { ContextCompactionConfig, createContextCompaction } from './context-compaction/index.js'; | ||
| export { AskUserConfig, askUser, askUserPrompt, createAskUser } from './ask-user/index.js'; | ||
@@ -25,1 +25,2 @@ export { SleepConfig, createSleep, sleep, sleepPrompt } from './sleep/index.js'; | ||
| import 'ai'; | ||
| import '@ai-sdk/provider'; |
+6
-10
@@ -17,14 +17,12 @@ import { | ||
| import { | ||
| contextCompaction, | ||
| createContextCompaction, | ||
| getPrompt as getPrompt19 | ||
| } from "./chunk-2JF3ZF2J.js"; | ||
| createContextCompaction | ||
| } from "./chunk-VCP53KEZ.js"; | ||
| import { | ||
| askUser, | ||
| createAskUser, | ||
| getPrompt as getPrompt20 | ||
| getPrompt as getPrompt19 | ||
| } from "./chunk-L7R4UZSK.js"; | ||
| import { | ||
| createSleep, | ||
| getPrompt as getPrompt21, | ||
| getPrompt as getPrompt20, | ||
| sleep | ||
@@ -115,7 +113,5 @@ } from "./chunk-M74OQYNK.js"; | ||
| askUser, | ||
| getPrompt20 as askUserPrompt, | ||
| getPrompt19 as askUserPrompt, | ||
| bash, | ||
| getPrompt as bashPrompt, | ||
| contextCompaction, | ||
| getPrompt19 as contextCompactionPrompt, | ||
| createAskUser, | ||
@@ -161,3 +157,3 @@ createBash, | ||
| sleep, | ||
| getPrompt21 as sleepPrompt, | ||
| getPrompt20 as sleepPrompt, | ||
| taskCreate, | ||
@@ -164,0 +160,0 @@ getPrompt11 as taskCreatePrompt, |
+3
-3
| { | ||
| "name": "agentool", | ||
| "version": "1.1.1", | ||
| "version": "1.2.0", | ||
| "type": "module", | ||
| "description": "22 AI agent tools as standalone Vercel AI SDK modules", | ||
| "description": "21 AI agent tools + context-compaction middleware as standalone Vercel AI SDK modules", | ||
| "author": "Z-M-Huang", | ||
@@ -164,3 +164,3 @@ "license": "Apache-2.0", | ||
| "peerDependencies": { | ||
| "ai": ">=4.0.0", | ||
| "ai": ">=5.0.17", | ||
| "zod": ">=3.23.0" | ||
@@ -167,0 +167,0 @@ }, |
+26
-25
@@ -5,3 +5,3 @@ <div align="center"> | ||
| **22 AI agent tools as standalone [Vercel AI SDK](https://sdk.vercel.ai/) modules.** | ||
| **21 AI agent tools + context-compaction middleware for the [Vercel AI SDK](https://sdk.vercel.ai/).** | ||
@@ -18,3 +18,3 @@ <p> | ||
| <img src="https://img.shields.io/badge/TypeScript-5.7+-3178c6?style=flat-square&logo=typescript&logoColor=white" alt="TypeScript" /> | ||
| <img src="https://img.shields.io/badge/Vercel%20AI%20SDK-v4%2B-000000?style=flat-square&logo=vercel&logoColor=white" alt="Vercel AI SDK" /> | ||
| <img src="https://img.shields.io/badge/Vercel%20AI%20SDK-v5%2B-000000?style=flat-square&logo=vercel&logoColor=white" alt="Vercel AI SDK" /> | ||
| <img src="https://img.shields.io/badge/ESM%20%2B%20CJS-supported-22c55e?style=flat-square" alt="ESM + CJS" /> | ||
@@ -32,3 +32,4 @@ <img src="https://img.shields.io/badge/coverage-96%25-brightgreen?style=flat-square" alt="Test Coverage" /> | ||
| - **22 production-ready tools** -- bash, grep, glob, read, edit, write, web-fetch, web-search, tool-search, memory, multi-edit, diff, task-create, task-get, task-update, task-list, lsp, http-request, context-compaction, ask-user, sleep | ||
| - **21 production-ready tools** -- bash, grep, glob, read, edit, write, web-fetch, web-search, tool-search, memory, multi-edit, diff, task-create, task-get, task-update, task-list, lsp, http-request, ask-user, sleep | ||
| - **Context-compaction middleware** -- transparent prompt compaction via `wrapLanguageModel()`, preserves system messages and recent turns | ||
| - **Vercel AI SDK compatible** -- works with `generateText()`, `streamText()`, and any AI SDK provider (OpenAI, Anthropic, Google, etc.) | ||
@@ -546,29 +547,30 @@ - **Factory + default pattern** -- `createBash({ cwd: '/my/project' })` for custom config, or just use `bash` with zero config | ||
| ### context-compaction | ||
| ### context-compaction (middleware) | ||
| Compact conversation history to fit within token budgets. | ||
| Transparent context compaction middleware for `wrapLanguageModel()`. When the prompt exceeds a configurable threshold, it automatically summarizes older conversation history while preserving system messages and recent turns. | ||
| ```typescript | ||
| import { createContextCompaction } from 'agentool/context-compaction'; | ||
| import { wrapLanguageModel, generateText } from 'ai'; | ||
| import { anthropic } from '@ai-sdk/anthropic'; | ||
| const compact = createContextCompaction({ | ||
| maxTokens: 4096, | ||
| summarize: async (messages) => { | ||
| // Call your LLM to summarize | ||
| return 'Summary of previous conversation...'; | ||
| }, | ||
| const model = wrapLanguageModel({ | ||
| model: anthropic('claude-sonnet-4-20250514'), | ||
| middleware: createContextCompaction({ | ||
| maxContextTokens: 200_000, // model's context window (required) | ||
| autoCompactThresholdPct: 0.80, // compact when 80% full (default) | ||
| summaryTargetPct: 0.05, // summarize to 5% of context (default) | ||
| }), | ||
| }); | ||
| const result = await compact.execute( | ||
| { | ||
| messages: [ | ||
| { role: 'user', content: 'Long conversation...' }, | ||
| { role: 'assistant', content: 'Long response...' }, | ||
| ], | ||
| }, | ||
| { toolCallId: 'id', messages: [] }, | ||
| ); | ||
| // Use the wrapped model normally — compaction is transparent | ||
| const { text } = await generateText({ | ||
| model, | ||
| tools: { bash, read, edit }, | ||
| maxSteps: 20, | ||
| prompt: 'Find and fix the bug in src/auth.ts', | ||
| }); | ||
| ``` | ||
| **Parameters:** `messages` (array of `{ role, content }`), `maxTokens?` (number) | ||
| **Config:** `maxContextTokens` (number, required), `autoCompactThresholdPct?` (0-1), `summaryTargetPct?` (0-1), `reservedOutputTokens?` (number), `estimateTokens?` (function), `summarize?` (function), `onCompactionFailure?` (`'passthrough'` | `'throw'`) | ||
@@ -664,3 +666,2 @@ --- | ||
| | `web-fetch` | `maxContentLength?: number`, `userAgent?: string` | | ||
| | `context-compaction` | `summarize?: (messages) => Promise<string>`, `maxTokens?: number` | | ||
| | `ask-user` | `onQuestion?: (question, options?) => Promise<string>` | | ||
@@ -707,3 +708,3 @@ | `sleep` | `maxDuration?: number` -- cap in ms (default: 300000) | | ||
| lsp, createLsp, | ||
| contextCompaction, createContextCompaction, | ||
| createContextCompaction, // middleware, not a tool | ||
| askUser, createAskUser, | ||
@@ -735,3 +736,3 @@ sleep, createSleep, | ||
| import { lsp } from 'agentool/lsp'; | ||
| import { contextCompaction } from 'agentool/context-compaction'; | ||
| import { createContextCompaction } from 'agentool/context-compaction'; // middleware | ||
| import { askUser } from 'agentool/ask-user'; | ||
@@ -767,3 +768,3 @@ import { sleep } from 'agentool/sleep'; | ||
| | Node.js | >= 18 | Yes | | ||
| | `ai` (Vercel AI SDK) | >= 4.0.0 | Peer dependency | | ||
| | `ai` (Vercel AI SDK) | >= 5.0.17 | Peer dependency | | ||
| | `zod` | >= 3.23.0 | Peer dependency | | ||
@@ -770,0 +771,0 @@ | `ripgrep` (`rg`) | any | For grep/glob tools | |
| import { | ||
| extractErrorMessage | ||
| } from "./chunk-X6ZY2KFU.js"; | ||
| // src/context-compaction/index.ts | ||
| import { tool, zodSchema } from "ai"; | ||
| import { z } from "zod"; | ||
| // src/context-compaction/prompt.ts | ||
| function getPrompt(config = {}) { | ||
| const maxTokens = config.maxTokens ?? 4096; | ||
| return `Compact conversation history by summarizing older messages to reduce context size. Target budget: ${maxTokens} tokens. | ||
| Requires a summarize callback to be configured \u2014 the application provides the summarization implementation. | ||
| ## When to Use | ||
| - When the conversation is getting long and approaching context limits | ||
| - When earlier messages contain details no longer relevant to the current task | ||
| - To free up context space for new work without losing important context | ||
| ## When NOT to Use | ||
| - When the conversation is still within budget \u2014 the tool returns early if already compact | ||
| - When every message contains critical details that shouldn't be summarized | ||
| - For persisting information long-term \u2014 use the memory tool instead | ||
| ## Usage Guidelines | ||
| - Messages already within the token budget (${maxTokens} tokens, ~${maxTokens * 4} characters) are returned unchanged | ||
| - The summarize callback receives all messages and should return a condensed summary | ||
| - The result replaces the original messages with a single system message containing the summary`; | ||
| } | ||
| // src/context-compaction/index.ts | ||
| var parametersSchema = z.object({ | ||
| messages: z.array( | ||
| z.object({ | ||
| role: z.string().describe("Message role (system, user, assistant)"), | ||
| content: z.string().describe("Message content") | ||
| }) | ||
| ).describe("The conversation messages to compact"), | ||
| maxTokens: z.number().optional().describe( | ||
| "Target maximum tokens (default: config.maxTokens or 4096)" | ||
| ) | ||
| }); | ||
| function createContextCompaction(config = {}) { | ||
| return tool({ | ||
| description: config.description ?? getPrompt(config), | ||
| inputSchema: zodSchema(parametersSchema), | ||
| execute: async ({ | ||
| messages, | ||
| maxTokens: inputMaxTokens | ||
| }) => { | ||
| const maxTokens = inputMaxTokens ?? config.maxTokens ?? 4096; | ||
| const charBudget = maxTokens * 4; | ||
| const totalChars = messages.reduce( | ||
| (sum, m) => sum + m.content.length, | ||
| 0 | ||
| ); | ||
| if (totalChars <= charBudget) { | ||
| return JSON.stringify({ | ||
| compacted: false, | ||
| messages, | ||
| reason: "Already within token budget" | ||
| }); | ||
| } | ||
| if (!config.summarize) { | ||
| return "Error [context-compaction]: No summarize function configured. Provide a summarize callback in the tool config."; | ||
| } | ||
| try { | ||
| const summary = await config.summarize(messages); | ||
| const compactedMessages = [{ role: "system", content: summary }]; | ||
| return JSON.stringify({ | ||
| compacted: true, | ||
| messages: compactedMessages, | ||
| originalCount: messages.length | ||
| }); | ||
| } catch (error) { | ||
| const msg = extractErrorMessage(error); | ||
| return `Error [context-compaction]: Summarization failed: ${msg}`; | ||
| } | ||
| } | ||
| }); | ||
| } | ||
| var contextCompaction = createContextCompaction(); | ||
| export { | ||
| getPrompt, | ||
| createContextCompaction, | ||
| contextCompaction | ||
| }; |
| "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } | ||
| var _chunkKONXT2SFcjs = require('./chunk-KONXT2SF.cjs'); | ||
| // src/context-compaction/index.ts | ||
| var _ai = require('ai'); | ||
| var _zod = require('zod'); | ||
| // src/context-compaction/prompt.ts | ||
| function getPrompt(config = {}) { | ||
| const maxTokens = _nullishCoalesce(config.maxTokens, () => ( 4096)); | ||
| return `Compact conversation history by summarizing older messages to reduce context size. Target budget: ${maxTokens} tokens. | ||
| Requires a summarize callback to be configured \u2014 the application provides the summarization implementation. | ||
| ## When to Use | ||
| - When the conversation is getting long and approaching context limits | ||
| - When earlier messages contain details no longer relevant to the current task | ||
| - To free up context space for new work without losing important context | ||
| ## When NOT to Use | ||
| - When the conversation is still within budget \u2014 the tool returns early if already compact | ||
| - When every message contains critical details that shouldn't be summarized | ||
| - For persisting information long-term \u2014 use the memory tool instead | ||
| ## Usage Guidelines | ||
| - Messages already within the token budget (${maxTokens} tokens, ~${maxTokens * 4} characters) are returned unchanged | ||
| - The summarize callback receives all messages and should return a condensed summary | ||
| - The result replaces the original messages with a single system message containing the summary`; | ||
| } | ||
| // src/context-compaction/index.ts | ||
| var parametersSchema = _zod.z.object({ | ||
| messages: _zod.z.array( | ||
| _zod.z.object({ | ||
| role: _zod.z.string().describe("Message role (system, user, assistant)"), | ||
| content: _zod.z.string().describe("Message content") | ||
| }) | ||
| ).describe("The conversation messages to compact"), | ||
| maxTokens: _zod.z.number().optional().describe( | ||
| "Target maximum tokens (default: config.maxTokens or 4096)" | ||
| ) | ||
| }); | ||
| function createContextCompaction(config = {}) { | ||
| return _ai.tool.call(void 0, { | ||
| description: _nullishCoalesce(config.description, () => ( getPrompt(config))), | ||
| inputSchema: _ai.zodSchema.call(void 0, parametersSchema), | ||
| execute: async ({ | ||
| messages, | ||
| maxTokens: inputMaxTokens | ||
| }) => { | ||
| const maxTokens = _nullishCoalesce(_nullishCoalesce(inputMaxTokens, () => ( config.maxTokens)), () => ( 4096)); | ||
| const charBudget = maxTokens * 4; | ||
| const totalChars = messages.reduce( | ||
| (sum, m) => sum + m.content.length, | ||
| 0 | ||
| ); | ||
| if (totalChars <= charBudget) { | ||
| return JSON.stringify({ | ||
| compacted: false, | ||
| messages, | ||
| reason: "Already within token budget" | ||
| }); | ||
| } | ||
| if (!config.summarize) { | ||
| return "Error [context-compaction]: No summarize function configured. Provide a summarize callback in the tool config."; | ||
| } | ||
| try { | ||
| const summary = await config.summarize(messages); | ||
| const compactedMessages = [{ role: "system", content: summary }]; | ||
| return JSON.stringify({ | ||
| compacted: true, | ||
| messages: compactedMessages, | ||
| originalCount: messages.length | ||
| }); | ||
| } catch (error) { | ||
| const msg = _chunkKONXT2SFcjs.extractErrorMessage.call(void 0, error); | ||
| return `Error [context-compaction]: Summarization failed: ${msg}`; | ||
| } | ||
| } | ||
| }); | ||
| } | ||
| var contextCompaction = createContextCompaction(); | ||
| exports.getPrompt = getPrompt; exports.createContextCompaction = createContextCompaction; exports.contextCompaction = contextCompaction; |
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
363265
3.97%7171
6.66%768
0.13%10
-9.09%23
9.52%