@inkeep/agents-manage-ui 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/.env.example +10 -0
  2. package/.turbo/turbo-build.log +48 -54
  3. package/.turbo/turbo-test.log +7 -7
  4. package/.turbo/turbo-typecheck.log +1 -1
  5. package/LICENSE.md +22 -17
  6. package/README.md +3 -3
  7. package/biome.json +3 -0
  8. package/package.json +10 -9
  9. package/src/app/api/signoz/conversations/[conversationId]/route.ts +95 -34
  10. package/src/app/api/signoz/route.ts +8 -6
  11. package/src/components/api-keys/form/api-key-form.tsx +2 -0
  12. package/src/components/api-keys/form/validation.ts +1 -1
  13. package/src/components/artifact-components/form/artifact-component-form.tsx +20 -6
  14. package/src/components/artifact-components/form/validation.ts +3 -3
  15. package/src/components/credentials/views/credential-form-validation.ts +1 -1
  16. package/src/components/credentials/views/credential-form.tsx +2 -0
  17. package/src/components/credentials/views/edit-credential-form.tsx +1 -1
  18. package/src/components/credentials/views/generic-auth-form.tsx +1 -1
  19. package/src/components/data-components/form/data-component-form.tsx +19 -1
  20. package/src/components/data-components/form/validation.ts +3 -2
  21. package/src/components/form/expandable-field.tsx +6 -1
  22. package/src/components/form/form-field-wrapper.tsx +3 -1
  23. package/src/components/form/generic-combo-box.tsx +3 -1
  24. package/src/components/form/generic-input.tsx +9 -1
  25. package/src/components/form/generic-select.tsx +3 -1
  26. package/src/components/form/generic-textarea.tsx +3 -1
  27. package/src/components/form/json-schema-input.tsx +9 -1
  28. package/src/components/graph/configuration/node-types.tsx +2 -4
  29. package/src/components/graph/graph.tsx +4 -26
  30. package/src/components/graph/nodes/agent-node.tsx +1 -1
  31. package/src/components/graph/nodes/external-agent-node.tsx +1 -1
  32. package/src/components/graph/playground/chat-widget.tsx +31 -2
  33. package/src/components/graph/playground/ikp-message.tsx +16 -16
  34. package/src/components/graph/playground/playground.tsx +12 -6
  35. package/src/components/graph/sidepane/metadata/metadata-editor.tsx +62 -45
  36. package/src/components/graph/sidepane/nodes/agent-node-editor.tsx +56 -27
  37. package/src/components/graph/sidepane/nodes/expandable-text-area.tsx +3 -1
  38. package/src/components/graph/sidepane/nodes/external-agent-node-editor.tsx +31 -11
  39. package/src/components/graph/sidepane/nodes/form-fields.tsx +10 -1
  40. package/src/components/graph/sidepane/nodes/mcp-node-editor.tsx +4 -9
  41. package/src/components/graph/sidepane/nodes/model-section.tsx +1 -1
  42. package/src/components/graph/toolbar/toolbar.tsx +1 -1
  43. package/src/components/mcp-servers/form/active-tools-selector.tsx +4 -2
  44. package/src/components/mcp-servers/form/mcp-server-form.tsx +8 -1
  45. package/src/components/mcp-servers/form/validation.ts +1 -1
  46. package/src/components/projects/edit-project-dialog.tsx +1 -1
  47. package/src/components/projects/form/project-form.tsx +20 -8
  48. package/src/components/projects/form/project-models-section.tsx +51 -23
  49. package/src/components/projects/form/project-stopwhen-section.tsx +25 -11
  50. package/src/components/projects/form/validation.ts +14 -10
  51. package/src/components/projects/new-project-dialog.tsx +1 -1
  52. package/src/components/traces/ai-calls-breakdown.tsx +1 -1
  53. package/src/components/traces/charts/area-chart-card.tsx +2 -2
  54. package/src/components/traces/charts/area-chart.tsx +2 -2
  55. package/src/components/traces/charts/chart-card.tsx +4 -4
  56. package/src/components/traces/conversation-detail.tsx +10 -8
  57. package/src/components/traces/conversation-stats/conversation-list-item.tsx +48 -37
  58. package/src/components/traces/conversation-stats/conversation-stats-card.tsx +1 -1
  59. package/src/components/traces/filters/date-picker.tsx +6 -6
  60. package/src/components/traces/filters/filter-trigger.tsx +1 -1
  61. package/src/components/traces/filters/graph-filter.tsx +3 -3
  62. package/src/components/traces/timeline/activity-details-sidepane.tsx +1 -1
  63. package/src/components/traces/timeline/activity-timeline.tsx +1 -1
  64. package/src/components/traces/timeline/blocks.tsx +2 -2
  65. package/src/components/traces/timeline/render-panel-content.tsx +11 -11
  66. package/src/components/traces/timeline/timeline-item.tsx +36 -22
  67. package/src/components/traces/timeline/timeline-wrapper.tsx +125 -37
  68. package/src/components/traces/traces-overview.tsx +3 -3
  69. package/src/components/ui/alert.tsx +60 -0
  70. package/src/components/ui/calendar.tsx +3 -4
  71. package/src/components/ui/external-link.tsx +2 -2
  72. package/src/components/ui/form.tsx +11 -4
  73. package/src/components/ui/inheritance-indicator.tsx +20 -17
  74. package/src/components/ui/resizable.tsx +13 -18
  75. package/src/constants/page-descriptions.tsx +2 -5
  76. package/src/constants/signoz.ts +15 -18
  77. package/src/features/graph/domain/__tests__/roundtrip.test.ts +5 -5
  78. package/src/features/graph/domain/serialize.ts +8 -9
  79. package/src/hooks/use-auto-prefill-id-zustand.ts +68 -0
  80. package/src/hooks/use-auto-prefill-id.ts +36 -0
  81. package/src/hooks/use-chat-activities-polling.ts +45 -12
  82. package/src/hooks/use-graph-errors.ts +2 -1
  83. package/src/hooks/use-project-data.ts +2 -2
  84. package/src/lib/actions/graph-full.ts +6 -2
  85. package/src/lib/actions/projects.ts +1 -1
  86. package/src/lib/api/api-config.ts +6 -6
  87. package/src/lib/api/api-keys.ts +4 -1
  88. package/src/lib/api/credentials.ts +1 -1
  89. package/src/lib/api/data-components.ts +1 -1
  90. package/src/lib/api/graph-full-client.ts +6 -3
  91. package/src/lib/api/projects.ts +1 -1
  92. package/src/lib/api/signoz-sql.ts +1 -1
  93. package/src/lib/api/signoz-stats.ts +958 -304
  94. package/src/lib/index.ts +1 -1
  95. package/src/lib/logger.ts +1 -2
  96. package/src/lib/types/graph-full.ts +1 -1
  97. package/src/lib/utils/generate-id.ts +14 -0
  98. package/src/lib/validation.ts +1 -1
  99. package/tsconfig.json +2 -2
  100. package/.env.sample +0 -5
  101. package/eslint.config.mjs +0 -14
@@ -1,7 +1,6 @@
1
1
  import { ExternalLink } from '@/components/ui/external-link';
2
2
 
3
- export const DOCS_BASE_URL =
4
- process.env.NEXT_PUBLIC_DOCS_BASE_URL || 'https://docs.inkeep.com';
3
+ export const DOCS_BASE_URL = process.env.NEXT_PUBLIC_DOCS_BASE_URL || 'https://docs.inkeep.com';
5
4
 
6
5
  export const artifactDescription = (
7
6
  <>
@@ -18,9 +17,7 @@ export const dataComponentDescription = (
18
17
  <>
19
18
  Data components are structured components that agents can use to display rich data.
20
19
  {'\n'}
21
- <ExternalLink href={`${DOCS_BASE_URL}/visual-builder/data-components`}>
22
- Learn more
23
- </ExternalLink>
20
+ <ExternalLink href={`${DOCS_BASE_URL}/visual-builder/data-components`}>Learn more</ExternalLink>
24
21
  </>
25
22
  );
26
23
 
@@ -21,13 +21,13 @@ export const SPAN_KEYS = {
21
21
  HAS_ERROR: 'hasError',
22
22
  STATUS_MESSAGE: 'status_message',
23
23
  OTEL_STATUS_DESCRIPTION: 'otel.status_description',
24
-
24
+
25
25
  // Graph attributes
26
26
  GRAPH_ID: 'graph.id',
27
27
  GRAPH_NAME: 'graph.name',
28
28
  TENANT_ID: 'tenant.id',
29
29
  PROJECT_ID: 'project.id',
30
-
30
+
31
31
  // AI/Agent attributes
32
32
  AI_AGENT_NAME: 'ai.agentName',
33
33
  AI_AGENT_NAME_ALT: 'ai.agent.name',
@@ -39,7 +39,7 @@ export const SPAN_KEYS = {
39
39
  AI_MODEL_PROVIDER: 'ai.model.provider',
40
40
  AI_TELEMETRY_FUNCTION_ID: 'ai.telemetry.functionId',
41
41
  AI_MODEL_ID: 'ai.model.id',
42
-
42
+
43
43
  // Tool attributes
44
44
  AI_TOOL_CALL_NAME: 'ai.toolCall.name',
45
45
  AI_TOOL_CALL_RESULT: 'ai.toolCall.result',
@@ -47,37 +47,37 @@ export const SPAN_KEYS = {
47
47
  AI_TOOL_CALL_ID: 'ai.toolCall.id',
48
48
  AI_TOOL_TYPE: 'ai.toolType',
49
49
  TOOL_PURPOSE: 'tool.purpose',
50
-
50
+
51
51
  // Agent attributes
52
52
  AGENT_ID: 'agent.id',
53
53
  AGENT_NAME: 'agent.name',
54
-
54
+
55
55
  // Token usage
56
56
  GEN_AI_USAGE_INPUT_TOKENS: 'gen_ai.usage.input_tokens',
57
57
  GEN_AI_USAGE_OUTPUT_TOKENS: 'gen_ai.usage.output_tokens',
58
-
58
+
59
59
  // Context attributes
60
60
  CONTEXT_URL: 'context.url',
61
61
  CONTEXT_CONFIG_ID: 'context.context_config_id',
62
62
  CONTEXT_AGENT_GRAPH_ID: 'context.agent_graph_id',
63
63
  CONTEXT_REQUEST_KEYS: 'context.request_context_keys',
64
-
64
+
65
65
  // Message attributes
66
66
  MESSAGE_CONTENT: 'message.content',
67
67
  MESSAGE_TIMESTAMP: 'message.timestamp',
68
68
  MCP_TOOL_DESCRIPTION: 'mcp.tool.description',
69
-
69
+
70
70
  // Delegation/Transfer attributes
71
71
  DELEGATION_FROM_AGENT_ID: 'delegation.from_agent_id',
72
72
  DELEGATION_TO_AGENT_ID: 'delegation.to_agent_id',
73
73
  TRANSFER_FROM_AGENT_ID: 'transfer.from_agent_id',
74
74
  TRANSFER_TO_AGENT_ID: 'transfer.to_agent_id',
75
-
75
+
76
76
  // HTTP attributes
77
77
  HTTP_URL: 'http.url',
78
78
  HTTP_STATUS_CODE: 'http.status_code',
79
79
  HTTP_RESPONSE_BODY_SIZE: 'http.response.body_size',
80
-
80
+
81
81
  // Core attributes
82
82
  NAME: 'name',
83
83
  PARENT_SPAN_ID: 'parentSpanID',
@@ -103,19 +103,18 @@ export const QUERY_FIELD_CONFIGS = {
103
103
  // String tag fields
104
104
  STRING_TAG: { dataType: DATA_TYPES.STRING, type: FIELD_TYPES.TAG, isColumn: false },
105
105
  STRING_TAG_COLUMN: { dataType: DATA_TYPES.STRING, type: FIELD_TYPES.TAG, isColumn: true },
106
-
106
+
107
107
  // Numeric tag fields
108
108
  INT64_TAG: { dataType: DATA_TYPES.INT64, type: FIELD_TYPES.TAG, isColumn: false },
109
109
  INT64_TAG_COLUMN: { dataType: DATA_TYPES.INT64, type: FIELD_TYPES.TAG, isColumn: true },
110
110
  FLOAT64_TAG: { dataType: DATA_TYPES.FLOAT64, type: FIELD_TYPES.TAG, isColumn: false },
111
111
  FLOAT64_TAG_COLUMN: { dataType: DATA_TYPES.FLOAT64, type: FIELD_TYPES.TAG, isColumn: true },
112
-
112
+
113
113
  // Boolean tag fields
114
114
  BOOL_TAG: { dataType: DATA_TYPES.BOOL, type: FIELD_TYPES.TAG, isColumn: false },
115
115
  BOOL_TAG_COLUMN: { dataType: DATA_TYPES.BOOL, type: FIELD_TYPES.TAG, isColumn: true },
116
116
  } as const;
117
117
 
118
-
119
118
  export const UNKNOWN_VALUE = 'unknown' as const;
120
119
 
121
120
  /** Activity Types */
@@ -174,15 +173,15 @@ export const OPERATORS = {
174
173
  GREATER_THAN: '>',
175
174
  LESS_THAN_OR_EQUAL: '<=',
176
175
  GREATER_THAN_OR_EQUAL: '>=',
177
-
176
+
178
177
  // String operators
179
178
  LIKE: 'like',
180
179
  NOT_LIKE: 'nlike',
181
-
180
+
182
181
  // Existence operators
183
182
  EXISTS: 'exists',
184
183
  NOT_EXISTS: 'nexists',
185
-
184
+
186
185
  // Logical operators
187
186
  AND: 'AND',
188
187
  OR: 'OR',
@@ -276,5 +275,3 @@ export const QUERY_DEFAULTS = {
276
275
  LIMIT_1000: 1000,
277
276
  EMPTY_GROUP_BY: [],
278
277
  } as const;
279
-
280
-
@@ -46,16 +46,16 @@ describe('graph serialize/deserialize', () => {
46
46
  } as Edge,
47
47
  ];
48
48
 
49
- const serialized = serializeGraphData(nodes, edges, {
50
- id: 'g1',
51
- name: 'G',
49
+ const serialized = serializeGraphData(nodes, edges, {
50
+ id: 'g1',
51
+ name: 'G',
52
52
  description: 'D',
53
53
  contextConfig: {
54
54
  name: 'Context',
55
55
  description: 'Context description',
56
56
  contextVariables: '{}',
57
- requestContextSchema: '{}'
58
- }
57
+ requestContextSchema: '{}',
58
+ },
59
59
  });
60
60
  expect(serialized.id).toBe('g1');
61
61
  expect(serialized.agents['a1']).toBeDefined();
@@ -44,7 +44,6 @@ function safeJsonParse(jsonString: string | undefined | null): any {
44
44
  }
45
45
  }
46
46
 
47
-
48
47
  /**
49
48
  * Transforms React Flow nodes and edges back into the API data structure
50
49
  */
@@ -71,13 +70,13 @@ export function serializeGraphData(
71
70
  agentArtifactComponents.forEach((componentId) => usedArtifactComponents.add(componentId));
72
71
  // Process models - only include if it has non-empty, non-whitespace values
73
72
  const modelsData = node.data.models as GraphMetadata['models'] | undefined;
74
- let processedModels: GraphMetadata['models'] | undefined = undefined;
75
-
73
+ let processedModels: GraphMetadata['models'] | undefined;
74
+
76
75
  if (modelsData && typeof modelsData === 'object') {
77
- const hasNonEmptyValue = Object.values(modelsData).some(value =>
78
- value !== null && value !== undefined && String(value).trim() !== ''
76
+ const hasNonEmptyValue = Object.values(modelsData).some(
77
+ (value) => value !== null && value !== undefined && String(value).trim() !== ''
79
78
  );
80
-
79
+
81
80
  if (hasNonEmptyValue) {
82
81
  processedModels = modelsData;
83
82
  }
@@ -247,15 +246,15 @@ export function serializeGraphData(
247
246
  if (metadata?.models) {
248
247
  (result as any).models = metadata.models;
249
248
  }
250
-
249
+
251
250
  if (metadata?.stopWhen) {
252
251
  (result as any).stopWhen = metadata.stopWhen;
253
252
  }
254
-
253
+
255
254
  if (metadata?.graphPrompt) {
256
255
  (result as any).graphPrompt = metadata.graphPrompt;
257
256
  }
258
-
257
+
259
258
  if (metadata?.statusUpdates) {
260
259
  (result as any).statusUpdates = metadata.statusUpdates;
261
260
  }
@@ -0,0 +1,68 @@
1
+ import { useEffect, useRef } from 'react';
2
+ import { generateId } from '@/lib/utils/generate-id';
3
+
4
+ interface UseAutoPrefillIdZustandOptions {
5
+ nameValue: string | undefined;
6
+ idValue: string | undefined;
7
+ onIdChange: (id: string) => void;
8
+ isEditing?: boolean;
9
+ }
10
+
11
+ /**
12
+ * Custom hook to auto-prefill an ID field based on a name field for Zustand state
13
+ * Only prefills when creating new items (not editing) and when the ID field hasn't been manually edited
14
+ */
15
+ export function useAutoPrefillIdZustand({
16
+ nameValue,
17
+ idValue,
18
+ onIdChange,
19
+ isEditing = false,
20
+ }: UseAutoPrefillIdZustandOptions) {
21
+ // Track if user has manually edited the ID field
22
+ const hasManuallyEditedId = useRef(false);
23
+ const lastGeneratedId = useRef('');
24
+ const onIdChangeRef = useRef(onIdChange);
25
+ const currentIdValue = useRef(idValue);
26
+
27
+ // Keep refs up to date
28
+ useEffect(() => {
29
+ onIdChangeRef.current = onIdChange;
30
+ currentIdValue.current = idValue;
31
+ }, [onIdChange, idValue]);
32
+
33
+ // Reset manual edit tracking when switching between graphs
34
+ useEffect(() => {
35
+ if (isEditing) {
36
+ hasManuallyEditedId.current = false;
37
+ lastGeneratedId.current = '';
38
+ }
39
+ }, [isEditing]);
40
+
41
+ // Track manual edits to ID field
42
+ useEffect(() => {
43
+ if (!isEditing) {
44
+ if (!idValue) {
45
+ // If ID field is empty, reset manual edit flag to allow auto-generation
46
+ hasManuallyEditedId.current = false;
47
+ lastGeneratedId.current = '';
48
+ } else if (idValue !== lastGeneratedId.current) {
49
+ // Consider it a manual edit if user typed something different from our generated value
50
+ // This includes typing into an empty field (when lastGeneratedId is empty)
51
+ hasManuallyEditedId.current = true;
52
+ }
53
+ }
54
+ }, [idValue, isEditing]);
55
+
56
+ // Auto-prefill ID based on name field
57
+ useEffect(() => {
58
+ if (!isEditing && nameValue && !hasManuallyEditedId.current) {
59
+ const generatedId = generateId(nameValue);
60
+
61
+ // Only update if the generated ID is different from current ID
62
+ if (generatedId !== currentIdValue.current) {
63
+ lastGeneratedId.current = generatedId;
64
+ onIdChangeRef.current(generatedId);
65
+ }
66
+ }
67
+ }, [nameValue, isEditing]);
68
+ }
@@ -0,0 +1,36 @@
1
+ import { useEffect } from 'react';
2
+ import { type UseFormReturn, type FieldValues, useWatch } from 'react-hook-form';
3
+ import { generateId } from '@/lib/utils/generate-id';
4
+
5
+ interface UseAutoPrefillIdOptions<T extends FieldValues> {
6
+ form: UseFormReturn<T>;
7
+ nameField: keyof T;
8
+ idField: keyof T;
9
+ isEditing?: boolean;
10
+ }
11
+
12
+ /**
13
+ * Custom hook to auto-prefill an ID field based on a name field
14
+ * Only prefills when creating new items (not editing) and when the ID field hasn't been manually edited
15
+ */
16
+ export function useAutoPrefillId<T extends FieldValues>({
17
+ form,
18
+ nameField,
19
+ idField,
20
+ isEditing = false,
21
+ }: UseAutoPrefillIdOptions<T>) {
22
+ const nameValue = useWatch({
23
+ control: form.control,
24
+ name: nameField as any,
25
+ });
26
+
27
+ const isIdFieldModified = (form.formState.dirtyFields as any)[idField];
28
+
29
+ // biome-ignore lint/correctness/useExhaustiveDependencies: we don't want to re-run this effect when the isIdFieldModified changes since that means the user has manually edited the ID field
30
+ useEffect(() => {
31
+ if (!isEditing && nameValue && !isIdFieldModified) {
32
+ const generatedId = generateId(nameValue);
33
+ form.setValue(idField as any, generatedId as any, { shouldValidate: true });
34
+ }
35
+ }, [nameValue, idField, isEditing]);
36
+ }
@@ -1,4 +1,4 @@
1
- import { useState, useCallback, useEffect, useRef } from 'react';
1
+ import { useCallback, useEffect, useRef, useState } from 'react';
2
2
  import type { ConversationDetail } from '@/components/traces/timeline/types';
3
3
 
4
4
  interface UseChatActivitiesPollingOptions {
@@ -12,6 +12,8 @@ interface UseChatActivitiesPollingReturn {
12
12
  error: string | null;
13
13
  startPolling: () => void;
14
14
  stopPolling: () => void;
15
+ retryConnection: () => void;
16
+ refreshOnce: () => Promise<{ hasNewActivity: boolean }>;
15
17
  }
16
18
 
17
19
  export const useChatActivitiesPolling = ({
@@ -27,15 +29,10 @@ export const useChatActivitiesPolling = ({
27
29
  const isComponentMountedRef = useRef(true);
28
30
  const abortControllerRef = useRef<AbortController | null>(null);
29
31
 
30
- const fetchChatActivities = useCallback(async () => {
32
+ const fetchChatActivities = useCallback(async (): Promise<ConversationDetail | null> => {
31
33
  try {
32
34
  setError(null);
33
35
 
34
- // Cancel any previous request
35
- if (abortControllerRef.current) {
36
- abortControllerRef.current.abort();
37
- }
38
-
39
36
  // Create new abort controller for this request
40
37
  abortControllerRef.current = new AbortController();
41
38
  const currentConversationId = conversationId; // Capture current ID
@@ -46,7 +43,9 @@ export const useChatActivitiesPolling = ({
46
43
 
47
44
  if (!response.ok) {
48
45
  // If conversation doesn't exist yet, that's fine - just return
49
- if (response.status === 404) return;
46
+ if (response.status === 404) {
47
+ return null;
48
+ }
50
49
  throw new Error('Failed to fetch chat activities');
51
50
  }
52
51
 
@@ -61,16 +60,29 @@ export const useChatActivitiesPolling = ({
61
60
  setLastActivityCount(newCount);
62
61
  }
63
62
  }
63
+
64
+ return data;
64
65
  } catch (err) {
65
66
  // Don't log abort errors as they are expected when cancelling requests
66
67
  if (err instanceof Error && err.name === 'AbortError') {
67
- return;
68
+ return null;
68
69
  }
69
70
 
70
71
  if (isComponentMountedRef.current) {
71
- console.warn('Error fetching chat activities:', err);
72
72
  setError(err instanceof Error ? err.message : 'An error occurred');
73
+ // Stop polling on error to prevent repeated failed requests
74
+ setIsPolling(false);
75
+ if (pollingIntervalRef.current) {
76
+ clearInterval(pollingIntervalRef.current);
77
+ pollingIntervalRef.current = null;
78
+ }
79
+ // Cancel any pending requests
80
+ if (abortControllerRef.current) {
81
+ abortControllerRef.current.abort();
82
+ abortControllerRef.current = null;
83
+ }
73
84
  }
85
+ throw err;
74
86
  }
75
87
  }, [conversationId, lastActivityCount]);
76
88
 
@@ -81,11 +93,15 @@ export const useChatActivitiesPolling = ({
81
93
  setIsPolling(true);
82
94
 
83
95
  // Initial fetch
84
- fetchChatActivities();
96
+ fetchChatActivities().catch(() => {
97
+ // Error handling is already done in fetchChatActivities
98
+ });
85
99
 
86
100
  // Set up polling interval
87
101
  pollingIntervalRef.current = setInterval(() => {
88
- fetchChatActivities();
102
+ fetchChatActivities().catch(() => {
103
+ // Error handling is already done in fetchChatActivities
104
+ });
89
105
  }, pollingInterval);
90
106
  }, [fetchChatActivities, pollingInterval]);
91
107
 
@@ -103,6 +119,21 @@ export const useChatActivitiesPolling = ({
103
119
  }
104
120
  }, []);
105
121
 
122
+ // Retry connection - clears error and restarts polling
123
+ const retryConnection = useCallback(() => {
124
+ setError(null);
125
+ stopPolling();
126
+ startPolling();
127
+ }, [startPolling, stopPolling]);
128
+
129
+ // Refresh once - makes a single request without starting polling
130
+ const refreshOnce = useCallback(async (): Promise<{ hasNewActivity: boolean }> => {
131
+ const currentCount = chatActivities?.activities?.length || 0;
132
+ const data = await fetchChatActivities();
133
+ const newCount = data?.activities?.length || 0;
134
+ return { hasNewActivity: newCount > currentCount };
135
+ }, [fetchChatActivities, chatActivities?.activities?.length]);
136
+
106
137
  // Cleanup on unmount
107
138
  useEffect(() => {
108
139
  isComponentMountedRef.current = true;
@@ -141,5 +172,7 @@ export const useChatActivitiesPolling = ({
141
172
  error,
142
173
  startPolling,
143
174
  stopPolling,
175
+ retryConnection,
176
+ refreshOnce,
144
177
  };
145
178
  };
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * Hook for accessing graph error state and utilities
3
3
  */
4
- import { useGraphStore } from '@/features/graph/state/use-graph-store';
4
+
5
5
  import { useCallback } from 'react';
6
+ import { useGraphStore } from '@/features/graph/state/use-graph-store';
6
7
 
7
8
  export interface ErrorHelpers {
8
9
  hasFieldError: (fieldName: string) => boolean;
@@ -1,9 +1,9 @@
1
1
  'use client';
2
2
 
3
- import { useEffect, useState } from 'react';
4
3
  import { useParams } from 'next/navigation';
5
- import type { Project } from '@/lib/types/project';
4
+ import { useEffect, useState } from 'react';
6
5
  import { fetchProjectAction } from '@/lib/actions/projects';
6
+ import type { Project } from '@/lib/types/project';
7
7
 
8
8
  export function useProjectData() {
9
9
  const params = useParams();
@@ -12,11 +12,15 @@ import {
12
12
  ApiError,
13
13
  createFullGraph as apiCreateFullGraph,
14
14
  deleteFullGraph as apiDeleteFullGraph,
15
+ fetchGraphs as apiFetchGraphs,
15
16
  getFullGraph as apiGetFullGraph,
16
17
  updateFullGraph as apiUpdateFullGraph,
17
- fetchGraphs as apiFetchGraphs,
18
18
  } from '../api/graph-full-client';
19
- import { type FullGraphDefinition, FullGraphDefinitionSchema, Graph } from '../types/graph-full';
19
+ import {
20
+ type FullGraphDefinition,
21
+ FullGraphDefinitionSchema,
22
+ type Graph,
23
+ } from '../types/graph-full';
20
24
 
21
25
  /**
22
26
  * Result type for server actions - follows a consistent pattern
@@ -1,6 +1,7 @@
1
1
  'use server';
2
2
 
3
3
  import { revalidatePath } from 'next/cache';
4
+ import type { ProjectFormData } from '@/components/projects/form/validation';
4
5
  import {
5
6
  createProject,
6
7
  deleteProject,
@@ -11,7 +12,6 @@ import {
11
12
  import { ApiError } from '../types/errors';
12
13
  import type { Project } from '../types/project';
13
14
  import type { ActionResult } from './types';
14
- import type { ProjectFormData } from '@/components/projects/form/validation';
15
15
 
16
16
  /**
17
17
  * Fetch all projects
@@ -10,23 +10,23 @@ const DEFAULT_MANAGEMENT_API_BASE_URL = 'http://localhost:3002';
10
10
  const DEFAULT_EXECUTION_API_BASE_URL = 'http://localhost:3003';
11
11
 
12
12
  // Management API (CRUD operations, configuration)
13
- if (!process.env.NEXT_PUBLIC_INKEEP_MANAGEMENT_API_URL) {
13
+ if (!process.env.NEXT_PUBLIC_INKEEP_AGENTS_MANAGE_API_URL) {
14
14
  console.warn(
15
- `NEXT_PUBLIC_INKEEP_MANAGEMENT_API_URL is not set, falling back to: ${DEFAULT_MANAGEMENT_API_BASE_URL}`
15
+ `NEXT_PUBLIC_INKEEP_AGENTS_MANAGE_API_URL is not set, falling back to: ${DEFAULT_MANAGEMENT_API_BASE_URL}`
16
16
  );
17
17
  }
18
18
 
19
19
  // Execution API (chat completions, agent execution)
20
- if (!process.env.NEXT_PUBLIC_INKEEP_EXECUTION_API_URL) {
20
+ if (!process.env.NEXT_PUBLIC_INKEEP_AGENTS_RUN_API_URL) {
21
21
  console.warn(
22
- `NEXT_PUBLIC_INKEEP_EXECUTION_API_URL is not set, falling back to: ${DEFAULT_EXECUTION_API_BASE_URL}`
22
+ `NEXT_PUBLIC_INKEEP_AGENTS_RUN_API_URL is not set, falling back to: ${DEFAULT_EXECUTION_API_BASE_URL}`
23
23
  );
24
24
  }
25
25
 
26
26
  export const MANAGEMENT_API_BASE_URL =
27
- process.env.NEXT_PUBLIC_INKEEP_MANAGEMENT_API_URL || DEFAULT_MANAGEMENT_API_BASE_URL;
27
+ process.env.NEXT_PUBLIC_INKEEP_AGENTS_MANAGE_API_URL || DEFAULT_MANAGEMENT_API_BASE_URL;
28
28
  export const EXECUTION_API_BASE_URL =
29
- process.env.NEXT_PUBLIC_INKEEP_EXECUTION_API_URL || DEFAULT_EXECUTION_API_BASE_URL;
29
+ process.env.NEXT_PUBLIC_INKEEP_AGENTS_RUN_API_URL || DEFAULT_EXECUTION_API_BASE_URL;
30
30
 
31
31
  async function makeApiRequestInternal<T>(
32
32
  baseUrl: string,
@@ -7,7 +7,10 @@
7
7
 
8
8
  'use server';
9
9
 
10
- import type { ApiKeyApiSelect, ApiKeyApiCreationResponse } from '@inkeep/agents-core/client-exports';
10
+ import type {
11
+ ApiKeyApiCreationResponse,
12
+ ApiKeyApiSelect,
13
+ } from '@inkeep/agents-core/client-exports';
11
14
  import type { ListResponse, SingleResponse } from '../types/response';
12
15
  import { makeManagementApiRequest } from './api-config';
13
16
  import { validateProjectId, validateTenantId } from './resource-validation';
@@ -1,8 +1,8 @@
1
1
  'use server';
2
2
 
3
3
  import type {
4
- CredentialReferenceApiSelect,
5
4
  CredentialReferenceApiInsert,
5
+ CredentialReferenceApiSelect,
6
6
  McpTool,
7
7
  } from '@inkeep/agents-core';
8
8
  import type { ListResponse, SingleResponse } from '../types/response';
@@ -8,8 +8,8 @@
8
8
  'use server';
9
9
 
10
10
  import type {
11
- DataComponentApiSelect,
12
11
  DataComponentApiInsert,
12
+ DataComponentApiSelect,
13
13
  DataComponentApiUpdate,
14
14
  } from '@inkeep/agents-core';
15
15
  import type { ListResponse, SingleResponse } from '../types/response';
@@ -100,9 +100,12 @@ export async function deleteFullGraph(
100
100
  validateTenantId(tenantId);
101
101
  validateProjectId(projectId);
102
102
 
103
- await makeManagementApiRequest(`tenants/${tenantId}/crud/projects/${projectId}/graph/${graphId}`, {
104
- method: 'DELETE',
105
- });
103
+ await makeManagementApiRequest(
104
+ `tenants/${tenantId}/crud/projects/${projectId}/graph/${graphId}`,
105
+ {
106
+ method: 'DELETE',
107
+ }
108
+ );
106
109
  }
107
110
 
108
111
  // Export the error class for use in server actions
@@ -1,8 +1,8 @@
1
1
  'use server';
2
2
 
3
+ import type { ProjectFormData } from '@/components/projects/form/validation';
3
4
  import type { Project } from '../types/project';
4
5
  import type { ListResponse, SingleResponse } from '../types/response';
5
- import type { ProjectFormData } from '@/components/projects/form/validation';
6
6
  import { makeManagementApiRequest } from './api-config';
7
7
  import { validateTenantId } from './resource-validation';
8
8
 
@@ -2,7 +2,7 @@ import axios from 'axios';
2
2
  import axiosRetry from 'axios-retry';
3
3
 
4
4
  // Configure axios retry
5
- axiosRetry(axios, {
5
+ axiosRetry(axios, {
6
6
  retries: 3,
7
7
  retryDelay: axiosRetry.exponentialDelay,
8
8
  });