@proteos/sdk 0.33.0 → 0.35.0

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.
@@ -1,5 +1,6 @@
1
1
  import type { ProteosClient } from '../client.js'
2
2
  import type {
3
+ AcceptMistranscribedTermRequest,
3
4
  AgentListener,
4
5
  AttachContactAddressRequest,
5
6
  BlockContactRequest,
@@ -11,9 +12,11 @@ import type {
11
12
  Conversation,
12
13
  ConversationFilter,
13
14
  ConversationFilterEvent,
15
+ ConversationType,
14
16
  CreateAgentListenerRequest,
15
17
  CreateConnectionRequest,
16
18
  CreateConversationFilterRequest,
19
+ CreateConversationTypeRequest,
17
20
  CreateGlossaryTermRequest,
18
21
  CreateTranscriptionRequest,
19
22
  DispatchMeetingBotRequest,
@@ -27,8 +30,10 @@ import type {
27
30
  ListConversationFilterEventsQuery,
28
31
  ListConversationFiltersQuery,
29
32
  ListConversationsQuery,
33
+ ListConversationTypesQuery,
30
34
  ListGlossaryTermsQuery,
31
35
  ListMessagesQuery,
36
+ ListMistranscribedTermsQuery,
32
37
  ListReactionsResponse,
33
38
  ListResponse,
34
39
  ListRoomsQuery,
@@ -36,10 +41,12 @@ import type {
36
41
  MaterializeTranscriptionRequest,
37
42
  MergeContactsRequest,
38
43
  Message,
44
+ MistranscribedTerm,
39
45
  Reaction,
40
46
  RecordPermissionEventRequest,
41
47
  Room,
42
48
  SendMessageRequest,
49
+ SyncConnectionRequest,
43
50
  Transcription,
44
51
  UnreadCounts,
45
52
  UpdateAgentListenerRequest,
@@ -47,8 +54,10 @@ import type {
47
54
  UpdateContactRequest,
48
55
  UpdateConversationFilterRequest,
49
56
  UpdateConversationRequest,
57
+ UpdateConversationTypeRequest,
50
58
  UpdateDraftRequest,
51
59
  UpdateGlossaryTermRequest,
60
+ UpdateTranscriptionRequest,
52
61
  } from './types.js'
53
62
  import { type VoiceService, VoiceServiceImpl } from './voice.js'
54
63
 
@@ -75,7 +84,11 @@ export class ConversationClient {
75
84
  readonly conversationFilters: ConversationFilterService
76
85
  /** Per-org glossary: custom vocabulary that boosts transcription accuracy. */
77
86
  readonly glossaryTerms: GlossaryTermService
87
+ /** Conversation taxonomy: the types the pre-summary classifier assigns. */
88
+ readonly conversationTypes: ConversationTypeService
78
89
  readonly transcriptions: TranscriptionService
90
+ /** Review-pass findings: likely misheard terms awaiting accept/reject. */
91
+ readonly mistranscribedTerms: MistranscribedTermService
79
92
  /** Meeting bots (Ava): dispatch into a meeting URL, remove from a meeting. */
80
93
  readonly meetings: MeetingService
81
94
  /** Realtime speech-to-text (dictation) — moved here from agent-service. */
@@ -89,7 +102,9 @@ export class ConversationClient {
89
102
  this.agentListeners = new AgentListenerServiceImpl(client)
90
103
  this.conversationFilters = new ConversationFilterServiceImpl(client)
91
104
  this.glossaryTerms = new GlossaryTermServiceImpl(client)
105
+ this.conversationTypes = new ConversationTypeServiceImpl(client)
92
106
  this.transcriptions = new TranscriptionServiceImpl(client)
107
+ this.mistranscribedTerms = new MistranscribedTermServiceImpl(client)
93
108
  this.meetings = new MeetingServiceImpl(client)
94
109
  this.voice = new VoiceServiceImpl(client)
95
110
  }
@@ -152,6 +167,15 @@ export interface ConnectionService {
152
167
  delete(id: string): Promise<void>
153
168
  /** Begin the connector's install flow; open the returned URL in a popup. */
154
169
  install(id: string): Promise<InstallConnectionResponse>
170
+ /**
171
+ * Trigger a historical backfill ("sync all") on an email connection: the
172
+ * server fetches provider mail in the chosen range and ingests it through
173
+ * the normal pipeline (conversation filters, dedupe, agent listeners).
174
+ * Returns 202 with the connection already showing
175
+ * settings.sync_status='in_progress' — poll get(id) until it flips to
176
+ * done/failed. A running sync answers 409 (sync_already_running).
177
+ */
178
+ sync(id: string, request: SyncConnectionRequest): Promise<Connection>
155
179
  /** Search the contact addresses this connection can reach (org-deduped) for the compose picker. */
156
180
  listContactAddresses(
157
181
  connectionId: string,
@@ -201,6 +225,14 @@ class ConnectionServiceImpl implements ConnectionService {
201
225
  )
202
226
  }
203
227
 
228
+ sync(id: string, request: SyncConnectionRequest): Promise<Connection> {
229
+ return this.client.request(
230
+ 'POST',
231
+ `${CONVERSATION_BASE_PATH}/connections/${encodeURIComponent(id)}/sync`,
232
+ request,
233
+ )
234
+ }
235
+
204
236
  listContactAddresses(
205
237
  connectionId: string,
206
238
  query: ListContactAddressesQuery = {},
@@ -685,6 +717,70 @@ class GlossaryTermServiceImpl implements GlossaryTermService {
685
717
  }
686
718
  }
687
719
 
720
+ /**
721
+ * The org's conversation taxonomy. Before a meeting summary is generated, a
722
+ * cheap classifier reads the transcript head plus every type's `definition`
723
+ * and picks the matching `key` — the result lands on `Conversation.type_key`,
724
+ * and a type whose `config.summary_prompt_key` names an agent-service prompt
725
+ * swaps the summary system prompt for that prompt's current body. Keyed by
726
+ * `key` (not id); `upsert` is the idempotent module-deploy door.
727
+ */
728
+ export interface ConversationTypeService {
729
+ list(query?: ListConversationTypesQuery): Promise<ListResponse<ConversationType>>
730
+ get(key: string): Promise<ConversationType>
731
+ create(request: CreateConversationTypeRequest): Promise<ConversationType>
732
+ update(key: string, request: UpdateConversationTypeRequest): Promise<ConversationType>
733
+ /** Idempotent create-or-update by key (PUT) — what `pro module deploy` calls. */
734
+ upsert(key: string, request: CreateConversationTypeRequest): Promise<ConversationType>
735
+ delete(key: string): Promise<void>
736
+ }
737
+
738
+ class ConversationTypeServiceImpl implements ConversationTypeService {
739
+ constructor(private readonly client: ProteosClient) {}
740
+
741
+ list(query: ListConversationTypesQuery = {}): Promise<ListResponse<ConversationType>> {
742
+ return this.client.requestWithQuery(
743
+ 'GET',
744
+ `${CONVERSATION_BASE_PATH}/conversation-types`,
745
+ query,
746
+ )
747
+ }
748
+
749
+ get(key: string): Promise<ConversationType> {
750
+ return this.client.request(
751
+ 'GET',
752
+ `${CONVERSATION_BASE_PATH}/conversation-types/${encodeURIComponent(key)}`,
753
+ )
754
+ }
755
+
756
+ create(request: CreateConversationTypeRequest): Promise<ConversationType> {
757
+ return this.client.request('POST', `${CONVERSATION_BASE_PATH}/conversation-types`, request)
758
+ }
759
+
760
+ update(key: string, request: UpdateConversationTypeRequest): Promise<ConversationType> {
761
+ return this.client.request(
762
+ 'PATCH',
763
+ `${CONVERSATION_BASE_PATH}/conversation-types/${encodeURIComponent(key)}`,
764
+ request,
765
+ )
766
+ }
767
+
768
+ upsert(key: string, request: CreateConversationTypeRequest): Promise<ConversationType> {
769
+ return this.client.request(
770
+ 'PUT',
771
+ `${CONVERSATION_BASE_PATH}/conversation-types/${encodeURIComponent(key)}`,
772
+ { ...request, key },
773
+ )
774
+ }
775
+
776
+ async delete(key: string): Promise<void> {
777
+ await this.client.request(
778
+ 'DELETE',
779
+ `${CONVERSATION_BASE_PATH}/conversation-types/${encodeURIComponent(key)}`,
780
+ )
781
+ }
782
+ }
783
+
688
784
  /** Batch transcription of stored audio files + materialization. */
689
785
  export interface TranscriptionService {
690
786
  /**
@@ -695,6 +791,17 @@ export interface TranscriptionService {
695
791
  createFromFile(request: CreateTranscriptionRequest): Promise<Transcription>
696
792
  list(query?: ListTranscriptionsQuery): Promise<ListResponse<Transcription>>
697
793
  get(id: string): Promise<Transcription>
794
+ /**
795
+ * Edits a completed transcription (turn text, speaker labels, language).
796
+ * Does NOT retro-update messages of an already-materialized conversation.
797
+ */
798
+ update(id: string, request: UpdateTranscriptionRequest): Promise<Transcription>
799
+ /**
800
+ * Kicks off the mistranscription review pass (glossary-aware LLM hunt for
801
+ * misheard terms) — resolves immediately with review_status `processing`;
802
+ * poll `get(id)` until it flips to feedback_pending/completed/failed.
803
+ */
804
+ review(id: string): Promise<Transcription>
698
805
  /** Turns a completed transcription into an adhoc/meeting conversation. */
699
806
  materialize(id: string, request?: MaterializeTranscriptionRequest): Promise<Conversation>
700
807
  }
@@ -717,6 +824,22 @@ class TranscriptionServiceImpl implements TranscriptionService {
717
824
  )
718
825
  }
719
826
 
827
+ update(id: string, request: UpdateTranscriptionRequest): Promise<Transcription> {
828
+ return this.client.request(
829
+ 'PATCH',
830
+ `${CONVERSATION_BASE_PATH}/transcriptions/${encodeURIComponent(id)}`,
831
+ request,
832
+ )
833
+ }
834
+
835
+ review(id: string): Promise<Transcription> {
836
+ return this.client.request(
837
+ 'POST',
838
+ `${CONVERSATION_BASE_PATH}/transcriptions/${encodeURIComponent(id)}/review`,
839
+ {},
840
+ )
841
+ }
842
+
720
843
  materialize(id: string, request: MaterializeTranscriptionRequest = {}): Promise<Conversation> {
721
844
  return this.client.request(
722
845
  'POST',
@@ -726,6 +849,55 @@ class TranscriptionServiceImpl implements TranscriptionService {
726
849
  }
727
850
  }
728
851
 
852
+ /**
853
+ * Findings of the post-transcription review pass: terms likely misheard by the
854
+ * transcription provider, awaiting accept/reject. Accepting applies the
855
+ * replacement to the transcription's turns and (optionally) promotes it into
856
+ * the glossary; resolving the last open finding flips the transcription's
857
+ * review_status to completed.
858
+ */
859
+ export interface MistranscribedTermService {
860
+ list(query?: ListMistranscribedTermsQuery): Promise<ListResponse<MistranscribedTerm>>
861
+ get(id: string): Promise<MistranscribedTerm>
862
+ accept(id: string, request?: AcceptMistranscribedTermRequest): Promise<MistranscribedTerm>
863
+ reject(id: string): Promise<MistranscribedTerm>
864
+ }
865
+
866
+ class MistranscribedTermServiceImpl implements MistranscribedTermService {
867
+ constructor(private readonly client: ProteosClient) {}
868
+
869
+ list(query: ListMistranscribedTermsQuery = {}): Promise<ListResponse<MistranscribedTerm>> {
870
+ return this.client.requestWithQuery(
871
+ 'GET',
872
+ `${CONVERSATION_BASE_PATH}/mistranscribed-terms`,
873
+ query,
874
+ )
875
+ }
876
+
877
+ get(id: string): Promise<MistranscribedTerm> {
878
+ return this.client.request(
879
+ 'GET',
880
+ `${CONVERSATION_BASE_PATH}/mistranscribed-terms/${encodeURIComponent(id)}`,
881
+ )
882
+ }
883
+
884
+ accept(id: string, request: AcceptMistranscribedTermRequest = {}): Promise<MistranscribedTerm> {
885
+ return this.client.request(
886
+ 'POST',
887
+ `${CONVERSATION_BASE_PATH}/mistranscribed-terms/${encodeURIComponent(id)}/accept`,
888
+ request,
889
+ )
890
+ }
891
+
892
+ reject(id: string): Promise<MistranscribedTerm> {
893
+ return this.client.request(
894
+ 'POST',
895
+ `${CONVERSATION_BASE_PATH}/mistranscribed-terms/${encodeURIComponent(id)}/reject`,
896
+ {},
897
+ )
898
+ }
899
+ }
900
+
729
901
  export type * from './types.js'
730
902
  // Re-export voice types (the live dictation stream lives on this service now)
731
903
  export type {
@@ -176,6 +176,20 @@ export interface ReactionCapability {
176
176
  max_per_actor: number
177
177
  }
178
178
 
179
+ /** How far back a historical backfill ("sync all") reaches. */
180
+ export type ConnectionSyncRange = '30d' | '90d' | '365d' | 'all'
181
+
182
+ /**
183
+ * Lifecycle of a historical backfill, carried as settings.sync_status.
184
+ * Absent key = 'none' (never synced). 'in_progress' is claimed before the
185
+ * trigger request returns — poll get(id) until it flips to done/failed.
186
+ */
187
+ export type ConnectionSyncStatus = 'none' | 'in_progress' | 'done' | 'failed'
188
+
189
+ export interface SyncConnectionRequest {
190
+ range: ConnectionSyncRange
191
+ }
192
+
179
193
  export interface Connection {
180
194
  id: string
181
195
  org_id: string
@@ -187,6 +201,11 @@ export interface Connection {
187
201
  owner?: UserRef
188
202
  external_account_id: string
189
203
  credentials: ConnectionCredentials
204
+ /**
205
+ * Connector-shaped configuration + state. A historical backfill writes the
206
+ * sync_* keys here: sync_status (ConnectionSyncStatus), sync_range,
207
+ * sync_started_at, sync_completed_at, sync_synced_count, sync_error.
208
+ */
190
209
  settings: Record<string, unknown>
191
210
  status: ConnectionStatus
192
211
  /** Computed on read: the connector implements the reaction capability. */
@@ -227,6 +246,11 @@ export interface Conversation {
227
246
  * claim server-side and gives clients an elapsed time to show.
228
247
  */
229
248
  summary_started_at?: string
249
+ /**
250
+ * ConversationType key the pre-summary classifier assigned; absent when never
251
+ * classified or no type matched.
252
+ */
253
+ type_key?: string
230
254
  status: ConversationStatus
231
255
  /**
232
256
  * Room directory row a room-borne thread (Slack channel conversation) lives
@@ -559,6 +583,61 @@ export interface ListGlossaryTermsQuery extends PaginationQuery {
559
583
  search?: string
560
584
  }
561
585
 
586
+ /** Optional per-type behavior configuration. */
587
+ export interface ConversationTypeConfig {
588
+ /**
589
+ * Agent-service prompt key whose CURRENT version body replaces the built-in
590
+ * summary system prompt for conversations classified as this type. Absent ⇒
591
+ * built-in prompt.
592
+ */
593
+ summary_prompt_key?: string
594
+ }
595
+
596
+ /**
597
+ * A per-org conversation taxonomy entry. Before a meeting summary is
598
+ * generated, a cheap classifier reads the transcript head plus every type's
599
+ * `definition` and picks the matching `key` (or none); the result lands on
600
+ * `Conversation.type_key` and `config.summary_prompt_key` may swap the summary
601
+ * system prompt. Module-deployable (conversation-types/<key>.json).
602
+ */
603
+ export interface ConversationType {
604
+ org_id: string
605
+ /** Immutable identity within the org — what the classifier answers with. */
606
+ key: string
607
+ name?: string
608
+ /** When a conversation IS this type — injected verbatim into the classifier prompt. */
609
+ definition: string
610
+ config: ConversationTypeConfig
611
+ /** Module that deployed the type; absent when not module-owned. */
612
+ module_slug?: string
613
+ created_at: string
614
+ created_by: UserRef
615
+ updated_at: string
616
+ updated_by: UserRef
617
+ }
618
+
619
+ export interface CreateConversationTypeRequest {
620
+ /** Lowercase kebab/snake/camel handle — no spaces. */
621
+ key: string
622
+ name?: string
623
+ definition: string
624
+ config?: ConversationTypeConfig
625
+ module_slug?: string
626
+ }
627
+
628
+ export interface UpdateConversationTypeRequest {
629
+ name?: string
630
+ definition?: string
631
+ /** Replaces the stored config wholesale; send `{}` to clear the prompt link. */
632
+ config?: ConversationTypeConfig
633
+ }
634
+
635
+ export interface ListConversationTypesQuery extends PaginationQuery {
636
+ /** Case-insensitive substring match on key or name. */
637
+ search?: string
638
+ module_slug?: string
639
+ }
640
+
562
641
  export interface PaginationQuery {
563
642
  page?: number
564
643
  page_size?: number
@@ -974,6 +1053,18 @@ export interface ListResponse<T> {
974
1053
 
975
1054
  export type TranscriptionStatus = 'pending' | 'processing' | 'completed' | 'failed'
976
1055
 
1056
+ /**
1057
+ * Post-transcription review lifecycle — the glossary-aware LLM pass that hunts
1058
+ * mistranscribed terms. `feedback_pending` means proposed mistranscribed terms
1059
+ * await human resolution; resolving the last one flips it to `completed`.
1060
+ */
1061
+ export type TranscriptionReviewStatus =
1062
+ | 'open'
1063
+ | 'processing'
1064
+ | 'feedback_pending'
1065
+ | 'completed'
1066
+ | 'failed'
1067
+
977
1068
  export interface TranscriptTurn {
978
1069
  speaker: number
979
1070
  speaker_label: string
@@ -990,6 +1081,8 @@ export interface Transcription {
990
1081
  audio_file_id: string
991
1082
  transcript_file_id: string
992
1083
  status: TranscriptionStatus
1084
+ review_status: TranscriptionReviewStatus
1085
+ review_started_at?: string
993
1086
  language: string
994
1087
  duration_seconds: number
995
1088
  model: string
@@ -1012,6 +1105,18 @@ export interface CreateTranscriptionRequest {
1012
1105
  is_diarized?: boolean
1013
1106
  }
1014
1107
 
1108
+ /**
1109
+ * Edits a COMPLETED transcription in place. `turns`, when present, replace the
1110
+ * diarized turns wholesale; `speaker_labels` maps diarized speaker indexes
1111
+ * ("0", "1", …) to display labels and is applied across all turns. Editing
1112
+ * does NOT retro-update messages of an already-materialized conversation.
1113
+ */
1114
+ export interface UpdateTranscriptionRequest {
1115
+ turns?: TranscriptTurn[]
1116
+ speaker_labels?: Record<string, string>
1117
+ language?: string
1118
+ }
1119
+
1015
1120
  /** Channel defaults to adhoc; meeting is the only other allowed target. */
1016
1121
  export interface MaterializeTranscriptionRequest {
1017
1122
  channel?: Channel
@@ -1028,6 +1133,63 @@ export interface ListTranscriptionsQuery extends PaginationQuery {
1028
1133
  provider_request_id?: string
1029
1134
  }
1030
1135
 
1136
+ export type MistranscribedTermStatus = 'auto_replaced' | 'proposed' | 'accepted' | 'rejected'
1137
+
1138
+ export type MistranscriptionSuggestionSource = 'glossary' | 'model'
1139
+
1140
+ /**
1141
+ * One term the post-transcription review pass judged likely misheard. High-
1142
+ * confidence findings are auto-replaced; the rest await accept/reject. An
1143
+ * accepted row referencing the glossary term it became (glossary_term_id) is a
1144
+ * known "misheard → term" mapping future passes replace deterministically.
1145
+ */
1146
+ export interface MistranscribedTerm {
1147
+ id: string
1148
+ org_id: string
1149
+ transcription_id: string
1150
+ conversation_id?: string
1151
+ /** The text as transcribed. */
1152
+ term: string
1153
+ context_snippet?: string
1154
+ /** Indexes into the transcription's turns where the term occurs. */
1155
+ turn_indexes: number[]
1156
+ suggested_replacement?: string
1157
+ suggestion_source?: MistranscriptionSuggestionSource
1158
+ glossary_term_id?: string
1159
+ /** Confidence (0..1) the term IS mistranscribed. */
1160
+ misheard_confidence: number
1161
+ /** Confidence (0..1) the suggested replacement fits. */
1162
+ replacement_confidence: number
1163
+ status: MistranscribedTermStatus
1164
+ /** The text actually written into the transcript, once applied. */
1165
+ applied_replacement?: string
1166
+ resolved_by?: UserRef
1167
+ resolved_at?: string
1168
+ created_at: string
1169
+ created_by: UserRef
1170
+ updated_at: string
1171
+ updated_by: UserRef
1172
+ }
1173
+
1174
+ /**
1175
+ * Accepts a proposed finding: `replacement` overrides the reviewer's
1176
+ * suggestion when set; `create_glossary_term` promotes the applied replacement
1177
+ * into the org glossary and links the finding to it.
1178
+ */
1179
+ export interface AcceptMistranscribedTermRequest {
1180
+ replacement?: string
1181
+ create_glossary_term?: {
1182
+ definition?: string
1183
+ priority?: number
1184
+ }
1185
+ }
1186
+
1187
+ export interface ListMistranscribedTermsQuery extends PaginationQuery {
1188
+ status?: MistranscribedTermStatus
1189
+ transcription_id?: string
1190
+ conversation_id?: string
1191
+ }
1192
+
1031
1193
  /**
1032
1194
  * Sends a meeting bot (Ava) into a meeting through an active meeting
1033
1195
  * connection (adhoc-meeting). join_at schedules the bot ahead of time (ISO
package/src/index.ts CHANGED
@@ -182,6 +182,7 @@ export type {
182
182
  export { ConnectorClient } from './connector/index.js'
183
183
  // Conversation types (conversation-service: connections, conversations, messages, listeners)
184
184
  export type {
185
+ AcceptMistranscribedTermRequest,
185
186
  AddressFilterConfig,
186
187
  AgentListener,
187
188
  AgentListenerService,
@@ -198,6 +199,8 @@ export type {
198
199
  ConnectionScope,
199
200
  ConnectionService,
200
201
  ConnectionStatus,
202
+ ConnectionSyncRange,
203
+ ConnectionSyncStatus,
201
204
  ConnectorKey,
202
205
  ConnectorProvider,
203
206
  ConsentStatus,
@@ -224,9 +227,13 @@ export type {
224
227
  ConversationService,
225
228
  ConversationStatus,
226
229
  ConversationSummaryStatus,
230
+ ConversationType,
231
+ ConversationTypeConfig,
232
+ ConversationTypeService,
227
233
  CreateAgentListenerRequest,
228
234
  CreateConnectionRequest,
229
235
  CreateConversationFilterRequest,
236
+ CreateConversationTypeRequest,
230
237
  CreateGlossaryTermRequest,
231
238
  DispatchMeetingBotRequest,
232
239
  DomainFilterConfig,
@@ -244,8 +251,10 @@ export type {
244
251
  ListConversationFilterEventsQuery,
245
252
  ListConversationFiltersQuery,
246
253
  ListConversationsQuery,
254
+ ListConversationTypesQuery,
247
255
  ListGlossaryTermsQuery,
248
256
  ListMessagesQuery,
257
+ ListMistranscribedTermsQuery,
249
258
  // Conversation-local page envelope ({meta, data}) — distinct from the
250
259
  // PageIterator-based ListResult used by the other modules.
251
260
  ListResponse,
@@ -259,6 +268,10 @@ export type {
259
268
  MessageRecipient,
260
269
  MessageService,
261
270
  MessageStatus,
271
+ MistranscribedTerm,
272
+ MistranscribedTermService,
273
+ MistranscribedTermStatus,
274
+ MistranscriptionSuggestionSource,
262
275
  PermissionEventSource,
263
276
  PermissionEventType,
264
277
  Reaction,
@@ -272,13 +285,19 @@ export type {
272
285
  Room,
273
286
  SendMessageRequest,
274
287
  SendRecipient,
288
+ SyncConnectionRequest,
275
289
  TranscribeStreamOptions,
290
+ Transcription,
291
+ TranscriptionReviewStatus,
292
+ TranscriptionStatus,
276
293
  TranscriptResult,
294
+ TranscriptTurn,
277
295
  UnreadCounts,
278
296
  UpdateAgentListenerRequest,
279
297
  UpdateConnectionRequest,
280
298
  UpdateContactRequest,
281
299
  UpdateConversationFilterRequest,
300
+ UpdateConversationTypeRequest,
282
301
  UpdateDraftRequest,
283
302
  UpdateGlossaryTermRequest,
284
303
  VoiceService,
@@ -422,6 +441,8 @@ export type {
422
441
  // App types
423
442
  App,
424
443
  AppService,
444
+ // Array/enum attribute metas (config_schema renderers narrow on these)
445
+ ArrayAttributeMeta,
425
446
  Attribute,
426
447
  AttributeType,
427
448
  Column,
@@ -451,6 +472,8 @@ export type {
451
472
  Entity,
452
473
  EntityService,
453
474
  EntityWithSchema,
475
+ EnumAttributeMeta,
476
+ EnumValue,
454
477
  // File attribute meta
455
478
  FileAttributeMeta,
456
479
  FilterElement,
@@ -614,6 +637,7 @@ export type {
614
637
  ListWorkflowsOptions,
615
638
  ManualTriggerParams,
616
639
  MessageTriggerParams,
640
+ ModelCallParams,
617
641
  NodeDescriptor,
618
642
  NodeExecution,
619
643
  NodeGroup,
@@ -631,6 +655,7 @@ export type {
631
655
  PropertyOption,
632
656
  PropertyType,
633
657
  RunWorkflowRequest,
658
+ SystemSource,
634
659
  TestNodeCandidate,
635
660
  TestNodeInputSource,
636
661
  TestNodeRequest,
@@ -15,6 +15,7 @@ export const LayoutElementType = {
15
15
  Tabs: 'tabs',
16
16
  Field: 'field',
17
17
  RelatedList: 'related_list',
18
+ RelatedRecord: 'related_record',
18
19
  Component: 'component',
19
20
  Divider: 'divider',
20
21
  Text: 'text',
@@ -96,6 +97,31 @@ export type RelatedListElement = CommonProps & {
96
97
  follows_parent_edit_mode?: boolean
97
98
  }
98
99
 
100
+ /**
101
+ * Renders the FIRST record of `related_entity_slug` that references the
102
+ * current record through the `via_attribute` relation attribute — the
103
+ * singular counterpart of `related_list`, addressing the relation the same
104
+ * inbound way. The match is taken oldest-first so the choice is stable.
105
+ *
106
+ * `page_slug` optionally pins which record page supplies the layout; when
107
+ * omitted (or dangling) the renderer falls back to the related entity's
108
+ * default record page. The element renders that page bare — wrap it in a
109
+ * `section` element for a title or collapse affordance.
110
+ */
111
+ export type RelatedRecordElement = CommonProps & {
112
+ type: 'related_record'
113
+ related_entity_slug: string
114
+ via_attribute: string
115
+ page_slug?: string
116
+ /**
117
+ * When absent or true, the element enters edit mode together with the host
118
+ * page's edit mode. False keeps it independent — editable only through its
119
+ * own hover control. Either way the element saves the related record
120
+ * itself; the host page's Save never covers it.
121
+ */
122
+ follows_parent_edit_mode?: boolean
123
+ }
124
+
99
125
  export type ComponentElement = CommonProps & {
100
126
  type: 'component'
101
127
  component_slug: string
@@ -127,6 +153,7 @@ export type LayoutElement =
127
153
  | TabsElement
128
154
  | FieldElement
129
155
  | RelatedListElement
156
+ | RelatedRecordElement
130
157
  | ComponentElement
131
158
  | DividerElement
132
159
  | TextElement
@@ -196,6 +223,14 @@ export const LayoutElementSchema: z.ZodType<LayoutElement> = z.lazy(() =>
196
223
  list_slug: z.string().min(1).optional(),
197
224
  follows_parent_edit_mode: z.boolean().optional(),
198
225
  }),
226
+ z.object({
227
+ type: z.literal('related_record'),
228
+ ...commonPropsShape,
229
+ related_entity_slug: z.string().min(1),
230
+ via_attribute: z.string().min(1),
231
+ page_slug: z.string().min(1).optional(),
232
+ follows_parent_edit_mode: z.boolean().optional(),
233
+ }),
199
234
  z.object({
200
235
  type: z.literal('component'),
201
236
  ...commonPropsShape,
@@ -26,6 +26,7 @@ export {
26
26
  LayoutElementType,
27
27
  type LayoutTab,
28
28
  type RelatedListElement,
29
+ type RelatedRecordElement,
29
30
  type RowElement,
30
31
  type SectionElement,
31
32
  type TabsElement,
package/src/meta/types.ts CHANGED
@@ -782,6 +782,8 @@ export interface List extends AuditFields {
782
782
  name: string
783
783
  entity_slug: string
784
784
  columns: Column[]
785
+ /** Record page to open from this list; empty/absent = org default for the entity. */
786
+ default_page_slug?: string
785
787
  sorting: SortConfig[]
786
788
  filters: FilterGroup[]
787
789
  }
@@ -803,6 +805,7 @@ export const ListSchema = AuditFieldsSchema.extend({
803
805
  name: z.string(),
804
806
  entity_slug: z.string(),
805
807
  columns: z.array(ColumnSchema),
808
+ default_page_slug: z.string().optional(),
806
809
  sorting: z.array(SortConfigSchema),
807
810
  filters: z.array(FilterGroupSchema),
808
811
  })
@@ -826,6 +829,7 @@ export interface CreateListRequest {
826
829
  entity_slug: string
827
830
  name: string
828
831
  columns: Column[]
832
+ default_page_slug?: string
829
833
  sorting: SortConfig[]
830
834
  filters: FilterGroup[]
831
835
  }
@@ -837,6 +841,8 @@ export interface UpdateListRequest {
837
841
  name?: string
838
842
  module_slug?: string
839
843
  columns?: Column[]
844
+ /** Set to '' to clear back to the org default. */
845
+ default_page_slug?: string
840
846
  sorting?: SortConfig[]
841
847
  filters?: FilterGroup[]
842
848
  }