khoros-aurora-sdk 26.3.4 → 26.4.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.
Files changed (38) hide show
  1. package/.github/workflows/npm-publish.yml +0 -1
  2. package/README.md +1 -1
  3. package/build-hash.txt +1 -1
  4. package/graphql/aiAnswerLogSchema.graphqls +51 -0
  5. package/graphql/aiReviewModeSchema.graphqls +43 -0
  6. package/graphql/baseSchema.graphqls +6 -0
  7. package/graphql/bookmarkSchema.graphqls +192 -0
  8. package/graphql/clarificationSchema.graphqls +28 -0
  9. package/graphql/clmSchema.graphqls +79 -0
  10. package/graphql/emailOrchestrationSchema.graphqls +24 -3
  11. package/graphql/expertSuggestionSchema.graphqls +21 -3
  12. package/graphql/followSchema.graphqls +31 -0
  13. package/graphql/kudoSchema.graphqls +2 -0
  14. package/graphql/messageSchema.graphqls +14 -0
  15. package/graphql/metricSchema.graphqls +92 -0
  16. package/graphql/muteSchema.graphqls +74 -0
  17. package/graphql/notificationSchema.graphqls +210 -0
  18. package/graphql/pollSchema.graphqls +4 -0
  19. package/graphql/reactionSchema.graphqls +181 -0
  20. package/graphql/reactionViewerSchema.graphqls +104 -0
  21. package/graphql/searchSchema.graphqls +20 -0
  22. package/graphql/socialFeedSchema.graphqls +65 -0
  23. package/graphql/startDiscussionSchema.graphqls +123 -0
  24. package/graphql/surveysSchema.graphqls +11 -1
  25. package/graphql/unreadSchema.graphqls +86 -0
  26. package/package.json +1 -1
  27. package/scripts/buildGraphqlTypesCli.js +5 -5
  28. package/scripts/buildPluginCli.js +8 -8
  29. package/scripts/formatPluginCli.js +5 -5
  30. package/scripts/generateCli.js +10 -10
  31. package/scripts/initPluginCli.js +8 -8
  32. package/scripts/lintPluginCli.js +4 -4
  33. package/scripts/pluginLoggerCli.js +5 -5
  34. package/scripts/pluginPreviewCli.js +6 -6
  35. package/scripts/validatePluginCli.js +6 -6
  36. package/types/graphql-schema-types.ts +4586 -26
  37. package/types/mf/index.d.ts +4124 -27
  38. package/types/pkg/index.d.ts +4113 -26
@@ -21,7 +21,6 @@ jobs:
21
21
  registry-url: https://registry.npmjs.org/
22
22
  - name: Publish to npm
23
23
  run: |
24
- VERSION=$(npm pkg get version | tr -d '"')
25
24
  LATEST=$(npm view khoros-aurora-sdk dist-tags.latest)
26
25
  if [[ "${{ github.event.release.prerelease }}" == "true" ]]; then
27
26
  tag=pre
package/README.md CHANGED
@@ -63,7 +63,7 @@ to admin users.
63
63
 
64
64
  ## Allowed Dependencies for Custom React Component
65
65
 
66
- The following dependencies are whitelisted and can be used for custom React Component development: react, react-bootstrap, react-hook-form, react-uid, react-dropzone, moment-timezone, @coveo/atomic-react, @coveo/atomic, @coveo/headless-react and @coveo/headless. Any additional dependencies will require approval.
66
+ The following dependencies are whitelisted and can be used for custom React Component development: react, react-bootstrap, react-hook-form, react-uid, react-dropzone, moment-timezone, @coveo/atomic-react, @coveo/atomic, @coveo/headless-react, @coveo/headless and quill-resize-image. Any additional dependencies will require approval.
67
67
 
68
68
  ## Developing a Custom Endpoint
69
69
 
package/build-hash.txt CHANGED
@@ -1 +1 @@
1
- 7d3ff26
1
+ a95f395
@@ -19,6 +19,15 @@ extend type Query {
19
19
  "The cursor to return results before."
20
20
  before: String
21
21
  ): AiAnswerLogConnection!
22
+
23
+ """
24
+ Returns the AI-generated answer preview for a message with a review mode log entry.
25
+ Admin-only: non-admins and missing entries both return null.
26
+ """
27
+ reviewModeAnswerPreview(
28
+ "The message ID to look up (string format, parsed to int internally)."
29
+ messageId: String!
30
+ ): AiAnswerLog
22
31
  }
23
32
 
24
33
  """
@@ -99,6 +108,45 @@ type AiAnswerLog {
99
108
 
100
109
  "Generation model used for answer creation (format: provider|model)."
101
110
  generationModel: String
111
+
112
+ "Whether this entry was generated in review mode."
113
+ isReviewMode: Boolean!
114
+
115
+ "Review status (PENDING_REVIEW, REVIEWED) - null for live mode entries."
116
+ reviewStatus: String
117
+
118
+ "Generated answer text - null for live mode entries."
119
+ answerText: String
120
+
121
+ "Expert names identified by AI - always non-null, empty for live mode entries."
122
+ expertNames: [ExpertName!]!
123
+
124
+ "Citations used in the review mode answer - always non-null, empty for live mode entries."
125
+ citations: [Citation!]!
126
+ }
127
+
128
+ """
129
+ A resolved expert name entry from AI-generated answer metadata.
130
+ """
131
+ type ExpertName {
132
+ "User ID of the expert."
133
+ userId: String!
134
+ "Display name of the expert."
135
+ displayName: String!
136
+ "Community profile URL for the expert — null for entries written before Phase 11."
137
+ profileUrl: String
138
+ }
139
+
140
+ """
141
+ A citation used in the AI-generated answer.
142
+ """
143
+ type Citation {
144
+ "1-based citation index."
145
+ index: Int!
146
+ "Display title of the cited post."
147
+ title: String!
148
+ "URL of the cited community post."
149
+ url: String!
102
150
  }
103
151
 
104
152
  """
@@ -142,6 +190,9 @@ input AiAnswerLogConstraints {
142
190
  "Filter by whether expert card was generated."
143
191
  expertCardGenerated: Boolean
144
192
 
193
+ "Filter to review mode entries (true), non-review entries (false), or all (absent)."
194
+ isReviewMode: Boolean
195
+
145
196
  "Filter by specific no-answer reasons (matches any in the list)."
146
197
  noAnswerReasons: [String!]
147
198
 
@@ -0,0 +1,43 @@
1
+ """
2
+ Review mode config for a module+board pair.
3
+ """
4
+ type AiReviewModeConfig {
5
+ "The AI module identifier."
6
+ moduleId: String!
7
+ "The board identifier, or null for community-level config."
8
+ boardId: String
9
+ """
10
+ Effective mode resolved for this moduleId+boardId pair — always "LIVE" or
11
+ "REVIEW". When a board has no explicit override, this equals
12
+ `communityDefault`. Admin UIs should use `rawMode` to distinguish inherited
13
+ values from explicit overrides.
14
+ """
15
+ mode: String!
16
+ """
17
+ Raw board-level setting for this moduleId+boardId pair. One of "DEFAULT",
18
+ "LIVE", or "REVIEW":
19
+ - "DEFAULT" — no board-specific row exists; the effective mode is
20
+ inherited from `communityDefault`.
21
+ - "LIVE" / "REVIEW" — explicit board-level override.
22
+ For community-level queries (boardId == null) this mirrors `mode`.
23
+ """
24
+ rawMode: String!
25
+ "Community-level default mode for this module (always present)."
26
+ communityDefault: String!
27
+ }
28
+
29
+ extend type Query {
30
+ """
31
+ Returns the current review mode config for a module+board pair. Nullable so
32
+ that non-admin callers receive a silent null (per the read-path denial
33
+ convention in AiReviewModeConfigDataFetcherSupport) instead of a GraphQL
34
+ execution error. Admin-gated mutation uses the non-null variant and throws
35
+ on denial — see updateAiReviewModeConfig below.
36
+ """
37
+ aiReviewModeConfig(moduleId: String!, boardId: String): AiReviewModeConfig
38
+ }
39
+
40
+ extend type Mutation {
41
+ "Updates the review mode config and returns the new state."
42
+ updateAiReviewModeConfig(moduleId: String!, boardId: String, mode: String!): AiReviewModeConfig!
43
+ }
@@ -317,6 +317,12 @@ directive @dateAsMs on FIELD
317
317
  # This directive measures the length of a string field
318
318
  directive @stringLength on FIELD
319
319
 
320
+ # This directive truncates a string field to a maximum character length
321
+ directive @truncate(
322
+ "The maximum character length to truncate to. 0 means no truncation."
323
+ length: Int!
324
+ ) on FIELD
325
+
320
326
  # This directive specifies which graph nodes to load asynchronously vs synchronously (default).
321
327
  #
322
328
  # For example:
@@ -0,0 +1,192 @@
1
+ """
2
+ Sort order for saved messages.
3
+ """
4
+ enum SavedMessagesSortBy {
5
+ "Sort by the date the message was saved (default, most recent first)"
6
+ SAVED_DATE
7
+ "Sort by the original post date of the message (newest first)"
8
+ POST_DATE
9
+ }
10
+
11
+ extend type Query {
12
+ "Fetches all saved/bookmarked messages for a user with pagination support"
13
+ savedMessages(
14
+ "The user ID to fetch saved messages for"
15
+ userId: String!
16
+ "The number of messages to return"
17
+ first: Int
18
+ "The cursor to return the results after"
19
+ after: String
20
+ "Sort order for results"
21
+ sortBy: SavedMessagesSortBy
22
+ ): MessageConnection!
23
+ }
24
+
25
+ extend type Mutation {
26
+ "Save/bookmark a message for later reference"
27
+ saveMessage(messageId: String!, notes: String): SaveMessageResponse!
28
+
29
+ "Remove a saved/bookmarked message"
30
+ unsaveMessage(messageId: String!): UnsaveMessageResponse!
31
+
32
+ "Update personal notes on a bookmarked message"
33
+ updateBookmarkNote(messageId: String!, notes: String!): UpdateBookmarkNoteResponse!
34
+ }
35
+
36
+ type SaveMessageResponse {
37
+ success: Boolean!
38
+ errors: [SaveMessageError!]
39
+ }
40
+
41
+ type UnsaveMessageResponse {
42
+ success: Boolean!
43
+ errors: [UnsaveMessageError!]
44
+ }
45
+
46
+ union SaveMessageError = SaveMessageFailedError | SaveMessageNotEnabledError
47
+ union UnsaveMessageError = UnsaveMessageFailedError | UnsaveMessageNotEnabledError
48
+
49
+ type SaveMessageFailedError implements Error {
50
+ message: String!
51
+ fields: [String]!
52
+ }
53
+
54
+ "Error when saving is attempted but the social feed feature is not enabled."
55
+ type SaveMessageNotEnabledError implements Error {
56
+ message: String!
57
+ fields: [String]!
58
+ }
59
+
60
+ type UnsaveMessageFailedError implements Error {
61
+ message: String!
62
+ fields: [String]!
63
+ }
64
+
65
+ "Error when unsaving is attempted but the social feed feature is not enabled."
66
+ type UnsaveMessageNotEnabledError implements Error {
67
+ message: String!
68
+ fields: [String]!
69
+ }
70
+
71
+ type UpdateBookmarkNoteResponse {
72
+ success: Boolean!
73
+ errors: [UpdateBookmarkNoteError!]
74
+ }
75
+
76
+ union UpdateBookmarkNoteError = UpdateBookmarkNoteFailedError | UpdateBookmarkNoteNotEnabledError
77
+
78
+ type UpdateBookmarkNoteFailedError implements Error {
79
+ message: String!
80
+ fields: [String]!
81
+ }
82
+
83
+ "Error when updating a bookmark note is attempted but the social feed feature is not enabled."
84
+ type UpdateBookmarkNoteNotEnabledError implements Error {
85
+ message: String!
86
+ fields: [String]!
87
+ }
88
+
89
+ extend interface Message {
90
+ "Whether the current user has bookmarked this message"
91
+ isBookmarked: Boolean!
92
+ "Personal note the current user attached when saving this message"
93
+ bookmarkNote: String
94
+ }
95
+
96
+ extend interface TopicMessage {
97
+ "Whether the current user has bookmarked this message"
98
+ isBookmarked: Boolean!
99
+ "Personal note the current user attached when saving this message"
100
+ bookmarkNote: String
101
+ }
102
+
103
+ extend interface ReplyMessage {
104
+ "Whether the current user has bookmarked this message"
105
+ isBookmarked: Boolean!
106
+ "Personal note the current user attached when saving this message"
107
+ bookmarkNote: String
108
+ }
109
+
110
+ extend interface Article {
111
+ "Whether the current user has bookmarked this message"
112
+ isBookmarked: Boolean!
113
+ "Personal note the current user attached when saving this message"
114
+ bookmarkNote: String
115
+ }
116
+
117
+ extend type ForumTopicMessage {
118
+ "Whether the current user has bookmarked this message"
119
+ isBookmarked: Boolean!
120
+ "Personal note the current user attached when saving this message"
121
+ bookmarkNote: String
122
+ }
123
+
124
+ extend type TkbTopicMessage {
125
+ "Whether the current user has bookmarked this message"
126
+ isBookmarked: Boolean!
127
+ "Personal note the current user attached when saving this message"
128
+ bookmarkNote: String
129
+ }
130
+
131
+ extend type BlogTopicMessage {
132
+ "Whether the current user has bookmarked this message"
133
+ isBookmarked: Boolean!
134
+ "Personal note the current user attached when saving this message"
135
+ bookmarkNote: String
136
+ }
137
+
138
+ extend type IdeaTopicMessage {
139
+ "Whether the current user has bookmarked this message"
140
+ isBookmarked: Boolean!
141
+ "Personal note the current user attached when saving this message"
142
+ bookmarkNote: String
143
+ }
144
+
145
+ extend type ForumReplyMessage {
146
+ "Whether the current user has bookmarked this message"
147
+ isBookmarked: Boolean!
148
+ "Personal note the current user attached when saving this message"
149
+ bookmarkNote: String
150
+ }
151
+
152
+ extend type TkbReplyMessage {
153
+ "Whether the current user has bookmarked this message"
154
+ isBookmarked: Boolean!
155
+ "Personal note the current user attached when saving this message"
156
+ bookmarkNote: String
157
+ }
158
+
159
+ extend type BlogReplyMessage {
160
+ "Whether the current user has bookmarked this message"
161
+ isBookmarked: Boolean!
162
+ "Personal note the current user attached when saving this message"
163
+ bookmarkNote: String
164
+ }
165
+
166
+ extend type IdeaReplyMessage {
167
+ "Whether the current user has bookmarked this message"
168
+ isBookmarked: Boolean!
169
+ "Personal note the current user attached when saving this message"
170
+ bookmarkNote: String
171
+ }
172
+
173
+ extend type OccasionReplyMessage {
174
+ "Whether the current user has bookmarked this message"
175
+ isBookmarked: Boolean!
176
+ "Personal note the current user attached when saving this message"
177
+ bookmarkNote: String
178
+ }
179
+
180
+ extend type AcceptedSolutionMessage {
181
+ "Whether the current user has bookmarked this message"
182
+ isBookmarked: Boolean!
183
+ "Personal note the current user attached when saving this message"
184
+ bookmarkNote: String
185
+ }
186
+
187
+ extend type OccasionTopicMessage {
188
+ "Whether the current user has bookmarked this message"
189
+ isBookmarked: Boolean!
190
+ "Personal note the current user attached when saving this message"
191
+ bookmarkNote: String
192
+ }
@@ -14,7 +14,9 @@ type ThreadResponseCollection {
14
14
  createdAt: String!
15
15
  updatedAt: String
16
16
  messageTitle: String
17
+ messageViewHref: String
17
18
  boardName: String
19
+ boardId: String
18
20
  authorName: String
19
21
  items: [ThreadResponseItem!]!
20
22
  }
@@ -52,6 +54,7 @@ type ClarificationGuidanceExample {
52
54
  displayOrder: Int!
53
55
  enabled: Boolean!
54
56
  createdAt: String!
57
+ isInherited: Boolean!
55
58
  }
56
59
 
57
60
  enum CollectionStatus {
@@ -93,6 +96,16 @@ type BoardClarificationSummary {
93
96
  boardTitle: String!
94
97
  conversationStyle: String!
95
98
  enabled: Boolean!
99
+ inherited: Boolean!
100
+ hasOverrides: Boolean!
101
+ isRootFolder: Boolean!
102
+ }
103
+
104
+ type CategoryClarificationDefault {
105
+ nodeId: ID!
106
+ enabled: Boolean!
107
+ hasOverrides: Boolean!
108
+ isRootFolder: Boolean!
96
109
  }
97
110
 
98
111
  type ClarificationSettings {
@@ -103,6 +116,9 @@ type ClarificationSettings {
103
116
  infoBannerText: String!
104
117
  reminderEmailDelay: Int!
105
118
  allowSkip: Boolean!
119
+ hasOverrides: Boolean!
120
+ hasAncestorOverrides: Boolean!
121
+ isRootFolder: Boolean!
106
122
  }
107
123
 
108
124
  input ClarificationSettingsInput @validationDefaults(prefix: "clarification_settings") {
@@ -149,6 +165,9 @@ extend type Query {
149
165
  "Get clarification enabled status for all eligible boards (Forum/Idea)"
150
166
  allBoardClarificationSettings: [BoardClarificationSummary!]
151
167
 
168
+ "Get the effective clarification default for each category/community that contains eligible boards"
169
+ categoryClarificationDefaults: [CategoryClarificationDefault!]
170
+
152
171
  "Get formatted clarification questions text for a message (used by Flow template variable resolution)"
153
172
  clarificationQuestionsText(messageUid: Int!): String
154
173
 
@@ -225,6 +244,15 @@ extend type Mutation {
225
244
 
226
245
  "Set clarification settings for a board (takes GraphQL node ID like 'board:forum-name')"
227
246
  setClarificationSettings(nodeId: ID!, settingsInput: ClarificationSettingsInput!): ClarificationSettingsResult
247
+
248
+ "Clear clarification overrides for a node so it falls back to parent (category/community) defaults"
249
+ clearClarificationSettings(nodeId: ID!): ClarificationSettingsResult
250
+
251
+ "Apply a category's clarification default to every board beneath it (clears per-board overrides)"
252
+ applyClarificationDefaultToBoards(nodeId: ID!): ClarificationSettingsResult
253
+
254
+ "Force a clarification on/off state across a container and every board beneath it (sets the container's own state and clears descendant enabled overrides so they inherit)"
255
+ applyClarificationEnabledToBoards(nodeId: ID!, enabled: Boolean!): ClarificationSettingsResult
228
256
  }
229
257
 
230
258
  input ClarificationAnswerInput @validationDefaults(prefix: "clarification_answer") {
@@ -36,6 +36,32 @@ type ExpertListEntry {
36
36
  activity90d: Int
37
37
  "Contributions (posts + solutions) in the last 180 days. Null if no tblia_expert_totals row exists."
38
38
  activity180d: Int
39
+ "Opt-out state for this expert. Null = routable, 'USER_OPT_OUT' = user opted out, 'ADMIN_BLACKLIST' = admin blacklisted."
40
+ optOutState: String
41
+ "ISO-8601 timestamp when this row's USER_OPT_OUT is snooze-bounded; null when state is null, ADMIN_BLACKLIST, or indefinite opt-out."
42
+ snoozeUntil: String
43
+ "Reason recorded with the latest opt-out/blacklist row that drives the current state. Null when state is null."
44
+ latestReason: String
45
+ "Login (canonical) of the actor who set the current state. Null for user-driven changes or when the actor account was deleted."
46
+ latestActorLogin: String
47
+ "ISO-8601 timestamp of the latest state change. Null when state is null."
48
+ latestChangedAt: String
49
+ "Topic slugs the user is currently admin-blacklisted on. Empty string entry represents the global (all-topics) blacklist row. Empty array when the user is not admin-blacklisted."
50
+ blacklistedTopics: [String!]!
51
+ }
52
+
53
+ """
54
+ Routing-status filter applied server-side when reading the experts leaderboard.
55
+ """
56
+ enum RoutingStatusFilter {
57
+ "Include every expert regardless of opt-out / blacklist state."
58
+ SHOW_ALL
59
+ "Only experts who are routable right now (no active opt-out and no admin blacklist)."
60
+ ROUTABLE_ONLY
61
+ "Only experts who have an active user-initiated global opt-out."
62
+ USER_OPT_OUT_ONLY
63
+ "Only experts who are admin-blacklisted (globally or on this row's topic)."
64
+ ADMIN_BLACKLIST_ONLY
39
65
  }
40
66
 
41
67
  extend type Query {
@@ -59,6 +85,10 @@ extend type Query {
59
85
  offset: Int
60
86
  "Maximum number of rows to return. Defaults to clm.topExpertsPerTopic setting."
61
87
  limit: Int
88
+ "When true, includes admin-blacklisted users in results. Defaults to false."
89
+ includeBlacklisted: Boolean = false
90
+ "Optional routing-status filter."
91
+ routingStatusFilter: RoutingStatusFilter
62
92
  ): [ExpertListEntry!]!
63
93
  }
64
94
 
@@ -117,6 +147,32 @@ extend type Query {
117
147
  expertRankNames: [String!]!
118
148
  }
119
149
 
150
+ "Filter-aware aggregate counts for the experts leaderboard summary strip."
151
+ type ExpertStatusCountsResult {
152
+ "Number of routable experts (no opt-out, no blacklist, accepting routing)."
153
+ routable: Int!
154
+ "Number of experts opted out indefinitely."
155
+ userOptOut: Int!
156
+ "Number of experts who paused via the 30-day snooze (snooze_until in the future)."
157
+ paused30d: Int!
158
+ "Number of experts admin-blacklisted from routing."
159
+ blacklisted: Int!
160
+ }
161
+
162
+ extend type Query {
163
+ """
164
+ Returns filter-aware status counts for the experts leaderboard summary strip.
165
+ Same filter args as expertList. Admin-only.
166
+ """
167
+ expertStatusCounts(
168
+ topic: String
169
+ staffOnly: Boolean
170
+ username: String
171
+ rankName: String
172
+ roleType: String
173
+ ): ExpertStatusCountsResult
174
+ }
175
+
120
176
  extend type Query {
121
177
  """
122
178
  Returns distinct community role names from the roles table for experts.
@@ -124,3 +180,26 @@ extend type Query {
124
180
  """
125
181
  expertRoleTypes: [String!]!
126
182
  }
183
+
184
+ """
185
+ Viewer-scoped type for fields that pertain to the currently authenticated user.
186
+ """
187
+ type Me {
188
+ """
189
+ True if the user has appeared as an expert candidate at least once.
190
+ Drives whether the Expert Routing Notifications section renders in the user's settings.
191
+ """
192
+ hasBeenExpertCandidate: Boolean!
193
+ """
194
+ True iff the community-level opt-out feature flag (expertRouting.optOutEnabled)
195
+ is enabled AND the current user has been an expert candidate. The frontend uses
196
+ this single combined boolean to gate the Expert Routing Notifications section,
197
+ so it does not need to read the admin setting separately.
198
+ """
199
+ expertRoutingOptOutAvailable: Boolean!
200
+ }
201
+
202
+ extend type Query {
203
+ "Returns viewer-scoped fields for the currently authenticated user."
204
+ me: Me
205
+ }
@@ -112,6 +112,8 @@ type EmailTemplate implements Node {
112
112
  isDefaultVersion: Boolean
113
113
  "The version number of the latest published version."
114
114
  versionNum: Int
115
+ "An optional custom display name for this version."
116
+ versionDisplayName: String
115
117
  "The status of the latest version."
116
118
  status: EmailTemplateVersionStatus
117
119
  "The locale of the latest version."
@@ -177,6 +179,7 @@ An enumeration that defines the status of an email template version.
177
179
  enum EmailTemplateVersionStatus {
178
180
  DRAFT
179
181
  ACTIVE
182
+ INACTIVE
180
183
  }
181
184
 
182
185
  """
@@ -220,15 +223,23 @@ extend type Mutation {
220
223
  versionNum: Int!
221
224
  ): UpdateEmailTemplateResult!
222
225
 
226
+ "Marks the currently active version of an email template as inactive. The template will fall back to the default version until another version is set active."
227
+ setInactiveEmailTemplateVersion(
228
+ "The unique template identifier."
229
+ templateId: String!
230
+ "The version number to mark inactive. Must currently be ACTIVE."
231
+ versionNum: Int!
232
+ ): UpdateEmailTemplateResult!
233
+
223
234
  "Creates a new custom email template with an initial version."
224
235
  createEmailTemplate(input: CreateEmailTemplateInput!): UpdateEmailTemplateResult!
225
236
 
226
- "Creates a new draft version of an existing email template, copying content from a source version."
237
+ "Creates a new draft version of an existing email template. If sourceVersionNum is provided, copies content from that version; otherwise creates a blank version."
227
238
  createEmailTemplateVersion(
228
239
  "The unique template identifier."
229
240
  templateId: String!
230
- "The version number to copy content from."
231
- sourceVersionNum: Int!
241
+ "The version number to copy content from. If omitted, a blank version is created."
242
+ sourceVersionNum: Int
232
243
  ): UpdateEmailTemplateResult!
233
244
 
234
245
  """
@@ -243,6 +254,16 @@ extend type Mutation {
243
254
  versionNum: Int
244
255
  ): SendTestEmailResult!
245
256
 
257
+ "Renames a specific version of an email template by setting a custom display name."
258
+ renameEmailTemplateVersion(
259
+ "The unique template identifier."
260
+ templateId: String!
261
+ "The version number to rename."
262
+ versionNum: Int!
263
+ "The new display name for the version. Pass null or empty string to clear."
264
+ displayName: String
265
+ ): UpdateEmailTemplateResult!
266
+
246
267
  "Deletes a specific version of an email template. Cannot delete the default (system) version."
247
268
  deleteEmailTemplateVersion(
248
269
  "The unique template identifier."
@@ -59,6 +59,15 @@ extend type TkbTopicMessage {
59
59
  expertSuggestions: [ExpertSuggestion!]
60
60
  }
61
61
 
62
+ extend type IdeaTopicMessage {
63
+ """
64
+ AI-generated expert suggestions for this idea.
65
+ Only populated when the AI system cannot find a direct answer but identifies relevant experts.
66
+ Visible only to the post author and moderators.
67
+ """
68
+ expertSuggestions: [ExpertSuggestion!]
69
+ }
70
+
62
71
  extend type Mutation {
63
72
  """
64
73
  Sets or updates the expert suggestions for a message.
@@ -140,11 +149,20 @@ extend type Query {
140
149
  ): [ExpertSuggestion!]!
141
150
 
142
151
  """
143
- Get all topics that have expert data in the community.
144
- Returns distinct topics ordered alphabetically.
152
+ Cursor-paginated list of topics that have expert data in the community.
153
+ Returns up to {@code limit} distinct topics ordered alphabetically and
154
+ strictly greater than {@code after}. The client requests the next page
155
+ by passing the last topic from the previous response as {@code after};
156
+ a response shorter than {@code limit} signals the end of the set.
157
+ {@code search} restricts results to topics with the given case-insensitive
158
+ prefix so typeahead and infinite scroll compose naturally.
145
159
  """
146
160
  allTopics(
147
- "Maximum number of topics to return (default: 50, max: 100)."
161
+ "Optional case-insensitive prefix. Empty/null returns all topics."
162
+ search: String
163
+ "Cursor: last topic from the previous page. Null or empty means start at the head."
164
+ after: String
165
+ "Page size (default: 50, max: 200)."
148
166
  limit: Int = 50
149
167
  ): [String!]!
150
168
  }
@@ -47,6 +47,12 @@ extend type Mutation {
47
47
  nodeId: ID
48
48
  settingsInput: FollowSettingsInput!
49
49
  ): SetSettingsResult!
50
+
51
+ "Deletes all thread follows for the current user that were auto-created by the given reaction type."
52
+ bulkUnfollowByReactionType(
53
+ "The reaction type that triggered the auto-follows to remove (e.g. 'i_need_this', 'good_question')."
54
+ reactionType: String!
55
+ ): BulkUnfollowByReactionTypeResponse!
50
56
  }
51
57
 
52
58
  "Error when a follow is invalid."
@@ -190,6 +196,8 @@ Defines sorts that can be used to sort the follow queries.
190
196
  input FollowSorts {
191
197
  "Sort by the follow ID."
192
198
  id: Sort
199
+ "Sort by last activity time."
200
+ lastActivityTime: Sort
193
201
  }
194
202
 
195
203
  """
@@ -231,6 +239,9 @@ type Follow implements Entity & Node @restV2Mapping(field: "subscription") {
231
239
 
232
240
  "The count of unread messages for a tag subscription for a user on a node"
233
241
  unreadMessageCount: Int
242
+
243
+ "The reaction type that triggered this follow (e.g. i_need_this, good_question), or null if manually followed."
244
+ sourceReactionType: String
234
245
  }
235
246
 
236
247
  extend type User implements FollowUserAdministrative & FollowUserApplicable {
@@ -357,6 +368,10 @@ type FollowUserSettings implements Settings {
357
368
  boardFollowMethod: InheritableStringSettingWithPossibleValues
358
369
  "The setting to whether skip notifications of the read posts."
359
370
  ignoreReadPosts: BooleanSetting
371
+ "The setting to auto follow threads when reacting with 'I Need This'."
372
+ autoFollowOnINeedThis: BooleanSetting
373
+ "The setting to auto follow threads when reacting with 'Good Question'."
374
+ autoFollowOnGoodQuestion: BooleanSetting
360
375
  }
361
376
 
362
377
  "Follows properties for an user."
@@ -369,6 +384,10 @@ type FollowUserProperties {
369
384
  boardFollowMethod: String
370
385
  "The setting to whether skip notifications of the read posts."
371
386
  ignoreReadPosts: Boolean
387
+ "The setting to auto follow threads when reacting with 'I Need This'."
388
+ autoFollowOnINeedThis: Boolean
389
+ "The setting to auto follow threads when reacting with 'Good Question'."
390
+ autoFollowOnGoodQuestion: Boolean
372
391
  }
373
392
 
374
393
  "Follow settings."
@@ -474,6 +493,10 @@ input FollowUserSettingsInput {
474
493
  boardFollowMethod: String
475
494
  "The setting to whether skip the notifications of the read posts."
476
495
  ignoreReadPosts: Boolean
496
+ "The setting to auto follow threads when reacting with 'I Need This'."
497
+ autoFollowOnINeedThis: Boolean
498
+ "The setting to auto follow threads when reacting with 'Good Question'."
499
+ autoFollowOnGoodQuestion: Boolean
477
500
  }
478
501
 
479
502
  """
@@ -497,3 +520,11 @@ type TagFollowable {
497
520
  "The node at which a tag was followed."
498
521
  coreNode: CoreNode!
499
522
  }
523
+
524
+ "Response to the bulkUnfollowByReactionType mutation."
525
+ type BulkUnfollowByReactionTypeResponse {
526
+ "Whether the bulk unfollow succeeded."
527
+ success: Boolean!
528
+ "The number of follows removed."
529
+ deletedCount: Int!
530
+ }