mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-04-29 10:26:23 +02:00
commit
51f2ad6e8a
2 changed files with 49 additions and 60 deletions
|
|
@ -127,8 +127,8 @@ const TITLEBAR_BUTTON_PX = 32
|
||||||
const TITLEBAR_BUTTON_GAP_PX = 4
|
const TITLEBAR_BUTTON_GAP_PX = 4
|
||||||
const TITLEBAR_HEADER_GAP_PX = 8
|
const TITLEBAR_HEADER_GAP_PX = 8
|
||||||
const TITLEBAR_TOGGLE_MARGIN_LEFT_PX = 12
|
const TITLEBAR_TOGGLE_MARGIN_LEFT_PX = 12
|
||||||
const TITLEBAR_BUTTONS_COLLAPSED = 4
|
const TITLEBAR_BUTTONS_COLLAPSED = 1
|
||||||
const TITLEBAR_BUTTON_GAPS_COLLAPSED = 3
|
const TITLEBAR_BUTTON_GAPS_COLLAPSED = 0
|
||||||
const GRAPH_TAB_PATH = '__rowboat_graph_view__'
|
const GRAPH_TAB_PATH = '__rowboat_graph_view__'
|
||||||
const SUGGESTED_TOPICS_TAB_PATH = '__rowboat_suggested_topics__'
|
const SUGGESTED_TOPICS_TAB_PATH = '__rowboat_suggested_topics__'
|
||||||
const BASES_DEFAULT_TAB_PATH = '__rowboat_bases_default__'
|
const BASES_DEFAULT_TAB_PATH = '__rowboat_bases_default__'
|
||||||
|
|
@ -506,22 +506,13 @@ function viewStatesEqual(a: ViewState, b: ViewState): boolean {
|
||||||
return true // both graph
|
return true // both graph
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sidebar toggle + utility buttons (fixed position, top-left) */
|
/** Sidebar toggle (fixed position, top-left) */
|
||||||
function FixedSidebarToggle({
|
function FixedSidebarToggle({
|
||||||
onNavigateBack,
|
|
||||||
onNavigateForward,
|
|
||||||
canNavigateBack,
|
|
||||||
canNavigateForward,
|
|
||||||
leftInsetPx,
|
leftInsetPx,
|
||||||
}: {
|
}: {
|
||||||
onNavigateBack: () => void
|
|
||||||
onNavigateForward: () => void
|
|
||||||
canNavigateBack: boolean
|
|
||||||
canNavigateForward: boolean
|
|
||||||
leftInsetPx: number
|
leftInsetPx: number
|
||||||
}) {
|
}) {
|
||||||
const { toggleSidebar, state } = useSidebar()
|
const { toggleSidebar } = useSidebar()
|
||||||
const isCollapsed = state === "collapsed"
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed left-0 top-0 z-50 flex h-10 items-center" style={{ WebkitAppRegion: 'no-drag' } as React.CSSProperties}>
|
<div className="fixed left-0 top-0 z-50 flex h-10 items-center" style={{ WebkitAppRegion: 'no-drag' } as React.CSSProperties}>
|
||||||
<div aria-hidden="true" className="h-10 shrink-0" style={{ width: leftInsetPx }} />
|
<div aria-hidden="true" className="h-10 shrink-0" style={{ width: leftInsetPx }} />
|
||||||
|
|
@ -535,30 +526,6 @@ function FixedSidebarToggle({
|
||||||
>
|
>
|
||||||
<PanelLeftIcon className="size-5" />
|
<PanelLeftIcon className="size-5" />
|
||||||
</button>
|
</button>
|
||||||
{/* Back / Forward navigation */}
|
|
||||||
{isCollapsed && (
|
|
||||||
<>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={onNavigateBack}
|
|
||||||
disabled={!canNavigateBack}
|
|
||||||
className="flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground transition-colors disabled:opacity-30 disabled:pointer-events-none"
|
|
||||||
style={{ marginLeft: TITLEBAR_BUTTON_GAP_PX }}
|
|
||||||
aria-label="Go back"
|
|
||||||
>
|
|
||||||
<ChevronLeftIcon className="size-5" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={onNavigateForward}
|
|
||||||
disabled={!canNavigateForward}
|
|
||||||
className="flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground transition-colors disabled:opacity-30 disabled:pointer-events-none"
|
|
||||||
aria-label="Go forward"
|
|
||||||
>
|
|
||||||
<ChevronRightIcon className="size-5" />
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -4756,10 +4723,6 @@ function App() {
|
||||||
)}
|
)}
|
||||||
{/* Rendered last so its no-drag region paints over the sidebar drag region */}
|
{/* Rendered last so its no-drag region paints over the sidebar drag region */}
|
||||||
<FixedSidebarToggle
|
<FixedSidebarToggle
|
||||||
onNavigateBack={() => { void navigateBack() }}
|
|
||||||
onNavigateForward={() => { void navigateForward() }}
|
|
||||||
canNavigateBack={canNavigateBack}
|
|
||||||
canNavigateForward={canNavigateForward}
|
|
||||||
leftInsetPx={isMac ? MACOS_TRAFFIC_LIGHTS_RESERVED_PX : 0}
|
leftInsetPx={isMac ? MACOS_TRAFFIC_LIGHTS_RESERVED_PX : 0}
|
||||||
/>
|
/>
|
||||||
</SidebarProvider>
|
</SidebarProvider>
|
||||||
|
|
|
||||||
|
|
@ -194,6 +194,19 @@ export class AgentRuntime implements IAgentRuntime {
|
||||||
await this.runsRepo.appendEvents(runId, [stoppedEvent]);
|
await this.runsRepo.appendEvents(runId, [stoppedEvent]);
|
||||||
await this.bus.publish(stoppedEvent);
|
await this.bus.publish(stoppedEvent);
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Run ${runId} failed:`, error);
|
||||||
|
const message = error instanceof Error
|
||||||
|
? (error.stack || error.message || error.name)
|
||||||
|
: typeof error === "string" ? error : JSON.stringify(error);
|
||||||
|
const errorEvent: z.infer<typeof RunEvent> = {
|
||||||
|
runId,
|
||||||
|
type: "error",
|
||||||
|
error: message,
|
||||||
|
subflow: [],
|
||||||
|
};
|
||||||
|
await this.runsRepo.appendEvents(runId, [errorEvent]);
|
||||||
|
await this.bus.publish(errorEvent);
|
||||||
} finally {
|
} finally {
|
||||||
this.abortRegistry.cleanup(runId);
|
this.abortRegistry.cleanup(runId);
|
||||||
await this.runsLock.release(runId);
|
await this.runsLock.release(runId);
|
||||||
|
|
@ -942,27 +955,40 @@ export async function* streamAgent({
|
||||||
subflow: [],
|
subflow: [],
|
||||||
});
|
});
|
||||||
let result: unknown = null;
|
let result: unknown = null;
|
||||||
if (agent.tools![toolCall.toolName].type === "agent") {
|
try {
|
||||||
const subflowState = state.subflowStates[toolCallId];
|
if (agent.tools![toolCall.toolName].type === "agent") {
|
||||||
for await (const event of streamAgent({
|
const subflowState = state.subflowStates[toolCallId];
|
||||||
state: subflowState,
|
for await (const event of streamAgent({
|
||||||
idGenerator,
|
state: subflowState,
|
||||||
runId,
|
idGenerator,
|
||||||
messageQueue,
|
runId,
|
||||||
modelConfigRepo,
|
messageQueue,
|
||||||
signal,
|
modelConfigRepo,
|
||||||
abortRegistry,
|
signal,
|
||||||
})) {
|
abortRegistry,
|
||||||
yield* processEvent({
|
})) {
|
||||||
...event,
|
yield* processEvent({
|
||||||
subflow: [toolCallId, ...event.subflow],
|
...event,
|
||||||
});
|
subflow: [toolCallId, ...event.subflow],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!subflowState.getPendingAskHumans().length && !subflowState.getPendingPermissions().length) {
|
||||||
|
result = subflowState.finalResponse();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = await execTool(agent.tools![toolCall.toolName], toolCall.arguments, { runId, signal, abortRegistry });
|
||||||
}
|
}
|
||||||
if (!subflowState.getPendingAskHumans().length && !subflowState.getPendingPermissions().length) {
|
} catch (error) {
|
||||||
result = subflowState.finalResponse();
|
if ((error instanceof Error && error.name === "AbortError") || signal.aborted) {
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
} else {
|
const message = error instanceof Error ? (error.message || error.name) : String(error);
|
||||||
result = await execTool(agent.tools![toolCall.toolName], toolCall.arguments, { runId, signal, abortRegistry });
|
_logger.log('tool failed', message);
|
||||||
|
result = {
|
||||||
|
success: false,
|
||||||
|
error: message,
|
||||||
|
toolName: toolCall.toolName,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
const resultPayload = result === undefined ? null : result;
|
const resultPayload = result === undefined ? null : result;
|
||||||
const resultMsg: z.infer<typeof ToolMessage> = {
|
const resultMsg: z.infer<typeof ToolMessage> = {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue