Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions src/agents/custom-agent-summaries.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { isRecord } from "../shared/record-type-guard"
import { truncateDescription } from "../shared/truncate-description"
import type { AgentPromptMetadata } from "./types"

Expand All @@ -14,9 +15,6 @@ function sanitizeMarkdownTableCell(value: string): string {
.trim()
}

function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null
}

export function parseRegisteredAgentSummaries(input: unknown): RegisteredAgentSummary[] {
if (!Array.isArray(input)) return []
Expand Down
18 changes: 1 addition & 17 deletions src/features/background-agent/error-helpers.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,6 @@
import { isRecord } from "../../shared/record-type-guard"
import type { EventProperties } from "./manager"

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`
}

export function getErrorText(error: unknown): string {
if (!error) return ""
Expand All @@ -37,9 +24,6 @@ export function isAbortedSessionError(error: unknown): boolean {
return message.toLowerCase().includes("aborted")
}

export function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null
}

export function getSessionErrorMessage(
properties: EventProperties,
Expand Down
8 changes: 3 additions & 5 deletions src/features/background-agent/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,10 @@
import type { PluginInput } from "@opencode-ai/plugin"
import type { BackgroundTaskConfig, TmuxConfig } from "../../config/schema"
import { getAgentToolRestrictions, log, normalizeSDKResponse, promptWithModelSuggestionRetry } from "../../shared"
import { setSessionTemperature } from "../../shared/session-temperature-store"
import { setSessionTools } from "../../shared/session-tools-store"
import { formatDuration } from "../../shared/format-duration"
import { setSessionTemperature, setSessionTools } from "../../shared/session-state"
import { isInsideTmux } from "../../shared/tmux"
import { subagentSessions } from "../session-state"

import { getTaskToastManager } from "../task-toast-manager"
import { ConcurrencyManager } from "./concurrency"
import {
Expand All @@ -21,10 +20,9 @@ import {
TASK_TTL_MS,
} from "./constants"
import {
formatDuration,
getSessionErrorMessage,
isAbortedSessionError,
} from "./error-helpers"
} from "./error-helpers"
import {
type CircuitBreakerSettings,
detectRepetitiveToolUse,
Expand Down
4 changes: 2 additions & 2 deletions src/features/task-toast-manager/types.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { ModelSource } from "../../shared/model-resolver"
import type { ModelResolutionProvenance } from "../../shared/model-resolution-pipeline"

export type TaskStatus = "running" | "queued" | "completed" | "error"

export interface ModelFallbackInfo {
model: string
type: "user-defined" | "inherited" | "category-default" | "system-default"
source?: ModelSource
source?: ModelResolutionProvenance
}

export interface TrackedTask {
Expand Down
5 changes: 2 additions & 3 deletions src/features/tmux-subagent/session-created-event.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { isRecord } from "../../shared/record-type-guard"

type UnknownRecord = Record<string, unknown>

function isRecord(value: unknown): value is UnknownRecord {
return typeof value === "object" && value !== null
}

function getNestedRecord(value: unknown, key: string): UnknownRecord | undefined {
if (!isRecord(value)) return undefined
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { formatBytes } from "../../shared/format-bytes"
import { log } from "../../shared/logger"
import type { Client } from "./client"
import { formatBytes } from "./message-builder"
import { clearSessionState } from "./state"
import { truncateUntilTargetTokens } from "./storage"
import type { AutoCompactState } from "./types"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,6 @@ export async function sanitizeEmptyMessagesBeforeSummarize(
return fixedCount
}

export function formatBytes(bytes: number): string {
if (bytes < 1024) return `${bytes}B`
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`
}

export async function getLastAssistant(
sessionID: string,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { TaskStatus } from "../../shared/status-types"
export interface StoredToolPart {
id: string
sessionID: string
Expand All @@ -6,7 +7,7 @@ export interface StoredToolPart {
callID: string
tool: string
state: {
status: "pending" | "running" | "completed" | "error"
status: TaskStatus
input: Record<string, unknown>
output?: string
error?: string
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/architect/event-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import type { PluginInput } from "@opencode-ai/plugin"
import { clearMissionState, getPlanProgress, readMissionState } from "../../features/mission-state"
import { subagentSessions } from "../../features/session-state"
import { getAgentConfigKey } from "../../shared/agent-display-names"
import { isAbortError } from "../../shared/is-abort-error"
import { log } from "../../shared/logger"
import { HOOK_NAME } from "./hook-name"
import { isAbortError } from "./is-abort-error"
import { injectMissionContinuation } from "./mission-continuation-injector"
import { getLastAgentFromSession } from "./session-last-agent"
import type { ArchitectHookOptions, SessionState } from "./types"
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/auto-slash-command/detector.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CODE_BLOCK_PATTERN } from "../../shared"
import { CODE_BLOCK_PATTERN } from "../../shared/code-patterns"
import {
EXCLUDED_COMMANDS,
SLASH_COMMAND_PATTERN,
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/auto-slash-command/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import {
parseFrontmatter,
resolveCommandsInText,
resolveFileReferencesInText,
sanitizeModelField,
} from "../../shared"
import { isMarkdownFile } from "../../shared/file-utils"
import { sanitizeModelField } from "../../shared/model-sanitizer"
import type { ParsedSlashCommand } from "./types"

interface CommandScope {
Expand Down
3 changes: 2 additions & 1 deletion src/hooks/hashline-edit-diff-enhancer/hook.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { isWriteTool, log } from "../../shared"
import { log } from "../../shared"
import { isWriteTool } from "../../shared/tool-guards"
import { countLineDiffs, generateUnifiedDiff } from "../../tools/hashline-edit/diff-utils"

interface HashlineEditDiffEnhancerConfig {
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/hashline-read-enhancer/hook.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { PluginInput } from "@opencode-ai/plugin"
import { isReadTool, isWriteTool } from "../../shared"
import { isReadTool, isWriteTool } from "../../shared/tool-guards"
import { computeLineHash } from "../../tools/hashline-edit/hash-computation"

const WRITE_SUCCESS_MARKER = "File written successfully."
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/keyword-detector/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { CODE_BLOCK_PATTERN, INLINE_CODE_PATTERN } from "../../shared"
export { CODE_BLOCK_PATTERN, INLINE_CODE_PATTERN } from "../../shared/code-patterns"
export { ANALYZE_MESSAGE, ANALYZE_PATTERN } from "./analyze"
export { SEARCH_MESSAGE, SEARCH_PATTERN } from "./search"
// Re-export from submodules
Expand Down
21 changes: 1 addition & 20 deletions src/hooks/matrix-loop/with-timeout.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1 @@
export async function withTimeout<TData>(
promise: Promise<TData>,
timeoutMs: number,
): Promise<TData> {
let timeoutId: ReturnType<typeof setTimeout> | undefined

const timeoutPromise = new Promise<never>((_, reject) => {
timeoutId = setTimeout(() => {
reject(new Error("API timeout"))
}, timeoutMs)
})

try {
return await Promise.race([promise, timeoutPromise])
} finally {
if (timeoutId !== undefined) {
clearTimeout(timeoutId)
}
}
}
export { withTimeout } from "../../shared/with-timeout"
18 changes: 1 addition & 17 deletions src/hooks/preemptive-compaction-degradation-monitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,23 +45,7 @@ export interface AssistantCompactionMessageInfo {
id?: string
}

async function withTimeout<TValue>(
promise: Promise<TValue>,
timeoutMs: number,
errorMessage: string,
): Promise<TValue> {
let timeoutID: unknown

const timeoutPromise = new Promise<never>((_, reject) => {
timeoutID = setTimeout(() => {
reject(new Error(errorMessage))
}, timeoutMs)
})

return await Promise.race([promise, timeoutPromise]).finally(() => {
clearTimeout(timeoutID)
})
}
import { withTimeout } from "../shared/with-timeout"

export function createPostCompactionDegradationMonitor(args: {
client: ClientLike
Expand Down
18 changes: 1 addition & 17 deletions src/hooks/preemptive-compaction-trigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,7 @@ const PREEMPTIVE_COMPACTION_COOLDOWN_MS = 60_000
declare function setTimeout(handler: () => void, timeout?: number): unknown
declare function clearTimeout(timeoutID: unknown): void

async function withTimeout<TValue>(
promise: Promise<TValue>,
timeoutMs: number,
errorMessage: string,
): Promise<TValue> {
let timeoutID: unknown

const timeoutPromise = new Promise<never>((_, reject) => {
timeoutID = setTimeout(() => {
reject(new Error(errorMessage))
}, timeoutMs)
})

return await Promise.race([promise, timeoutPromise]).finally(() => {
clearTimeout(timeoutID)
})
}
import { withTimeout } from "../shared/with-timeout"

export async function runPreemptiveCompactionIfNeeded(args: {
ctx: PreemptiveCompactionContext
Expand Down
5 changes: 3 additions & 2 deletions src/hooks/read-image-resizer/hook.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { PluginInput } from "@opencode-ai/plugin"
import { isReadTool, log } from "../../shared"
import { getSessionModel } from "../../shared/session-model-state"
import { log } from "../../shared"
import { getSessionModel } from "../../shared/session-state"
import { isReadTool } from "../../shared/tool-guards"
import { parseImageDimensions } from "./image-dimensions"
import { calculateTargetDimensions, resizeImage } from "./image-resizer"
import type { ImageAttachment, ImageDimensions } from "./types"
Expand Down
4 changes: 1 addition & 3 deletions src/hooks/read-image-resizer/image-resizer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { log } from "../../shared"
import { getErrorMessage } from "../../shared/error-formatting"
import { extractBase64Data } from "../../tools/look-at/mime-type-inference"
import { resizeImageFallback } from "./png-fallback-resizer"
import type { ImageDimensions, ResizeResult } from "./types"
Expand Down Expand Up @@ -75,9 +76,6 @@ async function renderResizedBuffer(args: {
.toBuffer()
}

function getErrorMessage(error: unknown): string {
return error instanceof Error ? error.message : String(error)
}

function loadSharpModule(): Promise<unknown | null> {
return Function('return import("sharp").catch(() => null)')() as Promise<unknown | null>
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/runtime-fallback/event-handler.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { isAbortError } from "../../shared/is-abort-error"
import { log } from "../../shared/logger"
import type { AutoRetryHelpers } from "./auto-retry"
import { HOOK_NAME } from "./constants"
Expand All @@ -6,7 +7,6 @@ import { resolveFallbackBootstrapModel } from "./fallback-bootstrap-model"
import { getFallbackModelsForSession } from "./fallback-models"
import { dispatchFallbackRetry } from "./fallback-retry-dispatcher"
import { createFallbackState } from "./fallback-state"
import { isAbortError } from "./is-abort-error"
import { SessionCategoryRegistry } from "./session-category-registry"
import { createSessionStatusHandler } from "./session-status-handler"
import type { HookDeps } from "./types"
Expand Down
20 changes: 0 additions & 20 deletions src/hooks/runtime-fallback/is-abort-error.ts

This file was deleted.

4 changes: 1 addition & 3 deletions src/hooks/runtime-fallback/session-messages.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { isRecord } from "../../shared/record-type-guard"
export type SessionMessagePart = {
type?: string
text?: string
Expand All @@ -8,9 +9,6 @@ export type SessionMessage = {
parts?: SessionMessagePart[]
}

function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null
}

function isSessionMessage(value: unknown): value is SessionMessage {
return isRecord(value)
Expand Down
3 changes: 2 additions & 1 deletion src/hooks/runtime-fallback/visible-assistant-response.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { isRecord } from "../../shared/record-type-guard"
import type { extractAutoRetrySignal } from "./error-classifier"
import type { SessionMessage, SessionMessagePart } from "./session-messages"
import { extractSessionMessages } from "./session-messages"
Expand Down Expand Up @@ -55,7 +56,7 @@ export function hasVisibleAssistantResponse(extractAutoRetrySignalFn: typeof ext

const infoParts = message.info?.parts
const infoMessageParts = Array.isArray(infoParts)
? infoParts.filter((part): part is SessionMessagePart => typeof part === "object" && part !== null)
? infoParts.filter((part): part is SessionMessagePart => isRecord(part))
: undefined
const parts = message.parts && message.parts.length > 0
? message.parts
Expand Down
3 changes: 2 additions & 1 deletion src/hooks/session-recovery/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { TaskStatus } from "../../shared/status-types"

export interface StoredMessageMeta {
id: string
Expand Down Expand Up @@ -29,7 +30,7 @@ export interface StoredToolPart {
callID: string
tool: string
state: {
status: "pending" | "running" | "completed" | "error"
status: TaskStatus
input: Record<string, unknown>
output?: string
error?: string
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/think-mode/detector.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CODE_BLOCK_PATTERN, INLINE_CODE_PATTERN } from "../../shared"
import { CODE_BLOCK_PATTERN, INLINE_CODE_PATTERN } from "../../shared/code-patterns"

const ENGLISH_PATTERNS = [
/\bultrathink\b/i,
Expand Down
4 changes: 1 addition & 3 deletions src/hooks/unstable-agent-babysitter/task-message-analyzer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { BackgroundTask } from "../../features/background-agent"
import { isRecord } from "../../shared/record-type-guard"

export const THINKING_SUMMARY_MAX_CHARS = 500 as const

Expand All @@ -20,9 +21,6 @@ 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
Expand Down
Loading
Loading