Skip to content

Commit fa54773

Browse files
feat(compress): include tool arguments and truncated output in delegated compression context
1 parent 3c316e5 commit fa54773

3 files changed

Lines changed: 76 additions & 4 deletions

File tree

lib/compress/message.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { ToolContext } from "./types"
33
import { countTokens } from "../token-utils"
44
import { getMessageFormatExtension } from "../prompts/extensions/tool"
55
import { formatIssues, formatResult, resolveMessages, validateArgs } from "./message-utils"
6-
import { finalizeSession, prepareSession, generateDelegatedSummary, resolveCompressionDelegate, type NotificationEntry } from "./pipeline"
6+
import { finalizeSession, prepareSession, generateDelegatedSummary, resolveCompressionDelegate, formatPartForDelegatedCompression, type NotificationEntry } from "./pipeline"
77
import { INTERNAL_COMPRESSION_SYSTEM_PROMPT } from "../prompts/system"
88
import { appendProtectedPromptInfo, appendProtectedTools } from "./protected-content"
99
import {
@@ -87,7 +87,7 @@ export function createCompressMessageTool(ctx: ToolContext): ReturnType<typeof t
8787
const rawText = plan.selection.messageIds
8888
.map((id) => {
8989
const msg = searchContext.rawMessagesById.get(id)
90-
return msg ? `[${msg.info.role}] ${msg.parts?.map((p: any) => p.text || p.prompt || p.tool).join("\n")}` : ""
90+
return msg ? `[${msg.info.role}] ${msg.parts?.map(formatPartForDelegatedCompression).join("\n")}` : ""
9191
})
9292
.filter(Boolean)
9393
.join("\n\n")

lib/compress/pipeline.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,78 @@ export async function prepareSession(
7676
}
7777
}
7878

79+
const TOOL_PAYLOAD_LIMIT = 3000
80+
const TRUNCATION_MARKER = "\n...[truncated]...\n"
81+
82+
function stringifyForCompression(value: unknown): string {
83+
if (typeof value === "string") return value
84+
if (value === undefined) return ""
85+
86+
try {
87+
const seen = new WeakSet<object>()
88+
89+
return JSON.stringify(
90+
value,
91+
(_key, val) => {
92+
if (typeof val === "bigint") return val.toString()
93+
if (typeof val === "object" && val !== null) {
94+
if (seen.has(val)) return "[Circular]"
95+
seen.add(val)
96+
}
97+
return val
98+
},
99+
2,
100+
)
101+
} catch {
102+
return String(value)
103+
}
104+
}
105+
106+
function truncateMiddle(value: string, limit = TOOL_PAYLOAD_LIMIT): string {
107+
if (value.length <= limit) return value
108+
109+
const available = Math.max(0, limit - TRUNCATION_MARKER.length)
110+
const headLength = Math.ceil(available / 2)
111+
const tailLength = Math.floor(available / 2)
112+
113+
return `${value.slice(0, headLength)}${TRUNCATION_MARKER}${value.slice(value.length - tailLength)}`
114+
}
115+
116+
export function formatPartForDelegatedCompression(part: any): string {
117+
if (!part || typeof part !== "object") return ""
118+
119+
if (part.type === "text") {
120+
return typeof part.text === "string" ? part.text : stringifyForCompression(part.text)
121+
}
122+
123+
if (part.type === "tool") {
124+
const state = part.state && typeof part.state === "object" ? part.state : {}
125+
const status = typeof state.status === "string" ? state.status : "unknown"
126+
const toolName = typeof part.tool === "string" ? part.tool : "unknown"
127+
const args = stringifyForCompression(state.input ?? {})
128+
129+
if (status === "completed") {
130+
return `[Tool: ${toolName} status=completed]\nargs: ${args}\noutput:\n${truncateMiddle(
131+
stringifyForCompression(state.output),
132+
)}`
133+
}
134+
135+
if (status === "error") {
136+
return `[Tool: ${toolName} status=error]\nargs: ${args}\nerror:\n${truncateMiddle(
137+
stringifyForCompression(state.error),
138+
)}`
139+
}
140+
141+
return `[Tool: ${toolName} status=${status}]\nargs: ${args}`
142+
}
143+
144+
if (typeof part.prompt === "string") {
145+
return part.prompt
146+
}
147+
148+
return ""
149+
}
150+
79151
export type CompressionDelegate =
80152
| {
81153
enabled: false

lib/compress/range.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { tool } from "@opencode-ai/plugin"
22
import type { ToolContext } from "./types"
33
import { countTokens } from "../token-utils"
44
import { getRangeFormatExtension } from "../prompts/extensions/tool"
5-
import { finalizeSession, prepareSession, generateDelegatedSummary, resolveCompressionDelegate, type NotificationEntry } from "./pipeline"
5+
import { finalizeSession, prepareSession, generateDelegatedSummary, resolveCompressionDelegate, formatPartForDelegatedCompression, type NotificationEntry } from "./pipeline"
66
import { INTERNAL_COMPRESSION_SYSTEM_PROMPT } from "../prompts/system"
77
import {
88
appendProtectedPromptInfo,
@@ -97,7 +97,7 @@ export function createCompressRangeTool(ctx: ToolContext): ReturnType<typeof too
9797
const rawText = plan.selection.messageIds
9898
.map((id) => {
9999
const msg = searchContext.rawMessagesById.get(id)
100-
return msg ? `[${msg.info.role}] ${msg.parts?.map((p: any) => p.text || p.prompt || p.tool).join("\n")}` : ""
100+
return msg ? `[${msg.info.role}] ${msg.parts?.map(formatPartForDelegatedCompression).join("\n")}` : ""
101101
})
102102
.filter(Boolean)
103103
.join("\n\n")

0 commit comments

Comments
 (0)