Files
oh-my-openagent/src/hooks/unstable-agent-babysitter/task-message-analyzer.ts

107 lines
3.4 KiB
TypeScript

import type { BackgroundTask } from "../../features/background-agent"
export const THINKING_SUMMARY_MAX_CHARS = 500 as const
type MessageInfo = {
role?: string
agent?: string
model?: { providerID: string; modelID: string }
providerID?: string
modelID?: string
tools?: Record<string, boolean | "allow" | "deny" | "ask">
}
type MessagePart = {
type?: string
text?: string
thinking?: string
}
function hasData(value: unknown): value is { data?: unknown } {
return typeof value === "object" && value !== null && "data" in value
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null
}
export function getMessageInfo(value: unknown): MessageInfo | undefined {
if (!isRecord(value)) return undefined
if (!isRecord(value.info)) return undefined
const info = value.info
const modelValue = isRecord(info.model)
? info.model
: undefined
const model = modelValue && typeof modelValue.providerID === "string" && typeof modelValue.modelID === "string"
? { providerID: modelValue.providerID, modelID: modelValue.modelID }
: undefined
return {
role: typeof info.role === "string" ? info.role : undefined,
agent: typeof info.agent === "string" ? info.agent : undefined,
model,
providerID: typeof info.providerID === "string" ? info.providerID : undefined,
modelID: typeof info.modelID === "string" ? info.modelID : undefined,
tools: isRecord(info.tools)
? Object.entries(info.tools).reduce<Record<string, boolean | "allow" | "deny" | "ask">>((acc, [key, value]) => {
if (
value === true ||
value === false ||
value === "allow" ||
value === "deny" ||
value === "ask"
) {
acc[key] = value
}
return acc
}, {})
: undefined,
}
}
export function getMessageParts(value: unknown): MessagePart[] {
if (!isRecord(value)) return []
if (!Array.isArray(value.parts)) return []
return value.parts.filter(isRecord).map((part) => ({
type: typeof part.type === "string" ? part.type : undefined,
text: typeof part.text === "string" ? part.text : undefined,
thinking: typeof part.thinking === "string" ? part.thinking : undefined,
}))
}
export function extractMessages(value: unknown): unknown[] {
if (Array.isArray(value)) {
return value
}
if (hasData(value) && Array.isArray(value.data)) {
return value.data
}
return []
}
export function isUnstableTask(task: BackgroundTask): boolean {
if (task.isUnstableAgent === true) return true
const modelId = task.model?.modelID?.toLowerCase()
return modelId ? modelId.includes("gemini") || modelId.includes("minimax") : false
}
export function buildReminder(task: BackgroundTask, summary: string | null, idleMs: number): string {
const idleSeconds = Math.round(idleMs / 1000)
const summaryText = summary ?? "(No thinking trace available)"
return `Unstable background agent appears idle for ${idleSeconds}s.
Task ID: ${task.id}
Description: ${task.description}
Agent: ${task.agent}
Status: ${task.status}
Session ID: ${task.sessionID ?? "N/A"}
Thinking summary (first ${THINKING_SUMMARY_MAX_CHARS} chars):
${summaryText}
Suggested actions:
- background_output task_id="${task.id}" full_session=true include_thinking=true include_tool_results=true message_limit=50
- background_cancel taskId="${task.id}"
This is a reminder only. No automatic action was taken.`
}