|
| 1 | +import type { SessionState, WithParts } from "../state" |
| 2 | +import type { Logger } from "../logger" |
| 3 | +import type { PluginConfig } from "../config" |
| 4 | +import { buildToolIdList } from "../utils" |
| 5 | +import { getLastUserMessage, extractParameterKey } from "./utils" |
| 6 | + |
| 7 | +const PRUNED_TOOL_OUTPUT_REPLACEMENT = '[Output removed to save context - information superseded or no longer needed]' |
| 8 | + |
| 9 | +const buildPrunableToolsList = ( |
| 10 | + state: SessionState, |
| 11 | + config: PluginConfig, |
| 12 | + logger: Logger, |
| 13 | + messages: WithParts[], |
| 14 | +): string => { |
| 15 | + const lines: string[] = [] |
| 16 | + const toolIdList: string[] = buildToolIdList(messages) |
| 17 | + |
| 18 | + state.toolParameters.forEach((toolParameterEntry, toolCallId) => { |
| 19 | + if (state.prune.toolIds.includes(toolCallId)) { |
| 20 | + return |
| 21 | + } |
| 22 | + if (config.strategies.pruneTool.protectedTools.includes(toolParameterEntry.tool)) { |
| 23 | + return |
| 24 | + } |
| 25 | + const numericId = toolIdList.indexOf(toolCallId) |
| 26 | + const paramKey = extractParameterKey(toolParameterEntry.tool, toolParameterEntry.parameters) |
| 27 | + const description = paramKey ? `${toolParameterEntry.tool}, ${paramKey}` : toolParameterEntry.tool |
| 28 | + lines.push(`${numericId}: ${description}`) |
| 29 | + }) |
| 30 | + |
| 31 | + return `<prunable-tools>\nThe following tools have been invoked and are available for pruning. This list does not mandate immediate action. Consider your current goals and the resources you need before discarding valuable tool outputs. Keep the context free of noise.\n${lines.join('\n')}\n</prunable-tools>` |
| 32 | +} |
| 33 | + |
| 34 | +export const insertPruneToolContext = ( |
| 35 | + state: SessionState, |
| 36 | + config: PluginConfig, |
| 37 | + logger: Logger, |
| 38 | + messages: WithParts[] |
| 39 | +): void => { |
| 40 | + const lastUserMessage = getLastUserMessage(messages) |
| 41 | + if (!lastUserMessage || lastUserMessage.info.role !== 'user') { |
| 42 | + return |
| 43 | + } |
| 44 | + |
| 45 | + const prunableToolsList = buildPrunableToolsList(state, config, logger, messages) |
| 46 | + |
| 47 | + const userMessage: WithParts = { |
| 48 | + info: { |
| 49 | + id: "msg_01234567890123456789012345", |
| 50 | + sessionID: lastUserMessage.info.sessionID, |
| 51 | + role: "user", |
| 52 | + time: { created: Date.now() }, |
| 53 | + agent: lastUserMessage.info.agent || "build", |
| 54 | + model: { |
| 55 | + providerID: lastUserMessage.info.model.providerID, |
| 56 | + modelID: lastUserMessage.info.model.modelID |
| 57 | + } |
| 58 | + }, |
| 59 | + parts: [ |
| 60 | + { |
| 61 | + id: "prt_01234567890123456789012345", |
| 62 | + sessionID: lastUserMessage.info.sessionID, |
| 63 | + messageID: "msg_01234567890123456789012345", |
| 64 | + type: "text", |
| 65 | + text: prunableToolsList, |
| 66 | + } |
| 67 | + ] |
| 68 | + } |
| 69 | + |
| 70 | + messages.push(userMessage) |
| 71 | +} |
| 72 | + |
| 73 | +export const prune = ( |
| 74 | + state: SessionState, |
| 75 | + logger: Logger, |
| 76 | + config: PluginConfig, |
| 77 | + messages: WithParts[] |
| 78 | +): void => { |
| 79 | + pruneToolOutputs(state, logger, messages) |
| 80 | +} |
| 81 | + |
| 82 | +const pruneToolOutputs = ( |
| 83 | + state: SessionState, |
| 84 | + logger: Logger, |
| 85 | + messages: WithParts[] |
| 86 | +): void => { |
| 87 | + for (const msg of messages) { |
| 88 | + for (const part of msg.parts) { |
| 89 | + if (part.type !== 'tool') { |
| 90 | + continue |
| 91 | + } |
| 92 | + if (!state.prune.toolIds.includes(part.id)) { |
| 93 | + continue |
| 94 | + } |
| 95 | + if (part.state.status === 'completed') { |
| 96 | + part.state.output = PRUNED_TOOL_OUTPUT_REPLACEMENT |
| 97 | + } |
| 98 | + // if (part.state.status === 'error') { |
| 99 | + // part.state.error = PRUNED_TOOL_OUTPUT_REPLACEMENT |
| 100 | + // } |
| 101 | + } |
| 102 | + } |
| 103 | +} |
0 commit comments