-
Notifications
You must be signed in to change notification settings - Fork 76
Expand file tree
/
Copy pathcontext-prune.ts
More file actions
87 lines (74 loc) · 3.31 KB
/
context-prune.ts
File metadata and controls
87 lines (74 loc) · 3.31 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import { tool } from "@opencode-ai/plugin/tool"
import { createCodeNomadRequester, type CodeNomadConfig } from "./request"
type ContextPruneRouteRequest = {
sessionID: string
indices: number[]
}
const MAX_SELECTABLE_INDICES = 1000
export function createContextPruneTools(config: CodeNomadConfig) {
const requester = createCodeNomadRequester(config)
return {
select_context_range: tool({
description: "Stage context-prune badge selections in the UI using 1-based positions in the current visible pruneable conversation order. The model usually sees conversation entries as message ids like m0001, m0002, m0003; treat those as the visible chronological sequence and convert them into 1-based positions for this tool. Example: if the pruneable visible sequence is m0007, m0008, m0009, m0010, then their positions for this tool are 1, 2, 3, 4. A single call can include multiple individual positions and multiple ranges, such as 1,3-5,8,10-12. Call this tool once with the full final selection because later calls replace the staged selection.",
args: {
range: tool.schema.string().describe("Full final selection to stage in one call, using 1-based positions in the current pruneable visible conversation order. Example: if the pruneable visible sequence is m0007, m0008, m0009, m0010, use 1,2,3,4 respectively, so selecting m0008 through m0010 would be 2-4. Supports multiple single positions and inclusive ranges combined with commas, for example: 1,3-5,8,10-12. Repeated tool calls replace the previous staged selection instead of merging with it."),
},
async execute(args, context) {
const indices = parseRange(args.range)
await requester.requestVoid("/context-prune/select", {
method: "POST",
body: JSON.stringify({
sessionID: context.sessionID,
indices,
} satisfies ContextPruneRouteRequest),
})
return ""
},
}),
}
}
function parseRange(input: string): number[] {
const raw = (input ?? "").trim()
if (!raw) {
throw new Error("Range is required")
}
const values = new Set<number>()
const tokens = raw.split(",")
for (const token of tokens) {
const part = token.trim()
if (!part) {
throw new Error("Range contains an empty entry")
}
const rangeMatch = part.match(/^(\d+)-(\d+)$/)
if (rangeMatch) {
const start = Number(rangeMatch[1])
const end = Number(rangeMatch[2])
if (start < 1 || end < 1) {
throw new Error(`Invalid range: ${part}`)
}
if (start > end) {
throw new Error(`Invalid range: ${part} (start must be less than or equal to end)`)
}
for (let index = start; index <= end; index += 1) {
values.add(index)
}
continue
}
if (!/^\d+$/.test(part)) {
throw new Error(`Invalid range token: ${part}`)
}
const value = Number(part)
if (value < 1) {
throw new Error(`Invalid index: ${part}`)
}
values.add(value)
}
const indices = Array.from(values).sort((left, right) => left - right)
if (indices.length === 0) {
throw new Error("Range did not resolve to any indices")
}
if (indices.length > MAX_SELECTABLE_INDICES) {
throw new Error(`Range selects too many indices (${indices.length}). Maximum allowed is ${MAX_SELECTABLE_INDICES}.`)
}
return indices
}