@vertesia/ui 1.5.0-dev.20260725.083715Z → 1.5.0-dev.20260804.124748Z
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.
- package/lib/core/hooks/useScrollableSearch.d.ts +1 -1
- package/lib/core/hooks/useScrollableSearch.d.ts.map +1 -1
- package/lib/core/hooks/useScrollableSearch.js +15 -2
- package/lib/core/hooks/useScrollableSearch.js.map +1 -1
- package/lib/env/index.d.ts +4 -0
- package/lib/env/index.d.ts.map +1 -1
- package/lib/env/index.js.map +1 -1
- package/lib/features/agent/chat/AgentRequestInputOverlay.d.ts +1 -1
- package/lib/features/agent/chat/AgentRequestInputOverlay.d.ts.map +1 -1
- package/lib/features/agent/chat/AgentRequestInputOverlay.js +3 -8
- package/lib/features/agent/chat/AgentRequestInputOverlay.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentConversation.d.ts +8 -1
- package/lib/features/agent/chat/ModernAgentConversation.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentConversation.js +53 -17
- package/lib/features/agent/chat/ModernAgentConversation.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/ActiveWorkstreamsSummary.d.ts +2 -1
- package/lib/features/agent/chat/ModernAgentOutput/ActiveWorkstreamsSummary.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/ActiveWorkstreamsSummary.js +25 -10
- package/lib/features/agent/chat/ModernAgentOutput/ActiveWorkstreamsSummary.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js +24 -9
- package/lib/features/agent/chat/ModernAgentOutput/AllMessagesMixed.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.js +15 -3
- package/lib/features/agent/chat/ModernAgentOutput/AttachmentPreview.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/Header.js +2 -2
- package/lib/features/agent/chat/ModernAgentOutput/Header.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageInput.d.ts +2 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageInput.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageInput.js +2 -2
- package/lib/features/agent/chat/ModernAgentOutput/MessageInput.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageItem.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js +2 -2
- package/lib/features/agent/chat/ModernAgentOutput/MessageItem.js.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/requestInputMessages.d.ts +9 -1
- package/lib/features/agent/chat/ModernAgentOutput/requestInputMessages.d.ts.map +1 -1
- package/lib/features/agent/chat/ModernAgentOutput/requestInputMessages.js +97 -2
- package/lib/features/agent/chat/ModernAgentOutput/requestInputMessages.js.map +1 -1
- package/lib/features/agent/chat/hooks/useAgentStream.d.ts.map +1 -1
- package/lib/features/agent/chat/hooks/useAgentStream.js +12 -3
- package/lib/features/agent/chat/hooks/useAgentStream.js.map +1 -1
- package/lib/features/agent/chat/index.d.ts +1 -1
- package/lib/features/agent/chat/index.d.ts.map +1 -1
- package/lib/features/agent/chat/index.js.map +1 -1
- package/lib/features/facets/PromptsFacetsNav.d.ts +8 -6
- package/lib/features/facets/PromptsFacetsNav.d.ts.map +1 -1
- package/lib/features/facets/PromptsFacetsNav.js +8 -3
- package/lib/features/facets/PromptsFacetsNav.js.map +1 -1
- package/lib/features/store/objects/components/ContentOverview.js +4 -1
- package/lib/features/store/objects/components/ContentOverview.js.map +1 -1
- package/lib/features/store/objects/components/useContentPanelHooks.d.ts +31 -3
- package/lib/features/store/objects/components/useContentPanelHooks.d.ts.map +1 -1
- package/lib/features/store/objects/search/DocumentSearchContext.d.ts +128 -6
- package/lib/features/store/objects/search/DocumentSearchContext.d.ts.map +1 -1
- package/lib/features/store/objects/selection/ObjectsActionContext.d.ts +2 -2
- package/lib/features/store/objects/selection/ObjectsActionContext.d.ts.map +1 -1
- package/lib/features/store/objects/selection/ObjectsActionSpec.d.ts +2 -2
- package/lib/features/store/objects/selection/ObjectsActionSpec.d.ts.map +1 -1
- package/lib/features/store/objects/selection/SelectionActions.d.ts +2 -2
- package/lib/features/store/objects/selection/SelectionActions.d.ts.map +1 -1
- package/lib/features/store/objects/upload/DocumentUploadModal.js.map +1 -1
- package/lib/features/store/types/IntakePolicyEditor.d.ts +2 -2
- package/lib/features/store/types/IntakePolicyEditor.d.ts.map +1 -1
- package/lib/features/store/types/IntakePolicyEditor.js +5 -1
- package/lib/features/store/types/IntakePolicyEditor.js.map +1 -1
- package/lib/features/store/types/IntakePolicyForm.d.ts.map +1 -1
- package/lib/features/store/types/IntakePolicyForm.js +2 -2
- package/lib/features/store/types/IntakePolicyForm.js.map +1 -1
- package/lib/features/store/types/ObjectSchemaEditor.d.ts +2 -2
- package/lib/features/store/types/ObjectSchemaEditor.d.ts.map +1 -1
- package/lib/features/store/types/SelectContentType.d.ts +2 -2
- package/lib/features/store/types/SelectContentType.d.ts.map +1 -1
- package/lib/features/store/types/SelectContentType.js.map +1 -1
- package/lib/features/store/types/SelectContentTypeModal.d.ts +2 -2
- package/lib/features/store/types/SelectContentTypeModal.d.ts.map +1 -1
- package/lib/features/store/types/SelectContentTypeModal.js.map +1 -1
- package/lib/features/store/types/TableLayoutEditor.d.ts +2 -2
- package/lib/features/store/types/TableLayoutEditor.d.ts.map +1 -1
- package/lib/features/store/types/TypeRegistry.d.ts +1267 -6
- package/lib/features/store/types/TypeRegistry.d.ts.map +1 -1
- package/lib/features/store/types/TypeRegistry.js +5 -0
- package/lib/features/store/types/TypeRegistry.js.map +1 -1
- package/lib/features/store/types/intake-policy-editor.logic.d.ts +9 -1
- package/lib/features/store/types/intake-policy-editor.logic.d.ts.map +1 -1
- package/lib/features/store/types/intake-policy-editor.logic.js +9 -1
- package/lib/features/store/types/intake-policy-editor.logic.js.map +1 -1
- package/lib/features/store/types/search/ObjectTypeSearchContext.d.ts +1251 -1
- package/lib/features/store/types/search/ObjectTypeSearchContext.d.ts.map +1 -1
- package/lib/features/store/types/typeCatalogCache.d.ts +2 -2
- package/lib/features/store/types/typeCatalogCache.d.ts.map +1 -1
- package/lib/features/store/types/typeCatalogCache.js +4 -0
- package/lib/features/store/types/typeCatalogCache.js.map +1 -1
- package/lib/features/user/UserInfo.d.ts +118 -7
- package/lib/features/user/UserInfo.d.ts.map +1 -1
- package/lib/i18n/locales/ar.json +2 -1
- package/lib/i18n/locales/de.json +2 -1
- package/lib/i18n/locales/en.json +5 -1
- package/lib/i18n/locales/es.json +2 -1
- package/lib/i18n/locales/fr.json +2 -1
- package/lib/i18n/locales/it.json +2 -1
- package/lib/i18n/locales/ja.json +2 -1
- package/lib/i18n/locales/ko.json +2 -1
- package/lib/i18n/locales/pt.json +2 -1
- package/lib/i18n/locales/ru.json +2 -1
- package/lib/i18n/locales/tr.json +2 -1
- package/lib/i18n/locales/zh-TW.json +2 -1
- package/lib/i18n/locales/zh.json +2 -1
- package/lib/session/UserSession.d.ts +18 -4
- package/lib/session/UserSession.d.ts.map +1 -1
- package/lib/shell/apps/AppInstallationProvider.d.ts +222 -2
- package/lib/shell/apps/AppInstallationProvider.d.ts.map +1 -1
- package/lib/shell/login/InviteAcceptModal.js.map +1 -1
- package/lib/vertesia-ui-core.js +1 -1
- package/lib/vertesia-ui-core.js.map +1 -1
- package/lib/vertesia-ui-features.js +34 -34
- package/lib/vertesia-ui-features.js.map +1 -1
- package/lib/vertesia-ui-i18n.js +13 -13
- package/lib/vertesia-ui-widgets.js +18 -18
- package/lib/vertesia-ui-widgets.js.map +1 -1
- package/lib/widgets/monacoEditor/MonacoEditor.d.ts +7 -1
- package/lib/widgets/monacoEditor/MonacoEditor.d.ts.map +1 -1
- package/lib/widgets/monacoEditor/MonacoEditor.js +62 -6
- package/lib/widgets/monacoEditor/MonacoEditor.js.map +1 -1
- package/package.json +31 -31
- package/src/core/hooks/useScrollableSearch.test.tsx +139 -0
- package/src/core/hooks/useScrollableSearch.tsx +17 -3
- package/src/env/index.ts +2 -0
- package/src/features/agent/chat/AgentChatPlaybackControls.test.tsx +2 -0
- package/src/features/agent/chat/AgentRequestInputOverlay.test.tsx +51 -6
- package/src/features/agent/chat/AgentRequestInputOverlay.tsx +5 -7
- package/src/features/agent/chat/ModernAgentConversation.test.tsx +195 -3
- package/src/features/agent/chat/ModernAgentConversation.tsx +106 -22
- package/src/features/agent/chat/ModernAgentOutput/ActiveWorkstreamsSummary.tsx +75 -20
- package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.test.tsx +112 -0
- package/src/features/agent/chat/ModernAgentOutput/AllMessagesMixed.tsx +62 -10
- package/src/features/agent/chat/ModernAgentOutput/AttachmentPreview.tsx +36 -4
- package/src/features/agent/chat/ModernAgentOutput/Header.tsx +2 -2
- package/src/features/agent/chat/ModernAgentOutput/MessageInput.test.tsx +71 -0
- package/src/features/agent/chat/ModernAgentOutput/MessageInput.tsx +3 -1
- package/src/features/agent/chat/ModernAgentOutput/MessageItem.tsx +18 -4
- package/src/features/agent/chat/ModernAgentOutput/requestInputMessages.test.ts +95 -0
- package/src/features/agent/chat/ModernAgentOutput/requestInputMessages.ts +111 -1
- package/src/features/agent/chat/hooks/useAgentStream.test.tsx +57 -0
- package/src/features/agent/chat/hooks/useAgentStream.ts +12 -3
- package/src/features/agent/chat/index.ts +1 -0
- package/src/features/facets/PromptsFacetsNav.tsx +17 -9
- package/src/features/store/objects/components/ContentOverview.tsx +4 -1
- package/src/features/store/objects/selection/ObjectsActionContext.tsx +2 -2
- package/src/features/store/objects/selection/ObjectsActionSpec.ts +2 -2
- package/src/features/store/objects/selection/SelectionActions.tsx +2 -2
- package/src/features/store/objects/upload/DocumentUploadModal.tsx +2 -2
- package/src/features/store/types/IntakePolicyEditor.tsx +12 -4
- package/src/features/store/types/IntakePolicyForm.tsx +25 -0
- package/src/features/store/types/ObjectSchemaEditor.tsx +2 -2
- package/src/features/store/types/SelectContentType.tsx +9 -9
- package/src/features/store/types/SelectContentTypeModal.tsx +5 -5
- package/src/features/store/types/TableLayoutEditor.tsx +2 -2
- package/src/features/store/types/TypeRegistry.ts +8 -3
- package/src/features/store/types/intake-policy-editor.logic.ts +10 -2
- package/src/features/store/types/typeCatalogCache.ts +10 -3
- package/src/i18n/locales/ar.json +2 -1
- package/src/i18n/locales/de.json +2 -1
- package/src/i18n/locales/en.json +5 -1
- package/src/i18n/locales/es.json +2 -1
- package/src/i18n/locales/fr.json +2 -1
- package/src/i18n/locales/it.json +2 -1
- package/src/i18n/locales/ja.json +2 -1
- package/src/i18n/locales/ko.json +2 -1
- package/src/i18n/locales/pt.json +2 -1
- package/src/i18n/locales/ru.json +2 -1
- package/src/i18n/locales/tr.json +2 -1
- package/src/i18n/locales/zh-TW.json +2 -1
- package/src/i18n/locales/zh.json +2 -1
- package/src/shell/login/InviteAcceptModal.tsx +4 -4
- package/src/widgets/markdown/ArtifactEditingSurface.test.tsx +13 -9
- package/src/widgets/monacoEditor/MonacoEditor.tsx +83 -7
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { type AgentMessage, AgentMessageType } from '@vertesia/common';
|
|
2
2
|
import { describe, expect, it } from 'vitest';
|
|
3
3
|
import {
|
|
4
|
+
clearRequestInputIdAnsweredForSession,
|
|
5
|
+
getAnsweredRequestInputKeys,
|
|
4
6
|
getAnsweredToolApprovalRequestInputKeys,
|
|
7
|
+
getPendingRequestInputMessage,
|
|
5
8
|
getRequestInputMessageKey,
|
|
9
|
+
getRequestInputResponseIdFromMetadata,
|
|
10
|
+
getRequestInputResponseMetadata,
|
|
6
11
|
getToolApprovalResponseMetadata,
|
|
12
|
+
isRequestInputAnsweredForSession,
|
|
13
|
+
markRequestInputIdAnsweredForSession,
|
|
7
14
|
} from './requestInputMessages';
|
|
8
15
|
|
|
9
16
|
const APPROVAL_KEY = 'create_interaction:name:JDE AR Payment Processing Agent';
|
|
@@ -78,3 +85,91 @@ describe('getAnsweredToolApprovalRequestInputKeys', () => {
|
|
|
78
85
|
expect(answered.size).toBe(0);
|
|
79
86
|
});
|
|
80
87
|
});
|
|
88
|
+
|
|
89
|
+
describe('request input correlation', () => {
|
|
90
|
+
it('does not apply session acknowledgement to non-request-input messages with the same id', () => {
|
|
91
|
+
const agentRunId = 'agent-run-1';
|
|
92
|
+
const requestId = 'request-1';
|
|
93
|
+
const request = {
|
|
94
|
+
...makeApprovalRequest(),
|
|
95
|
+
details: { request_id: requestId, ux: { options: [{ id: 'allow_once' }] } },
|
|
96
|
+
};
|
|
97
|
+
const unrelatedMessage = {
|
|
98
|
+
...makeUserAnswer('Still working'),
|
|
99
|
+
details: { request_id: requestId },
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
markRequestInputIdAnsweredForSession(agentRunId, requestId);
|
|
103
|
+
|
|
104
|
+
expect(isRequestInputAnsweredForSession(agentRunId, request)).toBe(true);
|
|
105
|
+
expect(isRequestInputAnsweredForSession(agentRunId, unrelatedMessage)).toBe(false);
|
|
106
|
+
|
|
107
|
+
clearRequestInputIdAnsweredForSession(agentRunId, requestId);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('correlates a persisted response to the matching request id', () => {
|
|
111
|
+
const firstRequest = {
|
|
112
|
+
...makeApprovalRequest(),
|
|
113
|
+
details: { request_id: 'request-1', ux: { options: [{ id: 'red', label: 'Red' }] } },
|
|
114
|
+
};
|
|
115
|
+
const secondRequest = {
|
|
116
|
+
...makeApprovalRequest(),
|
|
117
|
+
timestamp: 2,
|
|
118
|
+
details: { request_id: 'request-2', ux: { options: [{ id: 'blue', label: 'Blue' }] } },
|
|
119
|
+
};
|
|
120
|
+
const answer = {
|
|
121
|
+
...makeUserAnswer('red'),
|
|
122
|
+
timestamp: 3,
|
|
123
|
+
details: { request_input_response: { request_id: 'request-1' } },
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const answered = getAnsweredRequestInputKeys([firstRequest, secondRequest, answer]);
|
|
127
|
+
|
|
128
|
+
expect(answered.has(getRequestInputMessageKey(firstRequest))).toBe(true);
|
|
129
|
+
expect(answered.has(getRequestInputMessageKey(secondRequest))).toBe(false);
|
|
130
|
+
expect(getPendingRequestInputMessage([firstRequest, secondRequest, answer])).toBe(secondRequest);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('adds the request id without discarding existing response metadata', () => {
|
|
134
|
+
const request = {
|
|
135
|
+
...makeApprovalRequest(),
|
|
136
|
+
details: { request_id: 'request-1', ux: { options: [{ id: 'red', label: 'Red' }] } },
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
expect(getRequestInputResponseMetadata(request, { source: 'free-response' })).toEqual({
|
|
140
|
+
source: 'free-response',
|
|
141
|
+
request_input_response: { request_id: 'request-1' },
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('does not let an earlier response resolve a later prompt that reused its request id', () => {
|
|
146
|
+
const firstRequest = {
|
|
147
|
+
...makeApprovalRequest(),
|
|
148
|
+
details: { request_id: 'reused-request', ux: { options: [{ id: 'allow_once' }] } },
|
|
149
|
+
};
|
|
150
|
+
const firstAnswer = {
|
|
151
|
+
...makeUserAnswer('allow_once'),
|
|
152
|
+
details: { request_input_response: { request_id: 'reused-request' } },
|
|
153
|
+
};
|
|
154
|
+
const secondRequest = {
|
|
155
|
+
...makeApprovalRequest(),
|
|
156
|
+
timestamp: 3,
|
|
157
|
+
details: { request_id: 'reused-request', ux: { options: [{ id: 'allow_once' }] } },
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const answered = getAnsweredRequestInputKeys([firstRequest, firstAnswer, secondRequest]);
|
|
161
|
+
|
|
162
|
+
expect(answered.has(getRequestInputMessageKey(firstRequest))).toBe(true);
|
|
163
|
+
expect(answered.has(getRequestInputMessageKey(secondRequest))).toBe(false);
|
|
164
|
+
expect(getPendingRequestInputMessage([firstRequest, firstAnswer, secondRequest])).toBe(secondRequest);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('reads a request id from response metadata', () => {
|
|
168
|
+
expect(
|
|
169
|
+
getRequestInputResponseIdFromMetadata({
|
|
170
|
+
request_input_response: { request_id: 'request-1' },
|
|
171
|
+
}),
|
|
172
|
+
).toBe('request-1');
|
|
173
|
+
expect(getRequestInputResponseIdFromMetadata({ request_input_response: {} })).toBeUndefined();
|
|
174
|
+
});
|
|
175
|
+
});
|
|
@@ -96,6 +96,65 @@ export function getRequestInputMessageKey(message: AgentMessage): string {
|
|
|
96
96
|
.join('|');
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
export function getRequestInputId(message: AgentMessage): string | undefined {
|
|
100
|
+
const details = message.details as Record<string, unknown> | undefined;
|
|
101
|
+
const requestId = details?.request_id ?? details?.tool_use_id;
|
|
102
|
+
return typeof requestId === 'string' && requestId ? requestId : undefined;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function getRequestInputResolutionKey(message: AgentMessage): string {
|
|
106
|
+
return getRequestInputId(message) ?? getRequestInputMessageKey(message);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const ANSWERED_REQUEST_INPUT_STORAGE_PREFIX = 'vertesia:answered-request-input:';
|
|
110
|
+
|
|
111
|
+
function getAnsweredRequestInputStorageKey(agentRunId: string, requestId: string): string {
|
|
112
|
+
return `${ANSWERED_REQUEST_INPUT_STORAGE_PREFIX}${agentRunId}:${requestId}`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function markRequestInputIdAnsweredForSession(agentRunId: string, requestId: string): void {
|
|
116
|
+
if (typeof window === 'undefined') return;
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
window.sessionStorage.setItem(getAnsweredRequestInputStorageKey(agentRunId, requestId), '1');
|
|
120
|
+
} catch {
|
|
121
|
+
// Session storage can be unavailable in privacy-restricted browser contexts.
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function clearRequestInputIdAnsweredForSession(agentRunId: string, requestId: string): void {
|
|
126
|
+
if (typeof window === 'undefined') return;
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
window.sessionStorage.removeItem(getAnsweredRequestInputStorageKey(agentRunId, requestId));
|
|
130
|
+
} catch {
|
|
131
|
+
// Session storage can be unavailable in privacy-restricted browser contexts.
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function isRequestInputAnsweredForSession(agentRunId: string, message: AgentMessage): boolean {
|
|
136
|
+
if (message.type !== AgentMessageType.REQUEST_INPUT) return false;
|
|
137
|
+
|
|
138
|
+
const requestId = getRequestInputId(message);
|
|
139
|
+
if (!requestId || typeof window === 'undefined') return false;
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
return window.sessionStorage.getItem(getAnsweredRequestInputStorageKey(agentRunId, requestId)) === '1';
|
|
143
|
+
} catch {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function getRequestInputResponseId(message: AgentMessage): string | undefined {
|
|
149
|
+
if (message.type !== AgentMessageType.QUESTION) return undefined;
|
|
150
|
+
|
|
151
|
+
const details = getRecord(message.details);
|
|
152
|
+
const metadata = getRecord(details?.metadata);
|
|
153
|
+
const response = getRecord(details?.request_input_response) ?? getRecord(metadata?.request_input_response);
|
|
154
|
+
const requestId = response?.request_id;
|
|
155
|
+
return typeof requestId === 'string' && requestId ? requestId : undefined;
|
|
156
|
+
}
|
|
157
|
+
|
|
99
158
|
export function getRequestInputAnswerMessageKey(message: AgentMessage): string {
|
|
100
159
|
const details = message.details as Record<string, unknown> | undefined;
|
|
101
160
|
const keyParts = [
|
|
@@ -114,10 +173,22 @@ export function getRequestInputAnswerMessageKey(message: AgentMessage): string {
|
|
|
114
173
|
|
|
115
174
|
export function getAnsweredRequestInputKeys(messages: AgentMessage[]): Set<string> {
|
|
116
175
|
const answered = new Set<string>();
|
|
176
|
+
const latestResponseIndexByRequestId = new Map<string, number>();
|
|
177
|
+
messages.forEach((message, index) => {
|
|
178
|
+
const requestId = getRequestInputResponseId(message);
|
|
179
|
+
if (requestId) latestResponseIndexByRequestId.set(requestId, index);
|
|
180
|
+
});
|
|
117
181
|
|
|
118
182
|
messages.forEach((message, index) => {
|
|
119
183
|
if (message.type !== AgentMessageType.REQUEST_INPUT) return;
|
|
120
184
|
|
|
185
|
+
const requestId = getRequestInputId(message);
|
|
186
|
+
const correlatedResponseIndex = requestId ? latestResponseIndexByRequestId.get(requestId) : undefined;
|
|
187
|
+
if (correlatedResponseIndex !== undefined && correlatedResponseIndex > index) {
|
|
188
|
+
answered.add(getRequestInputMessageKey(message));
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
121
192
|
const workstreamId = getWorkstreamId(message);
|
|
122
193
|
for (let nextIndex = index + 1; nextIndex < messages.length; nextIndex += 1) {
|
|
123
194
|
const nextMessage = messages[nextIndex];
|
|
@@ -125,7 +196,9 @@ export function getAnsweredRequestInputKeys(messages: AgentMessage[]): Set<strin
|
|
|
125
196
|
|
|
126
197
|
if (nextMessage.type === AgentMessageType.REQUEST_INPUT) break;
|
|
127
198
|
if (nextMessage.type === AgentMessageType.QUESTION) {
|
|
128
|
-
|
|
199
|
+
if (!getRequestInputResponseId(nextMessage)) {
|
|
200
|
+
answered.add(getRequestInputMessageKey(message));
|
|
201
|
+
}
|
|
129
202
|
break;
|
|
130
203
|
}
|
|
131
204
|
}
|
|
@@ -134,6 +207,41 @@ export function getAnsweredRequestInputKeys(messages: AgentMessage[]): Set<strin
|
|
|
134
207
|
return answered;
|
|
135
208
|
}
|
|
136
209
|
|
|
210
|
+
export function getRequestInputResponseMetadata(
|
|
211
|
+
message: AgentMessage,
|
|
212
|
+
metadata?: Record<string, unknown>,
|
|
213
|
+
): Record<string, unknown> | undefined {
|
|
214
|
+
const requestId = getRequestInputId(message);
|
|
215
|
+
if (!requestId) return metadata;
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
...metadata,
|
|
219
|
+
request_input_response: { request_id: requestId },
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export function sendRequestInputResponse(
|
|
224
|
+
onSendMessage: ((message: string, metadata?: Record<string, unknown>) => void) | undefined,
|
|
225
|
+
requestMessage: AgentMessage,
|
|
226
|
+
response: string,
|
|
227
|
+
metadata?: Record<string, unknown>,
|
|
228
|
+
): void {
|
|
229
|
+
if (!onSendMessage) return;
|
|
230
|
+
|
|
231
|
+
const responseMetadata = getRequestInputResponseMetadata(requestMessage, metadata);
|
|
232
|
+
if (responseMetadata) {
|
|
233
|
+
onSendMessage(response, responseMetadata);
|
|
234
|
+
} else {
|
|
235
|
+
onSendMessage(response);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function getRequestInputResponseIdFromMetadata(metadata?: Record<string, unknown>): string | undefined {
|
|
240
|
+
const response = getRecord(metadata?.request_input_response);
|
|
241
|
+
const requestId = response?.request_id;
|
|
242
|
+
return typeof requestId === 'string' && requestId ? requestId : undefined;
|
|
243
|
+
}
|
|
244
|
+
|
|
137
245
|
export function getResolvedToolApprovalKeys(messages: AgentMessage[]): Set<string> {
|
|
138
246
|
const resolved = new Set<string>();
|
|
139
247
|
|
|
@@ -300,6 +408,7 @@ export function isRequestInputAnswered(message: AgentMessage, answeredRequestInp
|
|
|
300
408
|
export function getPendingRequestInputMessage(
|
|
301
409
|
messages: AgentMessage[],
|
|
302
410
|
workstreamId = 'main',
|
|
411
|
+
locallyAnsweredRequestInputKeys?: ReadonlySet<string>,
|
|
303
412
|
): RequestInputMessageWithUx | undefined {
|
|
304
413
|
const answeredRequestInputKeys = getAnsweredRequestInputKeys(messages);
|
|
305
414
|
const resolvedToolApprovalKeys = getResolvedToolApprovalKeys(messages);
|
|
@@ -308,6 +417,7 @@ export function getPendingRequestInputMessage(
|
|
|
308
417
|
const message = messages[index];
|
|
309
418
|
if (!hasRequestInputUx(message)) continue;
|
|
310
419
|
if (getWorkstreamId(message) !== workstreamId) continue;
|
|
420
|
+
if (locallyAnsweredRequestInputKeys?.has(getRequestInputResolutionKey(message))) continue;
|
|
311
421
|
if (isRequestInputAnswered(message, answeredRequestInputKeys)) continue;
|
|
312
422
|
if (isRequestInputResolvedByToolApprovalEvent(message, resolvedToolApprovalKeys)) continue;
|
|
313
423
|
return message;
|
|
@@ -312,6 +312,63 @@ describe('useAgentStream', () => {
|
|
|
312
312
|
expect(onMessage).toHaveBeenCalledTimes(1);
|
|
313
313
|
});
|
|
314
314
|
|
|
315
|
+
it('does not apply replayed file_processing snapshots to server file updates', async () => {
|
|
316
|
+
let deliver: ((message: AgentMessage) => void) | undefined;
|
|
317
|
+
const fileSnapshot = (timestamp: number, id: string): AgentMessage => ({
|
|
318
|
+
timestamp,
|
|
319
|
+
workflow_run_id: 'run-1',
|
|
320
|
+
type: AgentMessageType.SYSTEM,
|
|
321
|
+
message: '',
|
|
322
|
+
workstream_id: 'main',
|
|
323
|
+
details: {
|
|
324
|
+
system_type: 'file_processing',
|
|
325
|
+
batch_id: 'batch-1',
|
|
326
|
+
files: [
|
|
327
|
+
{
|
|
328
|
+
id,
|
|
329
|
+
name: `${id}.pdf`,
|
|
330
|
+
content_type: 'application/pdf',
|
|
331
|
+
size: 1,
|
|
332
|
+
status: FileProcessingStatus.READY,
|
|
333
|
+
artifact_path: `files/${id}.pdf`,
|
|
334
|
+
reference: `artifact:files/${id}.pdf`,
|
|
335
|
+
started_at: timestamp,
|
|
336
|
+
},
|
|
337
|
+
],
|
|
338
|
+
pending_count: 0,
|
|
339
|
+
ready_count: 1,
|
|
340
|
+
error_count: 0,
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
const historical = fileSnapshot(1_000, 'old-upload');
|
|
344
|
+
const streamMessages = vi.fn<
|
|
345
|
+
(
|
|
346
|
+
id: string,
|
|
347
|
+
onMessage?: (message: AgentMessage, exitFn?: (payload: unknown) => void) => void,
|
|
348
|
+
since?: number,
|
|
349
|
+
signal?: AbortSignal,
|
|
350
|
+
options?: AgentRunStreamMessagesOptions,
|
|
351
|
+
) => Promise<unknown>
|
|
352
|
+
>(async (_id, onMessage, _since, _signal, options) => {
|
|
353
|
+
deliver = onMessage;
|
|
354
|
+
options?.onHistoryLoaded?.([historical]);
|
|
355
|
+
// Completed runs replay archived file_processing snapshots through the
|
|
356
|
+
// live callback; applying them would rehydrate every historical upload
|
|
357
|
+
// as a staged composer chip on reopen.
|
|
358
|
+
onMessage?.(historical);
|
|
359
|
+
return null;
|
|
360
|
+
});
|
|
361
|
+
const client = createClient(streamMessages);
|
|
362
|
+
const { result } = renderHook(() => useAgentStream(client, 'agent-run-1', vi.fn()));
|
|
363
|
+
|
|
364
|
+
await waitFor(() => expect(deliver).toBeDefined());
|
|
365
|
+
expect(result.current.serverFileUpdates.size).toBe(0);
|
|
366
|
+
|
|
367
|
+
act(() => deliver?.(fileSnapshot(1_100, 'new-upload')));
|
|
368
|
+
expect(result.current.serverFileUpdates.size).toBe(1);
|
|
369
|
+
expect(result.current.serverFileUpdates.has('new-upload')).toBe(true);
|
|
370
|
+
});
|
|
371
|
+
|
|
315
372
|
it('marks initial history as errored when the history fetch fails', async () => {
|
|
316
373
|
const streamMessages = vi.fn<
|
|
317
374
|
(
|
|
@@ -357,9 +357,12 @@ export function useAgentStream(
|
|
|
357
357
|
(message) => {
|
|
358
358
|
if (abortController.signal.aborted) return;
|
|
359
359
|
// Completed and idle runs replay their archived history through the live
|
|
360
|
-
// stream.
|
|
360
|
+
// stream. Track replay status once, before the cursor advances below —
|
|
361
|
+
// every replay-sensitive consumer in this callback must use it.
|
|
362
|
+
const isReplay = Boolean(message.timestamp && message.timestamp <= lastDeliveredTsRef.current);
|
|
363
|
+
// Only forward genuinely new deliveries so onMessage consumers
|
|
361
364
|
// never treat replayed events as fresh ones.
|
|
362
|
-
if (!
|
|
365
|
+
if (!isReplay) {
|
|
363
366
|
onMessageRef.current?.(message);
|
|
364
367
|
}
|
|
365
368
|
|
|
@@ -426,7 +429,13 @@ export function useAgentStream(
|
|
|
426
429
|
if (message.type === AgentMessageType.SYSTEM) {
|
|
427
430
|
const details = message.details as FileProcessingDetails | undefined;
|
|
428
431
|
if (details?.system_type === 'file_processing' && details.files) {
|
|
429
|
-
|
|
432
|
+
// Replayed snapshots list every file ever uploaded to the run as
|
|
433
|
+
// READY; applying them on reconnect would rehydrate historical
|
|
434
|
+
// uploads as staged composer chips. Only genuinely new updates
|
|
435
|
+
// may touch the file state.
|
|
436
|
+
if (!isReplay) {
|
|
437
|
+
setServerFileUpdates(new Map(details.files.map((file) => [file.id, file])));
|
|
438
|
+
}
|
|
430
439
|
return;
|
|
431
440
|
}
|
|
432
441
|
// Other SYSTEM messages fall through to normal handling
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FacetBucket } from '@vertesia/common';
|
|
1
|
+
import type { ComputedFacetResponse, FacetBucket } from '@vertesia/common';
|
|
2
2
|
import {
|
|
3
3
|
type Filter as BaseFilter,
|
|
4
4
|
FilterBar,
|
|
@@ -11,11 +11,13 @@ import { useState } from 'react';
|
|
|
11
11
|
import { filterValueToQueryValue, type SearchInterface, setSearchQueryValue } from './utils/SearchInterface';
|
|
12
12
|
|
|
13
13
|
interface PromptsFacetsNavProps {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
/**
|
|
15
|
+
* The facet response as the endpoint publishes it: `total` plus one entry per facet the caller
|
|
16
|
+
* asked for. The names are chosen per request, so the buckets are narrowed below rather than
|
|
17
|
+
* declared here — the previous `{ role, status, tags }` shape named facets this component never
|
|
18
|
+
* requests and could not describe a caller that asks for a different one.
|
|
19
|
+
*/
|
|
20
|
+
facets: ComputedFacetResponse;
|
|
19
21
|
search: SearchInterface;
|
|
20
22
|
/**
|
|
21
23
|
* Optional controlled filter state. When provided, the parent owns the filter list (and is
|
|
@@ -28,8 +30,14 @@ interface PromptsFacetsNavProps {
|
|
|
28
30
|
filterGroups?: FilterGroup[];
|
|
29
31
|
}
|
|
30
32
|
|
|
33
|
+
function facetBuckets(facets: ComputedFacetResponse, name: string): FacetBucket[] {
|
|
34
|
+
const value = facets[name];
|
|
35
|
+
return Array.isArray(value) ? (value as FacetBucket[]) : [];
|
|
36
|
+
}
|
|
37
|
+
|
|
31
38
|
// Hook to create filter groups for prompts
|
|
32
39
|
export function usePromptsFilterGroups(facets: PromptsFacetsNavProps['facets']): FilterGroup[] {
|
|
40
|
+
const roleBuckets = facetBuckets(facets, 'role');
|
|
33
41
|
const customFilterGroups: FilterGroup[] = [];
|
|
34
42
|
|
|
35
43
|
// Add name filter as text type
|
|
@@ -42,12 +50,12 @@ export function usePromptsFilterGroups(facets: PromptsFacetsNavProps['facets']):
|
|
|
42
50
|
customFilterGroups.push(nameFilterGroup);
|
|
43
51
|
|
|
44
52
|
// Add role filter as select type if role facets are available
|
|
45
|
-
if (
|
|
53
|
+
if (roleBuckets.length > 0) {
|
|
46
54
|
const rolesFilterGroup = {
|
|
47
55
|
name: 'role',
|
|
48
56
|
placeholder: 'Role',
|
|
49
57
|
type: 'select' as const,
|
|
50
|
-
options:
|
|
58
|
+
options: roleBuckets.map((facet) => ({
|
|
51
59
|
label: facet._id,
|
|
52
60
|
value: facet._id,
|
|
53
61
|
count: facet.count,
|
|
@@ -62,7 +70,7 @@ export function usePromptsFilterGroups(facets: PromptsFacetsNavProps['facets']):
|
|
|
62
70
|
placeholder: 'Tags',
|
|
63
71
|
type: 'stringList' as const,
|
|
64
72
|
multiple: true,
|
|
65
|
-
options: (facets
|
|
73
|
+
options: facetBuckets(facets, 'tags').map((facet) => ({
|
|
66
74
|
label: facet._id,
|
|
67
75
|
value: facet._id,
|
|
68
76
|
})),
|
|
@@ -843,7 +843,10 @@ function TextActions({
|
|
|
843
843
|
() => (project ? client.projects.retrieve(project.id) : Promise.resolve(undefined)),
|
|
844
844
|
[project?.id],
|
|
845
845
|
);
|
|
846
|
-
|
|
846
|
+
// `null` is how the setting is cleared, and it means the same thing here as an absent value: no
|
|
847
|
+
// custom template, so the export falls back to the built-in one. Collapsing the two spellings at
|
|
848
|
+
// the read keeps `templateObjectId` below a plain optional string.
|
|
849
|
+
const pdfTemplateObjectId = fullProject?.configuration?.pdf_template_object_id ?? undefined;
|
|
847
850
|
|
|
848
851
|
const isMarkdown = isMarkdownContentType(content?.type);
|
|
849
852
|
const isDocumentEditingContent = isDocumentEditingContentType(content?.type);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ContentObjectTypeCatalogEntry } from '@vertesia/common';
|
|
2
2
|
import { useToast } from '@vertesia/ui/core';
|
|
3
3
|
import { useUserSession } from '@vertesia/ui/session';
|
|
4
4
|
import { type ReactNode, useMemo } from 'react';
|
|
@@ -25,7 +25,7 @@ const DEFAULT_ACTIONS: ObjectsActionSpec[] = [
|
|
|
25
25
|
|
|
26
26
|
interface ObjectsActionContextProps {
|
|
27
27
|
children: ReactNode;
|
|
28
|
-
type?:
|
|
28
|
+
type?: ContentObjectTypeCatalogEntry;
|
|
29
29
|
}
|
|
30
30
|
export function ObjectsActionContextProvider({ children, type }: ObjectsActionContextProps) {
|
|
31
31
|
const selection = useDocumentSelection();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { VertesiaClient } from '@vertesia/client';
|
|
2
|
-
import type { ColumnLayout,
|
|
2
|
+
import type { ColumnLayout, ContentObjectTypeCatalogEntry } from '@vertesia/common';
|
|
3
3
|
import type { ToastFn } from '@vertesia/ui/core';
|
|
4
4
|
import type { DocumentSelection } from '../DocumentSelectionProvider';
|
|
5
5
|
import type { DocumentSearch } from '../search/DocumentSearchContext';
|
|
@@ -22,7 +22,7 @@ export interface ObjectsActionParams {
|
|
|
22
22
|
toast: ToastFn;
|
|
23
23
|
search: DocumentSearch;
|
|
24
24
|
table_layout?: ColumnLayout[];
|
|
25
|
-
type?:
|
|
25
|
+
type?: ContentObjectTypeCatalogEntry;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
export interface ActionComponentTypeProps {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ContentObjectTypeCatalogEntry } from '@vertesia/common';
|
|
2
2
|
import { Button, Popover, PopoverContent, PopoverTrigger, SelectList } from '@vertesia/ui/core';
|
|
3
3
|
import { useUITranslation } from '@vertesia/ui/i18n';
|
|
4
4
|
import clsx from 'clsx';
|
|
@@ -13,7 +13,7 @@ import { useObjectsActionContext } from './ObjectsActionHooks';
|
|
|
13
13
|
import type { ObjectsActionSpec } from './ObjectsActionSpec';
|
|
14
14
|
|
|
15
15
|
interface SelectionActionsProps {
|
|
16
|
-
type?:
|
|
16
|
+
type?: ContentObjectTypeCatalogEntry;
|
|
17
17
|
allowMutations?: boolean;
|
|
18
18
|
allowDelete?: boolean;
|
|
19
19
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Collection,
|
|
1
|
+
import type { Collection, ContentObjectTypeCatalogEntry, DynamicCollection } from '@vertesia/common';
|
|
2
2
|
import {
|
|
3
3
|
Button,
|
|
4
4
|
errorMessage,
|
|
@@ -90,7 +90,7 @@ export function DocumentUploadModal({
|
|
|
90
90
|
const [files, setFiles] = useState<File[]>([]);
|
|
91
91
|
const [processedFiles, setProcessedFiles] = useState<FileWithMetadata[]>([]);
|
|
92
92
|
const [processingDone, setProcessingDone] = useState(false);
|
|
93
|
-
const [selectedType, setSelectedType] = useState<
|
|
93
|
+
const [selectedType, setSelectedType] = useState<ContentObjectTypeCatalogEntry | null>(null);
|
|
94
94
|
const [fileStatuses, setFileStatuses] = useState<FileUploadStatus[]>([]);
|
|
95
95
|
const [isUploading, setIsUploading] = useState(false);
|
|
96
96
|
const [uploadComplete, setUploadComplete] = useState(false);
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
type ContentObjectTypeCatalogEntry,
|
|
3
|
+
type ContentTypeIntakePolicy,
|
|
4
|
+
ContentTypeIntakePolicySchema,
|
|
5
|
+
} from '@vertesia/common';
|
|
2
6
|
import {
|
|
3
7
|
Badge,
|
|
4
8
|
Button,
|
|
@@ -25,7 +29,7 @@ import { IntakePolicyForm, type IntakePolicyFormSection } from './IntakePolicyFo
|
|
|
25
29
|
|
|
26
30
|
interface IntakePolicyEditorProps {
|
|
27
31
|
/** Content type whose intake policy is edited; omit when editing a standalone policy. */
|
|
28
|
-
objectType?:
|
|
32
|
+
objectType?: ContentObjectTypeCatalogEntry;
|
|
29
33
|
/** Initial policy value when no objectType is given (e.g. the project default policy). */
|
|
30
34
|
value?: ContentTypeIntakePolicy;
|
|
31
35
|
/** Custom persistence (e.g. save to project configuration). Returns the saved policy.
|
|
@@ -644,13 +648,17 @@ function stringifyPolicy(policy: ContentTypeIntakePolicy | undefined) {
|
|
|
644
648
|
return JSON.stringify(policy ?? EMPTY_POLICY, null, 2);
|
|
645
649
|
}
|
|
646
650
|
|
|
647
|
-
function createPolicyValidator() {
|
|
651
|
+
function createPolicyValidator(): ValidateFunction<ContentTypeIntakePolicy> {
|
|
648
652
|
const ajv = new Ajv({
|
|
649
653
|
strict: false,
|
|
650
654
|
allErrors: true,
|
|
651
655
|
});
|
|
652
656
|
addFormats(ajv);
|
|
653
|
-
|
|
657
|
+
// The schema is plain JSON — generated from the canonical Zod schema, and deliberately typed as
|
|
658
|
+
// opaque data so the package root can export it without pulling Zod into the browser bundle. AJV
|
|
659
|
+
// therefore infers `unknown`; the type argument states what compiling THIS schema validates, and
|
|
660
|
+
// `store.contract.test.ts` is what keeps the claim true.
|
|
661
|
+
return ajv.compile<ContentTypeIntakePolicy>(ContentTypeIntakePolicySchema);
|
|
654
662
|
}
|
|
655
663
|
|
|
656
664
|
function parseAndValidatePolicy(
|
|
@@ -152,6 +152,13 @@ export function IntakePolicyForm({ policy, section, onChange, readonly = false }
|
|
|
152
152
|
onChange={(value) => setValue(['text_conversion', 'scope'], value)}
|
|
153
153
|
readonly={readonly}
|
|
154
154
|
/>
|
|
155
|
+
<NumberField
|
|
156
|
+
label={t('intakePolicy.field.renderDpi')}
|
|
157
|
+
value={policy.text_conversion?.render_dpi}
|
|
158
|
+
min={72}
|
|
159
|
+
onChange={(value) => setValue(['text_conversion', 'render_dpi'], value)}
|
|
160
|
+
readonly={readonly}
|
|
161
|
+
/>
|
|
155
162
|
</div>
|
|
156
163
|
<TextAreaField
|
|
157
164
|
label={t('intakePolicy.field.instructions')}
|
|
@@ -161,6 +168,15 @@ export function IntakePolicyForm({ policy, section, onChange, readonly = false }
|
|
|
161
168
|
/>
|
|
162
169
|
</FormSection>
|
|
163
170
|
|
|
171
|
+
<FormSection title={t('intakePolicy.section.conversionModel')}>
|
|
172
|
+
<ModelConfigFields
|
|
173
|
+
title={t('intakePolicy.field.model')}
|
|
174
|
+
config={policy.text_conversion?.config}
|
|
175
|
+
onChange={(value) => setValue(['text_conversion', 'config'], value)}
|
|
176
|
+
readonly={readonly}
|
|
177
|
+
/>
|
|
178
|
+
</FormSection>
|
|
179
|
+
|
|
164
180
|
{policy.text_conversion?.method === 'custom' && (
|
|
165
181
|
<FormSection title={t('intakePolicy.section.customConversion')}>
|
|
166
182
|
<div className="grid gap-4 md:grid-cols-2">
|
|
@@ -243,6 +259,15 @@ export function IntakePolicyForm({ policy, section, onChange, readonly = false }
|
|
|
243
259
|
/>
|
|
244
260
|
</FormSection>
|
|
245
261
|
|
|
262
|
+
<FormSection title={t('intakePolicy.section.extractionModel')}>
|
|
263
|
+
<ModelConfigFields
|
|
264
|
+
title={t('intakePolicy.field.model')}
|
|
265
|
+
config={policy.extraction?.config}
|
|
266
|
+
onChange={(value) => setValue(['extraction', 'config'], value)}
|
|
267
|
+
readonly={readonly}
|
|
268
|
+
/>
|
|
269
|
+
</FormSection>
|
|
270
|
+
|
|
246
271
|
<FormSection title={t('intakePolicy.section.visionBudget')}>
|
|
247
272
|
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
|
|
248
273
|
<SelectField
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ContentObjectTypeCatalogEntry } from '@vertesia/common';
|
|
2
2
|
import { Button, errorMessage, Panel, useTheme, useToast } from '@vertesia/ui/core';
|
|
3
3
|
import { useUITranslation } from '@vertesia/ui/i18n';
|
|
4
4
|
import { useUserSession } from '@vertesia/ui/session';
|
|
@@ -7,7 +7,7 @@ import { Ajv } from 'ajv';
|
|
|
7
7
|
import { useMemo, useRef, useState } from 'react';
|
|
8
8
|
|
|
9
9
|
interface ObjectSchemaEditorProps {
|
|
10
|
-
objectType:
|
|
10
|
+
objectType: ContentObjectTypeCatalogEntry;
|
|
11
11
|
onSchemaUpdate: (jsonSchema: unknown) => void;
|
|
12
12
|
readonly?: boolean;
|
|
13
13
|
}
|