fix: minor tweaks and type improvements

This commit is contained in:
Christian Bromann
2026-01-13 15:13:43 -08:00
parent 9a706c8145
commit 17c462a549
9 changed files with 84 additions and 51 deletions
+7 -6
View File
@@ -23,7 +23,7 @@ let globalWorkspacePath: string | null = null
* Set the workspace path for filesystem synchronization.
* When set, files created by the agent will be synced to this directory.
*/
export function setWorkspacePath(path: string | null) {
export function setWorkspacePath(path: string | null): void {
globalWorkspacePath = path
console.log('[Runtime] Workspace path set to:', path)
}
@@ -98,7 +98,7 @@ export async function createAgentRuntime(options: CreateAgentRuntimeOptions = {}
const model = getModelInstance(modelId)
console.log('[Runtime] Model instance created:', typeof model)
const saver = await getCheckpointer()
const checkpointer = await getCheckpointer()
console.log('[Runtime] Checkpointer ready')
// Use provided workspace path, fall back to global, or null for no sync
@@ -107,18 +107,19 @@ export async function createAgentRuntime(options: CreateAgentRuntimeOptions = {}
// Using type assertion to work around version compatibility issues
// between @langchain packages and deepagentsjs types
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const agent = createDeepAgent({
model: model as any,
checkpointer: saver as any,
model,
checkpointer,
// Use SyncedStateBackend to enable bidirectional disk sync
backend: createSyncedBackendFactory(syncPath) as any
backend: createSyncedBackendFactory(syncPath)
})
console.log('[Runtime] Deep agent created with', syncPath ? 'disk sync' : 'state-only storage')
return agent
}
export type DeepAgent = ReturnType<typeof createDeepAgent>
// Clean up resources
export async function closeRuntime(): Promise<void> {
if (checkpointer) {
+4
View File
@@ -0,0 +1,4 @@
// @ts-ignore this is a workaround to avoid type errors in the main process
import type { createAgentRuntime } from './runtime'
export type DeepAgent = Awaited<ReturnType<typeof createAgentRuntime>>
+2
View File
@@ -36,7 +36,9 @@ interface CustomAPI {
}
models: {
list: () => Promise<ModelConfig[]>
listProviders: () => Promise<Provider[]>
getDefault: () => Promise<string>
deleteApiKey: (provider: string) => Promise<void>
setDefault: (modelId: string) => Promise<void>
setApiKey: (provider: string, apiKey: string) => Promise<void>
getApiKey: (provider: string) => Promise<string | null>
+3 -1
View File
@@ -152,7 +152,9 @@ const api = {
select: (threadId?: string): Promise<string | null> => {
return ipcRenderer.invoke('workspace:select', threadId)
},
syncToDisk: (threadId: string): Promise<{
syncToDisk: (
threadId: string
): Promise<{
success: boolean
synced?: string[]
errors?: string[]
+29 -19
View File
@@ -18,29 +18,35 @@ function App(): React.JSX.Element {
const [isLoading, setIsLoading] = useState(true)
const [leftWidth, setLeftWidth] = useState(LEFT_DEFAULT)
const [rightWidth, setRightWidth] = useState(RIGHT_DEFAULT)
// Track drag start widths
const dragStartWidths = useRef<{ left: number; right: number } | null>(null)
const handleLeftResize = useCallback((totalDelta: number) => {
if (!dragStartWidths.current) {
dragStartWidths.current = { left: leftWidth, right: rightWidth }
}
const newWidth = dragStartWidths.current.left + totalDelta
setLeftWidth(Math.min(LEFT_MAX, Math.max(LEFT_MIN, newWidth)))
}, [leftWidth, rightWidth])
const handleLeftResize = useCallback(
(totalDelta: number) => {
if (!dragStartWidths.current) {
dragStartWidths.current = { left: leftWidth, right: rightWidth }
}
const newWidth = dragStartWidths.current.left + totalDelta
setLeftWidth(Math.min(LEFT_MAX, Math.max(LEFT_MIN, newWidth)))
},
[leftWidth, rightWidth]
)
const handleRightResize = useCallback((totalDelta: number) => {
if (!dragStartWidths.current) {
dragStartWidths.current = { left: leftWidth, right: rightWidth }
}
const newWidth = dragStartWidths.current.right - totalDelta
setRightWidth(Math.min(RIGHT_MAX, Math.max(RIGHT_MIN, newWidth)))
}, [leftWidth, rightWidth])
const handleRightResize = useCallback(
(totalDelta: number) => {
if (!dragStartWidths.current) {
dragStartWidths.current = { left: leftWidth, right: rightWidth }
}
const newWidth = dragStartWidths.current.right - totalDelta
setRightWidth(Math.min(RIGHT_MAX, Math.max(RIGHT_MIN, newWidth)))
},
[leftWidth, rightWidth]
)
// Reset drag start on mouse up
useEffect(() => {
const handleMouseUp = () => {
const handleMouseUp = (): void => {
dragStartWidths.current = null
}
document.addEventListener('mouseup', handleMouseUp)
@@ -78,11 +84,15 @@ function App(): React.JSX.Element {
{/* Draggable titlebar region with app badge */}
<div className="h-8 w-full shrink-0 app-drag-region bg-sidebar relative">
<div className="absolute top-[14px] left-[76px] flex items-center gap-1.5 px-2 py-0.5 rounded-sm bg-primary/10 border border-primary/30 leading-none">
<span className="text-[10px] font-semibold uppercase tracking-[0.1em] text-primary leading-none">OPENWORK</span>
<span className="text-[9px] text-primary/70 font-mono leading-none">{__APP_VERSION__}</span>
<span className="text-[10px] font-semibold uppercase tracking-widest text-primary leading-none">
OPENWORK
</span>
<span className="text-[9px] text-primary/70 font-mono leading-none">
{__APP_VERSION__}
</span>
</div>
</div>
{/* Main content area */}
<div className="flex flex-1 overflow-hidden">
{/* Left Sidebar - Thread List */}
@@ -10,6 +10,7 @@ import { WorkspacePicker } from './WorkspacePicker'
import { ApprovalDialog } from '@/components/hitl/ApprovalDialog'
import { ElectronIPCTransport } from '@/lib/electron-transport'
import type { Message } from '@/types'
import type { DeepAgent } from '../../../../main/agent/types'
interface ChatContainerProps {
threadId: string
@@ -158,7 +159,7 @@ export function ChatContainer({ threadId }: ChatContainerProps): React.JSX.Eleme
)
// Use the useStream hook with our custom transport
const stream = useStream({
const stream = useStream<DeepAgent>({
transport,
threadId,
messagesKey: 'messages',
@@ -169,7 +170,6 @@ export function ChatContainer({ threadId }: ChatContainerProps): React.JSX.Eleme
console.error('[ChatContainer] Stream error:', error)
}
})
console.log('[ChatContainer] Stream:', stream.messages)
// Refresh threads when loading state changes from true to false (stream completed)
const prevLoadingRef = useRef(false)
@@ -33,15 +33,15 @@ const STATUS_CONFIG = {
}
}
export function TodoPanel() {
export function TodoPanel(): React.JSX.Element {
const { todos } = useAppStore()
const [completedExpanded, setCompletedExpanded] = useState(false)
// Group todos by status
const inProgress = todos.filter(t => t.status === 'in_progress')
const pending = todos.filter(t => t.status === 'pending')
const completed = todos.filter(t => t.status === 'completed')
const cancelled = todos.filter(t => t.status === 'cancelled')
const inProgress = todos.filter((t) => t.status === 'in_progress')
const pending = todos.filter((t) => t.status === 'pending')
const completed = todos.filter((t) => t.status === 'completed')
const cancelled = todos.filter((t) => t.status === 'cancelled')
// Completed section includes both completed and cancelled
const doneItems = [...completed, ...cancelled]
@@ -59,10 +59,12 @@ export function TodoPanel() {
<div className="p-4 border-b border-border">
<div className="flex items-center justify-between mb-2">
<span className="text-section-header">PROGRESS</span>
<span className="text-data text-sm">{done}/{total}</span>
<span className="text-data text-sm">
{done}/{total}
</span>
</div>
<div className="h-1.5 rounded-full bg-background overflow-hidden">
<div
<div
className="h-full bg-status-nominal transition-all duration-300"
style={{ width: `${progress}%` }}
/>
@@ -73,9 +75,7 @@ export function TodoPanel() {
<ScrollArea className="flex-1 min-h-0">
<div className="p-4 space-y-2">
{!hasAnyTodos ? (
<div className="text-center text-sm text-muted-foreground py-8">
No tasks yet
</div>
<div className="text-center text-sm text-muted-foreground py-8">No tasks yet</div>
) : (
<>
{/* Completed/Cancelled Section (Collapsible) */}
@@ -129,22 +129,26 @@ export function TodoPanel() {
)
}
function TodoItem({ todo }: { todo: Todo }) {
function TodoItem({ todo }: { todo: Todo }): React.JSX.Element {
const config = STATUS_CONFIG[todo.status]
const Icon = config.icon
return (
<div className={cn(
"flex items-start gap-3 rounded-sm border border-border p-3 transition-colors",
todo.status === 'completed' && "opacity-60",
todo.status === 'cancelled' && "opacity-40"
)}>
<Icon className={cn("size-4 shrink-0 mt-0.5", config.color)} />
<div
className={cn(
'flex items-start gap-3 rounded-sm border border-border p-3 transition-colors',
todo.status === 'completed' && 'opacity-60',
todo.status === 'cancelled' && 'opacity-40'
)}
>
<Icon className={cn('size-4 shrink-0 mt-0.5', config.color)} />
<div className="flex-1 min-w-0">
<div className={cn(
"text-sm",
(todo.status === 'completed' || todo.status === 'cancelled') && "line-through"
)}>
<div
className={cn(
'text-sm',
(todo.status === 'completed' || todo.status === 'cancelled') && 'line-through'
)}
>
{todo.content}
</div>
</div>
+10 -1
View File
@@ -1,5 +1,14 @@
import { create } from 'zustand'
import type { Thread, Message, Todo, ModelConfig, Provider, HITLRequest, FileInfo, Subagent } from '@/types'
import type {
Thread,
Message,
Todo,
ModelConfig,
Provider,
HITLRequest,
FileInfo,
Subagent
} from '@/types'
interface AppState {
// Threads
+2 -1
View File
@@ -5,7 +5,8 @@
"src/renderer/src/**/*",
"src/renderer/src/**/*.tsx",
"src/preload/*.d.ts",
"src/types.ts"
"src/types.ts",
"src/main/agent/types.ts"
],
"compilerOptions": {
"composite": true,