Skip to content

Commit 114a6c0

Browse files
authored
Merge pull request #184 from Opencode-DCP/feat/debug-context-logging
Add debug context logging for session messages
2 parents 8201162 + 481aef9 commit 114a6c0

File tree

3 files changed

+100
-0
lines changed

3 files changed

+100
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,4 @@ test-update.ts
3535

3636
# Documentation (local development only)
3737
docs/
38+
SCHEMA_NOTES.md

lib/hooks.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ export function createChatMessageTransformHandler(
2828
prune(state, logger, config, output.messages)
2929

3030
insertPruneToolContext(state, config, logger, output.messages)
31+
32+
if (state.sessionId) {
33+
await logger.saveContext(state.sessionId, output.messages)
34+
}
3135
}
3236
}
3337

lib/logger.ts

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,4 +107,99 @@ export class Logger {
107107
const component = this.getCallerFile(2)
108108
return this.write("ERROR", component, message, data)
109109
}
110+
111+
/**
112+
* Strips unnecessary metadata from messages for cleaner debug logs.
113+
*
114+
* Removed:
115+
* - All IDs (id, sessionID, messageID, parentID, callID on parts)
116+
* - summary, path, cost, model, agent, mode, finish, providerID, modelID
117+
* - step-start and step-finish parts entirely
118+
* - snapshot fields
119+
* - ignored text parts
120+
*
121+
* Kept:
122+
* - role, time (created only), tokens (input, output, reasoning, cache)
123+
* - text, reasoning, tool parts with content
124+
* - tool calls with: tool, callID, input, output
125+
*/
126+
private minimizeForDebug(messages: any[]): any[] {
127+
return messages.map((msg) => {
128+
const minimized: any = {
129+
role: msg.info?.role,
130+
}
131+
132+
if (msg.info?.time?.created) {
133+
minimized.time = msg.info.time.created
134+
}
135+
136+
if (msg.info?.tokens) {
137+
minimized.tokens = {
138+
input: msg.info.tokens.input,
139+
output: msg.info.tokens.output,
140+
reasoning: msg.info.tokens.reasoning,
141+
cache: msg.info.tokens.cache,
142+
}
143+
}
144+
145+
if (msg.parts) {
146+
minimized.parts = msg.parts
147+
.map((part: any) => {
148+
if (part.type === "step-start" || part.type === "step-finish") {
149+
return null
150+
}
151+
152+
if (part.type === "text") {
153+
if (part.ignored) return null
154+
return { type: "text", text: part.text }
155+
}
156+
157+
if (part.type === "reasoning") {
158+
return {
159+
type: "reasoning",
160+
text: part.text,
161+
}
162+
}
163+
164+
if (part.type === "tool") {
165+
const toolPart: any = {
166+
type: "tool",
167+
tool: part.tool,
168+
callID: part.callID,
169+
}
170+
171+
if (part.state?.input) {
172+
toolPart.input = part.state.input
173+
}
174+
if (part.state?.output) {
175+
toolPart.output = part.state.output
176+
}
177+
178+
return toolPart
179+
}
180+
181+
return null
182+
})
183+
.filter(Boolean)
184+
}
185+
186+
return minimized
187+
})
188+
}
189+
190+
async saveContext(sessionId: string, messages: any[]) {
191+
if (!this.enabled) return
192+
193+
try {
194+
const contextDir = join(this.logDir, "context", sessionId)
195+
if (!existsSync(contextDir)) {
196+
await mkdir(contextDir, { recursive: true })
197+
}
198+
199+
const minimized = this.minimizeForDebug(messages)
200+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-")
201+
const contextFile = join(contextDir, `${timestamp}.json`)
202+
await writeFile(contextFile, JSON.stringify(minimized, null, 2))
203+
} catch (error) {}
204+
}
110205
}

0 commit comments

Comments
 (0)