Reroute routes (#4088)

Co-authored-by: Douwe Osinga <douwe@squareup.com>
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Douwe Osinga
2025-08-14 14:23:02 -04:00
committed by GitHub
parent 290d29e961
commit d7e761c529
12 changed files with 466 additions and 523 deletions
+6 -14
View File
@@ -1,4 +1,4 @@
import { getApiUrl } from '../config';
import { updateAgentProvider } from '../api';
interface initializeAgentProps {
model: string;
@@ -6,23 +6,15 @@ interface initializeAgentProps {
}
export async function initializeAgent({ model, provider }: initializeAgentProps) {
const response = await fetch(getApiUrl('/agent/update_provider'), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Secret-Key': await window.electron.getSecretKey(),
},
body: JSON.stringify({
const response = await updateAgentProvider({
body: {
provider: provider.toLowerCase().replace(/ /g, '_'),
model: model,
}),
},
});
if (!response.ok) {
const responseText = await response.text();
throw new Error(
`Failed to initialize agent: ${response.status} ${response.statusText} - ${responseText}`
);
if (response.error) {
throw new Error(`Failed to initialize agent: ${response.error}`);
}
return response;
+41 -1
View File
@@ -1,7 +1,7 @@
// This file is auto-generated by @hey-api/openapi-ts
import type { Options as ClientOptions, TDataShape, Client } from '@hey-api/client-fetch';
import type { AddSubRecipesData, AddSubRecipesResponse2, GetToolsData, GetToolsResponse, ReadAllConfigData, ReadAllConfigResponse, BackupConfigData, BackupConfigResponse, GetExtensionsData, GetExtensionsResponse, AddExtensionData, AddExtensionResponse, RemoveExtensionData, RemoveExtensionResponse, InitConfigData, InitConfigResponse, UpsertPermissionsData, UpsertPermissionsResponse, ProvidersData, ProvidersResponse2, ReadConfigData, RecoverConfigData, RecoverConfigResponse, RemoveConfigData, RemoveConfigResponse, UpsertConfigData, UpsertConfigResponse, ValidateConfigData, ValidateConfigResponse, ConfirmPermissionData, ManageContextData, ManageContextResponse, CreateRecipeData, CreateRecipeResponse2, DecodeRecipeData, DecodeRecipeResponse2, EncodeRecipeData, EncodeRecipeResponse2, CreateScheduleData, CreateScheduleResponse, DeleteScheduleData, DeleteScheduleResponse, ListSchedulesData, ListSchedulesResponse2, UpdateScheduleData, UpdateScheduleResponse, InspectRunningJobData, InspectRunningJobResponse, KillRunningJobData, PauseScheduleData, PauseScheduleResponse, RunNowHandlerData, RunNowHandlerResponse, SessionsHandlerData, SessionsHandlerResponse, UnpauseScheduleData, UnpauseScheduleResponse, ListSessionsData, ListSessionsResponse, GetSessionHistoryData, GetSessionHistoryResponse } from './types.gen';
import type { AddSubRecipesData, AddSubRecipesResponse2, ExtendPromptData, ExtendPromptResponse2, UpdateSessionConfigData, UpdateSessionConfigResponse, GetToolsData, GetToolsResponse, UpdateAgentProviderData, UpdateRouterToolSelectorData, UpdateRouterToolSelectorResponse, ReadAllConfigData, ReadAllConfigResponse, BackupConfigData, BackupConfigResponse, GetExtensionsData, GetExtensionsResponse, AddExtensionData, AddExtensionResponse, RemoveExtensionData, RemoveExtensionResponse, InitConfigData, InitConfigResponse, UpsertPermissionsData, UpsertPermissionsResponse, ProvidersData, ProvidersResponse2, ReadConfigData, RecoverConfigData, RecoverConfigResponse, RemoveConfigData, RemoveConfigResponse, UpsertConfigData, UpsertConfigResponse, ValidateConfigData, ValidateConfigResponse, ConfirmPermissionData, ManageContextData, ManageContextResponse, CreateRecipeData, CreateRecipeResponse2, DecodeRecipeData, DecodeRecipeResponse2, EncodeRecipeData, EncodeRecipeResponse2, CreateScheduleData, CreateScheduleResponse, DeleteScheduleData, DeleteScheduleResponse, ListSchedulesData, ListSchedulesResponse2, UpdateScheduleData, UpdateScheduleResponse, InspectRunningJobData, InspectRunningJobResponse, KillRunningJobData, PauseScheduleData, PauseScheduleResponse, RunNowHandlerData, RunNowHandlerResponse, SessionsHandlerData, SessionsHandlerResponse, UnpauseScheduleData, UnpauseScheduleResponse, ListSessionsData, ListSessionsResponse, GetSessionHistoryData, GetSessionHistoryResponse } from './types.gen';
import { client as _heyApiClient } from './client.gen';
export type Options<TData extends TDataShape = TDataShape, ThrowOnError extends boolean = boolean> = ClientOptions<TData, ThrowOnError> & {
@@ -29,6 +29,28 @@ export const addSubRecipes = <ThrowOnError extends boolean = false>(options: Opt
});
};
export const extendPrompt = <ThrowOnError extends boolean = false>(options: Options<ExtendPromptData, ThrowOnError>) => {
return (options.client ?? _heyApiClient).post<ExtendPromptResponse2, unknown, ThrowOnError>({
url: '/agent/prompt',
...options,
headers: {
'Content-Type': 'application/json',
...options?.headers
}
});
};
export const updateSessionConfig = <ThrowOnError extends boolean = false>(options: Options<UpdateSessionConfigData, ThrowOnError>) => {
return (options.client ?? _heyApiClient).post<UpdateSessionConfigResponse, unknown, ThrowOnError>({
url: '/agent/session_config',
...options,
headers: {
'Content-Type': 'application/json',
...options?.headers
}
});
};
export const getTools = <ThrowOnError extends boolean = false>(options?: Options<GetToolsData, ThrowOnError>) => {
return (options?.client ?? _heyApiClient).get<GetToolsResponse, unknown, ThrowOnError>({
url: '/agent/tools',
@@ -36,6 +58,24 @@ export const getTools = <ThrowOnError extends boolean = false>(options?: Options
});
};
export const updateAgentProvider = <ThrowOnError extends boolean = false>(options: Options<UpdateAgentProviderData, ThrowOnError>) => {
return (options.client ?? _heyApiClient).post<unknown, unknown, ThrowOnError>({
url: '/agent/update_provider',
...options,
headers: {
'Content-Type': 'application/json',
...options?.headers
}
});
};
export const updateRouterToolSelector = <ThrowOnError extends boolean = false>(options?: Options<UpdateRouterToolSelectorData, ThrowOnError>) => {
return (options?.client ?? _heyApiClient).post<UpdateRouterToolSelectorResponse, unknown, ThrowOnError>({
url: '/agent/update_router_tool_selector',
...options
});
};
export const readAllConfig = <ThrowOnError extends boolean = false>(options?: Options<ReadAllConfigData, ThrowOnError>) => {
return (options?.client ?? _heyApiClient).get<ReadAllConfigResponse, unknown, ThrowOnError>({
url: '/config',
+152 -1
View File
@@ -143,6 +143,18 @@ export type Envs = {
[key: string]: string;
};
export type ErrorResponse = {
error: string;
};
export type ExtendPromptRequest = {
extension: string;
};
export type ExtendPromptResponse = {
success: boolean;
};
/**
* Represents the different types of MCP extensions that can be added to the manager
*/
@@ -273,6 +285,10 @@ export type FrontendToolRequest = {
};
};
export type GetToolsQuery = {
extension_name?: string | null;
};
export type ImageContent = {
annotations?: Annotations | {
[key: string]: unknown;
@@ -576,6 +592,10 @@ export type ScheduledJob = {
source: string;
};
export type SessionConfigRequest = {
response?: Response | null;
};
export type SessionDisplayInfo = {
accumulatedInputTokens?: number | null;
accumulatedOutputTokens?: number | null;
@@ -772,6 +792,11 @@ export type ToolResponse = {
};
};
export type UpdateProviderRequest = {
model?: string | null;
provider: string;
};
export type UpdateScheduleRequest = {
cron: string;
};
@@ -798,17 +823,79 @@ export type AddSubRecipesErrors = {
* Unauthorized - invalid secret key
*/
401: unknown;
/**
* Agent not initialized
*/
424: unknown;
};
export type AddSubRecipesResponses = {
/**
* added sub recipes to agent successfully
* Added sub recipes to agent successfully
*/
200: AddSubRecipesResponse;
};
export type AddSubRecipesResponse2 = AddSubRecipesResponses[keyof AddSubRecipesResponses];
export type ExtendPromptData = {
body: ExtendPromptRequest;
path?: never;
query?: never;
url: '/agent/prompt';
};
export type ExtendPromptErrors = {
/**
* Unauthorized - invalid secret key
*/
401: unknown;
/**
* Agent not initialized
*/
424: unknown;
};
export type ExtendPromptResponses = {
/**
* Extended system prompt successfully
*/
200: ExtendPromptResponse;
};
export type ExtendPromptResponse2 = ExtendPromptResponses[keyof ExtendPromptResponses];
export type UpdateSessionConfigData = {
body: SessionConfigRequest;
path?: never;
query?: never;
url: '/agent/session_config';
};
export type UpdateSessionConfigErrors = {
/**
* Unauthorized - invalid secret key
*/
401: unknown;
/**
* Agent not initialized
*/
424: unknown;
/**
* Internal server error
*/
500: unknown;
};
export type UpdateSessionConfigResponses = {
/**
* Session config updated successfully
*/
200: string;
};
export type UpdateSessionConfigResponse = UpdateSessionConfigResponses[keyof UpdateSessionConfigResponses];
export type GetToolsData = {
body?: never;
path?: never;
@@ -845,6 +932,70 @@ export type GetToolsResponses = {
export type GetToolsResponse = GetToolsResponses[keyof GetToolsResponses];
export type UpdateAgentProviderData = {
body: UpdateProviderRequest;
path?: never;
query?: never;
url: '/agent/update_provider';
};
export type UpdateAgentProviderErrors = {
/**
* Bad request - missing or invalid parameters
*/
400: unknown;
/**
* Unauthorized - invalid secret key
*/
401: unknown;
/**
* Agent not initialized
*/
424: unknown;
/**
* Internal server error
*/
500: unknown;
};
export type UpdateAgentProviderResponses = {
/**
* Provider updated successfully
*/
200: unknown;
};
export type UpdateRouterToolSelectorData = {
body?: never;
path?: never;
query?: never;
url: '/agent/update_router_tool_selector';
};
export type UpdateRouterToolSelectorErrors = {
/**
* Unauthorized - invalid secret key
*/
401: unknown;
/**
* Agent not initialized
*/
424: unknown;
/**
* Internal server error
*/
500: unknown;
};
export type UpdateRouterToolSelectorResponses = {
/**
* Tool selection strategy updated successfully
*/
200: string;
};
export type UpdateRouterToolSelectorResponse = UpdateRouterToolSelectorResponses[keyof UpdateRouterToolSelectorResponses];
export type ReadAllConfigData = {
body?: never;
path?: never;
@@ -1,276 +0,0 @@
import { useEffect, useState } from 'react';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../ui/Tooltip';
import { getApiUrl } from '../../config';
interface ActivityHeatmapCell {
week: number;
day: number;
count: number;
date?: string; // Add date for better display in tooltips
}
// Days of the week for labeling
const DAYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
// Number of weeks in a year
const WEEKS_IN_YEAR = 52;
export function ActivityHeatmap() {
const [heatmapData, setHeatmapData] = useState<ActivityHeatmapCell[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [currentYear] = useState(new Date().getFullYear());
// Calculate the intensity for coloring cells
const getColorIntensity = (count: number, maxCount: number) => {
if (count === 0) return 'bg-background-muted/30';
const normalizedCount = count / maxCount;
if (normalizedCount < 0.25) return 'bg-background-accent/20';
if (normalizedCount < 0.5) return 'bg-background-accent/40';
if (normalizedCount < 0.75) return 'bg-background-accent/60';
return 'bg-background-accent/80';
};
useEffect(() => {
const fetchHeatmapData = async () => {
try {
setLoading(true);
const response = await fetch(getApiUrl('/sessions/activity-heatmap'), {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'X-Secret-Key': await window.electron.getSecretKey(),
},
});
if (!response.ok) {
throw new Error(`Failed to fetch heatmap data: ${response.status}`);
}
const data = await response.json();
setHeatmapData(data);
setError(null);
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to load heatmap data');
} finally {
setLoading(false);
}
};
fetchHeatmapData();
}, []);
// Find the maximum count for scaling
const maxCount = Math.max(
1, // Avoid division by zero
...heatmapData.map((cell) => cell.count)
);
// Create a calendar grid from Jan 1st of current year to today
const prepareGridData = () => {
// Get current date
const now = new Date();
const startOfYear = new Date(currentYear, 0, 1); // Jan 1st of current year
// Calculate weeks to display - now showing full year (52 weeks)
// const weeksToDisplay = Math.ceil((daysSinceStartOfYear + getStartDayOfYear()) / 7);
const weeksToDisplay = WEEKS_IN_YEAR;
// Create a map to lookup counts easily
const dataMap = new Map<string, number>();
heatmapData.forEach((cell) => {
dataMap.set(`${cell.week}-${cell.day}`, cell.count);
});
// Build the grid
const grid = [];
// Fill grid with dates and activity data
for (let week = 0; week < weeksToDisplay; week++) {
const weekCells = [];
for (let day = 0; day < 7; day++) {
// Convert week and day to a real date
const cellDate = new Date(startOfYear);
cellDate.setDate(cellDate.getDate() + week * 7 + day - getStartDayOfYear());
// Only include dates up to today for real data
const isFuture = cellDate > now;
// Format the date string
const dateStr = cellDate.toLocaleDateString(undefined, {
month: 'short',
day: 'numeric',
});
// Get count from data if available
let count = 0;
// Try to find a matching date in our data
// This requires matching the specific week number (from ISO week) and day
if (!isFuture) {
for (const cell of heatmapData) {
if (cell.week === getWeekNumber(cellDate) && cell.day === day) {
count = cell.count;
break;
}
}
}
weekCells.push({
week,
day,
count,
date: dateStr,
});
}
grid.push(weekCells);
}
return grid;
};
// Helper to get day of week (0-6) of Jan 1st for current year
const getStartDayOfYear = () => {
return new Date(currentYear, 0, 1).getDay();
};
// Get ISO week number for a date
const getWeekNumber = (date: Date) => {
const d = new Date(date);
d.setHours(0, 0, 0, 0);
d.setDate(d.getDate() + 3 - ((d.getDay() + 6) % 7));
const week1 = new Date(d.getFullYear(), 0, 4);
return (
1 +
Math.round(((d.getTime() - week1.getTime()) / 86400000 - 3 + ((week1.getDay() + 6) % 7)) / 7)
);
};
const grid = prepareGridData();
if (loading) {
return (
<div className="h-[120px] flex items-center justify-center">
<div className="text-text-muted">Loading activity data...</div>
</div>
);
}
if (error) {
return (
<div className="h-[120px] flex items-center justify-center">
<div className="text-red-500">Error loading activity data</div>
</div>
);
}
// Get month labels - now showing all months
const getMonthLabels = () => {
const allMonths = [
'Jan',
'Feb',
'Mar',
'Apr',
'May',
'Jun',
'Jul',
'Aug',
'Sep',
'Oct',
'Nov',
'Dec',
];
return allMonths.map((month, i) => {
// Calculate position based on days in month and start day of year
const monthIndex = i;
const daysBeforeMonth = getDaysBeforeMonth(monthIndex);
const position = (daysBeforeMonth - getStartDayOfYear()) / 7;
return (
<div
key={month}
className="text-[10px] text-text-muted absolute"
style={{
left: `${(position / WEEKS_IN_YEAR) * 100}%`,
transform: 'translateX(-50%)',
}}
>
{month}
</div>
);
});
};
// Helper to calculate days before a month in current year
const getDaysBeforeMonth = (monthIndex: number) => {
const days = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
// Adjust for leap year
if (monthIndex > 1 && isLeapYear(currentYear)) {
return days[monthIndex] + 1;
}
return days[monthIndex];
};
// Helper to check if year is a leap year
const isLeapYear = (year: number) => {
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
};
return (
<div className="w-full px-4">
{/* Month labels */}
<div className="relative h-4 ml-12 mb-2 mr-4">{getMonthLabels()}</div>
<div className="flex w-full">
{/* Day labels - now right-aligned */}
<div className="flex flex-col pt-1 pr-2 w-10">
{DAYS.map((day, index) => (
<div
key={day}
className="h-3 text-[10px] text-text-muted flex items-center justify-end"
>
{index % 2 === 0 ? day : ''}
</div>
))}
</div>
{/* Grid - with smaller squares */}
<div className="flex gap-[1px] flex-1 mr-4">
{grid.map((week, weekIndex) => (
<div
key={weekIndex}
className="flex flex-col gap-[1px] flex-1"
style={{ maxWidth: `calc(100% / ${WEEKS_IN_YEAR})` }}
>
{week.map((cell) => (
<TooltipProvider key={`${cell.week}-${cell.day}`}>
<Tooltip>
<TooltipTrigger asChild>
<div
className={`aspect-square w-full h-2 rounded-[1px] ${getColorIntensity(cell.count, maxCount)}`}
role="gridcell"
/>
</TooltipTrigger>
<TooltipContent side="top">
{cell.date ? (
<p className="text-xs">
{cell.count} sessions on {cell.date}
</p>
) : (
<p className="text-xs">No data</p>
)}
</TooltipContent>
</Tooltip>
</TooltipProvider>
))}
</div>
))}
</div>
</div>
</div>
);
}
-9
View File
@@ -1812,15 +1812,6 @@ app.whenReady().then(async () => {
callback({ cancel: false, requestHeaders: details.requestHeaders });
});
// Test error feature - only enabled with GOOSE_TEST_ERROR=true
if (process.env.GOOSE_TEST_ERROR === 'true') {
console.log('Test error feature enabled, will throw error in 5 seconds');
setTimeout(() => {
console.log('Throwing test error now...');
throw new Error('Test error: This is a simulated fatal error after 5 seconds');
}, 5000);
}
// Create tray if enabled in settings
const settings = loadSettings();
if (settings.showMenuBarIcon) {
+12 -23
View File
@@ -15,6 +15,8 @@ import {
RecipeParameter,
SubRecipe,
addExtension as apiAddExtension,
updateSessionConfig,
extendPrompt,
} from '../api';
import { addSubRecipesToAgent } from '../recipe/add_sub_recipe_on_agent';
@@ -94,21 +96,13 @@ export const updateSystemPromptWithParameters = async (
const substitutedInstructions = substituteParameters(originalInstructions, recipeParameters);
// Update the system prompt with substituted instructions
const response = await fetch(getApiUrl('/agent/prompt'), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Secret-Key': await window.electron.getSecretKey(),
},
body: JSON.stringify({
const response = await extendPrompt({
body: {
extension: `${desktopPromptBot}\nIMPORTANT instructions for you to operate as agent:\n${substitutedInstructions}`,
}),
},
});
if (!response.ok) {
console.warn(
`Failed to update system prompt with parameters: ${response.status} ${response.statusText}`
);
if (response.error) {
console.warn(`Failed to update system prompt with parameters: ${response.error}`);
}
} catch (error) {
console.error('Error updating system prompt with parameters:', error);
@@ -245,18 +239,13 @@ export const initializeSystem = async (
}
// Configure session with response config if present
if (responseConfig?.json_schema) {
const sessionConfigResponse = await fetch(getApiUrl('/agent/session_config'), {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Secret-Key': await window.electron.getSecretKey(),
},
body: JSON.stringify({
const sessionConfigResponse = await updateSessionConfig({
body: {
response: responseConfig,
}),
},
});
if (!sessionConfigResponse.ok) {
console.warn(`Failed to configure session: ${sessionConfigResponse.statusText}`);
if (sessionConfigResponse.error) {
console.warn(`Failed to configure session: ${sessionConfigResponse.error}`);
}
}