Skip to content

Commit 71c142c

Browse files
authored
Merge pull request #492 from Opencode-DCP/dev
fix: filter chat messages in place
2 parents ce512cb + 6cfd0ab commit 71c142c

5 files changed

Lines changed: 44 additions & 26 deletions

File tree

lib/compress/search.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { SessionState, WithParts } from "../state"
22
import { formatBlockRef, parseBoundaryId } from "../message-ids"
33
import { isIgnoredUserMessage } from "../messages/query"
4-
import { filterProcessableMessages } from "../messages/shape"
4+
import { filterMessages } from "../messages/shape"
55
import { countAllMessageTokens } from "../token-utils"
66
import type { BoundaryReference, SearchContext, SelectionResolution } from "./types"
77

@@ -10,7 +10,7 @@ export async function fetchSessionMessages(client: any, sessionId: string): Prom
1010
path: { id: sessionId },
1111
})
1212

13-
return filterProcessableMessages(response?.data || response)
13+
return filterMessages(response?.data || response)
1414
}
1515

1616
export function buildSearchContext(state: SessionState, rawMessages: WithParts[]): SearchContext {

lib/hooks.ts

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
consumeCompressionStart,
2323
resolveCompressionDuration,
2424
} from "./compress/timing"
25-
import { filterProcessableMessages } from "./messages/shape"
25+
import { filterMessages, filterMessagesInPlace } from "./messages/shape"
2626
import {
2727
applyPendingManualTrigger,
2828
handleContextCommand,
@@ -104,49 +104,50 @@ export function createChatMessageTransformHandler(
104104
hostPermissions: HostPermissionSnapshot,
105105
) {
106106
return async (input: {}, output: { messages: WithParts[] }) => {
107-
const messages = filterProcessableMessages(output.messages)
108-
if (messages.length !== output.messages.length) {
107+
const receivedMessages = Array.isArray(output.messages) ? output.messages.length : 0
108+
const messages = filterMessagesInPlace(output.messages)
109+
if (messages.length !== receivedMessages) {
109110
logger.warn("Skipping messages with unexpected shape during chat transform", {
110-
received: output.messages.length,
111+
received: receivedMessages,
111112
usable: messages.length,
112113
})
113114
}
114115

115-
await checkSession(client, state, logger, messages, config.manualMode.enabled)
116+
await checkSession(client, state, logger, output.messages, config.manualMode.enabled)
116117

117-
syncCompressPermissionState(state, config, hostPermissions, messages)
118+
syncCompressPermissionState(state, config, hostPermissions, output.messages)
118119

119120
if (state.isSubAgent && !config.experimental.allowSubAgents) {
120121
return
121122
}
122123

123124
stripHallucinations(output.messages)
124-
cacheSystemPromptTokens(state, messages)
125-
assignMessageRefs(state, messages)
126-
syncCompressionBlocks(state, logger, messages)
127-
syncToolCache(state, config, logger, messages)
128-
buildToolIdList(state, messages)
129-
prune(state, logger, config, messages)
125+
cacheSystemPromptTokens(state, output.messages)
126+
assignMessageRefs(state, output.messages)
127+
syncCompressionBlocks(state, logger, output.messages)
128+
syncToolCache(state, config, logger, output.messages)
129+
buildToolIdList(state, output.messages)
130+
prune(state, logger, config, output.messages)
130131
await injectExtendedSubAgentResults(
131132
client,
132133
state,
133134
logger,
134-
messages,
135+
output.messages,
135136
config.experimental.allowSubAgents,
136137
)
137-
const compressionPriorities = buildPriorityMap(config, state, messages)
138+
const compressionPriorities = buildPriorityMap(config, state, output.messages)
138139
prompts.reload()
139140
injectCompressNudges(
140141
state,
141142
config,
142143
logger,
143-
messages,
144+
output.messages,
144145
prompts.getRuntimePrompts(),
145146
compressionPriorities,
146147
)
147-
injectMessageIds(state, config, messages, compressionPriorities)
148-
applyPendingManualTrigger(state, messages, logger)
149-
stripStaleMetadata(messages)
148+
injectMessageIds(state, config, output.messages, compressionPriorities)
149+
applyPendingManualTrigger(state, output.messages, logger)
150+
stripStaleMetadata(output.messages)
150151

151152
if (state.sessionId) {
152153
await logger.saveContext(state.sessionId, output.messages)
@@ -174,7 +175,7 @@ export function createCommandExecuteHandler(
174175
const messagesResponse = await client.session.messages({
175176
path: { id: input.sessionID },
176177
})
177-
const messages = filterProcessableMessages(messagesResponse.data || messagesResponse)
178+
const messages = filterMessages(messagesResponse.data || messagesResponse)
178179

179180
await ensureSessionInitialized(
180181
client,

lib/messages/inject/subagent-results.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { Logger } from "../../logger"
22
import type { SessionState, WithParts } from "../../state"
3-
import { filterProcessableMessages } from "../shape"
3+
import { filterMessages } from "../shape"
44
import {
55
buildSubagentResultText,
66
getSubAgentId,
@@ -13,7 +13,7 @@ async function fetchSubAgentMessages(client: any, sessionId: string): Promise<Wi
1313
path: { id: sessionId },
1414
})
1515

16-
return filterProcessableMessages(response?.data || response)
16+
return filterMessages(response?.data || response)
1717
}
1818

1919
export const injectExtendedSubAgentResults = async (

lib/messages/shape.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,27 @@ export function isMessageWithInfo(message: unknown): message is WithParts {
2424
)
2525
}
2626

27-
export function filterProcessableMessages(messages: unknown): WithParts[] {
27+
export function filterMessages(messages: unknown): WithParts[] {
2828
if (!Array.isArray(messages)) {
2929
return []
3030
}
3131

3232
return messages.filter(isMessageWithInfo)
3333
}
34+
35+
export function filterMessagesInPlace(messages: unknown): WithParts[] {
36+
if (!Array.isArray(messages)) {
37+
return []
38+
}
39+
40+
let writeIndex = 0
41+
42+
for (const message of messages) {
43+
if (isMessageWithInfo(message)) {
44+
messages[writeIndex++] = message
45+
}
46+
}
47+
48+
messages.length = writeIndex
49+
return messages as WithParts[]
50+
}

tests/hooks-permission.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ test("chat message transform strips hallucinated tags even when compress is deni
113113
assert.equal((output.messages[0]?.parts[0] as any).text, "alpha omega")
114114
})
115115

116-
test("chat message transform ignores messages without info instead of crashing", async () => {
116+
test("chat message transform drops messages without info instead of crashing", async () => {
117117
const state = createSessionState()
118118
const logger = new Logger(false)
119119
const config = buildConfig("deny")
@@ -148,7 +148,7 @@ test("chat message transform ignores messages without info instead of crashing",
148148
await handler({}, output as any)
149149

150150
assert.equal(state.sessionId, null)
151-
assert.equal(output.messages.length, 1)
151+
assert.equal(output.messages.length, 0)
152152
})
153153

154154
test("command execute exits after effective permission resolves to deny", async () => {

0 commit comments

Comments
 (0)