@tarquinen/opencode-dcp 3.1.11 → 3.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -9
- package/dist/index.js +106 -49
- package/dist/index.js.map +1 -1
- package/dist/lib/commands/context.d.ts +15 -0
- package/dist/lib/commands/context.d.ts.map +1 -1
- package/dist/lib/commands/help.d.ts +1 -0
- package/dist/lib/commands/help.d.ts.map +1 -1
- package/dist/lib/commands/manual.d.ts +1 -1
- package/dist/lib/commands/manual.d.ts.map +1 -1
- package/dist/lib/commands/stats.d.ts +10 -0
- package/dist/lib/commands/stats.d.ts.map +1 -1
- package/dist/lib/compress/pipeline.d.ts.map +1 -1
- package/dist/lib/hooks.d.ts +0 -2
- package/dist/lib/hooks.d.ts.map +1 -1
- package/dist/lib/messages/index.d.ts +0 -1
- package/dist/lib/messages/index.d.ts.map +1 -1
- package/dist/lib/messages/inject/utils.d.ts +0 -7
- package/dist/lib/messages/inject/utils.d.ts.map +1 -1
- package/dist/lib/state/persistence.d.ts +3 -0
- package/dist/lib/state/persistence.d.ts.map +1 -1
- package/dist/lib/state/state.d.ts +1 -0
- package/dist/lib/state/state.d.ts.map +1 -1
- package/dist/lib/tui/commands.d.ts +3 -0
- package/dist/lib/tui/commands.d.ts.map +1 -0
- package/dist/lib/tui/data.d.ts +14 -0
- package/dist/lib/tui/data.d.ts.map +1 -0
- package/dist/lib/tui/dialogs.d.ts +30 -0
- package/dist/lib/tui/dialogs.d.ts.map +1 -0
- package/dist/lib/tui/format.d.ts +4 -0
- package/dist/lib/tui/format.d.ts.map +1 -0
- package/dist/lib/tui/modals.d.ts +10 -0
- package/dist/lib/tui/modals.d.ts.map +1 -0
- package/dist/lib/tui/types.d.ts +15 -0
- package/dist/lib/tui/types.d.ts.map +1 -0
- package/dist/lib/tui/ui.d.ts +48 -0
- package/dist/lib/tui/ui.d.ts.map +1 -0
- package/dist/tui.d.ts +7 -0
- package/dist/tui.d.ts.map +1 -0
- package/lib/auth.ts +37 -0
- package/lib/commands/compression-targets.ts +137 -0
- package/lib/commands/context.ts +305 -0
- package/lib/commands/decompress.ts +275 -0
- package/lib/commands/help.ts +76 -0
- package/lib/commands/index.ts +11 -0
- package/lib/commands/manual.ts +127 -0
- package/lib/commands/recompress.ts +224 -0
- package/lib/commands/stats.ts +159 -0
- package/lib/commands/sweep.ts +268 -0
- package/lib/compress/index.ts +3 -0
- package/lib/compress/message-utils.ts +250 -0
- package/lib/compress/message.ts +145 -0
- package/lib/compress/pipeline.ts +108 -0
- package/lib/compress/protected-content.ts +208 -0
- package/lib/compress/range-utils.ts +308 -0
- package/lib/compress/range.ts +192 -0
- package/lib/compress/search.ts +267 -0
- package/lib/compress/state.ts +268 -0
- package/lib/compress/timing.ts +77 -0
- package/lib/compress/types.ts +108 -0
- package/lib/compress-permission.ts +25 -0
- package/lib/config.ts +1007 -0
- package/lib/hooks.ts +375 -0
- package/lib/host-permissions.ts +101 -0
- package/lib/logger.ts +226 -0
- package/lib/message-ids.ts +172 -0
- package/lib/messages/index.ts +8 -0
- package/lib/messages/inject/inject.ts +215 -0
- package/lib/messages/inject/subagent-results.ts +82 -0
- package/lib/messages/inject/utils.ts +374 -0
- package/lib/messages/priority.ts +102 -0
- package/lib/messages/prune.ts +233 -0
- package/lib/messages/query.ts +72 -0
- package/lib/messages/reasoning-strip.ts +40 -0
- package/lib/messages/shape.ts +50 -0
- package/lib/messages/sync.ts +124 -0
- package/lib/messages/utils.ts +185 -0
- package/lib/prompts/compress-message.ts +43 -0
- package/lib/prompts/compress-range.ts +60 -0
- package/lib/prompts/context-limit-nudge.ts +18 -0
- package/lib/prompts/extensions/nudge.ts +43 -0
- package/lib/prompts/extensions/system.ts +32 -0
- package/lib/prompts/extensions/tool.ts +35 -0
- package/lib/prompts/index.ts +29 -0
- package/lib/prompts/iteration-nudge.ts +6 -0
- package/lib/prompts/store.ts +467 -0
- package/lib/prompts/system.ts +33 -0
- package/lib/prompts/turn-nudge.ts +10 -0
- package/lib/protected-patterns.ts +128 -0
- package/lib/state/index.ts +4 -0
- package/lib/state/persistence.ts +305 -0
- package/lib/state/state.ts +208 -0
- package/lib/state/tool-cache.ts +98 -0
- package/lib/state/types.ts +111 -0
- package/lib/state/utils.ts +345 -0
- package/lib/strategies/deduplication.ts +127 -0
- package/lib/strategies/index.ts +2 -0
- package/lib/strategies/purge-errors.ts +88 -0
- package/lib/subagents/subagent-results.ts +74 -0
- package/lib/token-utils.ts +164 -0
- package/lib/tui/commands.ts +31 -0
- package/lib/tui/data.ts +73 -0
- package/lib/tui/dialogs.tsx +235 -0
- package/lib/tui/format.ts +25 -0
- package/lib/tui/modals.tsx +92 -0
- package/lib/tui/types.ts +16 -0
- package/lib/tui/ui.tsx +219 -0
- package/lib/ui/notification.ts +347 -0
- package/lib/ui/utils.ts +304 -0
- package/lib/update.ts +185 -0
- package/package.json +17 -5
- package/tui.tsx +26 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import type { CompressionBlock, PruneMessagesState, SessionState } from "../state"
|
|
2
|
+
import { formatBlockRef, formatMessageIdTag } from "../message-ids"
|
|
3
|
+
import type { AppliedCompressionResult, CompressionStateInput, SelectionResolution } from "./types"
|
|
4
|
+
|
|
5
|
+
export const COMPRESSED_BLOCK_HEADER = "[Compressed conversation section]"
|
|
6
|
+
|
|
7
|
+
export function allocateBlockId(state: SessionState): number {
|
|
8
|
+
const next = state.prune.messages.nextBlockId
|
|
9
|
+
if (!Number.isInteger(next) || next < 1) {
|
|
10
|
+
state.prune.messages.nextBlockId = 2
|
|
11
|
+
return 1
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
state.prune.messages.nextBlockId = next + 1
|
|
15
|
+
return next
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function allocateRunId(state: SessionState): number {
|
|
19
|
+
const next = state.prune.messages.nextRunId
|
|
20
|
+
if (!Number.isInteger(next) || next < 1) {
|
|
21
|
+
state.prune.messages.nextRunId = 2
|
|
22
|
+
return 1
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
state.prune.messages.nextRunId = next + 1
|
|
26
|
+
return next
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function attachCompressionDuration(
|
|
30
|
+
messagesState: PruneMessagesState,
|
|
31
|
+
messageId: string,
|
|
32
|
+
callId: string,
|
|
33
|
+
durationMs: number,
|
|
34
|
+
): number {
|
|
35
|
+
if (typeof durationMs !== "number" || !Number.isFinite(durationMs)) {
|
|
36
|
+
return 0
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let updates = 0
|
|
40
|
+
for (const block of messagesState.blocksById.values()) {
|
|
41
|
+
if (block.compressMessageId !== messageId || block.compressCallId !== callId) {
|
|
42
|
+
continue
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
block.durationMs = durationMs
|
|
46
|
+
updates++
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return updates
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function wrapCompressedSummary(blockId: number, summary: string): string {
|
|
53
|
+
const header = COMPRESSED_BLOCK_HEADER
|
|
54
|
+
const footer = formatMessageIdTag(formatBlockRef(blockId))
|
|
55
|
+
const body = summary.trim()
|
|
56
|
+
if (body.length === 0) {
|
|
57
|
+
return `${header}\n${footer}`
|
|
58
|
+
}
|
|
59
|
+
return `${header}\n${body}\n\n${footer}`
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function applyCompressionState(
|
|
63
|
+
state: SessionState,
|
|
64
|
+
input: CompressionStateInput,
|
|
65
|
+
selection: SelectionResolution,
|
|
66
|
+
anchorMessageId: string,
|
|
67
|
+
blockId: number,
|
|
68
|
+
summary: string,
|
|
69
|
+
consumedBlockIds: number[],
|
|
70
|
+
): AppliedCompressionResult {
|
|
71
|
+
const messagesState = state.prune.messages
|
|
72
|
+
const consumed = [...new Set(consumedBlockIds.filter((id) => Number.isInteger(id) && id > 0))]
|
|
73
|
+
const included = [...consumed]
|
|
74
|
+
|
|
75
|
+
const effectiveMessageIds = new Set<string>(selection.messageIds)
|
|
76
|
+
const effectiveToolIds = new Set<string>(selection.toolIds)
|
|
77
|
+
|
|
78
|
+
for (const consumedBlockId of consumed) {
|
|
79
|
+
const consumedBlock = messagesState.blocksById.get(consumedBlockId)
|
|
80
|
+
if (!consumedBlock) {
|
|
81
|
+
continue
|
|
82
|
+
}
|
|
83
|
+
for (const messageId of consumedBlock.effectiveMessageIds) {
|
|
84
|
+
effectiveMessageIds.add(messageId)
|
|
85
|
+
}
|
|
86
|
+
for (const toolId of consumedBlock.effectiveToolIds) {
|
|
87
|
+
effectiveToolIds.add(toolId)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const initiallyActiveMessages = new Set<string>()
|
|
92
|
+
for (const messageId of effectiveMessageIds) {
|
|
93
|
+
const entry = messagesState.byMessageId.get(messageId)
|
|
94
|
+
if (entry && entry.activeBlockIds.length > 0) {
|
|
95
|
+
initiallyActiveMessages.add(messageId)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const initiallyActiveToolIds = new Set<string>()
|
|
100
|
+
for (const activeBlockId of messagesState.activeBlockIds) {
|
|
101
|
+
const activeBlock = messagesState.blocksById.get(activeBlockId)
|
|
102
|
+
if (!activeBlock || !activeBlock.active) {
|
|
103
|
+
continue
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
for (const toolId of activeBlock.effectiveToolIds) {
|
|
107
|
+
initiallyActiveToolIds.add(toolId)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const createdAt = Date.now()
|
|
112
|
+
const block: CompressionBlock = {
|
|
113
|
+
blockId,
|
|
114
|
+
runId: input.runId,
|
|
115
|
+
active: true,
|
|
116
|
+
deactivatedByUser: false,
|
|
117
|
+
compressedTokens: 0,
|
|
118
|
+
summaryTokens: input.summaryTokens,
|
|
119
|
+
durationMs: 0,
|
|
120
|
+
mode: input.mode,
|
|
121
|
+
topic: input.topic,
|
|
122
|
+
batchTopic: input.batchTopic,
|
|
123
|
+
startId: input.startId,
|
|
124
|
+
endId: input.endId,
|
|
125
|
+
anchorMessageId,
|
|
126
|
+
compressMessageId: input.compressMessageId,
|
|
127
|
+
compressCallId: input.compressCallId,
|
|
128
|
+
includedBlockIds: included,
|
|
129
|
+
consumedBlockIds: consumed,
|
|
130
|
+
parentBlockIds: [],
|
|
131
|
+
directMessageIds: [],
|
|
132
|
+
directToolIds: [],
|
|
133
|
+
effectiveMessageIds: [...effectiveMessageIds],
|
|
134
|
+
effectiveToolIds: [...effectiveToolIds],
|
|
135
|
+
createdAt,
|
|
136
|
+
summary,
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
messagesState.blocksById.set(blockId, block)
|
|
140
|
+
messagesState.activeBlockIds.add(blockId)
|
|
141
|
+
messagesState.activeByAnchorMessageId.set(anchorMessageId, blockId)
|
|
142
|
+
|
|
143
|
+
const deactivatedAt = Date.now()
|
|
144
|
+
for (const consumedBlockId of consumed) {
|
|
145
|
+
const consumedBlock = messagesState.blocksById.get(consumedBlockId)
|
|
146
|
+
if (!consumedBlock || !consumedBlock.active) {
|
|
147
|
+
continue
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
consumedBlock.active = false
|
|
151
|
+
consumedBlock.deactivatedAt = deactivatedAt
|
|
152
|
+
consumedBlock.deactivatedByBlockId = blockId
|
|
153
|
+
if (!consumedBlock.parentBlockIds.includes(blockId)) {
|
|
154
|
+
consumedBlock.parentBlockIds.push(blockId)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
messagesState.activeBlockIds.delete(consumedBlockId)
|
|
158
|
+
const mappedBlockId = messagesState.activeByAnchorMessageId.get(
|
|
159
|
+
consumedBlock.anchorMessageId,
|
|
160
|
+
)
|
|
161
|
+
if (mappedBlockId === consumedBlockId) {
|
|
162
|
+
messagesState.activeByAnchorMessageId.delete(consumedBlock.anchorMessageId)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const removeActiveBlockId = (
|
|
167
|
+
entry: { activeBlockIds: number[] },
|
|
168
|
+
blockIdToRemove: number,
|
|
169
|
+
): void => {
|
|
170
|
+
if (entry.activeBlockIds.length === 0) {
|
|
171
|
+
return
|
|
172
|
+
}
|
|
173
|
+
entry.activeBlockIds = entry.activeBlockIds.filter((id) => id !== blockIdToRemove)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
for (const consumedBlockId of consumed) {
|
|
177
|
+
const consumedBlock = messagesState.blocksById.get(consumedBlockId)
|
|
178
|
+
if (!consumedBlock) {
|
|
179
|
+
continue
|
|
180
|
+
}
|
|
181
|
+
for (const messageId of consumedBlock.effectiveMessageIds) {
|
|
182
|
+
const entry = messagesState.byMessageId.get(messageId)
|
|
183
|
+
if (!entry) {
|
|
184
|
+
continue
|
|
185
|
+
}
|
|
186
|
+
removeActiveBlockId(entry, consumedBlockId)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
for (const messageId of selection.messageIds) {
|
|
191
|
+
const tokenCount = selection.messageTokenById.get(messageId) || 0
|
|
192
|
+
const existing = messagesState.byMessageId.get(messageId)
|
|
193
|
+
|
|
194
|
+
if (!existing) {
|
|
195
|
+
messagesState.byMessageId.set(messageId, {
|
|
196
|
+
tokenCount,
|
|
197
|
+
allBlockIds: [blockId],
|
|
198
|
+
activeBlockIds: [blockId],
|
|
199
|
+
})
|
|
200
|
+
continue
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
existing.tokenCount = Math.max(existing.tokenCount, tokenCount)
|
|
204
|
+
if (!existing.allBlockIds.includes(blockId)) {
|
|
205
|
+
existing.allBlockIds.push(blockId)
|
|
206
|
+
}
|
|
207
|
+
if (!existing.activeBlockIds.includes(blockId)) {
|
|
208
|
+
existing.activeBlockIds.push(blockId)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
for (const messageId of block.effectiveMessageIds) {
|
|
213
|
+
if (selection.messageTokenById.has(messageId)) {
|
|
214
|
+
continue
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const existing = messagesState.byMessageId.get(messageId)
|
|
218
|
+
if (!existing) {
|
|
219
|
+
continue
|
|
220
|
+
}
|
|
221
|
+
if (!existing.allBlockIds.includes(blockId)) {
|
|
222
|
+
existing.allBlockIds.push(blockId)
|
|
223
|
+
}
|
|
224
|
+
if (!existing.activeBlockIds.includes(blockId)) {
|
|
225
|
+
existing.activeBlockIds.push(blockId)
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
let compressedTokens = 0
|
|
230
|
+
const newlyCompressedMessageIds: string[] = []
|
|
231
|
+
for (const messageId of effectiveMessageIds) {
|
|
232
|
+
const entry = messagesState.byMessageId.get(messageId)
|
|
233
|
+
if (!entry) {
|
|
234
|
+
continue
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const isNowActive = entry.activeBlockIds.length > 0
|
|
238
|
+
const wasActive = initiallyActiveMessages.has(messageId)
|
|
239
|
+
|
|
240
|
+
if (isNowActive && !wasActive) {
|
|
241
|
+
compressedTokens += entry.tokenCount
|
|
242
|
+
newlyCompressedMessageIds.push(messageId)
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const newlyCompressedToolIds: string[] = []
|
|
247
|
+
for (const toolId of effectiveToolIds) {
|
|
248
|
+
if (!initiallyActiveToolIds.has(toolId)) {
|
|
249
|
+
newlyCompressedToolIds.push(toolId)
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
block.directMessageIds = [...newlyCompressedMessageIds]
|
|
254
|
+
block.directToolIds = [...newlyCompressedToolIds]
|
|
255
|
+
|
|
256
|
+
block.compressedTokens = compressedTokens
|
|
257
|
+
|
|
258
|
+
state.stats.pruneTokenCounter += compressedTokens
|
|
259
|
+
state.stats.totalPruneTokens += state.stats.pruneTokenCounter
|
|
260
|
+
state.stats.pruneTokenCounter = 0
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
compressedTokens,
|
|
264
|
+
messageIds: selection.messageIds,
|
|
265
|
+
newlyCompressedMessageIds,
|
|
266
|
+
newlyCompressedToolIds,
|
|
267
|
+
}
|
|
268
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { SessionState } from "../state/types"
|
|
2
|
+
import { attachCompressionDuration } from "./state"
|
|
3
|
+
|
|
4
|
+
export interface PendingCompressionDuration {
|
|
5
|
+
messageId: string
|
|
6
|
+
callId: string
|
|
7
|
+
durationMs: number
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface CompressionTimingState {
|
|
11
|
+
startsByCallId: Map<string, number>
|
|
12
|
+
pendingByCallId: Map<string, PendingCompressionDuration>
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function buildCompressionTimingKey(messageId: string, callId: string): string {
|
|
16
|
+
return `${messageId}:${callId}`
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function consumeCompressionStart(
|
|
20
|
+
state: SessionState,
|
|
21
|
+
messageId: string,
|
|
22
|
+
callId: string,
|
|
23
|
+
): number | undefined {
|
|
24
|
+
const key = buildCompressionTimingKey(messageId, callId)
|
|
25
|
+
const start = state.compressionTiming.startsByCallId.get(key)
|
|
26
|
+
state.compressionTiming.startsByCallId.delete(key)
|
|
27
|
+
return start
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function resolveCompressionDuration(
|
|
31
|
+
startedAt: number | undefined,
|
|
32
|
+
eventTime: number | undefined,
|
|
33
|
+
partTime: { start?: unknown; end?: unknown } | undefined,
|
|
34
|
+
): number | undefined {
|
|
35
|
+
const runningAt =
|
|
36
|
+
typeof partTime?.start === "number" && Number.isFinite(partTime.start)
|
|
37
|
+
? partTime.start
|
|
38
|
+
: eventTime
|
|
39
|
+
const pendingToRunningMs =
|
|
40
|
+
typeof startedAt === "number" && typeof runningAt === "number"
|
|
41
|
+
? Math.max(0, runningAt - startedAt)
|
|
42
|
+
: undefined
|
|
43
|
+
|
|
44
|
+
const toolStart = partTime?.start
|
|
45
|
+
const toolEnd = partTime?.end
|
|
46
|
+
const runtimeMs =
|
|
47
|
+
typeof toolStart === "number" &&
|
|
48
|
+
Number.isFinite(toolStart) &&
|
|
49
|
+
typeof toolEnd === "number" &&
|
|
50
|
+
Number.isFinite(toolEnd)
|
|
51
|
+
? Math.max(0, toolEnd - toolStart)
|
|
52
|
+
: undefined
|
|
53
|
+
|
|
54
|
+
return typeof pendingToRunningMs === "number" ? pendingToRunningMs : runtimeMs
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function applyPendingCompressionDurations(state: SessionState): number {
|
|
58
|
+
if (state.compressionTiming.pendingByCallId.size === 0) {
|
|
59
|
+
return 0
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let updates = 0
|
|
63
|
+
for (const [key, entry] of state.compressionTiming.pendingByCallId) {
|
|
64
|
+
const applied = attachCompressionDuration(
|
|
65
|
+
state.prune.messages,
|
|
66
|
+
entry.messageId,
|
|
67
|
+
entry.callId,
|
|
68
|
+
entry.durationMs,
|
|
69
|
+
)
|
|
70
|
+
if (applied > 0) {
|
|
71
|
+
updates += applied
|
|
72
|
+
state.compressionTiming.pendingByCallId.delete(key)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return updates
|
|
77
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type { PluginConfig } from "../config"
|
|
2
|
+
import type { Logger } from "../logger"
|
|
3
|
+
import type { PromptStore } from "../prompts/store"
|
|
4
|
+
import type { CompressionBlock, CompressionMode, SessionState, WithParts } from "../state"
|
|
5
|
+
|
|
6
|
+
export interface ToolContext {
|
|
7
|
+
client: any
|
|
8
|
+
state: SessionState
|
|
9
|
+
logger: Logger
|
|
10
|
+
config: PluginConfig
|
|
11
|
+
prompts: PromptStore
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface CompressRangeEntry {
|
|
15
|
+
startId: string
|
|
16
|
+
endId: string
|
|
17
|
+
summary: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface CompressRangeToolArgs {
|
|
21
|
+
topic: string
|
|
22
|
+
content: CompressRangeEntry[]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface CompressMessageEntry {
|
|
26
|
+
messageId: string
|
|
27
|
+
topic: string
|
|
28
|
+
summary: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface CompressMessageToolArgs {
|
|
32
|
+
topic: string
|
|
33
|
+
content: CompressMessageEntry[]
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface BoundaryReference {
|
|
37
|
+
kind: "message" | "compressed-block"
|
|
38
|
+
rawIndex: number
|
|
39
|
+
messageId?: string
|
|
40
|
+
blockId?: number
|
|
41
|
+
anchorMessageId?: string
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface SearchContext {
|
|
45
|
+
rawMessages: WithParts[]
|
|
46
|
+
rawMessagesById: Map<string, WithParts>
|
|
47
|
+
rawIndexById: Map<string, number>
|
|
48
|
+
summaryByBlockId: Map<number, CompressionBlock>
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface SelectionResolution {
|
|
52
|
+
startReference: BoundaryReference
|
|
53
|
+
endReference: BoundaryReference
|
|
54
|
+
messageIds: string[]
|
|
55
|
+
messageTokenById: Map<string, number>
|
|
56
|
+
toolIds: string[]
|
|
57
|
+
requiredBlockIds: number[]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface ResolvedMessageCompression {
|
|
61
|
+
entry: CompressMessageEntry
|
|
62
|
+
selection: SelectionResolution
|
|
63
|
+
anchorMessageId: string
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface ResolvedRangeCompression {
|
|
67
|
+
index: number
|
|
68
|
+
entry: CompressRangeEntry
|
|
69
|
+
selection: SelectionResolution
|
|
70
|
+
anchorMessageId: string
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface ResolvedMessageCompressionsResult {
|
|
74
|
+
plans: ResolvedMessageCompression[]
|
|
75
|
+
skippedIssues: string[]
|
|
76
|
+
skippedCount: number
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface ParsedBlockPlaceholder {
|
|
80
|
+
raw: string
|
|
81
|
+
blockId: number
|
|
82
|
+
startIndex: number
|
|
83
|
+
endIndex: number
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface InjectedSummaryResult {
|
|
87
|
+
expandedSummary: string
|
|
88
|
+
consumedBlockIds: number[]
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export interface AppliedCompressionResult {
|
|
92
|
+
compressedTokens: number
|
|
93
|
+
messageIds: string[]
|
|
94
|
+
newlyCompressedMessageIds: string[]
|
|
95
|
+
newlyCompressedToolIds: string[]
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface CompressionStateInput {
|
|
99
|
+
topic: string
|
|
100
|
+
batchTopic: string
|
|
101
|
+
startId: string
|
|
102
|
+
endId: string
|
|
103
|
+
mode: CompressionMode
|
|
104
|
+
runId: number
|
|
105
|
+
compressMessageId: string
|
|
106
|
+
compressCallId?: string
|
|
107
|
+
summaryTokens: number
|
|
108
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { PluginConfig } from "./config"
|
|
2
|
+
import { type HostPermissionSnapshot, resolveEffectiveCompressPermission } from "./host-permissions"
|
|
3
|
+
import type { SessionState, WithParts } from "./state"
|
|
4
|
+
import { getLastUserMessage } from "./messages/query"
|
|
5
|
+
|
|
6
|
+
export const compressPermission = (
|
|
7
|
+
state: SessionState,
|
|
8
|
+
config: PluginConfig,
|
|
9
|
+
): "ask" | "allow" | "deny" => {
|
|
10
|
+
return state.compressPermission ?? config.compress.permission
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const syncCompressPermissionState = (
|
|
14
|
+
state: SessionState,
|
|
15
|
+
config: PluginConfig,
|
|
16
|
+
hostPermissions: HostPermissionSnapshot,
|
|
17
|
+
messages: WithParts[],
|
|
18
|
+
): void => {
|
|
19
|
+
const activeAgent = getLastUserMessage(messages)?.info.agent
|
|
20
|
+
state.compressPermission = resolveEffectiveCompressPermission(
|
|
21
|
+
config.compress.permission,
|
|
22
|
+
hostPermissions,
|
|
23
|
+
activeAgent,
|
|
24
|
+
)
|
|
25
|
+
}
|