diff --git a/apps/x/apps/renderer/src/App.tsx b/apps/x/apps/renderer/src/App.tsx
index f933b604..602c0956 100644
--- a/apps/x/apps/renderer/src/App.tsx
+++ b/apps/x/apps/renderer/src/App.tsx
@@ -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 (
@@ -535,30 +526,6 @@ function FixedSidebarToggle({
>
- {/* Back / Forward navigation */}
- {isCollapsed && (
- <>
-
-
- >
- )}
)
}
@@ -4756,10 +4723,6 @@ function App() {
)}
{/* Rendered last so its no-drag region paints over the sidebar drag region */}
{ void navigateBack() }}
- onNavigateForward={() => { void navigateForward() }}
- canNavigateBack={canNavigateBack}
- canNavigateForward={canNavigateForward}
leftInsetPx={isMac ? MACOS_TRAFFIC_LIGHTS_RESERVED_PX : 0}
/>
diff --git a/apps/x/packages/core/src/agents/runtime.ts b/apps/x/packages/core/src/agents/runtime.ts
index f978449b..ae69d60c 100644
--- a/apps/x/packages/core/src/agents/runtime.ts
+++ b/apps/x/packages/core/src/agents/runtime.ts
@@ -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 = {
+ 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 = {