Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/six-zoos-push.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@inkeep/agents-manage-ui": patch
"@inkeep/agents-cli": patch
---

Revert
4 changes: 2 additions & 2 deletions agents-cli/src/commands/pull-v4/merge-ui/merge-app.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { ConflictItem, ConflictResolution } from '@inkeep/agents-core';
import { Box, Text, useApp, useInput } from 'ink';
import { useEffect, useReducer } from 'react';
import { useEffect, useMemo, useReducer } from 'react';
import { ConflictView } from './conflict-view';
import { HelpBar } from './help-bar';
import { ResolutionSummary } from './resolution-summary';
Expand Down Expand Up @@ -106,7 +106,7 @@ export function MergeApp({ conflicts }: MergeAppProps) {
const [state, dispatch] = useReducer(mergeReducer, conflicts, createInitialState);
const isEmpty = conflicts.length === 0;

const allChangedColumns = conflicts.map(getChangedColumns);
const allChangedColumns = useMemo(() => conflicts.map(getChangedColumns), [conflicts]);

const currentConflict = isEmpty ? undefined : conflicts[state.currentConflictIndex];
const isRowLevelOnly = currentConflict?.ours === null || currentConflict?.theirs === null;
Expand Down
12 changes: 6 additions & 6 deletions agents-docs/src/components/feedback-dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client';

import { usePathname } from 'next/navigation';
import { type FormEvent, useEffect, useRef, useState } from 'react';
import { type FormEvent, useCallback, useEffect, useRef, useState } from 'react';

const FEEDBACK_URL = '/api/feedback';

Expand All @@ -15,19 +15,19 @@ type Status = 'idle' | 'submitting' | 'success' | 'error';

export function FeedbackDialog() {
const dialogRef = useRef<HTMLDialogElement>(null);
const [mood, setMood] = useState('');
const [mood, setMood] = useState<string>('');
const [message, setMessage] = useState('');
const [email, setEmail] = useState('');
const [status, setStatus] = useState<Status>('idle');
const pathname = usePathname();

function open() {
const open = useCallback(() => {
dialogRef.current?.showModal();
}
}, []);

function close() {
const close = useCallback(() => {
dialogRef.current?.close();
}
}, []);

useEffect(() => {
const dialog = dialogRef.current;
Expand Down
53 changes: 27 additions & 26 deletions agents-manage-ui/src/app/[tenantId]/billing/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
QUOTA_RESOURCE_TYPES,
SEAT_RESOURCE_TYPES,
} from '@inkeep/agents-core/client-exports';
import { use, useEffect, useState } from 'react';
import { use, useCallback, useEffect, useState } from 'react';
import { ErrorContent } from '@/components/errors/full-page-error';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Progress } from '@/components/ui/progress';
Expand Down Expand Up @@ -48,35 +48,36 @@ export default function BillingPage({ params }: PageProps<'/[tenantId]/billing'>
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);

useEffect(() => {
async function fetchData() {
if (!tenantId) return;

try {
const [entitlementsResult, orgResult] = await Promise.all([
fetchEntitlements(tenantId).catch(() => [] as OrgEntitlement[]),
authClient.organization.getFullOrganization({
query: { organizationId: tenantId, membersLimit: DEFAULT_MEMBERSHIP_LIMIT },
}),
]);

setEntitlements(entitlementsResult);

if (orgResult.data?.members) {
const serviceAccountUserId = orgResult.data.serviceAccountUserId;
const members = orgResult.data.members.filter((m) => m.user.id !== serviceAccountUserId);
const adminCount = members.filter((m) => m.role === 'admin' || m.role === 'owner').length;
const memberCount = members.filter((m) => m.role === 'member').length;
setSeatCounts({ admin: adminCount, member: memberCount });
}
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to load usage data');
const fetchData = useCallback(async () => {
if (!tenantId) return;

try {
const [entitlementsResult, orgResult] = await Promise.all([
fetchEntitlements(tenantId).catch(() => [] as OrgEntitlement[]),
authClient.organization.getFullOrganization({
query: { organizationId: tenantId, membersLimit: DEFAULT_MEMBERSHIP_LIMIT },
}),
]);

setEntitlements(entitlementsResult);

if (orgResult.data?.members) {
const serviceAccountUserId = orgResult.data.serviceAccountUserId;
const members = orgResult.data.members.filter((m) => m.user.id !== serviceAccountUserId);
const adminCount = members.filter((m) => m.role === 'admin' || m.role === 'owner').length;
const memberCount = members.filter((m) => m.role === 'member').length;
setSeatCounts({ admin: adminCount, member: memberCount });
}
setLoading(false);
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to load usage data');
}
fetchData();
setLoading(false);
}, [tenantId, authClient]);

useEffect(() => {
fetchData();
}, [fetchData]);

if (isAdminLoading || loading || isProjectsFetching) {
return <BillingLoadingSkeleton />;
}
Expand Down
6 changes: 3 additions & 3 deletions agents-manage-ui/src/app/[tenantId]/cost/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { notFound } from 'next/navigation';
import { parseAsString, useQueryState } from 'nuqs';
import { use } from 'react';
import { use, useMemo } from 'react';
import { CostDashboard } from '@/components/cost/cost-dashboard';
import { PageHeader } from '@/components/layout/page-header';
import { CUSTOM, DatePickerWithPresets } from '@/components/traces/filters/date-picker';
Expand Down Expand Up @@ -39,7 +39,7 @@ export default function TenantUsagePage({ params }: PageProps<'/[tenantId]/cost'
const [projectId, setProjectId] = useQueryState('projectId', parseAsString);
const selectedProjectId = projectId ?? undefined;

const { startTime, endTime } = (() => {
const { startTime, endTime } = useMemo(() => {
if (selectedTimeRange === CUSTOM && customStartDate && customEndDate) {
return {
startTime: new Date(customStartDate).toISOString(),
Expand All @@ -50,7 +50,7 @@ export default function TenantUsagePage({ params }: PageProps<'/[tenantId]/cost'
const end = new Date();
const start = new Date(end.getTime() - range.hours * 60 * 60 * 1000);
return { startTime: start.toISOString(), endTime: end.toISOString() };
})();
}, [selectedTimeRange, customStartDate, customEndDate]);

return (
<div className="flex flex-col gap-6">
Expand Down
11 changes: 4 additions & 7 deletions agents-manage-ui/src/app/[tenantId]/members/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client';

import { DEFAULT_MEMBERSHIP_LIMIT } from '@inkeep/agents-core/client-exports';
import { use, useEffect, useState } from 'react';
import { use, useCallback, useEffect, useState } from 'react';
import { ErrorContent } from '@/components/errors/full-page-error';
import { MembersTable } from '@/components/members/members-table';
import { OrgRoles } from '@/constants/signoz';
Expand All @@ -23,7 +23,7 @@ export default function MembersPage({ params }: PageProps<'/[tenantId]/members'>
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);

async function fetchData() {
const fetchData = useCallback(async () => {
if (!tenantId) return;

try {
Expand Down Expand Up @@ -67,14 +67,11 @@ export default function MembersPage({ params }: PageProps<'/[tenantId]/members'>
setError(err instanceof Error ? err.message : 'Failed to fetch organization');
}
setLoading(false);
}
}, [tenantId, authClient]);

useEffect(() => {
fetchData();
}, [
// biome-ignore lint/correctness/useExhaustiveDependencies: false positive, variable is stable and optimized by the React Compiler
fetchData,
]);
}, [fetchData]);

if (loading) {
return <MembersLoadingSkeleton />;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -469,71 +469,74 @@ export const Agent: FC<AgentProps> = ({ agent }) => {

useAgentShortcuts();

const onSubmit = form.handleSubmit(async ({ mcpRelations, defaultSubAgentNodeId, ...data }) => {
const serializedData = editorToPayload(nodes, edges, {
mcpRelations: mcpRelations ?? {},
functionToolRelations: data.functionToolRelations ?? {},
functionTools: data.functionTools ?? {},
externalAgents: data.externalAgents ?? {},
teamAgents: data.teamAgents ?? {},
subAgents: data.subAgents ?? {},
functions: data.functions ?? {},
defaultSubAgentNodeId,
});
const res = await updateFullAgentAction(tenantId, projectId, agentId, {
...data,
defaultSubAgentId: serializedData.defaultSubAgentId,
subAgents: serializedData.subAgents,
functionTools: serializedData.functionTools,
functions: serializedData.functions,
});

if (res.success) {
toast.success('Agent saved', { closeButton: true });
markSaved();
const syncedGraph = syncSavedAgentGraph({
nodes,
edges,
savedAgent: res.data,
nodeId,
edgeId,
subAgentFormData: data.subAgents,
functionToolRelations: data.functionToolRelations,
const onSubmit = form.handleSubmit(
async ({ mcpRelations, defaultSubAgentNodeId, ...data }): Promise<void> => {
const serializedData = editorToPayload(nodes, edges, {
mcpRelations: mcpRelations ?? {},
functionToolRelations: data.functionToolRelations ?? {},
functionTools: data.functionTools ?? {},
externalAgents: data.externalAgents ?? {},
teamAgents: data.teamAgents ?? {},
subAgents: data.subAgents ?? {},
functions: data.functions ?? {},
defaultSubAgentNodeId,
});
const res = await updateFullAgentAction(tenantId, projectId, agentId, {
...data,
defaultSubAgentId: serializedData.defaultSubAgentId,
subAgents: serializedData.subAgents,
functionTools: serializedData.functionTools,
functions: serializedData.functions,
});

setQueryState((prev) => ({
...prev,
pane:
(prev.pane === 'node' && !syncedGraph.nodeId) ||
(prev.pane === 'edge' && !syncedGraph.edgeId)
? 'agent'
: prev.pane,
nodeId: syncedGraph.nodeId,
edgeId: syncedGraph.edgeId,
}));
form.reset(apiToFormValues(res.data));
setInitial(syncedGraph.nodes, syncedGraph.edges);
return;
}
if (res.success) {
toast.success('Agent saved', { closeButton: true });
markSaved();
const syncedGraph = syncSavedAgentGraph({
nodes,
edges,
savedAgent: res.data,
nodeId,
edgeId,
subAgentFormData: data.subAgents,
functionToolRelations: data.functionToolRelations,
});

setQueryState((prev) => ({
...prev,
pane:
(prev.pane === 'node' && !syncedGraph.nodeId) ||
(prev.pane === 'edge' && !syncedGraph.edgeId)
? 'agent'
: prev.pane,
nodeId: syncedGraph.nodeId,
edgeId: syncedGraph.edgeId,
}));
form.reset(apiToFormValues(res.data));
setInitial(syncedGraph.nodes, syncedGraph.edges);
return;
}

if (res.code && nonValidationErrors.has(res.code)) {
const error = res.error || 'An error occurred while saving the agent';
toast.error(error, { closeButton: true });
return;
}
if (res.code && nonValidationErrors.has(res.code)) {
const error = res.error || 'An error occurred while saving the agent';
toast.error(error, { closeButton: true });
return;
}

// Handle validation errors (422 status - unprocessable_entity)
try {
const issues: z.ZodIssue[] = JSON.parse(res.error);
issues.forEach(({ path, code, message }) => {
form.setError(path.join('.') as any, { type: code, message });
});
} catch (parseError) {
// Fallback for unparseable errors
console.error('Failed to parse validation errors:', parseError);
toast.error('Failed to save agent', { closeButton: true });
}
}, console.error);
// Handle validation errors (422 status - unprocessable_entity)
try {
const issues: z.ZodIssue[] = JSON.parse(res.error);
issues.forEach(({ path, code, message }) => {
form.setError(path.join('.') as any, { type: code, message });
});
} catch (parseError) {
// Fallback for unparseable errors
console.error('Failed to parse validation errors:', parseError);
toast.error('Failed to save agent', { closeButton: true });
}
},
console.error
);

useAnimateGraph();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use client';

import { notFound } from 'next/navigation';
import { use } from 'react';
import { use, useMemo } from 'react';
import { CostDashboard } from '@/components/cost/cost-dashboard';
import { PageHeader } from '@/components/layout/page-header';
import { CUSTOM, DatePickerWithPresets } from '@/components/traces/filters/date-picker';
Expand Down Expand Up @@ -33,7 +33,7 @@ export default function ProjectUsagePage({
setCustomDateRange,
} = useTracesQueryState();

const { startTime, endTime } = (() => {
const { startTime, endTime } = useMemo(() => {
if (selectedTimeRange === CUSTOM && customStartDate && customEndDate) {
return {
startTime: new Date(customStartDate).toISOString(),
Expand All @@ -44,7 +44,7 @@ export default function ProjectUsagePage({
const end = new Date();
const start = new Date(end.getTime() - range.hours * 60 * 60 * 1000);
return { startTime: start.toISOString(), endTime: end.toISOString() };
})();
}, [selectedTimeRange, customStartDate, customEndDate]);

return (
<div className="flex flex-col gap-6">
Expand Down
Loading
Loading