fix(thinking-block-validator): handle text content parts in message validation
Previously, the validator only checked for tool_use parts, causing 'Expected thinking but found text' errors when messages had text content. Renamed hasToolParts to hasContentParts to include both tool_use and text types. Also added CommentCheckerConfigSchema support for custom prompt configuration. 🤖 Generated with assistance of OhMyOpenCode (https://github.com/code-yeongyu/oh-my-opencode)
This commit is contained in:
@@ -115,6 +115,11 @@ export const SisyphusAgentConfigSchema = z.object({
|
|||||||
replace_plan: z.boolean().optional(),
|
replace_plan: z.boolean().optional(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const CommentCheckerConfigSchema = z.object({
|
||||||
|
/** Custom prompt to replace the default warning message. Use {{comments}} placeholder for detected comments XML. */
|
||||||
|
custom_prompt: z.string().optional(),
|
||||||
|
})
|
||||||
|
|
||||||
export const DynamicContextPruningConfigSchema = z.object({
|
export const DynamicContextPruningConfigSchema = z.object({
|
||||||
/** Enable dynamic context pruning (default: false) */
|
/** Enable dynamic context pruning (default: false) */
|
||||||
enabled: z.boolean().default(false),
|
enabled: z.boolean().default(false),
|
||||||
@@ -175,6 +180,7 @@ export const OhMyOpenCodeConfigSchema = z.object({
|
|||||||
claude_code: ClaudeCodeConfigSchema.optional(),
|
claude_code: ClaudeCodeConfigSchema.optional(),
|
||||||
google_auth: z.boolean().optional(),
|
google_auth: z.boolean().optional(),
|
||||||
sisyphus_agent: SisyphusAgentConfigSchema.optional(),
|
sisyphus_agent: SisyphusAgentConfigSchema.optional(),
|
||||||
|
comment_checker: CommentCheckerConfigSchema.optional(),
|
||||||
experimental: ExperimentalConfigSchema.optional(),
|
experimental: ExperimentalConfigSchema.optional(),
|
||||||
auto_update: z.boolean().optional(),
|
auto_update: z.boolean().optional(),
|
||||||
})
|
})
|
||||||
@@ -185,6 +191,7 @@ export type AgentOverrides = z.infer<typeof AgentOverridesSchema>
|
|||||||
export type AgentName = z.infer<typeof AgentNameSchema>
|
export type AgentName = z.infer<typeof AgentNameSchema>
|
||||||
export type HookName = z.infer<typeof HookNameSchema>
|
export type HookName = z.infer<typeof HookNameSchema>
|
||||||
export type SisyphusAgentConfig = z.infer<typeof SisyphusAgentConfigSchema>
|
export type SisyphusAgentConfig = z.infer<typeof SisyphusAgentConfigSchema>
|
||||||
|
export type CommentCheckerConfig = z.infer<typeof CommentCheckerConfigSchema>
|
||||||
export type ExperimentalConfig = z.infer<typeof ExperimentalConfigSchema>
|
export type ExperimentalConfig = z.infer<typeof ExperimentalConfigSchema>
|
||||||
export type DynamicContextPruningConfig = z.infer<typeof DynamicContextPruningConfigSchema>
|
export type DynamicContextPruningConfig = z.infer<typeof DynamicContextPruningConfigSchema>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { PendingCall } from "./types"
|
import type { PendingCall } from "./types"
|
||||||
import { runCommentChecker, getCommentCheckerPath, startBackgroundInit, type HookInput } from "./cli"
|
import { runCommentChecker, getCommentCheckerPath, startBackgroundInit, type HookInput } from "./cli"
|
||||||
|
import type { CommentCheckerConfig } from "../../config/schema"
|
||||||
|
|
||||||
import * as fs from "fs"
|
import * as fs from "fs"
|
||||||
import { existsSync } from "fs"
|
import { existsSync } from "fs"
|
||||||
@@ -32,8 +33,8 @@ function cleanupOldPendingCalls(): void {
|
|||||||
|
|
||||||
setInterval(cleanupOldPendingCalls, 10_000)
|
setInterval(cleanupOldPendingCalls, 10_000)
|
||||||
|
|
||||||
export function createCommentCheckerHooks() {
|
export function createCommentCheckerHooks(config?: CommentCheckerConfig) {
|
||||||
debugLog("createCommentCheckerHooks called")
|
debugLog("createCommentCheckerHooks called", { config })
|
||||||
|
|
||||||
// Start background CLI initialization (may trigger lazy download)
|
// Start background CLI initialization (may trigger lazy download)
|
||||||
startBackgroundInit()
|
startBackgroundInit()
|
||||||
@@ -123,7 +124,7 @@ export function createCommentCheckerHooks() {
|
|||||||
|
|
||||||
// CLI mode only
|
// CLI mode only
|
||||||
debugLog("using CLI:", cliPath)
|
debugLog("using CLI:", cliPath)
|
||||||
await processWithCli(input, pendingCall, output, cliPath)
|
await processWithCli(input, pendingCall, output, cliPath, config?.custom_prompt)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
debugLog("tool.execute.after failed:", err)
|
debugLog("tool.execute.after failed:", err)
|
||||||
}
|
}
|
||||||
@@ -135,7 +136,8 @@ async function processWithCli(
|
|||||||
input: { tool: string; sessionID: string; callID: string },
|
input: { tool: string; sessionID: string; callID: string },
|
||||||
pendingCall: PendingCall,
|
pendingCall: PendingCall,
|
||||||
output: { output: string },
|
output: { output: string },
|
||||||
cliPath: string
|
cliPath: string,
|
||||||
|
customPrompt?: string
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
debugLog("using CLI mode with path:", cliPath)
|
debugLog("using CLI mode with path:", cliPath)
|
||||||
|
|
||||||
@@ -154,7 +156,7 @@ async function processWithCli(
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await runCommentChecker(hookInput, cliPath)
|
const result = await runCommentChecker(hookInput, cliPath, customPrompt)
|
||||||
|
|
||||||
if (result.hasComments && result.message) {
|
if (result.hasComments && result.message) {
|
||||||
debugLog("CLI detected comments, appending message")
|
debugLog("CLI detected comments, appending message")
|
||||||
|
|||||||
@@ -51,14 +51,15 @@ function isExtendedThinkingModel(modelID: string): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a message has tool parts (tool_use)
|
* Check if a message has any content parts (tool_use, text, or other non-thinking content)
|
||||||
*/
|
*/
|
||||||
function hasToolParts(parts: Part[]): boolean {
|
function hasContentParts(parts: Part[]): boolean {
|
||||||
if (!parts || parts.length === 0) return false
|
if (!parts || parts.length === 0) return false
|
||||||
|
|
||||||
return parts.some((part: Part) => {
|
return parts.some((part: Part) => {
|
||||||
const type = part.type as string
|
const type = part.type as string
|
||||||
return type === "tool" || type === "tool_use"
|
// Include tool parts and text parts (anything that's not thinking/reasoning)
|
||||||
|
return type === "tool" || type === "tool_use" || type === "text"
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,8 +155,8 @@ export function createThinkingBlockValidatorHook(): MessagesTransformHook {
|
|||||||
// Only check assistant messages
|
// Only check assistant messages
|
||||||
if (msg.info.role !== "assistant") continue
|
if (msg.info.role !== "assistant") continue
|
||||||
|
|
||||||
// Check if message has tool parts but doesn't start with thinking
|
// Check if message has content parts but doesn't start with thinking
|
||||||
if (hasToolParts(msg.parts) && !startsWithThinkingBlock(msg.parts)) {
|
if (hasContentParts(msg.parts) && !startsWithThinkingBlock(msg.parts)) {
|
||||||
// Find thinking content from previous turns
|
// Find thinking content from previous turns
|
||||||
const previousThinking = findPreviousThinkingContent(messages, i)
|
const previousThinking = findPreviousThinkingContent(messages, i)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user