@spinabot/brigade 1.0.1 → 1.0.2

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 (51) hide show
  1. package/convex/_generated/api.d.ts +85 -0
  2. package/convex/_generated/api.js +23 -0
  3. package/convex/_generated/dataModel.d.ts +60 -0
  4. package/convex/_generated/server.d.ts +143 -0
  5. package/convex/_generated/server.js +93 -0
  6. package/convex/admin.d.ts +57 -0
  7. package/convex/admin.ts +315 -0
  8. package/convex/auth.d.ts +159 -0
  9. package/convex/auth.ts +217 -0
  10. package/convex/blobs.d.ts +38 -0
  11. package/convex/blobs.ts +115 -0
  12. package/convex/channels.d.ts +150 -0
  13. package/convex/channels.ts +455 -0
  14. package/convex/config.d.ts +67 -0
  15. package/convex/config.ts +168 -0
  16. package/convex/cron.d.ts +237 -0
  17. package/convex/cron.ts +199 -0
  18. package/convex/execApprovals.d.ts +31 -0
  19. package/convex/execApprovals.ts +58 -0
  20. package/convex/extensions.d.ts +30 -0
  21. package/convex/extensions.ts +51 -0
  22. package/convex/health.d.ts +18 -0
  23. package/convex/health.ts +69 -0
  24. package/convex/instance.d.ts +34 -0
  25. package/convex/instance.ts +82 -0
  26. package/convex/logs.d.ts +178 -0
  27. package/convex/logs.ts +253 -0
  28. package/convex/memory.d.ts +354 -0
  29. package/convex/memory.ts +536 -0
  30. package/convex/messages.d.ts +124 -0
  31. package/convex/messages.ts +347 -0
  32. package/convex/org.d.ts +75 -0
  33. package/convex/org.ts +99 -0
  34. package/convex/schema.d.ts +1130 -0
  35. package/convex/schema.ts +847 -0
  36. package/convex/sessions.d.ts +100 -0
  37. package/convex/sessions.ts +105 -0
  38. package/convex/skills.d.ts +73 -0
  39. package/convex/skills.ts +102 -0
  40. package/convex/subagents.d.ts +214 -0
  41. package/convex/subagents.ts +99 -0
  42. package/convex/tsconfig.json +23 -0
  43. package/convex/whatsappAuth.d.ts +52 -0
  44. package/convex/whatsappAuth.ts +151 -0
  45. package/convex/workspace.d.ts +49 -0
  46. package/convex/workspace.ts +106 -0
  47. package/dist/buildstamp.json +1 -1
  48. package/package.json +7 -1
  49. package/scripts/convex-dev.mjs +321 -0
  50. package/scripts/convex-push.mjs +69 -0
  51. package/scripts/install-convex.mjs +123 -0
@@ -0,0 +1,237 @@
1
+ export declare const listJobs: import("convex/server").RegisteredQuery<"public", {
2
+ enabledOnly?: boolean | undefined;
3
+ ownerUserId: string;
4
+ }, Promise<{
5
+ _id: import("convex/values").GenericId<"cronJobs">;
6
+ _creationTime: number;
7
+ agentId?: string | undefined;
8
+ description?: string | undefined;
9
+ sessionKey?: string | undefined;
10
+ wakeMode?: string | undefined;
11
+ delivery?: ArrayBuffer | undefined;
12
+ failureAlert?: any;
13
+ deleteAfterRun?: boolean | undefined;
14
+ createdByChannelId?: string | undefined;
15
+ createdByConversationId?: string | undefined;
16
+ createdByAccountId?: string | undefined;
17
+ scheduleExpr?: string | undefined;
18
+ scheduleTz?: string | undefined;
19
+ scheduleStaggerMs?: number | undefined;
20
+ scheduleEveryMs?: number | undefined;
21
+ scheduleAnchorMs?: number | undefined;
22
+ scheduleAt?: number | undefined;
23
+ stateNextRunAtMs?: number | undefined;
24
+ stateLastRunAtMs?: number | undefined;
25
+ stateRunningAtMs?: number | undefined;
26
+ stateLastStatus?: string | undefined;
27
+ stateLastError?: string | undefined;
28
+ stateScheduleErrorCount?: number | undefined;
29
+ stateConsecutiveErrorCount?: number | undefined;
30
+ stateLastFailureAlertAtMs?: number | undefined;
31
+ stateLastDelivered?: boolean | undefined;
32
+ stateLastDeliveryStatus?: string | undefined;
33
+ stateLastDeliveryError?: string | undefined;
34
+ name: string;
35
+ enabled: boolean;
36
+ sessionTarget: string;
37
+ payload: ArrayBuffer;
38
+ jobId: string;
39
+ updatedAtMs: number;
40
+ createdByKind: "owner" | "channel" | "legacy";
41
+ ownerUserId: string;
42
+ scheduleKind: "at" | "every" | "cron";
43
+ createdAtMs: number;
44
+ }[]>>;
45
+ export declare const getJob: import("convex/server").RegisteredQuery<"public", {
46
+ jobId: string;
47
+ ownerUserId: string;
48
+ }, Promise<{
49
+ _id: import("convex/values").GenericId<"cronJobs">;
50
+ _creationTime: number;
51
+ agentId?: string | undefined;
52
+ description?: string | undefined;
53
+ sessionKey?: string | undefined;
54
+ wakeMode?: string | undefined;
55
+ delivery?: ArrayBuffer | undefined;
56
+ failureAlert?: any;
57
+ deleteAfterRun?: boolean | undefined;
58
+ createdByChannelId?: string | undefined;
59
+ createdByConversationId?: string | undefined;
60
+ createdByAccountId?: string | undefined;
61
+ scheduleExpr?: string | undefined;
62
+ scheduleTz?: string | undefined;
63
+ scheduleStaggerMs?: number | undefined;
64
+ scheduleEveryMs?: number | undefined;
65
+ scheduleAnchorMs?: number | undefined;
66
+ scheduleAt?: number | undefined;
67
+ stateNextRunAtMs?: number | undefined;
68
+ stateLastRunAtMs?: number | undefined;
69
+ stateRunningAtMs?: number | undefined;
70
+ stateLastStatus?: string | undefined;
71
+ stateLastError?: string | undefined;
72
+ stateScheduleErrorCount?: number | undefined;
73
+ stateConsecutiveErrorCount?: number | undefined;
74
+ stateLastFailureAlertAtMs?: number | undefined;
75
+ stateLastDelivered?: boolean | undefined;
76
+ stateLastDeliveryStatus?: string | undefined;
77
+ stateLastDeliveryError?: string | undefined;
78
+ name: string;
79
+ enabled: boolean;
80
+ sessionTarget: string;
81
+ payload: ArrayBuffer;
82
+ jobId: string;
83
+ updatedAtMs: number;
84
+ createdByKind: "owner" | "channel" | "legacy";
85
+ ownerUserId: string;
86
+ scheduleKind: "at" | "every" | "cron";
87
+ createdAtMs: number;
88
+ } | null>>;
89
+ export declare const insertJob: import("convex/server").RegisteredMutation<"public", {
90
+ agentId?: string | undefined;
91
+ description?: string | undefined;
92
+ sessionKey?: string | undefined;
93
+ wakeMode?: string | undefined;
94
+ delivery?: ArrayBuffer | undefined;
95
+ failureAlert?: any;
96
+ deleteAfterRun?: boolean | undefined;
97
+ updatedAtMs?: number | undefined;
98
+ createdByChannelId?: string | undefined;
99
+ createdByConversationId?: string | undefined;
100
+ createdByAccountId?: string | undefined;
101
+ scheduleExpr?: string | undefined;
102
+ scheduleTz?: string | undefined;
103
+ scheduleStaggerMs?: number | undefined;
104
+ scheduleEveryMs?: number | undefined;
105
+ scheduleAnchorMs?: number | undefined;
106
+ scheduleAt?: number | undefined;
107
+ createdAtMs?: number | undefined;
108
+ stateNextRunAtMs?: number | undefined;
109
+ stateLastRunAtMs?: number | undefined;
110
+ stateRunningAtMs?: number | undefined;
111
+ stateLastStatus?: string | undefined;
112
+ stateLastError?: string | undefined;
113
+ stateScheduleErrorCount?: number | undefined;
114
+ stateConsecutiveErrorCount?: number | undefined;
115
+ stateLastFailureAlertAtMs?: number | undefined;
116
+ stateLastDelivered?: boolean | undefined;
117
+ stateLastDeliveryStatus?: string | undefined;
118
+ stateLastDeliveryError?: string | undefined;
119
+ name: string;
120
+ enabled: boolean;
121
+ sessionTarget: string;
122
+ payload: ArrayBuffer;
123
+ jobId: string;
124
+ createdByKind: "owner" | "channel" | "legacy";
125
+ ownerUserId: string;
126
+ scheduleKind: "at" | "every" | "cron";
127
+ }, Promise<void>>;
128
+ export declare const patchJob: import("convex/server").RegisteredMutation<"public", {
129
+ unset?: string[] | undefined;
130
+ jobId: string;
131
+ ownerUserId: string;
132
+ patch: any;
133
+ }, Promise<{
134
+ _id: import("convex/values").GenericId<"cronJobs">;
135
+ _creationTime: number;
136
+ agentId?: string | undefined;
137
+ description?: string | undefined;
138
+ sessionKey?: string | undefined;
139
+ wakeMode?: string | undefined;
140
+ delivery?: ArrayBuffer | undefined;
141
+ failureAlert?: any;
142
+ deleteAfterRun?: boolean | undefined;
143
+ createdByChannelId?: string | undefined;
144
+ createdByConversationId?: string | undefined;
145
+ createdByAccountId?: string | undefined;
146
+ scheduleExpr?: string | undefined;
147
+ scheduleTz?: string | undefined;
148
+ scheduleStaggerMs?: number | undefined;
149
+ scheduleEveryMs?: number | undefined;
150
+ scheduleAnchorMs?: number | undefined;
151
+ scheduleAt?: number | undefined;
152
+ stateNextRunAtMs?: number | undefined;
153
+ stateLastRunAtMs?: number | undefined;
154
+ stateRunningAtMs?: number | undefined;
155
+ stateLastStatus?: string | undefined;
156
+ stateLastError?: string | undefined;
157
+ stateScheduleErrorCount?: number | undefined;
158
+ stateConsecutiveErrorCount?: number | undefined;
159
+ stateLastFailureAlertAtMs?: number | undefined;
160
+ stateLastDelivered?: boolean | undefined;
161
+ stateLastDeliveryStatus?: string | undefined;
162
+ stateLastDeliveryError?: string | undefined;
163
+ name: string;
164
+ enabled: boolean;
165
+ sessionTarget: string;
166
+ payload: ArrayBuffer;
167
+ jobId: string;
168
+ updatedAtMs: number;
169
+ createdByKind: "owner" | "channel" | "legacy";
170
+ ownerUserId: string;
171
+ scheduleKind: "at" | "every" | "cron";
172
+ createdAtMs: number;
173
+ } | null>>;
174
+ export declare const deleteJob: import("convex/server").RegisteredMutation<"public", {
175
+ jobId: string;
176
+ ownerUserId: string;
177
+ }, Promise<boolean>>;
178
+ export declare const markRunning: import("convex/server").RegisteredMutation<"public", {
179
+ jobId: string;
180
+ ownerUserId: string;
181
+ runningAtMs: number;
182
+ }, Promise<boolean>>;
183
+ export declare const appendRunLog: import("convex/server").RegisteredMutation<"public", {
184
+ error?: string | undefined;
185
+ provider?: string | undefined;
186
+ delivered?: boolean | undefined;
187
+ sessionKey?: string | undefined;
188
+ nextRunAtMs?: number | undefined;
189
+ model?: string | undefined;
190
+ sessionId?: string | undefined;
191
+ runAtMs?: number | undefined;
192
+ summary?: ArrayBuffer | undefined;
193
+ durationMs?: number | undefined;
194
+ deliveryStatus?: string | undefined;
195
+ deliveryError?: string | undefined;
196
+ usageInput?: number | undefined;
197
+ usageOutput?: number | undefined;
198
+ usageCacheRead?: number | undefined;
199
+ usageCacheWrite?: number | undefined;
200
+ usageTotalTokens?: number | undefined;
201
+ usageCostUsd?: number | undefined;
202
+ jobId: string;
203
+ status: "error" | "ok" | "skipped";
204
+ ts: number;
205
+ ownerUserId: string;
206
+ }, Promise<void>>;
207
+ export declare const listRunLog: import("convex/server").RegisteredQuery<"public", {
208
+ limit?: number | undefined;
209
+ jobId: string;
210
+ ownerUserId: string;
211
+ }, Promise<{
212
+ _id: import("convex/values").GenericId<"cronRuns">;
213
+ _creationTime: number;
214
+ error?: string | undefined;
215
+ provider?: string | undefined;
216
+ delivered?: boolean | undefined;
217
+ sessionKey?: string | undefined;
218
+ nextRunAtMs?: number | undefined;
219
+ model?: string | undefined;
220
+ sessionId?: string | undefined;
221
+ runAtMs?: number | undefined;
222
+ summary?: ArrayBuffer | undefined;
223
+ durationMs?: number | undefined;
224
+ deliveryStatus?: string | undefined;
225
+ deliveryError?: string | undefined;
226
+ usageInput?: number | undefined;
227
+ usageOutput?: number | undefined;
228
+ usageCacheRead?: number | undefined;
229
+ usageCacheWrite?: number | undefined;
230
+ usageTotalTokens?: number | undefined;
231
+ usageCostUsd?: number | undefined;
232
+ jobId: string;
233
+ status: "error" | "ok" | "skipped";
234
+ ts: number;
235
+ ownerUserId: string;
236
+ }[]>>;
237
+ //# sourceMappingURL=cron.d.ts.map
package/convex/cron.ts ADDED
@@ -0,0 +1,199 @@
1
+ // convex/cron.ts — cronJobs + cronRuns + cronServiceState
2
+ import { v } from "convex/values";
3
+ import { mutation, query } from "./_generated/server.js";
4
+
5
+ const ScheduleKind = v.union(v.literal("cron"), v.literal("every"), v.literal("at"));
6
+ const RunStatus = v.union(v.literal("ok"), v.literal("error"), v.literal("skipped"));
7
+ const CreatedByKind = v.union(v.literal("owner"), v.literal("channel"), v.literal("legacy"));
8
+
9
+ export const listJobs = query({
10
+ args: { ownerUserId: v.string(), enabledOnly: v.optional(v.boolean()) },
11
+ handler: async (ctx, args) => {
12
+ if (args.enabledOnly) {
13
+ return ctx.db
14
+ .query("cronJobs")
15
+ .withIndex("by_owner_enabled_next", (q) =>
16
+ q.eq("ownerUserId", args.ownerUserId).eq("enabled", true),
17
+ )
18
+ .collect();
19
+ }
20
+ return ctx.db
21
+ .query("cronJobs")
22
+ .withIndex("by_owner_job", (q) => q.eq("ownerUserId", args.ownerUserId))
23
+ .collect();
24
+ },
25
+ });
26
+
27
+ export const getJob = query({
28
+ args: { ownerUserId: v.string(), jobId: v.string() },
29
+ handler: async (ctx, args) => {
30
+ return ctx.db
31
+ .query("cronJobs")
32
+ .withIndex("by_owner_job", (q) =>
33
+ q.eq("ownerUserId", args.ownerUserId).eq("jobId", args.jobId),
34
+ )
35
+ .first();
36
+ },
37
+ });
38
+
39
+ export const insertJob = mutation({
40
+ args: {
41
+ jobId: v.string(),
42
+ ownerUserId: v.string(),
43
+ name: v.string(),
44
+ description: v.optional(v.string()),
45
+ enabled: v.boolean(),
46
+ agentId: v.optional(v.string()),
47
+ sessionKey: v.optional(v.string()),
48
+ scheduleKind: ScheduleKind,
49
+ scheduleExpr: v.optional(v.string()),
50
+ scheduleTz: v.optional(v.string()),
51
+ scheduleStaggerMs: v.optional(v.number()),
52
+ scheduleEveryMs: v.optional(v.number()),
53
+ scheduleAnchorMs: v.optional(v.number()),
54
+ scheduleAt: v.optional(v.number()),
55
+ sessionTarget: v.string(),
56
+ wakeMode: v.optional(v.string()),
57
+ payload: v.bytes(),
58
+ delivery: v.optional(v.bytes()),
59
+ failureAlert: v.optional(v.any()),
60
+ deleteAfterRun: v.optional(v.boolean()),
61
+ createdByKind: CreatedByKind,
62
+ createdByChannelId: v.optional(v.string()),
63
+ createdByConversationId: v.optional(v.string()),
64
+ createdByAccountId: v.optional(v.string()),
65
+ // Honour client timestamps so a filesystem→convex migration preserves
66
+ // the original create/update times; default to now for fresh jobs.
67
+ createdAtMs: v.optional(v.number()),
68
+ updatedAtMs: v.optional(v.number()),
69
+ // Run-time state — carried through on migration so failure counts +
70
+ // last-fire bookkeeping survive the move.
71
+ stateNextRunAtMs: v.optional(v.number()),
72
+ stateLastRunAtMs: v.optional(v.number()),
73
+ stateRunningAtMs: v.optional(v.number()),
74
+ stateLastStatus: v.optional(v.string()),
75
+ stateLastError: v.optional(v.string()),
76
+ stateScheduleErrorCount: v.optional(v.number()),
77
+ stateConsecutiveErrorCount: v.optional(v.number()),
78
+ stateLastFailureAlertAtMs: v.optional(v.number()),
79
+ stateLastDelivered: v.optional(v.boolean()),
80
+ stateLastDeliveryStatus: v.optional(v.string()),
81
+ stateLastDeliveryError: v.optional(v.string()),
82
+ },
83
+ handler: async (ctx, args) => {
84
+ const now = Date.now();
85
+ const { createdAtMs, updatedAtMs, ...rest } = args;
86
+ await ctx.db.insert("cronJobs", {
87
+ ...rest,
88
+ createdAtMs: createdAtMs ?? now,
89
+ updatedAtMs: updatedAtMs ?? now,
90
+ });
91
+ },
92
+ });
93
+
94
+ export const patchJob = mutation({
95
+ args: {
96
+ ownerUserId: v.string(),
97
+ jobId: v.string(),
98
+ patch: v.any(),
99
+ // Column names to DELETE. Convex's arg serialiser strips
100
+ // `undefined`-valued object fields before they reach the handler, so a
101
+ // column can't be cleared from the client by patching it to undefined.
102
+ // We re-introduce the undefined HERE (server-side) where `db.patch`
103
+ // honours it as a field deletion.
104
+ unset: v.optional(v.array(v.string())),
105
+ },
106
+ handler: async (ctx, args) => {
107
+ const existing = await ctx.db
108
+ .query("cronJobs")
109
+ .withIndex("by_owner_job", (q) =>
110
+ q.eq("ownerUserId", args.ownerUserId).eq("jobId", args.jobId),
111
+ )
112
+ .first();
113
+ if (!existing) throw new Error(`cron: job ${args.jobId} not found`);
114
+ const patchObj: Record<string, unknown> = {
115
+ ...(args.patch as Record<string, unknown>),
116
+ updatedAtMs: Date.now(),
117
+ };
118
+ if (args.unset) {
119
+ for (const col of args.unset) patchObj[col] = undefined;
120
+ }
121
+ await ctx.db.patch(existing._id, patchObj);
122
+ return await ctx.db.get(existing._id);
123
+ },
124
+ });
125
+
126
+ export const deleteJob = mutation({
127
+ args: { ownerUserId: v.string(), jobId: v.string() },
128
+ handler: async (ctx, args) => {
129
+ const existing = await ctx.db
130
+ .query("cronJobs")
131
+ .withIndex("by_owner_job", (q) =>
132
+ q.eq("ownerUserId", args.ownerUserId).eq("jobId", args.jobId),
133
+ )
134
+ .first();
135
+ if (!existing) return false;
136
+ await ctx.db.delete(existing._id);
137
+ return true;
138
+ },
139
+ });
140
+
141
+ export const markRunning = mutation({
142
+ args: { ownerUserId: v.string(), jobId: v.string(), runningAtMs: v.number() },
143
+ handler: async (ctx, args) => {
144
+ const job = await ctx.db
145
+ .query("cronJobs")
146
+ .withIndex("by_owner_job", (q) =>
147
+ q.eq("ownerUserId", args.ownerUserId).eq("jobId", args.jobId),
148
+ )
149
+ .first();
150
+ if (!job) return false;
151
+ if (job.stateRunningAtMs && job.stateRunningAtMs > 0) return false;
152
+ await ctx.db.patch(job._id, { stateRunningAtMs: args.runningAtMs });
153
+ return true;
154
+ },
155
+ });
156
+
157
+ export const appendRunLog = mutation({
158
+ args: {
159
+ ownerUserId: v.string(),
160
+ jobId: v.string(),
161
+ ts: v.number(),
162
+ status: RunStatus,
163
+ error: v.optional(v.string()),
164
+ summary: v.optional(v.bytes()),
165
+ delivered: v.optional(v.boolean()),
166
+ deliveryStatus: v.optional(v.string()),
167
+ deliveryError: v.optional(v.string()),
168
+ sessionId: v.optional(v.string()),
169
+ sessionKey: v.optional(v.string()),
170
+ runAtMs: v.optional(v.number()),
171
+ durationMs: v.optional(v.number()),
172
+ nextRunAtMs: v.optional(v.number()),
173
+ model: v.optional(v.string()),
174
+ provider: v.optional(v.string()),
175
+ usageInput: v.optional(v.number()),
176
+ usageOutput: v.optional(v.number()),
177
+ usageCacheRead: v.optional(v.number()),
178
+ usageCacheWrite: v.optional(v.number()),
179
+ usageTotalTokens: v.optional(v.number()),
180
+ usageCostUsd: v.optional(v.number()),
181
+ },
182
+ handler: async (ctx, args) => {
183
+ await ctx.db.insert("cronRuns", args);
184
+ },
185
+ });
186
+
187
+ export const listRunLog = query({
188
+ args: { ownerUserId: v.string(), jobId: v.string(), limit: v.optional(v.number()) },
189
+ handler: async (ctx, args) => {
190
+ const limit = args.limit && args.limit > 0 ? args.limit : 50;
191
+ return ctx.db
192
+ .query("cronRuns")
193
+ .withIndex("by_owner_job_ts", (q) =>
194
+ q.eq("ownerUserId", args.ownerUserId).eq("jobId", args.jobId),
195
+ )
196
+ .order("desc")
197
+ .take(limit);
198
+ },
199
+ });
@@ -0,0 +1,31 @@
1
+ export declare const list: import("convex/server").RegisteredQuery<"public", {
2
+ agentId: string;
3
+ ownerId: string;
4
+ }, Promise<{
5
+ _id: import("convex/values").GenericId<"execApprovals">;
6
+ _creationTime: number;
7
+ agentId: string;
8
+ value: string;
9
+ kind: "exact" | "pattern";
10
+ createdAt: number;
11
+ ownerId: string;
12
+ valueNormalised: string;
13
+ }[]>>;
14
+ export declare const insert: import("convex/server").RegisteredMutation<"public", {
15
+ agentId: string;
16
+ value: string;
17
+ kind: "exact" | "pattern";
18
+ ownerId: string;
19
+ valueNormalised: string;
20
+ }, Promise<{
21
+ inserted: boolean;
22
+ }>>;
23
+ export declare const remove: import("convex/server").RegisteredMutation<"public", {
24
+ agentId: string;
25
+ ownerId: string;
26
+ valueNormalised: string;
27
+ }, Promise<{
28
+ removedCommands: number;
29
+ removedPatterns: number;
30
+ }>>;
31
+ //# sourceMappingURL=execApprovals.d.ts.map
@@ -0,0 +1,58 @@
1
+ // convex/execApprovals.ts
2
+ import { v } from "convex/values";
3
+ import { mutation, query } from "./_generated/server.js";
4
+
5
+ const Kind = v.union(v.literal("exact"), v.literal("pattern"));
6
+
7
+ export const list = query({
8
+ args: { ownerId: v.string(), agentId: v.string() },
9
+ handler: async (ctx, args) => {
10
+ return ctx.db
11
+ .query("execApprovals")
12
+ .withIndex("by_owner_agent_kind", (q) =>
13
+ q.eq("ownerId", args.ownerId).eq("agentId", args.agentId),
14
+ )
15
+ .collect();
16
+ },
17
+ });
18
+
19
+ export const insert = mutation({
20
+ args: { ownerId: v.string(), agentId: v.string(), kind: Kind, value: v.string(), valueNormalised: v.string() },
21
+ handler: async (ctx, args) => {
22
+ const existing = await ctx.db
23
+ .query("execApprovals")
24
+ .withIndex("by_owner_agent_value", (q) =>
25
+ q
26
+ .eq("ownerId", args.ownerId)
27
+ .eq("agentId", args.agentId)
28
+ .eq("valueNormalised", args.valueNormalised),
29
+ )
30
+ .first();
31
+ if (existing) return { inserted: false };
32
+ await ctx.db.insert("execApprovals", { ...args, createdAt: Date.now() });
33
+ return { inserted: true };
34
+ },
35
+ });
36
+
37
+ export const remove = mutation({
38
+ args: { ownerId: v.string(), agentId: v.string(), valueNormalised: v.string() },
39
+ handler: async (ctx, args) => {
40
+ const rows = await ctx.db
41
+ .query("execApprovals")
42
+ .withIndex("by_owner_agent_value", (q) =>
43
+ q
44
+ .eq("ownerId", args.ownerId)
45
+ .eq("agentId", args.agentId)
46
+ .eq("valueNormalised", args.valueNormalised),
47
+ )
48
+ .collect();
49
+ let removedCommands = 0;
50
+ let removedPatterns = 0;
51
+ for (const r of rows) {
52
+ if (r.kind === "exact") removedCommands += 1;
53
+ else removedPatterns += 1;
54
+ await ctx.db.delete(r._id);
55
+ }
56
+ return { removedCommands, removedPatterns };
57
+ },
58
+ });
@@ -0,0 +1,30 @@
1
+ export declare const list: import("convex/server").RegisteredQuery<"public", {}, Promise<{
2
+ _id: import("convex/values").GenericId<"extensions">;
3
+ _creationTime: number;
4
+ config?: ArrayBuffer | undefined;
5
+ bundleBytes?: ArrayBuffer | undefined;
6
+ manifest?: any;
7
+ bundleSha?: string | undefined;
8
+ enabled: boolean;
9
+ createdBy: string;
10
+ createdAt: number;
11
+ origin: "bundled" | "user";
12
+ updatedAt: number;
13
+ moduleId: string;
14
+ sourceLabel: string;
15
+ }[]>>;
16
+ export declare const upsert: import("convex/server").RegisteredMutation<"public", {
17
+ config?: ArrayBuffer | undefined;
18
+ bundleBytes?: ArrayBuffer | undefined;
19
+ manifest?: any;
20
+ bundleSha?: string | undefined;
21
+ enabled: boolean;
22
+ createdBy: string;
23
+ origin: "bundled" | "user";
24
+ moduleId: string;
25
+ sourceLabel: string;
26
+ }, Promise<void>>;
27
+ export declare const remove: import("convex/server").RegisteredMutation<"public", {
28
+ moduleId: string;
29
+ }, Promise<boolean>>;
30
+ //# sourceMappingURL=extensions.d.ts.map
@@ -0,0 +1,51 @@
1
+ // convex/extensions.ts
2
+ import { v } from "convex/values";
3
+ import { mutation, query } from "./_generated/server.js";
4
+
5
+ const Origin = v.union(v.literal("bundled"), v.literal("user"));
6
+
7
+ export const list = query({
8
+ args: {},
9
+ handler: async (ctx) => {
10
+ return ctx.db.query("extensions").collect();
11
+ },
12
+ });
13
+
14
+ export const upsert = mutation({
15
+ args: {
16
+ moduleId: v.string(),
17
+ origin: Origin,
18
+ bundleBytes: v.optional(v.bytes()),
19
+ sourceLabel: v.string(),
20
+ manifest: v.optional(v.any()),
21
+ enabled: v.boolean(),
22
+ config: v.optional(v.bytes()),
23
+ bundleSha: v.optional(v.string()),
24
+ createdBy: v.string(),
25
+ },
26
+ handler: async (ctx, args) => {
27
+ const existing = await ctx.db
28
+ .query("extensions")
29
+ .withIndex("by_moduleId", (q) => q.eq("moduleId", args.moduleId))
30
+ .first();
31
+ const now = Date.now();
32
+ if (existing) {
33
+ await ctx.db.replace(existing._id, { ...args, createdAt: existing.createdAt, updatedAt: now });
34
+ } else {
35
+ await ctx.db.insert("extensions", { ...args, createdAt: now, updatedAt: now });
36
+ }
37
+ },
38
+ });
39
+
40
+ export const remove = mutation({
41
+ args: { moduleId: v.string() },
42
+ handler: async (ctx, args) => {
43
+ const existing = await ctx.db
44
+ .query("extensions")
45
+ .withIndex("by_moduleId", (q) => q.eq("moduleId", args.moduleId))
46
+ .first();
47
+ if (!existing) return false;
48
+ await ctx.db.delete(existing._id);
49
+ return true;
50
+ },
51
+ });
@@ -0,0 +1,18 @@
1
+ export declare const ping: import("convex/server").RegisteredQuery<"public", {}, Promise<{
2
+ ok: boolean;
3
+ schemaVersion: number;
4
+ hasConfig: boolean;
5
+ now: number;
6
+ }>>;
7
+ export declare const BUNDLE_VERSION = 7;
8
+ export declare const bundleVersion: import("convex/server").RegisteredQuery<"public", {}, Promise<number>>;
9
+ export declare const getMeta: import("convex/server").RegisteredQuery<"public", {
10
+ key: string;
11
+ }, Promise<string | null>>;
12
+ export declare const setMeta: import("convex/server").RegisteredMutation<"public", {
13
+ key: string;
14
+ value: string;
15
+ }, Promise<{
16
+ updated: boolean;
17
+ }>>;
18
+ //# sourceMappingURL=health.d.ts.map
@@ -0,0 +1,69 @@
1
+ // convex/health.ts
2
+ //
3
+ // Convex-side liveness probe. ConvexBrigadeStore.healthcheck() calls this
4
+ // from filesystem-mode boot to confirm the backend is reachable + the
5
+ // schema is deployed. Returns the schema version + the number of seeded
6
+ // brigadeConfig rows so a freshly-deployed-but-empty backend reports
7
+ // distinctly from a populated one.
8
+
9
+ import { v } from "convex/values";
10
+ import { mutation, query } from "./_generated/server.js";
11
+
12
+ export const ping = query({
13
+ args: {},
14
+ handler: async (ctx) => {
15
+ const configRows = await ctx.db.query("brigadeConfig").take(1);
16
+ return {
17
+ ok: true,
18
+ schemaVersion: 2,
19
+ hasConfig: configRows.length > 0,
20
+ now: Date.now(),
21
+ };
22
+ },
23
+ });
24
+
25
+ // Brigade function-bundle version. The Node client (verifyConvexBundleVersion
26
+ // in src/storage/boot.ts) requires remote >= its expected constant at every
27
+ // convex boot, so a backend serving an older push fails the boot with ONE
28
+ // clear "run npm run convex:push" error instead of per-domain "Could not
29
+ // find public function" spam (auth/memory/channels hydration failures +
30
+ // per-turn transcript-flush errors). BUMP THIS — and the twin
31
+ // EXPECTED_CONVEX_BUNDLE_VERSION in src/storage/boot.ts — together whenever
32
+ // convex/ functions or the schema change shape.
33
+ export const BUNDLE_VERSION = 7;
34
+
35
+ export const bundleVersion = query({
36
+ args: {},
37
+ handler: async () => BUNDLE_VERSION,
38
+ });
39
+
40
+ // ============================================================================
41
+ // systemMeta — small singleton facts (encryption-key fingerprint, markers)
42
+ // ============================================================================
43
+
44
+ export const getMeta = query({
45
+ args: { key: v.string() },
46
+ handler: async (ctx, args) => {
47
+ const row = await ctx.db
48
+ .query("systemMeta")
49
+ .withIndex("by_key", (q) => q.eq("key", args.key))
50
+ .first();
51
+ return row?.value ?? null;
52
+ },
53
+ });
54
+
55
+ export const setMeta = mutation({
56
+ args: { key: v.string(), value: v.string() },
57
+ handler: async (ctx, args) => {
58
+ const existing = await ctx.db
59
+ .query("systemMeta")
60
+ .withIndex("by_key", (q) => q.eq("key", args.key))
61
+ .first();
62
+ if (existing) {
63
+ await ctx.db.replace(existing._id, { ...args, updatedAt: Date.now() });
64
+ return { updated: true };
65
+ }
66
+ await ctx.db.insert("systemMeta", { ...args, updatedAt: Date.now() });
67
+ return { updated: false };
68
+ },
69
+ });