a691a3ac0a
- Rename delegate_task tool to task across codebase (100 files) - Update model references: claude-opus-4-6 → 4-5, gpt-5.3-codex → 5.2-codex - Add tool-metadata-store to restore metadata overwritten by fromPlugin() - Add session ID polling for BackgroundManager task sessions - Await async ctx.metadata() calls in tool executors - Add ses_ prefix guard to getMessageDir for performance - Harden BackgroundManager with idle deferral and error handling - Fix duplicate task key in sisyphus-junior test object literals - Fix unawaited showOutputToUser in ast_grep_replace - Fix background=true → run_in_background=true in ultrawork prompt - Fix duplicate task/task references in docs and comments
102 lines
2.9 KiB
TypeScript
102 lines
2.9 KiB
TypeScript
import { existsSync, readdirSync } from "node:fs"
|
|
import { join } from "node:path"
|
|
import { MESSAGE_STORAGE } from "../../features/hook-message-injector"
|
|
import type { DelegateTaskArgs } from "./types"
|
|
|
|
/**
|
|
* Parse a model string in "provider/model" format.
|
|
*/
|
|
export function parseModelString(model: string): { providerID: string; modelID: string } | undefined {
|
|
const parts = model.split("/")
|
|
if (parts.length >= 2) {
|
|
return { providerID: parts[0], modelID: parts.slice(1).join("/") }
|
|
}
|
|
return undefined
|
|
}
|
|
|
|
/**
|
|
* Get the message directory for a session, checking both direct and nested paths.
|
|
*/
|
|
export function getMessageDir(sessionID: string): string | null {
|
|
if (!sessionID.startsWith("ses_")) return null
|
|
if (!existsSync(MESSAGE_STORAGE)) return null
|
|
|
|
const directPath = join(MESSAGE_STORAGE, sessionID)
|
|
if (existsSync(directPath)) return directPath
|
|
|
|
for (const dir of readdirSync(MESSAGE_STORAGE)) {
|
|
const sessionPath = join(MESSAGE_STORAGE, dir, sessionID)
|
|
if (existsSync(sessionPath)) return sessionPath
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
/**
|
|
* Format a duration between two dates as a human-readable string.
|
|
*/
|
|
export function formatDuration(start: Date, end?: Date): string {
|
|
const duration = (end ?? new Date()).getTime() - start.getTime()
|
|
const seconds = Math.floor(duration / 1000)
|
|
const minutes = Math.floor(seconds / 60)
|
|
const hours = Math.floor(minutes / 60)
|
|
|
|
if (hours > 0) return `${hours}h ${minutes % 60}m ${seconds % 60}s`
|
|
if (minutes > 0) return `${minutes}m ${seconds % 60}s`
|
|
return `${seconds}s`
|
|
}
|
|
|
|
/**
|
|
* Context for error formatting.
|
|
*/
|
|
export interface ErrorContext {
|
|
operation: string
|
|
args?: DelegateTaskArgs
|
|
sessionID?: string
|
|
agent?: string
|
|
category?: string
|
|
}
|
|
|
|
/**
|
|
* Format an error with detailed context for debugging.
|
|
*/
|
|
export function formatDetailedError(error: unknown, ctx: ErrorContext): string {
|
|
const message = error instanceof Error ? error.message : String(error)
|
|
const stack = error instanceof Error ? error.stack : undefined
|
|
|
|
const lines: string[] = [
|
|
`${ctx.operation} failed`,
|
|
"",
|
|
`**Error**: ${message}`,
|
|
]
|
|
|
|
if (ctx.sessionID) {
|
|
lines.push(`**Session ID**: ${ctx.sessionID}`)
|
|
}
|
|
|
|
if (ctx.agent) {
|
|
lines.push(`**Agent**: ${ctx.agent}${ctx.category ? ` (category: ${ctx.category})` : ""}`)
|
|
}
|
|
|
|
if (ctx.args) {
|
|
lines.push("", "**Arguments**:")
|
|
lines.push(`- description: "${ctx.args.description}"`)
|
|
lines.push(`- category: ${ctx.args.category ?? "(none)"}`)
|
|
lines.push(`- subagent_type: ${ctx.args.subagent_type ?? "(none)"}`)
|
|
lines.push(`- run_in_background: ${ctx.args.run_in_background}`)
|
|
lines.push(`- load_skills: [${ctx.args.load_skills?.join(", ") ?? ""}]`)
|
|
if (ctx.args.session_id) {
|
|
lines.push(`- session_id: ${ctx.args.session_id}`)
|
|
}
|
|
}
|
|
|
|
if (stack) {
|
|
lines.push("", "**Stack Trace**:")
|
|
lines.push("```")
|
|
lines.push(stack.split("\n").slice(0, 10).join("\n"))
|
|
lines.push("```")
|
|
}
|
|
|
|
return lines.join("\n")
|
|
}
|