@dench.com/cli 2.8.7-staging.189 → 2.8.8-staging.192

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.
@@ -545,6 +545,73 @@ const crmEntryShape = z
545
545
 
546
546
  const entryWriteResponse = z.object({ entryId: z.string() });
547
547
 
548
+ const commentActorShape = z
549
+ .object({
550
+ kind: z.string(),
551
+ label: z.string(),
552
+ displayName: z.string().optional(),
553
+ imageUrl: z.string().optional(),
554
+ userId: z.string().optional(),
555
+ isDench: z.boolean(),
556
+ })
557
+ .passthrough();
558
+
559
+ const commentShape = z
560
+ .object({
561
+ _id: z.string(),
562
+ entryId: z.string(),
563
+ parentCommentId: z.string().optional(),
564
+ rootCommentId: z.string().optional(),
565
+ depth: z.number(),
566
+ body: z.string().describe("Markdown."),
567
+ actor: commentActorShape,
568
+ mentionedUserIds: z.array(z.string()),
569
+ mentionsDench: z.boolean(),
570
+ replyCount: z.number(),
571
+ reactionSummary: z.array(
572
+ z.object({ emoji: z.string(), count: z.number() }),
573
+ ),
574
+ viewerReactions: z.array(z.string()),
575
+ editedAt: z.number().optional(),
576
+ deletedAt: z.number().optional(),
577
+ denchStatus: z.enum(["pending", "failed", "done"]).optional(),
578
+ canEdit: z.boolean(),
579
+ createdAt: z.number(),
580
+ })
581
+ .passthrough()
582
+ .describe("A comment on a CRM entry.");
583
+
584
+ const commentPage = z.object({
585
+ page: z.array(commentShape),
586
+ isDone: z.boolean(),
587
+ continueCursor: z.string(),
588
+ });
589
+
590
+ const notificationShape = z
591
+ .object({
592
+ _id: z.string(),
593
+ kind: z.string(),
594
+ title: z.string(),
595
+ body: z.string().optional(),
596
+ link: z.string().optional(),
597
+ readAt: z.number().optional(),
598
+ archivedAt: z.number().optional(),
599
+ createdAt: z.number(),
600
+ })
601
+ .passthrough();
602
+
603
+ const notificationPreferencesShape = z.object({
604
+ inAppEnabled: z.boolean(),
605
+ emailEnabled: z.boolean(),
606
+ perKind: z.record(
607
+ z.string(),
608
+ z.object({ inApp: z.boolean().optional(), email: z.boolean().optional() }),
609
+ ),
610
+ timezone: z.string().nullable(),
611
+ digestWindowMinutes: z.number(),
612
+ maxEmailsPerDay: z.number(),
613
+ });
614
+
548
615
  const crmBatchResults = z.object({
549
616
  results: z.array(
550
617
  z
@@ -1593,7 +1660,9 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
1593
1660
  icon: z
1594
1661
  .string()
1595
1662
  .optional()
1596
- .describe("Hugeicons (or legacy Lucide) icon name, e.g. user-multiple, target-01."),
1663
+ .describe(
1664
+ "Hugeicons (or legacy Lucide) icon name, e.g. user-multiple, target-01.",
1665
+ ),
1597
1666
  }),
1598
1667
  "crm.objects.update": z.object({
1599
1668
  name: z.string(),
@@ -1804,6 +1873,92 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
1804
1873
  entryIds: z.array(z.string()).max(200),
1805
1874
  }),
1806
1875
 
1876
+ // CRM comments
1877
+ "crm.comments.list": z.object({
1878
+ objectName: z.string(),
1879
+ entryId: z.string(),
1880
+ limit: z.coerce.number().int().min(1).max(100).optional(),
1881
+ cursor: z
1882
+ .string()
1883
+ .optional()
1884
+ .describe("continueCursor from the prior page."),
1885
+ replies: z.coerce
1886
+ .number()
1887
+ .int()
1888
+ .min(0)
1889
+ .max(50)
1890
+ .optional()
1891
+ .describe("Preview replies per root (default 2)."),
1892
+ }),
1893
+ "crm.comments.replies": z.object({
1894
+ commentId: z.string().describe("Root comment id."),
1895
+ limit: z.coerce.number().int().min(1).max(100).optional(),
1896
+ cursor: z.string().optional(),
1897
+ }),
1898
+ "crm.comments.get": z.object({ commentId: z.string() }),
1899
+ "crm.comments.create": z.object({
1900
+ objectName: z.string(),
1901
+ entryId: z.string(),
1902
+ body: z
1903
+ .string()
1904
+ .min(1)
1905
+ .describe(
1906
+ "Markdown. Mention a member with [@Name](/?member=<userId>); mention Dench with [@Dench](/?member=dench).",
1907
+ ),
1908
+ parentCommentId: z
1909
+ .string()
1910
+ .optional()
1911
+ .describe("Reply under this comment (any depth)."),
1912
+ mentionUserIds: z
1913
+ .array(z.string())
1914
+ .optional()
1915
+ .describe("Extra members to notify without writing mention links."),
1916
+ mentionDench: z
1917
+ .boolean()
1918
+ .optional()
1919
+ .describe("Summon Dench to reply in the thread."),
1920
+ }),
1921
+ "crm.comments.update": z.object({
1922
+ commentId: z.string(),
1923
+ body: z.string().min(1),
1924
+ }),
1925
+ "crm.comments.delete": z.object({ commentId: z.string() }),
1926
+ "crm.comments.react": z.object({
1927
+ commentId: z.string(),
1928
+ emoji: z.string().describe("A single emoji, e.g. 👍."),
1929
+ }),
1930
+ "crm.watch.get": z.object({ objectName: z.string(), entryId: z.string() }),
1931
+ "crm.watch.set": z.object({
1932
+ objectName: z.string(),
1933
+ entryId: z.string(),
1934
+ watching: z
1935
+ .boolean()
1936
+ .optional()
1937
+ .describe("Defaults to true; false unfollows."),
1938
+ }),
1939
+
1940
+ // CRM tasks
1941
+ "crm.tasks.mine": z.object({
1942
+ limit: z.coerce.number().int().min(1).max(500).optional(),
1943
+ }),
1944
+ "crm.tasks.forEntry": z.object({
1945
+ objectName: z.string(),
1946
+ entryId: z.string(),
1947
+ }),
1948
+ "crm.tasks.createForEntry": z.object({
1949
+ objectName: z.string(),
1950
+ entryId: z.string(),
1951
+ name: z.string().min(1),
1952
+ assigneeUserId: z.string().optional(),
1953
+ dueDateKey: z
1954
+ .string()
1955
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
1956
+ .optional()
1957
+ .describe("YYYY-MM-DD"),
1958
+ }),
1959
+ "crm.tasks.complete": z.object({ entryId: z.string() }),
1960
+ "crm.tasks.reopen": z.object({ entryId: z.string() }),
1961
+
1807
1962
  // CRM cells
1808
1963
  "crm.cells.get": z.object({
1809
1964
  objectName: z.string(),
@@ -2265,6 +2420,39 @@ export const requestSchemas: Record<string, ZodTypeAny> = {
2265
2420
  .string()
2266
2421
  .describe("Human name for the account. Empty clears it."),
2267
2422
  }),
2423
+
2424
+ // Notifications
2425
+ "notifications.list": z.object({
2426
+ filter: z.enum(["inbox", "unread", "archived", "all"]).optional(),
2427
+ limit: z.coerce.number().int().min(1).max(100).optional(),
2428
+ }),
2429
+ "notifications.unreadCount": noInput,
2430
+ "notifications.read": z.object({
2431
+ notificationId: z.string(),
2432
+ read: z
2433
+ .boolean()
2434
+ .optional()
2435
+ .describe("Defaults to true; false marks unread."),
2436
+ }),
2437
+ "notifications.readAll": noInput,
2438
+ "notifications.archive": z.object({ notificationId: z.string() }),
2439
+ "notifications.preferences.get": noInput,
2440
+ "notifications.preferences.update": z.object({
2441
+ inAppEnabled: z.boolean().optional(),
2442
+ emailEnabled: z.boolean().optional(),
2443
+ perKind: z
2444
+ .record(
2445
+ z.string(),
2446
+ z.object({
2447
+ inApp: z.boolean().optional(),
2448
+ email: z.boolean().optional(),
2449
+ }),
2450
+ )
2451
+ .optional(),
2452
+ timezone: z.string().optional(),
2453
+ digestWindowMinutes: z.number().int().optional(),
2454
+ maxEmailsPerDay: z.number().int().optional(),
2455
+ }),
2268
2456
  };
2269
2457
 
2270
2458
  // ---------------------------------------------------------------------------
@@ -2552,6 +2740,54 @@ export const responseSchemas: Record<string, ZodTypeAny> = {
2552
2740
  }),
2553
2741
  "crm.cells.append": entryWriteResponse,
2554
2742
 
2743
+ // CRM comments + tasks
2744
+ "crm.comments.list": commentPage.extend({
2745
+ page: z.array(commentShape.extend({ replies: z.array(commentShape) })),
2746
+ }),
2747
+ "crm.comments.replies": commentPage,
2748
+ "crm.comments.get": commentShape.extend({
2749
+ objectName: z.string(),
2750
+ entryLabel: z.string(),
2751
+ }),
2752
+ "crm.comments.create": z.object({
2753
+ id: z.string(),
2754
+ rootCommentId: z.string().optional(),
2755
+ depth: z.number(),
2756
+ mentionedUserIds: z.array(z.string()),
2757
+ mentionsDench: z.boolean(),
2758
+ }),
2759
+ "crm.comments.update": z.object({
2760
+ ok: z.boolean(),
2761
+ newlyMentioned: z.array(z.string()),
2762
+ }),
2763
+ "crm.comments.delete": z.object({ ok: z.boolean(), tombstoned: z.boolean() }),
2764
+ "crm.comments.react": z.object({
2765
+ reacted: z.boolean(),
2766
+ reactionSummary: z.array(
2767
+ z.object({ emoji: z.string(), count: z.number() }),
2768
+ ),
2769
+ }),
2770
+ "crm.watch.get": z.object({
2771
+ watching: z.boolean(),
2772
+ reason: z.string().optional(),
2773
+ watcherCount: z.number(),
2774
+ }),
2775
+ "crm.watch.set": z.object({ watching: z.boolean() }),
2776
+ "crm.tasks.mine": z.array(z.object({}).passthrough()),
2777
+ "crm.tasks.forEntry": z.array(z.object({}).passthrough()),
2778
+ "crm.tasks.createForEntry": z.object({}).passthrough(),
2779
+ "crm.tasks.complete": z.object({}).passthrough(),
2780
+ "crm.tasks.reopen": z.object({}).passthrough(),
2781
+
2782
+ // Notifications
2783
+ "notifications.list": z.array(notificationShape),
2784
+ "notifications.unreadCount": z.number(),
2785
+ "notifications.read": okResponse,
2786
+ "notifications.readAll": z.object({ ok: z.boolean(), patched: z.number() }),
2787
+ "notifications.archive": okResponse,
2788
+ "notifications.preferences.get": notificationPreferencesShape.nullable(),
2789
+ "notifications.preferences.update": okResponse,
2790
+
2555
2791
  // CRM query + search
2556
2792
  "crm.query": z
2557
2793
  .array(crmEntryShape)
@@ -3147,6 +3383,21 @@ export const requestExamples: Record<string, unknown> = {
3147
3383
  Status: "New",
3148
3384
  },
3149
3385
  },
3386
+ "crm.comments.create": {
3387
+ body: "Kickoff went well — [@Bob](/?member=user_bob) can you send the proposal? [@Dench](/?member=dench) what's the ARR here?",
3388
+ },
3389
+ "crm.comments.update": { body: "Kickoff went well — proposal sent." },
3390
+ "crm.watch.set": { watching: true },
3391
+ "crm.tasks.createForEntry": {
3392
+ name: "Send proposal",
3393
+ assigneeUserId: "user_bob",
3394
+ dueDateKey: "2026-09-05",
3395
+ },
3396
+ "notifications.read": { read: true },
3397
+ "notifications.preferences.update": {
3398
+ emailEnabled: true,
3399
+ perKind: { entry_activity: { email: false } },
3400
+ },
3150
3401
  "crm.entries.createMany": {
3151
3402
  entries: [
3152
3403
  { Name: "Jane", Email: "jane@example.com" },
@@ -3696,6 +3947,79 @@ export const responseExamples: Record<string, unknown> = {
3696
3947
  ok: true,
3697
3948
  enumColors: ["#94a3b8", "#3b82f6", "#22c55e", "#ef4444"],
3698
3949
  },
3950
+ "crm.comments.list": {
3951
+ page: [
3952
+ {
3953
+ _id: "cmt_01",
3954
+ entryId: "ent_lead99",
3955
+ depth: 0,
3956
+ body: "Kickoff went well — [@Bob](/?member=user_bob) can you send the proposal?",
3957
+ actor: {
3958
+ kind: "user",
3959
+ label: "Ada",
3960
+ displayName: "Ada Lovelace",
3961
+ userId: "user_ada",
3962
+ isDench: false,
3963
+ },
3964
+ mentionedUserIds: ["user_bob"],
3965
+ mentionsDench: false,
3966
+ replyCount: 1,
3967
+ reactionSummary: [{ emoji: "👍", count: 2 }],
3968
+ viewerReactions: ["👍"],
3969
+ canEdit: true,
3970
+ createdAt: 1765526400000,
3971
+ replies: [
3972
+ {
3973
+ _id: "cmt_02",
3974
+ entryId: "ent_lead99",
3975
+ parentCommentId: "cmt_01",
3976
+ rootCommentId: "cmt_01",
3977
+ depth: 1,
3978
+ body: "On it.",
3979
+ actor: {
3980
+ kind: "user",
3981
+ label: "Bob",
3982
+ displayName: "Bob Smith",
3983
+ userId: "user_bob",
3984
+ isDench: false,
3985
+ },
3986
+ mentionedUserIds: [],
3987
+ mentionsDench: false,
3988
+ replyCount: 0,
3989
+ reactionSummary: [],
3990
+ viewerReactions: [],
3991
+ canEdit: false,
3992
+ createdAt: 1765530000000,
3993
+ },
3994
+ ],
3995
+ },
3996
+ ],
3997
+ isDone: true,
3998
+ continueCursor: "",
3999
+ },
4000
+ "crm.comments.create": {
4001
+ id: "cmt_03",
4002
+ rootCommentId: "cmt_01",
4003
+ depth: 1,
4004
+ mentionedUserIds: ["user_bob"],
4005
+ mentionsDench: true,
4006
+ },
4007
+ "crm.comments.react": {
4008
+ reacted: true,
4009
+ reactionSummary: [{ emoji: "👍", count: 3 }],
4010
+ },
4011
+ "crm.watch.get": { watching: true, reason: "commented", watcherCount: 3 },
4012
+ "notifications.list": [
4013
+ {
4014
+ _id: "ntf_01",
4015
+ kind: "mention",
4016
+ title: "Ada Lovelace mentioned you in Ramp",
4017
+ body: "Kickoff went well — @Bob can you send the proposal?",
4018
+ link: "/acme?entry=company%3Aent_lead99&comment=cmt_01",
4019
+ createdAt: 1765526400000,
4020
+ },
4021
+ ],
4022
+ "notifications.unreadCount": 3,
3699
4023
  "crm.entries.list": [
3700
4024
  {
3701
4025
  id: "ent_lead99",
@@ -79,6 +79,24 @@ function op(operation: ApiOperation): ApiOperation {
79
79
  return operation;
80
80
  }
81
81
 
82
+ /** `?limit=&cursor=` → Convex `paginationOpts`. */
83
+ function paginationOptsFrom(
84
+ input: Record<string, unknown>,
85
+ defaultLimit: number,
86
+ ): { numItems: number; cursor: string | null } {
87
+ const limit =
88
+ typeof input.limit === "number"
89
+ ? Math.min(Math.max(Math.floor(input.limit), 1), 100)
90
+ : defaultLimit;
91
+ return {
92
+ numItems: limit,
93
+ cursor:
94
+ typeof input.cursor === "string" && input.cursor.length > 0
95
+ ? input.cursor
96
+ : null,
97
+ };
98
+ }
99
+
82
100
  function convex(
83
101
  functionType: ConvexOperation["functionType"],
84
102
  functionName: string,
@@ -1042,6 +1060,198 @@ const rawApiOperations = [
1042
1060
  },
1043
1061
  },
1044
1062
  }),
1063
+
1064
+ // CRM comments — threaded conversation on a record, merged with history
1065
+ // into the record's Activity feed. `@Dench` in a body summons the agent.
1066
+ op({
1067
+ id: "crm.comments.list",
1068
+ group: "crm",
1069
+ summary:
1070
+ "List comments on a CRM entry (roots newest first, with preview replies).",
1071
+ cli: "dench crm comments list",
1072
+ method: "GET",
1073
+ path: "/crm/objects/{objectName}/entries/{entryId}/comments",
1074
+ requestSchema: noBody,
1075
+ responseSchema: anyResponse,
1076
+ backend: {
1077
+ ...convex("query", "functions/crm/comments:listForEntry"),
1078
+ transformArgs: (input) => ({
1079
+ objectName: input.objectName,
1080
+ entryId: input.entryId,
1081
+ previewReplies:
1082
+ typeof input.replies === "number"
1083
+ ? Math.min(Math.max(Math.floor(input.replies), 0), 50)
1084
+ : undefined,
1085
+ paginationOpts: paginationOptsFrom(input, 20),
1086
+ }),
1087
+ },
1088
+ }),
1089
+ op({
1090
+ id: "crm.comments.replies",
1091
+ group: "crm",
1092
+ summary: "List every reply under a root comment, oldest first.",
1093
+ cli: "dench crm comments replies",
1094
+ method: "GET",
1095
+ path: "/crm/comments/{commentId}/replies",
1096
+ requestSchema: noBody,
1097
+ responseSchema: anyResponse,
1098
+ backend: {
1099
+ ...convex("query", "functions/crm/comments:listReplies"),
1100
+ transformArgs: (input) => ({
1101
+ rootCommentId: input.commentId,
1102
+ paginationOpts: paginationOptsFrom(input, 50),
1103
+ }),
1104
+ },
1105
+ }),
1106
+ op({
1107
+ id: "crm.comments.get",
1108
+ group: "crm",
1109
+ summary: "Get one comment with its entry label.",
1110
+ cli: "dench crm comments get",
1111
+ method: "GET",
1112
+ path: "/crm/comments/{commentId}",
1113
+ requestSchema: noBody,
1114
+ responseSchema: anyResponse,
1115
+ backend: convex("query", "functions/crm/comments:get"),
1116
+ }),
1117
+ op({
1118
+ id: "crm.comments.create",
1119
+ group: "crm",
1120
+ summary:
1121
+ "Comment on a CRM entry (markdown). Mention members with [@Name](/?member=<userId>); mention Dench with [@Dench](/?member=dench) or mentionDench.",
1122
+ cli: "dench crm comments create",
1123
+ method: "POST",
1124
+ path: "/crm/objects/{objectName}/entries/{entryId}/comments",
1125
+ requestSchema: anyObject,
1126
+ responseSchema: anyResponse,
1127
+ backend: convex("mutation", "functions/crm/comments:create", "bearer", {
1128
+ includeRunId: true,
1129
+ }),
1130
+ }),
1131
+ op({
1132
+ id: "crm.comments.update",
1133
+ group: "crm",
1134
+ summary: "Edit a comment you wrote.",
1135
+ cli: "dench crm comments update",
1136
+ method: "PATCH",
1137
+ path: "/crm/comments/{commentId}",
1138
+ requestSchema: anyObject,
1139
+ responseSchema: anyResponse,
1140
+ backend: convex("mutation", "functions/crm/comments:edit"),
1141
+ }),
1142
+ op({
1143
+ id: "crm.comments.delete",
1144
+ group: "crm",
1145
+ summary: "Delete a comment (kept as a tombstone when it has replies).",
1146
+ cli: "dench crm comments delete",
1147
+ method: "DELETE",
1148
+ path: "/crm/comments/{commentId}",
1149
+ requestSchema: noBody,
1150
+ responseSchema: anyResponse,
1151
+ backend: convex("mutation", "functions/crm/comments:remove"),
1152
+ }),
1153
+ op({
1154
+ id: "crm.comments.react",
1155
+ group: "crm",
1156
+ summary: "Toggle an emoji reaction on a comment.",
1157
+ cli: "dench crm comments react",
1158
+ method: "PUT",
1159
+ path: "/crm/comments/{commentId}/reactions/{emoji}",
1160
+ requestSchema: noBody,
1161
+ responseSchema: anyResponse,
1162
+ backend: convex("mutation", "functions/crm/comments:toggleReaction"),
1163
+ }),
1164
+ op({
1165
+ id: "crm.watch.get",
1166
+ group: "crm",
1167
+ summary: "Whether you follow a CRM entry's activity.",
1168
+ cli: "dench crm watch status",
1169
+ method: "GET",
1170
+ path: "/crm/objects/{objectName}/entries/{entryId}/watch",
1171
+ requestSchema: noBody,
1172
+ responseSchema: anyResponse,
1173
+ backend: {
1174
+ ...convex("query", "functions/crm/comments:getWatchState"),
1175
+ transformArgs: (input) => ({ entryId: input.entryId }),
1176
+ },
1177
+ }),
1178
+ op({
1179
+ id: "crm.watch.set",
1180
+ group: "crm",
1181
+ summary: "Follow or unfollow a CRM entry's activity.",
1182
+ cli: "dench crm watch",
1183
+ method: "PUT",
1184
+ path: "/crm/objects/{objectName}/entries/{entryId}/watch",
1185
+ requestSchema: anyObject,
1186
+ responseSchema: anyResponse,
1187
+ backend: {
1188
+ ...convex("mutation", "functions/crm/comments:setWatching"),
1189
+ transformArgs: (input) => ({
1190
+ entryId: input.entryId,
1191
+ watching: input.watching !== false,
1192
+ }),
1193
+ },
1194
+ }),
1195
+
1196
+ // CRM tasks — conveniences over the protected `task` object. Tasks are
1197
+ // still ordinary entries (`crm.entries.*` works on them); these add the
1198
+ // "mine", "for this record", complete/reopen shortcuts the app uses.
1199
+ op({
1200
+ id: "crm.tasks.mine",
1201
+ group: "crm",
1202
+ summary: "Your open tasks (plus ones completed today).",
1203
+ cli: "dench crm tasks mine",
1204
+ method: "GET",
1205
+ path: "/crm/tasks/mine",
1206
+ requestSchema: noBody,
1207
+ responseSchema: anyResponse,
1208
+ backend: convex("query", "functions/crm/tasks:listMyOpenTasks"),
1209
+ }),
1210
+ op({
1211
+ id: "crm.tasks.forEntry",
1212
+ group: "crm",
1213
+ summary: "Tasks linked to a CRM entry.",
1214
+ cli: "dench crm tasks list",
1215
+ method: "GET",
1216
+ path: "/crm/objects/{objectName}/entries/{entryId}/tasks",
1217
+ requestSchema: noBody,
1218
+ responseSchema: anyResponse,
1219
+ backend: convex("query", "functions/crm/tasks:listEntryTasks"),
1220
+ }),
1221
+ op({
1222
+ id: "crm.tasks.createForEntry",
1223
+ group: "crm",
1224
+ summary: "Create a task linked to a CRM entry.",
1225
+ cli: "dench crm tasks create",
1226
+ method: "POST",
1227
+ path: "/crm/objects/{objectName}/entries/{entryId}/tasks",
1228
+ requestSchema: anyObject,
1229
+ responseSchema: anyResponse,
1230
+ backend: convex("mutation", "functions/crm/tasks:createEntryTask"),
1231
+ }),
1232
+ op({
1233
+ id: "crm.tasks.complete",
1234
+ group: "crm",
1235
+ summary: "Mark a task done.",
1236
+ cli: "dench crm tasks complete",
1237
+ method: "POST",
1238
+ path: "/crm/tasks/{entryId}/complete",
1239
+ requestSchema: noBody,
1240
+ responseSchema: anyResponse,
1241
+ backend: convex("mutation", "functions/crm/tasks:completeTask"),
1242
+ }),
1243
+ op({
1244
+ id: "crm.tasks.reopen",
1245
+ group: "crm",
1246
+ summary: "Reopen a completed task.",
1247
+ cli: "dench crm tasks reopen",
1248
+ method: "POST",
1249
+ path: "/crm/tasks/{entryId}/reopen",
1250
+ requestSchema: noBody,
1251
+ responseSchema: anyResponse,
1252
+ backend: convex("mutation", "functions/crm/tasks:reopenTask"),
1253
+ }),
1254
+
1045
1255
  op({
1046
1256
  id: "crm.entries.create",
1047
1257
  group: "crm",
@@ -2981,6 +3191,99 @@ const rawApiOperations = [
2981
3191
  responseSchema: anyResponse,
2982
3192
  backend: convex("query", "functions/agentConfig:getAgentConfig"),
2983
3193
  }),
3194
+
3195
+ // Notifications — the signed-in member's inbox (the bell). Agent sessions
3196
+ // read their owner's inbox; an org API key with no person behind it is
3197
+ // refused by the backend.
3198
+ op({
3199
+ id: "notifications.list",
3200
+ group: "notifications",
3201
+ summary: "List your notifications (inbox, unread, archived, or all).",
3202
+ cli: "dench notifications list",
3203
+ method: "GET",
3204
+ path: "/notifications",
3205
+ requestSchema: noBody,
3206
+ responseSchema: anyResponse,
3207
+ backend: convex("query", "functions/notifications:listMyNotifications"),
3208
+ }),
3209
+ op({
3210
+ id: "notifications.unreadCount",
3211
+ group: "notifications",
3212
+ summary: "How many unread notifications you have.",
3213
+ cli: "dench notifications count",
3214
+ method: "GET",
3215
+ path: "/notifications/unread-count",
3216
+ requestSchema: noBody,
3217
+ responseSchema: anyResponse,
3218
+ backend: convex("query", "functions/notifications:unreadCount"),
3219
+ }),
3220
+ op({
3221
+ id: "notifications.read",
3222
+ group: "notifications",
3223
+ summary: "Mark a notification read (or unread).",
3224
+ cli: "dench notifications read",
3225
+ method: "PATCH",
3226
+ path: "/notifications/{notificationId}",
3227
+ requestSchema: anyObject,
3228
+ responseSchema: anyResponse,
3229
+ backend: {
3230
+ ...convex("mutation", "functions/notifications:setNotificationRead"),
3231
+ transformArgs: (input) => ({
3232
+ notificationId: input.notificationId,
3233
+ read: input.read !== false,
3234
+ }),
3235
+ },
3236
+ }),
3237
+ op({
3238
+ id: "notifications.readAll",
3239
+ group: "notifications",
3240
+ summary: "Mark every inbox notification read.",
3241
+ cli: "dench notifications read-all",
3242
+ method: "POST",
3243
+ path: "/notifications/read-all",
3244
+ requestSchema: noBody,
3245
+ responseSchema: anyResponse,
3246
+ backend: convex("mutation", "functions/notifications:markAllRead"),
3247
+ }),
3248
+ op({
3249
+ id: "notifications.archive",
3250
+ group: "notifications",
3251
+ summary: "Archive a notification.",
3252
+ cli: "dench notifications archive",
3253
+ method: "POST",
3254
+ path: "/notifications/{notificationId}/archive",
3255
+ requestSchema: noBody,
3256
+ responseSchema: anyResponse,
3257
+ backend: convex("mutation", "functions/notifications:archiveNotification"),
3258
+ }),
3259
+ op({
3260
+ id: "notifications.preferences.get",
3261
+ group: "notifications",
3262
+ summary: "Read your notification preferences.",
3263
+ cli: "dench notifications prefs",
3264
+ method: "GET",
3265
+ path: "/notifications/preferences",
3266
+ requestSchema: noBody,
3267
+ responseSchema: anyResponse,
3268
+ backend: convex(
3269
+ "query",
3270
+ "functions/notifications:getMyNotificationPreferences",
3271
+ ),
3272
+ }),
3273
+ op({
3274
+ id: "notifications.preferences.update",
3275
+ group: "notifications",
3276
+ summary: "Update your notification preferences.",
3277
+ cli: "dench notifications prefs --set",
3278
+ method: "PATCH",
3279
+ path: "/notifications/preferences",
3280
+ requestSchema: anyObject,
3281
+ responseSchema: anyResponse,
3282
+ backend: convex(
3283
+ "mutation",
3284
+ "functions/notifications:updateMyNotificationPreferences",
3285
+ ),
3286
+ }),
2984
3287
  ] as const satisfies readonly ApiOperation[];
2985
3288
 
2986
3289
  /**