@okrlinkhub/agent-factory 3.0.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/index.d.ts +42 -14
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +68 -2
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/api.d.ts +2 -0
- package/dist/component/_generated/api.d.ts.map +1 -1
- package/dist/component/_generated/api.js.map +1 -1
- package/dist/component/_generated/component.d.ts +114 -0
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/lib.d.ts +2 -1
- package/dist/component/lib.d.ts.map +1 -1
- package/dist/component/lib.js +2 -1
- package/dist/component/lib.js.map +1 -1
- package/dist/component/messageTemplates.d.ts +37 -0
- package/dist/component/messageTemplates.d.ts.map +1 -0
- package/dist/component/messageTemplates.js +177 -0
- package/dist/component/messageTemplates.js.map +1 -0
- package/dist/component/pushing.d.ts +14 -14
- package/dist/component/queue.d.ts +20 -0
- package/dist/component/queue.d.ts.map +1 -1
- package/dist/component/queue.js +51 -0
- package/dist/component/queue.js.map +1 -1
- package/dist/component/schema.d.ts +34 -8
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +14 -0
- package/dist/component/schema.js.map +1 -1
- package/package.json +1 -1
- package/src/client/index.ts +69 -2
- package/src/component/_generated/api.ts +2 -0
- package/src/component/_generated/component.ts +158 -0
- package/src/component/lib.test.ts +125 -0
- package/src/component/lib.ts +8 -0
- package/src/component/messageTemplates.ts +205 -0
- package/src/component/queue.ts +61 -0
- package/src/component/schema.ts +15 -0
|
@@ -201,6 +201,131 @@ describe("component lib", () => {
|
|
|
201
201
|
expect(claim?.payload.messageText).toBe("hello");
|
|
202
202
|
});
|
|
203
203
|
|
|
204
|
+
test("message templates should normalize tags and auto-generate key", async () => {
|
|
205
|
+
const t = initConvexTest();
|
|
206
|
+
|
|
207
|
+
const templateId = await t.mutation(api.lib.createMessageTemplate, {
|
|
208
|
+
title: " Status Update ",
|
|
209
|
+
text: " Share your latest progress. ",
|
|
210
|
+
tags: [" Urgent ", "follow-up", "urgent", " Team "],
|
|
211
|
+
actorUserId: "user-admin-1",
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
expect(templateId).toBeDefined();
|
|
215
|
+
|
|
216
|
+
const templates = await t.query(api.lib.listMessageTemplatesByCompany, {
|
|
217
|
+
includeDisabled: true,
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
expect(templates).toHaveLength(1);
|
|
221
|
+
expect(templates[0]).toMatchObject({
|
|
222
|
+
templateKey: "status-update",
|
|
223
|
+
title: "Status Update",
|
|
224
|
+
text: "Share your latest progress.",
|
|
225
|
+
tags: ["follow-up", "team", "urgent"],
|
|
226
|
+
usageCount: 0,
|
|
227
|
+
enabled: true,
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
test("message templates should generate a unique key globally", async () => {
|
|
232
|
+
const t = initConvexTest();
|
|
233
|
+
|
|
234
|
+
const firstTemplateId = await t.mutation(api.lib.createMessageTemplate, {
|
|
235
|
+
title: "Daily check-in",
|
|
236
|
+
text: "Share your update.",
|
|
237
|
+
tags: [],
|
|
238
|
+
actorUserId: "user-admin-1",
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
const secondTemplateId = await t.mutation(api.lib.createMessageTemplate, {
|
|
242
|
+
title: "Daily check-in",
|
|
243
|
+
text: "Duplicate title",
|
|
244
|
+
tags: [],
|
|
245
|
+
actorUserId: "user-admin-2",
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
const templates = await t.query(api.lib.listMessageTemplatesByCompany, {
|
|
249
|
+
includeDisabled: true,
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
expect(firstTemplateId).toBeDefined();
|
|
253
|
+
expect(secondTemplateId).toBeDefined();
|
|
254
|
+
expect(templates.map((template) => template.templateKey).sort()).toEqual([
|
|
255
|
+
"daily-check-in",
|
|
256
|
+
"daily-check-in-2",
|
|
257
|
+
]);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
test("message templates should track usage and surface most used templates first", async () => {
|
|
261
|
+
const t = initConvexTest();
|
|
262
|
+
await t.mutation(api.queue.upsertAgentProfile, {
|
|
263
|
+
agentKey: "template-agent",
|
|
264
|
+
version: "1.0.0",
|
|
265
|
+
secretsRef: [],
|
|
266
|
+
enabled: true,
|
|
267
|
+
});
|
|
268
|
+
await t.mutation(api.lib.bindUserAgent, {
|
|
269
|
+
consumerUserId: "user-template-1",
|
|
270
|
+
agentKey: "template-agent",
|
|
271
|
+
source: "manual",
|
|
272
|
+
metadata: { companyId: "co-templates" },
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
const firstTemplateId = await t.mutation(api.lib.createMessageTemplate, {
|
|
276
|
+
title: "Weekly recap",
|
|
277
|
+
text: "Condividi il recap della settimana.",
|
|
278
|
+
tags: ["weekly", "recap"],
|
|
279
|
+
actorUserId: "user-admin-1",
|
|
280
|
+
});
|
|
281
|
+
const secondTemplateId = await t.mutation(api.lib.createMessageTemplate, {
|
|
282
|
+
title: "Quick nudge",
|
|
283
|
+
text: "Mandami un aggiornamento rapido.",
|
|
284
|
+
tags: ["follow-up"],
|
|
285
|
+
actorUserId: "user-admin-1",
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
await t.mutation((api.lib as any).sendMessageTemplateToUserAgent, {
|
|
289
|
+
consumerUserId: "user-template-1",
|
|
290
|
+
agentKey: "template-agent",
|
|
291
|
+
templateId: firstTemplateId,
|
|
292
|
+
});
|
|
293
|
+
await t.mutation((api.lib as any).sendMessageTemplateToUserAgent, {
|
|
294
|
+
consumerUserId: "user-template-1",
|
|
295
|
+
agentKey: "template-agent",
|
|
296
|
+
templateId: firstTemplateId,
|
|
297
|
+
});
|
|
298
|
+
await t.mutation((api.lib as any).sendMessageTemplateToUserAgent, {
|
|
299
|
+
consumerUserId: "user-template-1",
|
|
300
|
+
agentKey: "template-agent",
|
|
301
|
+
templateId: secondTemplateId,
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
const templates = await t.query(api.lib.listMessageTemplatesByCompany, {
|
|
305
|
+
includeDisabled: true,
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
expect(templates.map((template) => ({
|
|
309
|
+
title: template.title,
|
|
310
|
+
usageCount: template.usageCount,
|
|
311
|
+
}))).toEqual([
|
|
312
|
+
{ title: "Weekly recap", usageCount: 2 },
|
|
313
|
+
{ title: "Quick nudge", usageCount: 1 },
|
|
314
|
+
]);
|
|
315
|
+
|
|
316
|
+
const queuedItems = await t.query(api.lib.listQueueItemsForUserAgent, {
|
|
317
|
+
consumerUserId: "user-template-1",
|
|
318
|
+
agentKey: "template-agent",
|
|
319
|
+
limit: 10,
|
|
320
|
+
});
|
|
321
|
+
expect(queuedItems).toHaveLength(3);
|
|
322
|
+
expect(queuedItems.map((item) => item.payload.messageText).sort()).toEqual([
|
|
323
|
+
"Condividi il recap della settimana.",
|
|
324
|
+
"Condividi il recap della settimana.",
|
|
325
|
+
"Mandami un aggiornamento rapido.",
|
|
326
|
+
].sort());
|
|
327
|
+
});
|
|
328
|
+
|
|
204
329
|
test("message runtime config should store telegram attachment retention", async () => {
|
|
205
330
|
const t = initConvexTest();
|
|
206
331
|
await t.mutation(api.lib.setMessageRuntimeConfig, {
|
package/src/component/lib.ts
CHANGED
|
@@ -23,6 +23,7 @@ export {
|
|
|
23
23
|
listQueueItemsForUserAgent,
|
|
24
24
|
getConversationViewForUserAgent,
|
|
25
25
|
sendMessageToUserAgent,
|
|
26
|
+
sendMessageTemplateToUserAgent,
|
|
26
27
|
listSnapshotsForConversation,
|
|
27
28
|
listSnapshotsForUserAgent,
|
|
28
29
|
getLatestSnapshotForUserAgent,
|
|
@@ -59,6 +60,13 @@ export {
|
|
|
59
60
|
getWebhookReadiness,
|
|
60
61
|
} from "./identity.js";
|
|
61
62
|
|
|
63
|
+
export {
|
|
64
|
+
createMessageTemplate,
|
|
65
|
+
updateMessageTemplate,
|
|
66
|
+
deleteMessageTemplate,
|
|
67
|
+
listMessageTemplatesByCompany,
|
|
68
|
+
} from "./messageTemplates.js";
|
|
69
|
+
|
|
62
70
|
export {
|
|
63
71
|
createPushTemplate,
|
|
64
72
|
updatePushTemplate,
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { v } from "convex/values";
|
|
2
|
+
import { mutation, query } from "./_generated/server.js";
|
|
3
|
+
import type { MutationCtx } from "./_generated/server.js";
|
|
4
|
+
|
|
5
|
+
const messageTemplateViewValidator = v.object({
|
|
6
|
+
_id: v.id("messageTemplates"),
|
|
7
|
+
templateKey: v.string(),
|
|
8
|
+
title: v.string(),
|
|
9
|
+
text: v.string(),
|
|
10
|
+
tags: v.array(v.string()),
|
|
11
|
+
usageCount: v.number(),
|
|
12
|
+
enabled: v.boolean(),
|
|
13
|
+
createdBy: v.string(),
|
|
14
|
+
updatedBy: v.string(),
|
|
15
|
+
createdAt: v.number(),
|
|
16
|
+
updatedAt: v.number(),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
function normalizeTemplateKey(value: string) {
|
|
20
|
+
return value.trim().toLowerCase();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function buildTemplateKeyBase(title: string) {
|
|
24
|
+
const normalized = title
|
|
25
|
+
.trim()
|
|
26
|
+
.toLowerCase()
|
|
27
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
28
|
+
.replace(/^-+|-+$/g, "");
|
|
29
|
+
return normalized || "message-template";
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function normalizeTitle(value: string) {
|
|
33
|
+
return value.trim();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function normalizeText(value: string) {
|
|
37
|
+
return value.trim();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function normalizeTags(tags: Array<string>) {
|
|
41
|
+
return Array.from(
|
|
42
|
+
new Set(
|
|
43
|
+
tags
|
|
44
|
+
.map((tag) => tag.trim().toLowerCase())
|
|
45
|
+
.filter((tag) => tag.length > 0),
|
|
46
|
+
),
|
|
47
|
+
).sort((left, right) => left.localeCompare(right));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function validateMessageTemplateFields(input: {
|
|
51
|
+
title?: string;
|
|
52
|
+
text?: string;
|
|
53
|
+
}) {
|
|
54
|
+
if (input.title !== undefined && input.title.length === 0) {
|
|
55
|
+
throw new Error("Template title is required");
|
|
56
|
+
}
|
|
57
|
+
if (input.text !== undefined && input.text.length === 0) {
|
|
58
|
+
throw new Error("Template text is required");
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function resolveUniqueTemplateKey(
|
|
63
|
+
ctx: MutationCtx,
|
|
64
|
+
title: string,
|
|
65
|
+
currentTemplateId?: string,
|
|
66
|
+
) {
|
|
67
|
+
const baseKey = buildTemplateKeyBase(title);
|
|
68
|
+
let candidate = baseKey;
|
|
69
|
+
let suffix = 2;
|
|
70
|
+
|
|
71
|
+
while (true) {
|
|
72
|
+
const existing = await ctx.db
|
|
73
|
+
.query("messageTemplates")
|
|
74
|
+
.withIndex("by_templateKey", (q) => q.eq("templateKey", candidate))
|
|
75
|
+
.unique();
|
|
76
|
+
if (!existing || String(existing._id) === currentTemplateId) {
|
|
77
|
+
return candidate;
|
|
78
|
+
}
|
|
79
|
+
candidate = `${baseKey}-${suffix}`;
|
|
80
|
+
suffix += 1;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export const createMessageTemplate = mutation({
|
|
85
|
+
args: {
|
|
86
|
+
title: v.string(),
|
|
87
|
+
text: v.string(),
|
|
88
|
+
tags: v.array(v.string()),
|
|
89
|
+
enabled: v.optional(v.boolean()),
|
|
90
|
+
actorUserId: v.string(),
|
|
91
|
+
nowMs: v.optional(v.number()),
|
|
92
|
+
},
|
|
93
|
+
returns: v.id("messageTemplates"),
|
|
94
|
+
handler: async (ctx, args) => {
|
|
95
|
+
const title = normalizeTitle(args.title);
|
|
96
|
+
const text = normalizeText(args.text);
|
|
97
|
+
const tags = normalizeTags(args.tags);
|
|
98
|
+
validateMessageTemplateFields({ title, text });
|
|
99
|
+
const templateKey = normalizeTemplateKey(await resolveUniqueTemplateKey(ctx, title));
|
|
100
|
+
|
|
101
|
+
const nowMs = args.nowMs ?? Date.now();
|
|
102
|
+
return await ctx.db.insert("messageTemplates", {
|
|
103
|
+
templateKey,
|
|
104
|
+
title,
|
|
105
|
+
text,
|
|
106
|
+
tags,
|
|
107
|
+
usageCount: 0,
|
|
108
|
+
enabled: args.enabled ?? true,
|
|
109
|
+
createdBy: args.actorUserId,
|
|
110
|
+
updatedBy: args.actorUserId,
|
|
111
|
+
createdAt: nowMs,
|
|
112
|
+
updatedAt: nowMs,
|
|
113
|
+
});
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
export const updateMessageTemplate = mutation({
|
|
118
|
+
args: {
|
|
119
|
+
templateId: v.id("messageTemplates"),
|
|
120
|
+
title: v.optional(v.string()),
|
|
121
|
+
text: v.optional(v.string()),
|
|
122
|
+
tags: v.optional(v.array(v.string())),
|
|
123
|
+
enabled: v.optional(v.boolean()),
|
|
124
|
+
actorUserId: v.string(),
|
|
125
|
+
nowMs: v.optional(v.number()),
|
|
126
|
+
},
|
|
127
|
+
returns: v.boolean(),
|
|
128
|
+
handler: async (ctx, args) => {
|
|
129
|
+
const template = await ctx.db.get(args.templateId);
|
|
130
|
+
if (!template) return false;
|
|
131
|
+
|
|
132
|
+
const title = args.title !== undefined ? normalizeTitle(args.title) : template.title;
|
|
133
|
+
const text = args.text !== undefined ? normalizeText(args.text) : template.text;
|
|
134
|
+
const tags = args.tags !== undefined ? normalizeTags(args.tags) : template.tags;
|
|
135
|
+
validateMessageTemplateFields({ title, text });
|
|
136
|
+
const templateKey =
|
|
137
|
+
title !== template.title
|
|
138
|
+
? normalizeTemplateKey(await resolveUniqueTemplateKey(ctx, title, String(template._id)))
|
|
139
|
+
: template.templateKey;
|
|
140
|
+
|
|
141
|
+
const nowMs = args.nowMs ?? Date.now();
|
|
142
|
+
await ctx.db.patch(template._id, {
|
|
143
|
+
templateKey,
|
|
144
|
+
title,
|
|
145
|
+
text,
|
|
146
|
+
tags,
|
|
147
|
+
enabled: args.enabled ?? template.enabled,
|
|
148
|
+
updatedBy: args.actorUserId,
|
|
149
|
+
updatedAt: nowMs,
|
|
150
|
+
});
|
|
151
|
+
return true;
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
export const deleteMessageTemplate = mutation({
|
|
156
|
+
args: {
|
|
157
|
+
templateId: v.id("messageTemplates"),
|
|
158
|
+
},
|
|
159
|
+
returns: v.boolean(),
|
|
160
|
+
handler: async (ctx, args) => {
|
|
161
|
+
const template = await ctx.db.get(args.templateId);
|
|
162
|
+
if (!template) return false;
|
|
163
|
+
await ctx.db.delete(template._id);
|
|
164
|
+
return true;
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
export const listMessageTemplatesByCompany = query({
|
|
169
|
+
args: {
|
|
170
|
+
includeDisabled: v.optional(v.boolean()),
|
|
171
|
+
limit: v.optional(v.number()),
|
|
172
|
+
},
|
|
173
|
+
returns: v.array(messageTemplateViewValidator),
|
|
174
|
+
handler: async (ctx, args) => {
|
|
175
|
+
const includeDisabled = args.includeDisabled ?? false;
|
|
176
|
+
const limit = Math.max(1, Math.min(args.limit ?? 100, 200));
|
|
177
|
+
|
|
178
|
+
const rows = includeDisabled
|
|
179
|
+
? await ctx.db.query("messageTemplates").take(limit)
|
|
180
|
+
: await ctx.db
|
|
181
|
+
.query("messageTemplates")
|
|
182
|
+
.withIndex("by_enabled", (q) => q.eq("enabled", true))
|
|
183
|
+
.take(limit);
|
|
184
|
+
|
|
185
|
+
rows.sort((left, right) => {
|
|
186
|
+
if (right.usageCount !== left.usageCount) {
|
|
187
|
+
return right.usageCount - left.usageCount;
|
|
188
|
+
}
|
|
189
|
+
return right.updatedAt - left.updatedAt;
|
|
190
|
+
});
|
|
191
|
+
return rows.map((row) => ({
|
|
192
|
+
_id: row._id,
|
|
193
|
+
templateKey: row.templateKey,
|
|
194
|
+
title: row.title,
|
|
195
|
+
text: row.text,
|
|
196
|
+
tags: row.tags,
|
|
197
|
+
usageCount: row.usageCount,
|
|
198
|
+
enabled: row.enabled,
|
|
199
|
+
createdBy: row.createdBy,
|
|
200
|
+
updatedBy: row.updatedBy,
|
|
201
|
+
createdAt: row.createdAt,
|
|
202
|
+
updatedAt: row.updatedAt,
|
|
203
|
+
}));
|
|
204
|
+
},
|
|
205
|
+
});
|
package/src/component/queue.ts
CHANGED
|
@@ -2013,6 +2013,67 @@ export const sendMessageToUserAgent = mutation({
|
|
|
2013
2013
|
},
|
|
2014
2014
|
});
|
|
2015
2015
|
|
|
2016
|
+
export const sendMessageTemplateToUserAgent = mutation({
|
|
2017
|
+
args: {
|
|
2018
|
+
consumerUserId: v.string(),
|
|
2019
|
+
agentKey: v.string(),
|
|
2020
|
+
templateId: v.id("messageTemplates"),
|
|
2021
|
+
metadata: v.optional(v.record(v.string(), v.string())),
|
|
2022
|
+
nowMs: v.optional(v.number()),
|
|
2023
|
+
providerConfig: v.optional(providerConfigValidator),
|
|
2024
|
+
},
|
|
2025
|
+
returns: v.object({
|
|
2026
|
+
messageId: v.id("messageQueue"),
|
|
2027
|
+
usageCount: v.number(),
|
|
2028
|
+
}),
|
|
2029
|
+
handler: async (ctx, args) => {
|
|
2030
|
+
const template = await ctx.db.get(args.templateId);
|
|
2031
|
+
if (!template || !template.enabled) {
|
|
2032
|
+
throw new Error("Message template not found");
|
|
2033
|
+
}
|
|
2034
|
+
|
|
2035
|
+
const nowMs = args.nowMs ?? Date.now();
|
|
2036
|
+
const target = await resolveConversationTargetForUserAgent(
|
|
2037
|
+
ctx,
|
|
2038
|
+
args.consumerUserId,
|
|
2039
|
+
args.agentKey,
|
|
2040
|
+
true,
|
|
2041
|
+
);
|
|
2042
|
+
const providerUserId =
|
|
2043
|
+
target.telegramUserId ?? target.telegramChatId ?? args.consumerUserId;
|
|
2044
|
+
const usageCount = template.usageCount + 1;
|
|
2045
|
+
const messageId = await enqueueMessageRecord(ctx, {
|
|
2046
|
+
conversationId: target.conversationId,
|
|
2047
|
+
agentKey: args.agentKey,
|
|
2048
|
+
payload: {
|
|
2049
|
+
provider: target.provider,
|
|
2050
|
+
providerUserId,
|
|
2051
|
+
messageText: template.text,
|
|
2052
|
+
metadata: {
|
|
2053
|
+
...(args.metadata ?? {}),
|
|
2054
|
+
consumerUserId: args.consumerUserId,
|
|
2055
|
+
source: "message_template",
|
|
2056
|
+
templateId: String(template._id),
|
|
2057
|
+
templateKey: template.templateKey,
|
|
2058
|
+
...(target.telegramChatId ? { telegramChatId: target.telegramChatId } : {}),
|
|
2059
|
+
...(target.telegramUserId ? { telegramUserId: target.telegramUserId } : {}),
|
|
2060
|
+
},
|
|
2061
|
+
},
|
|
2062
|
+
scheduledFor: nowMs,
|
|
2063
|
+
providerConfig: args.providerConfig,
|
|
2064
|
+
});
|
|
2065
|
+
|
|
2066
|
+
await ctx.db.patch(template._id, {
|
|
2067
|
+
usageCount,
|
|
2068
|
+
});
|
|
2069
|
+
|
|
2070
|
+
return {
|
|
2071
|
+
messageId,
|
|
2072
|
+
usageCount,
|
|
2073
|
+
};
|
|
2074
|
+
},
|
|
2075
|
+
});
|
|
2076
|
+
|
|
2016
2077
|
export const listSnapshotsForConversation = query({
|
|
2017
2078
|
args: {
|
|
2018
2079
|
conversationId: v.string(),
|
package/src/component/schema.ts
CHANGED
|
@@ -410,6 +410,21 @@ export default defineSchema({
|
|
|
410
410
|
.index("by_companyId_and_templateKey", ["companyId", "templateKey"])
|
|
411
411
|
.index("by_companyId_and_enabled", ["companyId", "enabled"]),
|
|
412
412
|
|
|
413
|
+
messageTemplates: defineTable({
|
|
414
|
+
templateKey: v.string(),
|
|
415
|
+
title: v.string(),
|
|
416
|
+
text: v.string(),
|
|
417
|
+
tags: v.array(v.string()),
|
|
418
|
+
usageCount: v.number(),
|
|
419
|
+
enabled: v.boolean(),
|
|
420
|
+
createdBy: v.string(),
|
|
421
|
+
updatedBy: v.string(),
|
|
422
|
+
createdAt: v.number(),
|
|
423
|
+
updatedAt: v.number(),
|
|
424
|
+
})
|
|
425
|
+
.index("by_templateKey", ["templateKey"])
|
|
426
|
+
.index("by_enabled", ["enabled"]),
|
|
427
|
+
|
|
413
428
|
messagePushJobs: defineTable({
|
|
414
429
|
companyId: v.string(),
|
|
415
430
|
consumerUserId: v.string(),
|