Skip to content

Commit 30ea9e1

Browse files
messages: sanitize visible DCP metadata output
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
1 parent fbd75bc commit 30ea9e1

4 files changed

Lines changed: 25 additions & 44 deletions

File tree

index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
createCommandExecuteHandler,
1515
createSystemPromptHandler,
1616
createTextCompleteHandler,
17+
createToolExecuteAfterHandler,
1718
} from "./lib/hooks"
1819
import { configureClientAuth, isSecureMode } from "./lib/auth"
1920

@@ -71,6 +72,7 @@ const plugin: Plugin = (async (ctx) => {
7172
logger.debug("Cached variant from chat.message hook", { variant: input.variant })
7273
},
7374
"experimental.text.complete": createTextCompleteHandler(),
75+
"tool.execute.after": createToolExecuteAfterHandler(),
7476
"command.execute.before": createCommandExecuteHandler(
7577
ctx.client,
7678
state,

lib/hooks.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,12 @@ import {
1111
injectExtendedSubAgentResults,
1212
stripStaleMetadata,
1313
} from "./messages"
14-
import { buildToolIdList, isIgnoredUserMessage, stripHallucinations } from "./messages/utils"
14+
import {
15+
buildToolIdList,
16+
isIgnoredUserMessage,
17+
sanitizeVisibleOutput,
18+
stripHallucinations,
19+
} from "./messages/utils"
1520
import { checkSession } from "./state"
1621
import { renderSystemPrompt } from "./prompts"
1722
import { handleStatsCommand } from "./commands/stats"
@@ -33,9 +38,6 @@ const INTERNAL_AGENT_SIGNATURES = [
3338
"Summarize what was done in this conversation",
3439
]
3540

36-
const DCP_MESSAGE_ID_TAG_REGEX = /<dcp-message-id>(?:m\d+|b\d+)<\/dcp-message-id>/g
37-
const DCP_SYSTEM_REMINDER_REGEX = /<dcp-system-reminder\b[^>]*>[\s\S]*?<\/dcp-system-reminder>/g
38-
3941
function applyManualPrompt(state: SessionState, messages: WithParts[], logger: Logger): void {
4042
const pending = state.pendingManualTrigger
4143
if (!pending) {
@@ -125,7 +127,7 @@ export function createChatMessageTransformHandler(
125127
prompts: PromptStore,
126128
hostPermissions: HostPermissionSnapshot,
127129
) {
128-
return async (input: {}, output: { messages: WithParts[] }) => {
130+
return async (_input: {}, output: { messages: WithParts[] }) => {
129131
await checkSession(client, state, logger, output.messages, config.manualMode.enabled)
130132

131133
syncCompressPermissionState(state, config, hostPermissions, output.messages)
@@ -280,8 +282,16 @@ export function createTextCompleteHandler() {
280282
_input: { sessionID: string; messageID: string; partID: string },
281283
output: { text: string },
282284
) => {
283-
output.text = output.text
284-
.replace(DCP_SYSTEM_REMINDER_REGEX, "")
285-
.replace(DCP_MESSAGE_ID_TAG_REGEX, "")
285+
output.text = sanitizeVisibleOutput(output.text)
286+
}
287+
}
288+
289+
export function createToolExecuteAfterHandler() {
290+
return async (
291+
_input: { tool: string; sessionID: string; callID: string },
292+
output: { title: string; output: string; metadata: unknown },
293+
) => {
294+
output.title = sanitizeVisibleOutput(output.title)
295+
output.output = sanitizeVisibleOutput(output.output)
286296
}
287297
}

lib/messages/inject/inject.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@ import { formatMessageIdTag } from "../../message-ids"
66
import { compressPermission, getLastUserMessage } from "../../shared-utils"
77
import { saveSessionState } from "../../state/persistence"
88
import {
9-
appendIdToTool,
109
createSyntheticTextPart,
11-
findLastToolPart,
1210
isIgnoredUserMessage,
1311
} from "../utils"
1412
import {
@@ -164,11 +162,6 @@ export const injectMessageIds = (
164162
continue
165163
}
166164

167-
const lastToolPart = findLastToolPart(message)
168-
if (lastToolPart && appendIdToTool(lastToolPart, tag)) {
169-
continue
170-
}
171-
172165
const syntheticPart = createSyntheticTextPart(message, tag)
173166
const firstToolIndex = message.parts.findIndex((p) => p.type === "tool")
174167
if (firstToolIndex === -1) {

lib/messages/utils.ts

Lines changed: 5 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -61,38 +61,10 @@ export const createSyntheticTextPart = (
6161
messageID: userInfo.id,
6262
type: "text" as const,
6363
text: content,
64+
synthetic: true,
6465
}
6566
}
6667

67-
type MessagePart = WithParts["parts"][number]
68-
type ToolPart = Extract<MessagePart, { type: "tool" }>
69-
70-
export const appendIdToTool = (part: ToolPart, tag: string): boolean => {
71-
if (part.type !== "tool") {
72-
return false
73-
}
74-
if (part.state?.status !== "completed" || typeof part.state.output !== "string") {
75-
return false
76-
}
77-
if (part.state.output.includes(tag)) {
78-
return true
79-
}
80-
81-
part.state.output = `${part.state.output}${tag}`
82-
return true
83-
}
84-
85-
export const findLastToolPart = (message: WithParts): ToolPart | null => {
86-
for (let i = message.parts.length - 1; i >= 0; i--) {
87-
const part = message.parts[i]
88-
if (part.type === "tool") {
89-
return part
90-
}
91-
}
92-
93-
return null
94-
}
95-
9668
export function buildToolIdList(state: SessionState, messages: WithParts[]): string[] {
9769
const toolIds: string[] = []
9870
for (const msg of messages) {
@@ -131,6 +103,10 @@ export const stripHallucinationsFromString = (text: string): string => {
131103
return text.replace(DCP_SYSTEM_REMINDER_REGEX, "").replace(DCP_MESSAGE_ID_TAG_REGEX, "")
132104
}
133105

106+
export const sanitizeVisibleOutput = (text: string): string => {
107+
return stripHallucinationsFromString(text).replace(/\n{3,}/g, "\n\n").trimEnd()
108+
}
109+
134110
export const stripHallucinations = (messages: WithParts[]): void => {
135111
for (const message of messages) {
136112
for (const part of message.parts) {

0 commit comments

Comments
 (0)