Merge pull request #517 from rowboatlabs/dev

Dev
This commit is contained in:
Ramnique Singh 2026-04-21 14:39:46 +05:30 committed by GitHub
commit 51f2ad6e8a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 49 additions and 60 deletions

View file

@ -127,8 +127,8 @@ const TITLEBAR_BUTTON_PX = 32
const TITLEBAR_BUTTON_GAP_PX = 4
const TITLEBAR_HEADER_GAP_PX = 8
const TITLEBAR_TOGGLE_MARGIN_LEFT_PX = 12
const TITLEBAR_BUTTONS_COLLAPSED = 4
const TITLEBAR_BUTTON_GAPS_COLLAPSED = 3
const TITLEBAR_BUTTONS_COLLAPSED = 1
const TITLEBAR_BUTTON_GAPS_COLLAPSED = 0
const GRAPH_TAB_PATH = '__rowboat_graph_view__'
const SUGGESTED_TOPICS_TAB_PATH = '__rowboat_suggested_topics__'
const BASES_DEFAULT_TAB_PATH = '__rowboat_bases_default__'
@ -506,22 +506,13 @@ function viewStatesEqual(a: ViewState, b: ViewState): boolean {
return true // both graph
}
/** Sidebar toggle + utility buttons (fixed position, top-left) */
/** Sidebar toggle (fixed position, top-left) */
function FixedSidebarToggle({
onNavigateBack,
onNavigateForward,
canNavigateBack,
canNavigateForward,
leftInsetPx,
}: {
onNavigateBack: () => void
onNavigateForward: () => void
canNavigateBack: boolean
canNavigateForward: boolean
leftInsetPx: number
}) {
const { toggleSidebar, state } = useSidebar()
const isCollapsed = state === "collapsed"
const { toggleSidebar } = useSidebar()
return (
<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 }} />
@ -535,30 +526,6 @@ function FixedSidebarToggle({
>
<PanelLeftIcon className="size-5" />
</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>
)
}
@ -4756,10 +4723,6 @@ function App() {
)}
{/* Rendered last so its no-drag region paints over the sidebar drag region */}
<FixedSidebarToggle
onNavigateBack={() => { void navigateBack() }}
onNavigateForward={() => { void navigateForward() }}
canNavigateBack={canNavigateBack}
canNavigateForward={canNavigateForward}
leftInsetPx={isMac ? MACOS_TRAFFIC_LIGHTS_RESERVED_PX : 0}
/>
</SidebarProvider>

View file

@ -194,6 +194,19 @@ export class AgentRuntime implements IAgentRuntime {
await this.runsRepo.appendEvents(runId, [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 {
this.abortRegistry.cleanup(runId);
await this.runsLock.release(runId);
@ -942,27 +955,40 @@ export async function* streamAgent({
subflow: [],
});
let result: unknown = null;
if (agent.tools![toolCall.toolName].type === "agent") {
const subflowState = state.subflowStates[toolCallId];
for await (const event of streamAgent({
state: subflowState,
idGenerator,
runId,
messageQueue,
modelConfigRepo,
signal,
abortRegistry,
})) {
yield* processEvent({
...event,
subflow: [toolCallId, ...event.subflow],
});
try {
if (agent.tools![toolCall.toolName].type === "agent") {
const subflowState = state.subflowStates[toolCallId];
for await (const event of streamAgent({
state: subflowState,
idGenerator,
runId,
messageQueue,
modelConfigRepo,
signal,
abortRegistry,
})) {
yield* processEvent({
...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) {
result = subflowState.finalResponse();
} catch (error) {
if ((error instanceof Error && error.name === "AbortError") || signal.aborted) {
throw error;
}
} else {
result = await execTool(agent.tools![toolCall.toolName], toolCall.arguments, { runId, signal, abortRegistry });
const message = error instanceof Error ? (error.message || error.name) : String(error);
_logger.log('tool failed', message);
result = {
success: false,
error: message,
toolName: toolCall.toolName,
};
}
const resultPayload = result === undefined ? null : result;
const resultMsg: z.infer<typeof ToolMessage> = {