@proteos/sdk 0.57.1 → 0.59.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.
@@ -3,6 +3,7 @@
3
3
  * mirror of packages/go/model/conversation (snake_case, Is-prefixed booleans).
4
4
  */
5
5
 
6
+ import type { PrincipalRef } from '../meta/types.js'
6
7
  import type { FileRef } from '../types/common.js'
7
8
 
8
9
  /**
@@ -862,6 +863,579 @@ export interface ListConversationTypesQuery extends PaginationQuery {
862
863
  module_slug?: string
863
864
  }
864
865
 
866
+ // ---------------------------------------------------------------------------
867
+ // Conversation intelligence — tag definitions, tag sets, tags, evaluations.
868
+ // Hand-maintained mirror of conversation-tag-*.go + api/conversation-tag-*.go.
869
+ // ---------------------------------------------------------------------------
870
+
871
+ /**
872
+ * Discriminates a tag definition's question (1:1 with the agent-service
873
+ * decision question types): boolean ("is this present?"), choice ("which
874
+ * one?"), score ("where on this ordered scale?").
875
+ */
876
+ export type ConversationTagQuestionType = 'boolean' | 'choice' | 'score'
877
+
878
+ /**
879
+ * What a tag anchors to: the whole conversation (one value per conversation),
880
+ * one message (a verdict per message), or a span (character ranges inside a
881
+ * message, or message-to-message ranges). `span` REQUIRES evaluator kind
882
+ * `reason` — only a reasoning generation can localise verbatim quotes.
883
+ */
884
+ export type ConversationTagScope = 'conversation' | 'message' | 'span'
885
+
886
+ /**
887
+ * The model path a definition is judged by: `decide` (agent-service's
888
+ * calibrated typed decision — probabilities, no prose, no localisation) or
889
+ * `reason` (a schema-constrained generation that returns rationale + verbatim
890
+ * anchors; required for span scope).
891
+ */
892
+ export type ConversationTagEvaluatorKind = 'decide' | 'reason'
893
+
894
+ /**
895
+ * WHY an evaluation ran: `realtime` (the debounced window after new messages),
896
+ * `completion` (the conversation completed — final transcript + summary for
897
+ * spoken media, End for the rest) or `manual` (`conversationTags.evaluate`).
898
+ * A tag set's `evaluate_on` lists the automatic ones it subscribes to; manual
899
+ * ignores it.
900
+ */
901
+ export type ConversationTagTrigger = 'realtime' | 'completion' | 'manual'
902
+
903
+ /** Who produced a tag: the evaluator (`model`) or a person (`user`). */
904
+ export type ConversationTagSource = 'model' | 'user'
905
+
906
+ /**
907
+ * `pending` = a provisional realtime observation on a spoken-medium
908
+ * conversation that is still live (a completion evaluation will replace it);
909
+ * `active` = current; `dismissed` = a person rejected it — kept as feedback
910
+ * and never re-created by a later evaluation.
911
+ */
912
+ export type ConversationTagStatus = 'pending' | 'active' | 'dismissed'
913
+
914
+ /**
915
+ * One evaluation's lifecycle: `pending` (a manual evaluation was accepted and
916
+ * not yet started), `running`, `done`, `failed`.
917
+ */
918
+ export type ConversationTagEvaluationStatus = 'pending' | 'running' | 'done' | 'failed'
919
+
920
+ /**
921
+ * Spells out what makes a boolean question hold (`true`) or not (`false`)
922
+ * when the boundary is subtle.
923
+ */
924
+ export interface BooleanTagCriteria {
925
+ true: string
926
+ false: string
927
+ }
928
+
929
+ /**
930
+ * Populated per question type: boolean → `boolean` (optional, both
931
+ * descriptions or neither), choice → `choice` (option key → description,
932
+ * ≥ 2), score → `score` (ordered level descriptions, ≥ 2; the tag value is
933
+ * the 0-based level index). Descriptions are handed to the evaluator
934
+ * verbatim, so they should describe the observable content, not internal
935
+ * process.
936
+ */
937
+ export interface ConversationTagCriteria {
938
+ boolean?: BooleanTagCriteria
939
+ choice?: Record<string, string>
940
+ score?: string[]
941
+ }
942
+
943
+ /**
944
+ * The decide-shaped question a definition asks over a conversation — the
945
+ * same three types as the agent-service decision question, with criteria in
946
+ * an explicit struct so manifests and SDKs need no tagged-union decoder.
947
+ * `type` is immutable after create: tag values are typed by it.
948
+ */
949
+ export interface ConversationTagQuestion {
950
+ type: ConversationTagQuestionType
951
+ instructions: string
952
+ /** Always on the wire; pass `{}` for a boolean question without descriptions. */
953
+ criteria: ConversationTagCriteria
954
+ }
955
+
956
+ /**
957
+ * Makes a `reason` definition ask the cheap decide model first whether it
958
+ * applies anywhere in the judged window (its own boolean question, one answer
959
+ * per window); only a definition that passes (probability ≥ `min_probability`)
960
+ * reaches the reasoning call, which is skipped entirely when nothing passes. A
961
+ * screened-out definition's stale tags are still replaced. Boolean questions
962
+ * only — choice and score always have an answer. Omitted = screening on with
963
+ * the service default threshold (0.3, `CONVERSATION_TAG_SCREENING_MIN_PROBABILITY`);
964
+ * `{ is_enabled: false }` turns it off. The service rejects `screening` on a
965
+ * `decide` evaluator or on a choice/score question, and a `min_probability`
966
+ * outside 0..1.
967
+ */
968
+ export interface ConversationTagScreening {
969
+ is_enabled: boolean
970
+ /** Pass threshold in 0..1; absent = the service default. */
971
+ min_probability?: number
972
+ }
973
+
974
+ /**
975
+ * Selects the model path. `model_id` is only meaningful for `reason` (absent =
976
+ * the service default); `decide` always uses the service-wide decision model.
977
+ * `screening` is only meaningful for `reason` with a boolean question; absent =
978
+ * screening on with the service default threshold.
979
+ */
980
+ export interface ConversationTagEvaluator {
981
+ kind: ConversationTagEvaluatorKind
982
+ model_id?: string
983
+ screening?: ConversationTagScreening
984
+ }
985
+
986
+ /**
987
+ * Describes WHAT to look for in a conversation: one typed question plus where
988
+ * its answers anchor (`scope`) and which model path answers it (`evaluator`).
989
+ * WHERE and WHEN a definition is evaluated is a ConversationTagSet's business
990
+ * — a definition that no enabled set references is never evaluated. Keyed
991
+ * (org_id, key) and module-deployable (conversation-tag-definitions/<key>.json),
992
+ * like conversation types.
993
+ */
994
+ export interface ConversationTagDefinition {
995
+ org_id: string
996
+ /** Immutable identity within the org — the value every tag carries as `definition_key`. */
997
+ key: string
998
+ name: string
999
+ /** Free grouping for UIs ("content", "sentiment", "signal"). */
1000
+ group?: string
1001
+ question: ConversationTagQuestion
1002
+ /** `span` requires `evaluator.kind = 'reason'`. */
1003
+ scope: ConversationTagScope
1004
+ evaluator: ConversationTagEvaluator
1005
+ /**
1006
+ * Narrows message/span-scope judging to messages of these directions (e.g.
1007
+ * inbound only for customer sentiment); empty = all. Ignored for
1008
+ * conversation scope.
1009
+ */
1010
+ directions: MessageDirection[]
1011
+ /**
1012
+ * Observations below it are dropped, for every question type — an absent
1013
+ * tag means "not determined", not "false".
1014
+ */
1015
+ min_confidence: number
1016
+ /** Optional UI hint (any CSS color token the client accepts). */
1017
+ color?: string
1018
+ is_enabled: boolean
1019
+ /** Module that deployed the definition; absent when not module-owned. */
1020
+ module_slug?: string
1021
+ created_at: string
1022
+ created_by: UserRef
1023
+ updated_at: string
1024
+ updated_by: UserRef
1025
+ }
1026
+
1027
+ /**
1028
+ * Describes WHERE and WHEN a group of tag definitions is evaluated. A
1029
+ * conversation matches a set when every NON-EMPTY selector contains it:
1030
+ * `channels`, `connection_ids`, `type_keys` (an empty conversation `type_key`
1031
+ * never matches a type-scoped set, so type-scoped sets only apply once a
1032
+ * conversation has been classified). At least one of those three must be
1033
+ * non-empty — tagging is opt-in, never org-wide by accident. `directions` is
1034
+ * a message-level filter on top (which messages arm / are judged), not a
1035
+ * conversation selector. Matched sets union their definition keys; timing
1036
+ * takes the minimum quiet window and max wait over the matched sets that
1037
+ * evaluate in realtime. Keyed (org_id, key) and module-deployable
1038
+ * (conversation-tag-sets/<key>.json).
1039
+ */
1040
+ export interface ConversationTagSet {
1041
+ org_id: string
1042
+ key: string
1043
+ name: string
1044
+ /**
1045
+ * The definitions this set evaluates (≥ 1; each must exist in the org at
1046
+ * write time — a definition deleted later is skipped).
1047
+ */
1048
+ definition_keys: string[]
1049
+ channels: Channel[]
1050
+ connection_ids: string[]
1051
+ type_keys: string[]
1052
+ /**
1053
+ * Narrows which NEW messages arm a realtime evaluation (and which messages a
1054
+ * completion evaluation judges for message/span scope): inbound only,
1055
+ * outbound only, or both when empty. A definition's own `directions`
1056
+ * narrows further.
1057
+ */
1058
+ directions: MessageDirection[]
1059
+ /**
1060
+ * A non-empty subset of [realtime, completion]: realtime = the debounced
1061
+ * window after new messages arrive; completion = once the conversation
1062
+ * completed (final transcript + summary for spoken media, End for the
1063
+ * rest). Manual evaluations ignore it.
1064
+ */
1065
+ evaluate_on: ConversationTagTrigger[]
1066
+ /** A realtime evaluation fires this long after the LAST new message (default 90, min 5). */
1067
+ quiet_window_seconds: number
1068
+ /**
1069
+ * Bounds the wait since the FIRST unjudged message (default 600, ≥ quiet
1070
+ * window) so a busy stream is still tagged periodically.
1071
+ */
1072
+ max_wait_seconds: number
1073
+ is_enabled: boolean
1074
+ /** Module that deployed the set; absent when not module-owned. */
1075
+ module_slug?: string
1076
+ created_at: string
1077
+ created_by: UserRef
1078
+ updated_at: string
1079
+ updated_by: UserRef
1080
+ }
1081
+
1082
+ /**
1083
+ * One observation a definition made on a conversation.
1084
+ *
1085
+ * Anchors encode the definition's scope: conversation scope = every anchor
1086
+ * field absent; message scope = `start_message_id === end_message_id` with no
1087
+ * offsets; span = offsets set (a span may cross messages, then start/end
1088
+ * message ids differ). Offsets are UNICODE CODE POINT indexes into the
1089
+ * message's first text content block, end exclusive — JS strings are UTF-16,
1090
+ * so convert (e.g. via `Array.from(text)` / code-point iteration) before
1091
+ * slicing. `quote` (and `end_quote` for a span that continues into a later
1092
+ * message) is the verbatim text at the anchor: the reason evaluator emits
1093
+ * quotes because a model cannot count characters, the service resolves them
1094
+ * to offsets, and afterwards the quote is the evidence that survives a message
1095
+ * row change (the final transcript swaps realtime message ids; a term
1096
+ * correction edits text) — a tag whose anchors no longer resolve still shows
1097
+ * its quote and can be re-anchored by search.
1098
+ *
1099
+ * `value` is normalized per question type: `"true"` for a boolean (false is
1100
+ * never stored), the option key for a choice, the 0-based level index AS A
1101
+ * STRING for a score. Status `pending` marks a provisional realtime
1102
+ * observation on a live spoken-medium conversation; a completion evaluation
1103
+ * replaces it. Dismissed tags are feedback: a later evaluation never
1104
+ * re-creates a tag matching a dismissed one on (definition_key, value,
1105
+ * start/end message).
1106
+ */
1107
+ export interface ConversationTag {
1108
+ id: string
1109
+ org_id: string
1110
+ conversation_id: string
1111
+ /**
1112
+ * Denormalized from the conversation so org-wide lists and counts group by
1113
+ * channel without a join.
1114
+ */
1115
+ channel: Channel
1116
+ definition_key: string
1117
+ /** `"true"` | choice option key | score level index as a string. */
1118
+ value: string
1119
+ confidence: number
1120
+ /**
1121
+ * The raw decide distribution (`{"true":p,"false":1-p}`, option → p, or
1122
+ * `{"levels":[…],"mean":x}`); a screened reason tag carries
1123
+ * `{"screening":p}` instead; absent for unscreened reason and user tags.
1124
+ */
1125
+ probabilities?: Record<string, unknown>
1126
+ /** The reason evaluator's one-line rationale; absent for decide. */
1127
+ why?: string
1128
+ source: ConversationTagSource
1129
+ status: ConversationTagStatus
1130
+ /** Links a model tag to the evaluation that produced it. */
1131
+ evaluation_id?: string
1132
+ start_message_id?: string
1133
+ /** Unicode code-point index, inclusive. */
1134
+ start_offset?: number
1135
+ end_message_id?: string
1136
+ /** Unicode code-point index, exclusive. */
1137
+ end_offset?: number
1138
+ start_occurred_at?: string
1139
+ end_occurred_at?: string
1140
+ quote?: string
1141
+ end_quote?: string
1142
+ /**
1143
+ * When the observed thing happened (the end message's `occurred_at`, else
1144
+ * the last message's) — the time-series axis.
1145
+ */
1146
+ observed_at: string
1147
+ created_at: string
1148
+ created_by: UserRef
1149
+ updated_at: string
1150
+ updated_by: UserRef
1151
+ }
1152
+
1153
+ /**
1154
+ * One row of `conversationTags.counts`: the grouped columns that were
1155
+ * requested plus the count.
1156
+ */
1157
+ export interface ConversationTagCount {
1158
+ definition_key?: string
1159
+ value?: string
1160
+ channel?: Channel
1161
+ count: number
1162
+ }
1163
+
1164
+ /**
1165
+ * The model spend of one evaluation, summed over its decide and generate
1166
+ * calls.
1167
+ */
1168
+ export interface ConversationTagUsage {
1169
+ input_tokens: number
1170
+ output_tokens: number
1171
+ request_count: number
1172
+ }
1173
+
1174
+ /**
1175
+ * One screening verdict of an evaluation: the decide probability that the
1176
+ * definition applies in the judged window and whether that passed the
1177
+ * threshold. `probability` is absent (and `error` set) when the screening
1178
+ * could not be asked — it then passes, fail-open.
1179
+ */
1180
+ export interface ConversationTagScreeningResult {
1181
+ definition_key: string
1182
+ probability?: number
1183
+ is_passed: boolean
1184
+ error?: string
1185
+ }
1186
+
1187
+ /**
1188
+ * Records one time the tagger ran on a conversation: why (`trigger`), what it
1189
+ * judged (`definition_keys`, the message window), with which models, what it
1190
+ * cost, and how it ended. Model tags reference it through `evaluation_id`
1191
+ * (provenance), the UI polls it after a manual evaluate (202), and the latest
1192
+ * done realtime/completion evaluation's `window_end_occurred_at` is the
1193
+ * watermark the next realtime window starts after.
1194
+ *
1195
+ * Window = the messages JUDGED by this evaluation. realtime: everything after
1196
+ * the previous watermark (older messages ride along as context only);
1197
+ * completion/manual: all messages. Lifecycle: manual creates `pending` on the
1198
+ * 202 then flips to `running`; realtime/completion create `running` directly;
1199
+ * ends `done` or `failed`.
1200
+ */
1201
+ export interface ConversationTagEvaluation {
1202
+ id: string
1203
+ org_id: string
1204
+ conversation_id: string
1205
+ trigger: ConversationTagTrigger
1206
+ status: ConversationTagEvaluationStatus
1207
+ definition_keys: string[]
1208
+ model_ids: string[]
1209
+ usage: ConversationTagUsage
1210
+ /**
1211
+ * The rendered state exceeded the cap and older context (or, for a huge
1212
+ * window, the newest window messages — deferred to the next evaluation) was
1213
+ * dropped.
1214
+ */
1215
+ is_truncated: boolean
1216
+ window_start_occurred_at?: string
1217
+ window_end_occurred_at?: string
1218
+ window_message_ids: string[]
1219
+ /**
1220
+ * Every screened reason definition's verdict, so "why was nothing tagged"
1221
+ * is answerable and thresholds can be tuned. Empty when no definition was
1222
+ * screened.
1223
+ */
1224
+ screenings: ConversationTagScreeningResult[]
1225
+ error?: string
1226
+ started_at: string
1227
+ finished_at?: string
1228
+ created_at: string
1229
+ created_by: UserRef
1230
+ updated_at: string
1231
+ updated_by: UserRef
1232
+ }
1233
+
1234
+ /**
1235
+ * The `conversation.tagged` event payload: the conversation, the evaluation
1236
+ * that landed, and every tag it produced (status pending or active — the
1237
+ * event header carries the status so a consumer can filter provisional
1238
+ * realtime observations without decoding).
1239
+ */
1240
+ export interface ConversationTaggedEvent {
1241
+ conversation: Conversation
1242
+ evaluation: ConversationTagEvaluation
1243
+ tags: ConversationTag[]
1244
+ }
1245
+
1246
+ /**
1247
+ * Creates one tag definition. `key` is the immutable identity (kebab/snake,
1248
+ * like every platform key); `question` is the decide-shaped question the
1249
+ * evaluator answers; `scope` and `evaluator` say where answers anchor and
1250
+ * which model path answers (`span` requires `evaluator.kind = 'reason'`).
1251
+ * `is_enabled` omitted ⇒ true (manifests may omit it). `module_slug` is
1252
+ * stamped by `pro module deploy` — interactive callers leave it empty.
1253
+ */
1254
+ export interface CreateConversationTagDefinitionRequest {
1255
+ key: string
1256
+ name?: string
1257
+ group?: string
1258
+ question: ConversationTagQuestion
1259
+ scope: ConversationTagScope
1260
+ evaluator: ConversationTagEvaluator
1261
+ directions?: MessageDirection[]
1262
+ /** 0..1 */
1263
+ min_confidence?: number
1264
+ color?: string
1265
+ is_enabled?: boolean
1266
+ module_slug?: string
1267
+ }
1268
+
1269
+ /**
1270
+ * Partial update — an omitted field leaves the stored value untouched.
1271
+ * `question`, when present, replaces the stored question wholesale but must
1272
+ * keep its `type` (400 `conversation_tag_question_type_immutable` otherwise).
1273
+ * `directions` replaces wholesale. `key` is immutable.
1274
+ */
1275
+ export interface UpdateConversationTagDefinitionRequest {
1276
+ name?: string
1277
+ group?: string
1278
+ question?: ConversationTagQuestion
1279
+ scope?: ConversationTagScope
1280
+ evaluator?: ConversationTagEvaluator
1281
+ directions?: MessageDirection[]
1282
+ /** 0..1 */
1283
+ min_confidence?: number
1284
+ color?: string
1285
+ is_enabled?: boolean
1286
+ }
1287
+
1288
+ export interface ListConversationTagDefinitionsQuery extends PaginationQuery {
1289
+ /** Definitions deployed by one module — the `pro module` remote-state discovery door. */
1290
+ module_slug?: string
1291
+ /** Exact filter. */
1292
+ group?: string
1293
+ /** Exact filter. */
1294
+ scope?: string
1295
+ is_enabled?: boolean
1296
+ /** Case-insensitive substring match on key or name. */
1297
+ search?: string
1298
+ }
1299
+
1300
+ /**
1301
+ * Creates one tag set: which definitions to evaluate (`definition_keys`, ≥ 1,
1302
+ * each must exist), for which conversations (at least one of `channels` /
1303
+ * `connection_ids` / `type_keys` non-empty; `directions` additionally limits
1304
+ * which messages arm/are judged — empty = both), when (`evaluate_on` ⊆
1305
+ * [realtime, completion], ≥ 1) and the realtime timing (omitted ⇒ defaults:
1306
+ * quiet 90s, max wait 600s). `is_enabled` omitted ⇒ true. `module_slug` is
1307
+ * stamped by `pro module deploy`.
1308
+ */
1309
+ export interface CreateConversationTagSetRequest {
1310
+ key: string
1311
+ name?: string
1312
+ definition_keys: string[]
1313
+ channels?: Channel[]
1314
+ connection_ids?: string[]
1315
+ type_keys?: string[]
1316
+ directions?: MessageDirection[]
1317
+ evaluate_on: ConversationTagTrigger[]
1318
+ quiet_window_seconds?: number
1319
+ max_wait_seconds?: number
1320
+ is_enabled?: boolean
1321
+ module_slug?: string
1322
+ }
1323
+
1324
+ /**
1325
+ * Partial update — an omitted field leaves the stored value untouched; arrays
1326
+ * replace wholesale. `key` is immutable.
1327
+ */
1328
+ export interface UpdateConversationTagSetRequest {
1329
+ name?: string
1330
+ definition_keys?: string[]
1331
+ channels?: Channel[]
1332
+ connection_ids?: string[]
1333
+ type_keys?: string[]
1334
+ directions?: MessageDirection[]
1335
+ evaluate_on?: ConversationTagTrigger[]
1336
+ quiet_window_seconds?: number
1337
+ max_wait_seconds?: number
1338
+ is_enabled?: boolean
1339
+ }
1340
+
1341
+ export interface ListConversationTagSetsQuery extends PaginationQuery {
1342
+ /** Sets deployed by one module. */
1343
+ module_slug?: string
1344
+ /** Sets that reference the definition. */
1345
+ definition_key?: string
1346
+ is_enabled?: boolean
1347
+ /** Case-insensitive substring match on key or name. */
1348
+ search?: string
1349
+ }
1350
+
1351
+ /**
1352
+ * The manual door (`conversationTags.create`): a person tags a conversation,
1353
+ * a message or a span. Anchors follow the definition's scope (conversation:
1354
+ * none; message: start == end message, no offsets; span: UNICODE CODE POINT
1355
+ * offsets into the message's first text block, end exclusive — convert from
1356
+ * UTF-16 indexes before sending). The service stamps `source = 'user'`,
1357
+ * confidence 1, and fills `quote` from the anchored text.
1358
+ */
1359
+ export interface CreateConversationTagRequest {
1360
+ definition_key: string
1361
+ /** `"true"` | choice option key | score level index as a string. */
1362
+ value: string
1363
+ why?: string
1364
+ start_message_id?: string
1365
+ /** Unicode code-point index, inclusive. */
1366
+ start_offset?: number
1367
+ end_message_id?: string
1368
+ /** Unicode code-point index, exclusive. */
1369
+ end_offset?: number
1370
+ }
1371
+
1372
+ /**
1373
+ * Flips a tag between `active` and `dismissed` (the feedback loop) and lets a
1374
+ * person annotate why.
1375
+ */
1376
+ export interface UpdateConversationTagRequest {
1377
+ status?: ConversationTagStatus
1378
+ why?: string
1379
+ }
1380
+
1381
+ /**
1382
+ * Starts a manual evaluation (`conversationTags.evaluate`, 202). Omitted or
1383
+ * empty `definition_keys` = every definition the currently matching sets
1384
+ * reference (and every model tag on the conversation is replaced); a list
1385
+ * narrows both the questions and the replacement to those keys.
1386
+ */
1387
+ export interface EvaluateConversationTagsRequest {
1388
+ definition_keys?: string[]
1389
+ }
1390
+
1391
+ /**
1392
+ * Serves both `conversationTags.listByConversation` (the service forces
1393
+ * `conversation_id` from the path) and the org-wide `conversationTags.list`.
1394
+ */
1395
+ export interface ListConversationTagsQuery extends PaginationQuery {
1396
+ conversation_id?: string
1397
+ definition_key?: string
1398
+ value?: string
1399
+ channel?: string
1400
+ source?: string
1401
+ status?: string
1402
+ /** Tags anchored on the message at either end. */
1403
+ message_id?: string
1404
+ /** Tags on conversations whose roster contains the contact. */
1405
+ contact_id?: string
1406
+ /** Lower bound on `observed_at` (inclusive), RFC 3339. */
1407
+ since?: string
1408
+ /** Upper bound on `observed_at` (exclusive), RFC 3339. */
1409
+ until?: string
1410
+ }
1411
+
1412
+ /**
1413
+ * Groups active tags for dashboards. `group_by` is a COMMA-JOINED string
1414
+ * (`'definition_key,value'`) — not an array, because the query serializer
1415
+ * repeats array keys — whitelisted to definition_key, value, channel (order
1416
+ * kept).
1417
+ */
1418
+ export interface CountConversationTagsQuery {
1419
+ group_by?: string
1420
+ definition_key?: string
1421
+ channel?: string
1422
+ source?: string
1423
+ status?: string
1424
+ /** Lower bound on `observed_at` (inclusive), RFC 3339. */
1425
+ since?: string
1426
+ /** Upper bound on `observed_at` (exclusive), RFC 3339. */
1427
+ until?: string
1428
+ }
1429
+
1430
+ export interface CountConversationTagsResponse {
1431
+ data: ConversationTagCount[]
1432
+ }
1433
+
1434
+ export interface ListConversationTagEvaluationsQuery extends PaginationQuery {
1435
+ trigger?: string
1436
+ status?: string
1437
+ }
1438
+
865
1439
  /** Who assigned a contact's group membership. */
866
1440
  export type ContactGroupSource = 'manual' | 'model'
867
1441
 
@@ -1039,6 +1613,11 @@ export interface ListConnectionsQuery extends PaginationQuery {
1039
1613
  */
1040
1614
  owner_id?: string
1041
1615
  status?: string
1616
+ /**
1617
+ * The caller's intent: `read` (default) lists every connection they reach;
1618
+ * `write` only those they may send through — the compose picker's question.
1619
+ */
1620
+ permission?: 'read' | 'write'
1042
1621
  }
1043
1622
 
1044
1623
  /**
@@ -1079,6 +1658,13 @@ export interface ListConversationsQuery extends PaginationQuery {
1079
1658
  sender_address_id?: string
1080
1659
  /** Opt into the root/latest message projection on each row. */
1081
1660
  include?: 'messages_summary'
1661
+ /**
1662
+ * Conversations carrying an ACTIVE conversation tag of this definition —
1663
+ * the "which conversations talk about billing" stream.
1664
+ */
1665
+ tag_key?: string
1666
+ /** With `tag_key`: only conversations whose tag has this value. */
1667
+ tag_value?: string
1082
1668
  }
1083
1669
 
1084
1670
  /**
@@ -1136,7 +1722,13 @@ export type ConversationFilterType =
1136
1722
  | 'internal_participant'
1137
1723
  | 'internal_conversations'
1138
1724
  | 'all'
1139
- export type ConversationFilterAction = 'block' | 'allow'
1725
+ /**
1726
+ * block drops the message; allow keeps it. restrict and share keep it too
1727
+ * (they rank as allow) and decide who sees the thread the message CREATES:
1728
+ * restrict writes only the rule's principals onto the new thread, share adds
1729
+ * them on top of what the thread inherits. Applied once, at creation.
1730
+ */
1731
+ export type ConversationFilterAction = 'block' | 'allow' | 'restrict' | 'share'
1140
1732
  /** Which side of the message address/domain rules test (default: sender). */
1141
1733
  export type FilterMatchOn = 'sender' | 'any_participant'
1142
1734
  /** Deterministic machine-mail header signals the automated filter matches. */
@@ -1203,7 +1795,11 @@ export interface ConversationFilter {
1203
1795
  action: ConversationFilterAction
1204
1796
  filter_config?: ConversationFilterConfig
1205
1797
  is_enabled: boolean
1206
- /** Read-only rollup: messages this rule dropped in the last 30 days. */
1798
+ /** restrict / share only: who the created thread is opened to. */
1799
+ principals?: PrincipalRef[]
1800
+ /** restrict / share only: read or write (write implies read; the default). */
1801
+ permission?: 'read' | 'write'
1802
+ /** Read-only rollup: messages this rule matched in the last 30 days. */
1207
1803
  recent_event_count?: number
1208
1804
  created_at: string
1209
1805
  created_by: UserRef
@@ -1222,6 +1818,8 @@ export interface ConversationFilterEvent {
1222
1818
  sender_kind?: ContactAddressKind
1223
1819
  sender_value?: string
1224
1820
  external_message_id?: string
1821
+ /** The thread a restrict / share match applied to; empty on a drop. */
1822
+ conversation_id?: string
1225
1823
  occurred_at: string
1226
1824
  }
1227
1825
 
@@ -1231,6 +1829,9 @@ export interface CreateConversationFilterRequest {
1231
1829
  action: ConversationFilterAction
1232
1830
  filter_config?: Record<string, unknown>
1233
1831
  is_enabled?: boolean
1832
+ /** Required for restrict / share, empty otherwise. */
1833
+ principals?: PrincipalRef[]
1834
+ permission?: 'read' | 'write'
1234
1835
  /** Opts past the own-domain guard (self_domain_block). */
1235
1836
  is_self_domain_acknowledged?: boolean
1236
1837
  }
@@ -1241,6 +1842,8 @@ export interface UpdateConversationFilterRequest {
1241
1842
  action?: ConversationFilterAction
1242
1843
  filter_config?: Record<string, unknown>
1243
1844
  is_enabled?: boolean
1845
+ principals?: PrincipalRef[]
1846
+ permission?: 'read' | 'write'
1244
1847
  is_self_domain_acknowledged?: boolean
1245
1848
  }
1246
1849