@vantageos/agent-engine 0.1.0-alpha.8 → 0.3.0-alpha.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/README.md +33 -8
- package/dist/component/_generated/api.d.ts +14 -3
- package/dist/component/_generated/api.js +0 -25
- package/dist/component/_generated/component.d.ts +369 -73
- package/dist/component/_generated/component.js +0 -9
- package/dist/component/_generated/dataModel.js +0 -9
- package/dist/component/_generated/server.js +0 -69
- package/dist/component/convex.config.js +0 -44
- package/dist/component/engine/agents.d.ts +3 -3
- package/dist/component/engine/agents.js +0 -7
- package/dist/component/engine/awaitEventTimeout.d.ts +1 -1
- package/dist/component/engine/awaitEventTimeout.js +16 -26
- package/dist/component/engine/connections.d.ts +74 -0
- package/dist/component/engine/connections.js +254 -0
- package/dist/component/engine/delegation.d.ts +5 -5
- package/dist/component/engine/delegation.js +0 -74
- package/dist/component/engine/directory.js +0 -18
- package/dist/component/engine/durableJob.d.ts +24 -17
- package/dist/component/engine/durableJob.js +182 -138
- package/dist/component/engine/mandates.d.ts +2 -2
- package/dist/component/engine/mandates.js +0 -59
- package/dist/component/engine/queue.d.ts +97 -0
- package/dist/component/engine/queue.js +201 -0
- package/dist/component/engine/resolvers.d.ts +1 -1
- package/dist/component/engine/resolvers.js +6 -18
- package/dist/component/engine/sandboxCallback.d.ts +2 -2
- package/dist/component/engine/sandboxCallback.js +2 -79
- package/dist/component/engine/sandboxConfigProvider.d.ts +9 -0
- package/dist/component/engine/sandboxConfigProvider.js +32 -0
- package/dist/component/engine/sessions.d.ts +28 -10
- package/dist/component/engine/sessions.js +130 -170
- package/dist/component/engine/streams.d.ts +47 -0
- package/dist/component/engine/streams.js +162 -0
- package/dist/component/engine/toolApprovals.d.ts +1 -1
- package/dist/component/engine/toolApprovals.js +12 -37
- package/dist/component/engine/vpConnection.js +0 -40
- package/dist/component/engine/wakeup.d.ts +7 -9
- package/dist/component/engine/wakeup.js +54 -127
- package/dist/component/engine/workflows.d.ts +26 -8
- package/dist/component/engine/workflows.js +59 -64
- package/dist/component/engine/world.d.ts +250 -0
- package/dist/component/engine/world.js +689 -0
- package/dist/component/lib/auth.js +0 -26
- package/dist/component/lib/connectionSecret.d.ts +5 -0
- package/dist/component/lib/connectionSecret.js +32 -0
- package/dist/component/schema.d.ts +240 -175
- package/dist/component/schema.js +134 -294
- package/package.json +21 -6
- package/dist/component/engine/habits.d.ts +0 -70
- package/dist/component/engine/habits.js +0 -218
- package/dist/component/testFixtures/callerSteps.d.ts +0 -20
- package/dist/component/testFixtures/callerSteps.js +0 -90
- package/dist/component/testFixtures/delegationDemoWorkflow.d.ts +0 -18
- package/dist/component/testFixtures/delegationDemoWorkflow.js +0 -75
- package/dist/component/testFixtures/sandboxDemoWorkflow.d.ts +0 -19
- package/dist/component/testFixtures/sandboxDemoWorkflow.js +0 -69
- package/dist/component/testFixtures/vpTool.d.ts +0 -4
- package/dist/component/testFixtures/vpTool.js +0 -25
- package/dist/component/testFixtures/wakeupDemoWorkflow.d.ts +0 -19
- package/dist/component/testFixtures/wakeupDemoWorkflow.js +0 -77
- package/dist/component/testFixtures/wakeupWork.d.ts +0 -8
- package/dist/component/testFixtures/wakeupWork.js +0 -21
|
@@ -0,0 +1,689 @@
|
|
|
1
|
+
import { v } from "convex/values";
|
|
2
|
+
import { mutation, query, } from "../_generated/server.js";
|
|
3
|
+
import { assertSameOrg, requireOrgId } from "../lib/auth.js";
|
|
4
|
+
function pad26(n) {
|
|
5
|
+
return String(n).padStart(26, "0");
|
|
6
|
+
}
|
|
7
|
+
function slotToEventId(seq) {
|
|
8
|
+
return `evnt_${pad26(seq)}`;
|
|
9
|
+
}
|
|
10
|
+
function newRunId() {
|
|
11
|
+
return `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 14)}`;
|
|
12
|
+
}
|
|
13
|
+
const eventResult = v.object({
|
|
14
|
+
eventId: v.string(),
|
|
15
|
+
seq: v.number(),
|
|
16
|
+
runId: v.string(),
|
|
17
|
+
eventType: v.string(),
|
|
18
|
+
stepId: v.optional(v.string()),
|
|
19
|
+
hookId: v.optional(v.string()),
|
|
20
|
+
correlationId: v.optional(v.string()),
|
|
21
|
+
dataJson: v.string(),
|
|
22
|
+
createdAt: v.number(),
|
|
23
|
+
});
|
|
24
|
+
const runResult = v.object({
|
|
25
|
+
runId: v.string(),
|
|
26
|
+
status: v.union(v.literal("running"), v.literal("completed"), v.literal("failed"), v.literal("cancelled")),
|
|
27
|
+
eventCount: v.number(),
|
|
28
|
+
createdAt: v.number(),
|
|
29
|
+
updatedAt: v.number(),
|
|
30
|
+
});
|
|
31
|
+
const stepResult = v.object({
|
|
32
|
+
runId: v.string(),
|
|
33
|
+
stepId: v.string(),
|
|
34
|
+
status: v.union(v.literal("running"), v.literal("completed"), v.literal("failed")),
|
|
35
|
+
dataJson: v.optional(v.string()),
|
|
36
|
+
createdAt: v.number(),
|
|
37
|
+
updatedAt: v.number(),
|
|
38
|
+
});
|
|
39
|
+
const hookResult = v.object({
|
|
40
|
+
runId: v.string(),
|
|
41
|
+
hookId: v.string(),
|
|
42
|
+
token: v.string(),
|
|
43
|
+
status: v.union(v.literal("pending"), v.literal("disposed")),
|
|
44
|
+
createdAt: v.number(),
|
|
45
|
+
updatedAt: v.number(),
|
|
46
|
+
});
|
|
47
|
+
export async function appendEventCore(ctx, args) {
|
|
48
|
+
const orgId = requireOrgId(args.orgId);
|
|
49
|
+
const now = Date.now();
|
|
50
|
+
let runId = args.runId;
|
|
51
|
+
let runDoc;
|
|
52
|
+
if (runId === null) {
|
|
53
|
+
if (args.eventType !== "run_created") {
|
|
54
|
+
throw new Error("Validation: a null runId is only valid for a run_created event");
|
|
55
|
+
}
|
|
56
|
+
if (args.correlationId !== undefined) {
|
|
57
|
+
const correlationId = args.correlationId;
|
|
58
|
+
const prior = await ctx.db
|
|
59
|
+
.query("worldEvents")
|
|
60
|
+
.withIndex("by_org_correlation", (q) => q.eq("orgId", orgId).eq("correlationId", correlationId))
|
|
61
|
+
.collect();
|
|
62
|
+
const priorCreated = prior.find((e) => e.eventType === "run_created");
|
|
63
|
+
if (priorCreated) {
|
|
64
|
+
const existingRun = await ctx.db
|
|
65
|
+
.query("worldRuns")
|
|
66
|
+
.withIndex("by_org_run", (q) => q.eq("orgId", orgId).eq("runId", priorCreated.runId))
|
|
67
|
+
.unique();
|
|
68
|
+
assertSameOrg(orgId, existingRun, "Run not found");
|
|
69
|
+
return {
|
|
70
|
+
event: {
|
|
71
|
+
eventId: slotToEventId(priorCreated.seq),
|
|
72
|
+
seq: priorCreated.seq,
|
|
73
|
+
runId: priorCreated.runId,
|
|
74
|
+
eventType: priorCreated.eventType,
|
|
75
|
+
stepId: priorCreated.stepId,
|
|
76
|
+
hookId: priorCreated.hookId,
|
|
77
|
+
correlationId: priorCreated.correlationId,
|
|
78
|
+
dataJson: priorCreated.dataJson,
|
|
79
|
+
createdAt: priorCreated.createdAt,
|
|
80
|
+
},
|
|
81
|
+
run: {
|
|
82
|
+
runId: existingRun.runId,
|
|
83
|
+
status: existingRun.status,
|
|
84
|
+
eventCount: existingRun.eventCount,
|
|
85
|
+
createdAt: existingRun.createdAt,
|
|
86
|
+
updatedAt: existingRun.updatedAt,
|
|
87
|
+
},
|
|
88
|
+
step: null,
|
|
89
|
+
hook: null,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
runId = newRunId();
|
|
94
|
+
const runDocId = await ctx.db.insert("worldRuns", {
|
|
95
|
+
orgId,
|
|
96
|
+
runId,
|
|
97
|
+
status: "running",
|
|
98
|
+
eventCount: 0,
|
|
99
|
+
createdAt: now,
|
|
100
|
+
updatedAt: now,
|
|
101
|
+
});
|
|
102
|
+
runDoc = await ctx.db.get(runDocId);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
runDoc = await ctx.db
|
|
106
|
+
.query("worldRuns")
|
|
107
|
+
.withIndex("by_org_run", (q) => q.eq("orgId", orgId).eq("runId", runId))
|
|
108
|
+
.unique();
|
|
109
|
+
assertSameOrg(orgId, runDoc, "Run not found");
|
|
110
|
+
}
|
|
111
|
+
if (!runDoc) {
|
|
112
|
+
throw new Error("Run not found");
|
|
113
|
+
}
|
|
114
|
+
let stepAlreadyExists = false;
|
|
115
|
+
if (args.eventType === "step_created") {
|
|
116
|
+
if (!args.stepId) {
|
|
117
|
+
throw new Error("Validation: step_created/step_started requires stepId");
|
|
118
|
+
}
|
|
119
|
+
const existingStep = await ctx.db
|
|
120
|
+
.query("worldSteps")
|
|
121
|
+
.withIndex("by_org_run_step", (q) => q.eq("orgId", orgId).eq("runId", runId).eq("stepId", args.stepId))
|
|
122
|
+
.unique();
|
|
123
|
+
if (existingStep) {
|
|
124
|
+
assertSameOrg(orgId, existingStep, "Step not found");
|
|
125
|
+
stepAlreadyExists = true;
|
|
126
|
+
const journal = await ctx.db
|
|
127
|
+
.query("worldEvents")
|
|
128
|
+
.withIndex("by_org_run", (q) => q.eq("orgId", orgId).eq("runId", runId))
|
|
129
|
+
.collect();
|
|
130
|
+
const prior = journal.find((e) => e.eventType === "step_created" && e.stepId === args.stepId);
|
|
131
|
+
if (prior) {
|
|
132
|
+
return {
|
|
133
|
+
event: {
|
|
134
|
+
eventId: slotToEventId(prior.seq),
|
|
135
|
+
seq: prior.seq,
|
|
136
|
+
runId: prior.runId,
|
|
137
|
+
eventType: prior.eventType,
|
|
138
|
+
stepId: prior.stepId,
|
|
139
|
+
hookId: prior.hookId,
|
|
140
|
+
correlationId: prior.correlationId,
|
|
141
|
+
dataJson: prior.dataJson,
|
|
142
|
+
createdAt: prior.createdAt,
|
|
143
|
+
},
|
|
144
|
+
run: {
|
|
145
|
+
runId: runDoc.runId,
|
|
146
|
+
status: runDoc.status,
|
|
147
|
+
eventCount: runDoc.eventCount,
|
|
148
|
+
createdAt: runDoc.createdAt,
|
|
149
|
+
updatedAt: runDoc.updatedAt,
|
|
150
|
+
},
|
|
151
|
+
step: {
|
|
152
|
+
runId: existingStep.runId,
|
|
153
|
+
stepId: existingStep.stepId,
|
|
154
|
+
status: existingStep.status,
|
|
155
|
+
dataJson: existingStep.dataJson,
|
|
156
|
+
createdAt: existingStep.createdAt,
|
|
157
|
+
updatedAt: existingStep.updatedAt,
|
|
158
|
+
},
|
|
159
|
+
hook: null,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const seq = runDoc.eventCount + 1;
|
|
165
|
+
const eventId = slotToEventId(seq);
|
|
166
|
+
await ctx.db.insert("worldEvents", {
|
|
167
|
+
orgId,
|
|
168
|
+
runId,
|
|
169
|
+
seq,
|
|
170
|
+
eventType: args.eventType,
|
|
171
|
+
stepId: args.stepId,
|
|
172
|
+
hookId: args.hookId,
|
|
173
|
+
correlationId: args.correlationId,
|
|
174
|
+
dataJson: args.dataJson,
|
|
175
|
+
createdAt: now,
|
|
176
|
+
});
|
|
177
|
+
await ctx.db.patch(runDoc._id, { eventCount: seq, updatedAt: now });
|
|
178
|
+
let stepDoc = null;
|
|
179
|
+
let hookDoc = null;
|
|
180
|
+
let finalRunDoc = { ...runDoc, eventCount: seq, updatedAt: now };
|
|
181
|
+
switch (args.eventType) {
|
|
182
|
+
case "step_created":
|
|
183
|
+
case "step_started": {
|
|
184
|
+
if (!args.stepId) {
|
|
185
|
+
throw new Error("Validation: step_created/step_started requires stepId");
|
|
186
|
+
}
|
|
187
|
+
const existingStep = await ctx.db
|
|
188
|
+
.query("worldSteps")
|
|
189
|
+
.withIndex("by_org_run_step", (q) => q.eq("orgId", orgId).eq("runId", runId).eq("stepId", args.stepId))
|
|
190
|
+
.unique();
|
|
191
|
+
if (existingStep && args.eventType === "step_started") {
|
|
192
|
+
assertSameOrg(orgId, existingStep, "Step not found");
|
|
193
|
+
await ctx.db.patch(existingStep._id, {
|
|
194
|
+
status: "running",
|
|
195
|
+
dataJson: args.dataJson,
|
|
196
|
+
updatedAt: now,
|
|
197
|
+
});
|
|
198
|
+
stepDoc = await ctx.db.get(existingStep._id);
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
if (stepAlreadyExists) {
|
|
202
|
+
stepDoc = await ctx.db
|
|
203
|
+
.query("worldSteps")
|
|
204
|
+
.withIndex("by_org_run_step", (q) => q.eq("orgId", orgId).eq("runId", runId).eq("stepId", args.stepId))
|
|
205
|
+
.unique();
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
const stepId = await ctx.db.insert("worldSteps", {
|
|
209
|
+
orgId,
|
|
210
|
+
runId,
|
|
211
|
+
stepId: args.stepId,
|
|
212
|
+
status: "running",
|
|
213
|
+
dataJson: args.dataJson,
|
|
214
|
+
createdAt: now,
|
|
215
|
+
updatedAt: now,
|
|
216
|
+
});
|
|
217
|
+
stepDoc = await ctx.db.get(stepId);
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
case "step_completed":
|
|
221
|
+
case "step_failed": {
|
|
222
|
+
if (!args.stepId) {
|
|
223
|
+
throw new Error("Validation: step_completed/step_failed requires stepId");
|
|
224
|
+
}
|
|
225
|
+
const existing = await ctx.db
|
|
226
|
+
.query("worldSteps")
|
|
227
|
+
.withIndex("by_org_run_step", (q) => q.eq("orgId", orgId).eq("runId", runId).eq("stepId", args.stepId))
|
|
228
|
+
.unique();
|
|
229
|
+
assertSameOrg(orgId, existing, "Step not found");
|
|
230
|
+
await ctx.db.patch(existing._id, {
|
|
231
|
+
status: args.eventType === "step_completed" ? "completed" : "failed",
|
|
232
|
+
dataJson: args.dataJson,
|
|
233
|
+
updatedAt: now,
|
|
234
|
+
});
|
|
235
|
+
stepDoc = await ctx.db.get(existing._id);
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
case "hook_created": {
|
|
239
|
+
if (!args.hookId) {
|
|
240
|
+
throw new Error("Validation: hook_created requires hookId");
|
|
241
|
+
}
|
|
242
|
+
const parsed = JSON.parse(args.dataJson);
|
|
243
|
+
if (!parsed.token) {
|
|
244
|
+
throw new Error("Validation: hook_created payload requires token");
|
|
245
|
+
}
|
|
246
|
+
const hookDocId = await ctx.db.insert("worldHooks", {
|
|
247
|
+
orgId,
|
|
248
|
+
runId,
|
|
249
|
+
hookId: args.hookId,
|
|
250
|
+
token: parsed.token,
|
|
251
|
+
status: "pending",
|
|
252
|
+
createdAt: now,
|
|
253
|
+
updatedAt: now,
|
|
254
|
+
});
|
|
255
|
+
hookDoc = await ctx.db.get(hookDocId);
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
case "hook_disposed": {
|
|
259
|
+
if (!args.hookId) {
|
|
260
|
+
throw new Error("Validation: hook_disposed requires hookId");
|
|
261
|
+
}
|
|
262
|
+
const existing = await ctx.db
|
|
263
|
+
.query("worldHooks")
|
|
264
|
+
.withIndex("by_org_hook", (q) => q.eq("orgId", orgId).eq("hookId", args.hookId))
|
|
265
|
+
.unique();
|
|
266
|
+
assertSameOrg(orgId, existing, "Hook not found");
|
|
267
|
+
await ctx.db.delete(existing._id);
|
|
268
|
+
hookDoc = null;
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
case "run_cancelled": {
|
|
272
|
+
await ctx.db.patch(runDoc._id, { status: "cancelled", updatedAt: now });
|
|
273
|
+
finalRunDoc = { ...finalRunDoc, status: "cancelled" };
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
case "run_completed": {
|
|
277
|
+
await ctx.db.patch(runDoc._id, { status: "completed", updatedAt: now });
|
|
278
|
+
finalRunDoc = { ...finalRunDoc, status: "completed" };
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
case "run_failed": {
|
|
282
|
+
await ctx.db.patch(runDoc._id, { status: "failed", updatedAt: now });
|
|
283
|
+
finalRunDoc = { ...finalRunDoc, status: "failed" };
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
case "run_created":
|
|
287
|
+
break;
|
|
288
|
+
default:
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
return {
|
|
292
|
+
event: {
|
|
293
|
+
eventId,
|
|
294
|
+
seq,
|
|
295
|
+
runId,
|
|
296
|
+
eventType: args.eventType,
|
|
297
|
+
stepId: args.stepId,
|
|
298
|
+
hookId: args.hookId,
|
|
299
|
+
correlationId: args.correlationId,
|
|
300
|
+
dataJson: args.dataJson,
|
|
301
|
+
createdAt: now,
|
|
302
|
+
},
|
|
303
|
+
run: {
|
|
304
|
+
runId: finalRunDoc.runId,
|
|
305
|
+
status: finalRunDoc.status,
|
|
306
|
+
eventCount: finalRunDoc.eventCount,
|
|
307
|
+
createdAt: finalRunDoc.createdAt,
|
|
308
|
+
updatedAt: finalRunDoc.updatedAt,
|
|
309
|
+
},
|
|
310
|
+
step: stepDoc
|
|
311
|
+
? {
|
|
312
|
+
runId: stepDoc.runId,
|
|
313
|
+
stepId: stepDoc.stepId,
|
|
314
|
+
status: stepDoc.status,
|
|
315
|
+
dataJson: stepDoc.dataJson,
|
|
316
|
+
createdAt: stepDoc.createdAt,
|
|
317
|
+
updatedAt: stepDoc.updatedAt,
|
|
318
|
+
}
|
|
319
|
+
: null,
|
|
320
|
+
hook: hookDoc
|
|
321
|
+
? {
|
|
322
|
+
runId: hookDoc.runId,
|
|
323
|
+
hookId: hookDoc.hookId,
|
|
324
|
+
token: hookDoc.token,
|
|
325
|
+
status: hookDoc.status,
|
|
326
|
+
createdAt: hookDoc.createdAt,
|
|
327
|
+
updatedAt: hookDoc.updatedAt,
|
|
328
|
+
}
|
|
329
|
+
: null,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
export const appendEvent = mutation({
|
|
333
|
+
args: {
|
|
334
|
+
orgId: v.string(),
|
|
335
|
+
runId: v.union(v.string(), v.null()),
|
|
336
|
+
eventType: v.string(),
|
|
337
|
+
stepId: v.optional(v.string()),
|
|
338
|
+
hookId: v.optional(v.string()),
|
|
339
|
+
correlationId: v.optional(v.string()),
|
|
340
|
+
dataJson: v.string(),
|
|
341
|
+
},
|
|
342
|
+
returns: v.object({
|
|
343
|
+
event: eventResult,
|
|
344
|
+
run: v.union(runResult, v.null()),
|
|
345
|
+
step: v.union(stepResult, v.null()),
|
|
346
|
+
hook: v.union(hookResult, v.null()),
|
|
347
|
+
}),
|
|
348
|
+
handler: async (ctx, args) => {
|
|
349
|
+
if (args.correlationId?.startsWith(RUN_KIND_PREFIX) ||
|
|
350
|
+
args.correlationId?.startsWith(LEGACY_KIND_PREFIX)) {
|
|
351
|
+
throw new Error("Validation: invalid correlationId");
|
|
352
|
+
}
|
|
353
|
+
if (args.eventType === RUN_WORKFLOW_BOUND) {
|
|
354
|
+
throw new Error("Validation: invalid eventType");
|
|
355
|
+
}
|
|
356
|
+
return appendEventCore(ctx, args);
|
|
357
|
+
},
|
|
358
|
+
});
|
|
359
|
+
export const RUN_KIND_PREFIX = "run:";
|
|
360
|
+
export const LEGACY_KIND_PREFIX = "legacy:";
|
|
361
|
+
const LEGACY_KIND_NAME = {
|
|
362
|
+
session: "sessions",
|
|
363
|
+
job: "durableJobs",
|
|
364
|
+
};
|
|
365
|
+
function runKindPrefix(kind) {
|
|
366
|
+
return `${RUN_KIND_PREFIX}${kind}:`;
|
|
367
|
+
}
|
|
368
|
+
function legacyKindPrefix(kind) {
|
|
369
|
+
return `${LEGACY_KIND_PREFIX}${LEGACY_KIND_NAME[kind]}:`;
|
|
370
|
+
}
|
|
371
|
+
export function legacyCorrelationId(kind, legacyId) {
|
|
372
|
+
return `${legacyKindPrefix(kind)}${legacyId}`;
|
|
373
|
+
}
|
|
374
|
+
export async function findRunIdByCorrelation(ctx, orgId, correlationId) {
|
|
375
|
+
const events = await ctx.db
|
|
376
|
+
.query("worldEvents")
|
|
377
|
+
.withIndex("by_org_correlation", (q) => q.eq("orgId", orgId).eq("correlationId", correlationId))
|
|
378
|
+
.collect();
|
|
379
|
+
return events.find((e) => e.eventType === "run_created")?.runId ?? null;
|
|
380
|
+
}
|
|
381
|
+
export async function openRun(ctx, args) {
|
|
382
|
+
const result = await appendEventCore(ctx, {
|
|
383
|
+
orgId: args.orgId,
|
|
384
|
+
runId: null,
|
|
385
|
+
eventType: "run_created",
|
|
386
|
+
correlationId: `${runKindPrefix(args.kind)}${crypto.randomUUID()}`,
|
|
387
|
+
dataJson: JSON.stringify(args.data),
|
|
388
|
+
});
|
|
389
|
+
return result.event.runId;
|
|
390
|
+
}
|
|
391
|
+
export const RUN_WORKFLOW_BOUND = "run_workflow_bound";
|
|
392
|
+
function workflowCorrelationId(runId) {
|
|
393
|
+
return `workflow:${runId}`;
|
|
394
|
+
}
|
|
395
|
+
export async function bindWorkflowToRun(ctx, args) {
|
|
396
|
+
await appendEventCore(ctx, {
|
|
397
|
+
orgId: args.orgId,
|
|
398
|
+
runId: args.runId,
|
|
399
|
+
eventType: RUN_WORKFLOW_BOUND,
|
|
400
|
+
correlationId: workflowCorrelationId(args.runId),
|
|
401
|
+
dataJson: JSON.stringify({
|
|
402
|
+
workflowId: args.workflowId,
|
|
403
|
+
workflowVersion: args.workflowVersion,
|
|
404
|
+
}),
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
export async function workflowOfRun(ctx, orgId, runId) {
|
|
408
|
+
const events = await ctx.db
|
|
409
|
+
.query("worldEvents")
|
|
410
|
+
.withIndex("by_org_correlation", (q) => q.eq("orgId", orgId).eq("correlationId", workflowCorrelationId(runId)))
|
|
411
|
+
.collect();
|
|
412
|
+
let latest = null;
|
|
413
|
+
for (const e of events) {
|
|
414
|
+
if (e.eventType !== RUN_WORKFLOW_BOUND || e.runId !== runId)
|
|
415
|
+
continue;
|
|
416
|
+
if (latest === null || e.seq > latest.seq)
|
|
417
|
+
latest = e;
|
|
418
|
+
}
|
|
419
|
+
if (latest === null)
|
|
420
|
+
return null;
|
|
421
|
+
try {
|
|
422
|
+
const data = JSON.parse(latest.dataJson);
|
|
423
|
+
if (typeof data.workflowId !== "string" || typeof data.workflowVersion !== "string") {
|
|
424
|
+
return null;
|
|
425
|
+
}
|
|
426
|
+
return { workflowId: data.workflowId, workflowVersion: data.workflowVersion };
|
|
427
|
+
}
|
|
428
|
+
catch {
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
export async function loadRun(ctx, orgId, runId, kind, notFoundMessage) {
|
|
433
|
+
const run = await ctx.db
|
|
434
|
+
.query("worldRuns")
|
|
435
|
+
.withIndex("by_org_run", (q) => q.eq("orgId", orgId).eq("runId", runId))
|
|
436
|
+
.unique();
|
|
437
|
+
assertSameOrg(orgId, run, notFoundMessage);
|
|
438
|
+
const first = await ctx.db
|
|
439
|
+
.query("worldEvents")
|
|
440
|
+
.withIndex("by_org_run_seq", (q) => q.eq("orgId", orgId).eq("runId", runId).eq("seq", 1))
|
|
441
|
+
.unique();
|
|
442
|
+
if (!first ||
|
|
443
|
+
first.eventType !== "run_created" ||
|
|
444
|
+
first.correlationId === undefined ||
|
|
445
|
+
!(first.correlationId.startsWith(runKindPrefix(kind)) ||
|
|
446
|
+
first.correlationId.startsWith(legacyKindPrefix(kind)))) {
|
|
447
|
+
throw new Error(notFoundMessage);
|
|
448
|
+
}
|
|
449
|
+
let created = {};
|
|
450
|
+
try {
|
|
451
|
+
const parsed = JSON.parse(first.dataJson);
|
|
452
|
+
if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
453
|
+
created = parsed;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
catch {
|
|
457
|
+
created = {};
|
|
458
|
+
}
|
|
459
|
+
return {
|
|
460
|
+
runId: run.runId,
|
|
461
|
+
status: run.status,
|
|
462
|
+
eventCount: run.eventCount,
|
|
463
|
+
created,
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
export function agentIdOfRun(ctx, run) {
|
|
467
|
+
const raw = run.created.agentId;
|
|
468
|
+
const id = typeof raw === "string" ? ctx.db.normalizeId("agents", raw) : null;
|
|
469
|
+
if (id === null)
|
|
470
|
+
throw new Error("Agent not found");
|
|
471
|
+
return id;
|
|
472
|
+
}
|
|
473
|
+
export function payloadOrUndefined(dataJson) {
|
|
474
|
+
return dataJson === "{}" ? undefined : dataJson;
|
|
475
|
+
}
|
|
476
|
+
export { slotToEventId };
|
|
477
|
+
export const getRun = query({
|
|
478
|
+
args: { orgId: v.string(), runId: v.string() },
|
|
479
|
+
returns: v.union(runResult, v.null()),
|
|
480
|
+
handler: async (ctx, args) => {
|
|
481
|
+
const orgId = requireOrgId(args.orgId);
|
|
482
|
+
const doc = await ctx.db
|
|
483
|
+
.query("worldRuns")
|
|
484
|
+
.withIndex("by_org_run", (q) => q.eq("orgId", orgId).eq("runId", args.runId))
|
|
485
|
+
.unique();
|
|
486
|
+
if (!doc || doc.orgId !== orgId) {
|
|
487
|
+
return null;
|
|
488
|
+
}
|
|
489
|
+
return {
|
|
490
|
+
runId: doc.runId,
|
|
491
|
+
status: doc.status,
|
|
492
|
+
eventCount: doc.eventCount,
|
|
493
|
+
createdAt: doc.createdAt,
|
|
494
|
+
updatedAt: doc.updatedAt,
|
|
495
|
+
};
|
|
496
|
+
},
|
|
497
|
+
});
|
|
498
|
+
export const listRuns = query({
|
|
499
|
+
args: { orgId: v.string() },
|
|
500
|
+
returns: v.array(runResult),
|
|
501
|
+
handler: async (ctx, args) => {
|
|
502
|
+
const orgId = requireOrgId(args.orgId);
|
|
503
|
+
const docs = await ctx.db
|
|
504
|
+
.query("worldRuns")
|
|
505
|
+
.withIndex("by_org", (q) => q.eq("orgId", orgId))
|
|
506
|
+
.collect();
|
|
507
|
+
return docs.map((doc) => ({
|
|
508
|
+
runId: doc.runId,
|
|
509
|
+
status: doc.status,
|
|
510
|
+
eventCount: doc.eventCount,
|
|
511
|
+
createdAt: doc.createdAt,
|
|
512
|
+
updatedAt: doc.updatedAt,
|
|
513
|
+
}));
|
|
514
|
+
},
|
|
515
|
+
});
|
|
516
|
+
export const getStep = query({
|
|
517
|
+
args: { orgId: v.string(), runId: v.string(), stepId: v.string() },
|
|
518
|
+
returns: v.union(stepResult, v.null()),
|
|
519
|
+
handler: async (ctx, args) => {
|
|
520
|
+
const orgId = requireOrgId(args.orgId);
|
|
521
|
+
const doc = await ctx.db
|
|
522
|
+
.query("worldSteps")
|
|
523
|
+
.withIndex("by_org_run_step", (q) => q.eq("orgId", orgId).eq("runId", args.runId).eq("stepId", args.stepId))
|
|
524
|
+
.unique();
|
|
525
|
+
if (!doc || doc.orgId !== orgId) {
|
|
526
|
+
return null;
|
|
527
|
+
}
|
|
528
|
+
return {
|
|
529
|
+
runId: doc.runId,
|
|
530
|
+
stepId: doc.stepId,
|
|
531
|
+
status: doc.status,
|
|
532
|
+
dataJson: doc.dataJson,
|
|
533
|
+
createdAt: doc.createdAt,
|
|
534
|
+
updatedAt: doc.updatedAt,
|
|
535
|
+
};
|
|
536
|
+
},
|
|
537
|
+
});
|
|
538
|
+
export const listSteps = query({
|
|
539
|
+
args: { orgId: v.string(), runId: v.string() },
|
|
540
|
+
returns: v.array(stepResult),
|
|
541
|
+
handler: async (ctx, args) => {
|
|
542
|
+
const orgId = requireOrgId(args.orgId);
|
|
543
|
+
const docs = await ctx.db
|
|
544
|
+
.query("worldSteps")
|
|
545
|
+
.withIndex("by_org_run", (q) => q.eq("orgId", orgId).eq("runId", args.runId))
|
|
546
|
+
.collect();
|
|
547
|
+
return docs.map((doc) => ({
|
|
548
|
+
runId: doc.runId,
|
|
549
|
+
stepId: doc.stepId,
|
|
550
|
+
status: doc.status,
|
|
551
|
+
dataJson: doc.dataJson,
|
|
552
|
+
createdAt: doc.createdAt,
|
|
553
|
+
updatedAt: doc.updatedAt,
|
|
554
|
+
}));
|
|
555
|
+
},
|
|
556
|
+
});
|
|
557
|
+
export const getEvent = query({
|
|
558
|
+
args: { orgId: v.string(), runId: v.string(), eventId: v.string() },
|
|
559
|
+
returns: v.union(eventResult, v.null()),
|
|
560
|
+
handler: async (ctx, args) => {
|
|
561
|
+
const orgId = requireOrgId(args.orgId);
|
|
562
|
+
const docs = await ctx.db
|
|
563
|
+
.query("worldEvents")
|
|
564
|
+
.withIndex("by_org_run", (q) => q.eq("orgId", orgId).eq("runId", args.runId))
|
|
565
|
+
.collect();
|
|
566
|
+
const doc = docs.find((d) => slotToEventId(d.seq) === args.eventId);
|
|
567
|
+
if (!doc || doc.orgId !== orgId) {
|
|
568
|
+
return null;
|
|
569
|
+
}
|
|
570
|
+
return {
|
|
571
|
+
eventId: slotToEventId(doc.seq),
|
|
572
|
+
seq: doc.seq,
|
|
573
|
+
runId: doc.runId,
|
|
574
|
+
eventType: doc.eventType,
|
|
575
|
+
stepId: doc.stepId,
|
|
576
|
+
hookId: doc.hookId,
|
|
577
|
+
correlationId: doc.correlationId,
|
|
578
|
+
dataJson: doc.dataJson,
|
|
579
|
+
createdAt: doc.createdAt,
|
|
580
|
+
};
|
|
581
|
+
},
|
|
582
|
+
});
|
|
583
|
+
export const listEvents = query({
|
|
584
|
+
args: { orgId: v.string(), runId: v.string() },
|
|
585
|
+
returns: v.array(eventResult),
|
|
586
|
+
handler: async (ctx, args) => {
|
|
587
|
+
const orgId = requireOrgId(args.orgId);
|
|
588
|
+
const docs = await ctx.db
|
|
589
|
+
.query("worldEvents")
|
|
590
|
+
.withIndex("by_org_run_seq", (q) => q.eq("orgId", orgId).eq("runId", args.runId))
|
|
591
|
+
.collect();
|
|
592
|
+
return docs.map((doc) => ({
|
|
593
|
+
eventId: slotToEventId(doc.seq),
|
|
594
|
+
seq: doc.seq,
|
|
595
|
+
runId: doc.runId,
|
|
596
|
+
eventType: doc.eventType,
|
|
597
|
+
stepId: doc.stepId,
|
|
598
|
+
hookId: doc.hookId,
|
|
599
|
+
correlationId: doc.correlationId,
|
|
600
|
+
dataJson: doc.dataJson,
|
|
601
|
+
createdAt: doc.createdAt,
|
|
602
|
+
}));
|
|
603
|
+
},
|
|
604
|
+
});
|
|
605
|
+
export const listEventsByCorrelationId = query({
|
|
606
|
+
args: { orgId: v.string(), correlationId: v.string() },
|
|
607
|
+
returns: v.array(eventResult),
|
|
608
|
+
handler: async (ctx, args) => {
|
|
609
|
+
const orgId = requireOrgId(args.orgId);
|
|
610
|
+
const docs = await ctx.db
|
|
611
|
+
.query("worldEvents")
|
|
612
|
+
.withIndex("by_org_correlation", (q) => q.eq("orgId", orgId).eq("correlationId", args.correlationId))
|
|
613
|
+
.collect();
|
|
614
|
+
return docs.map((doc) => ({
|
|
615
|
+
eventId: slotToEventId(doc.seq),
|
|
616
|
+
seq: doc.seq,
|
|
617
|
+
runId: doc.runId,
|
|
618
|
+
eventType: doc.eventType,
|
|
619
|
+
stepId: doc.stepId,
|
|
620
|
+
hookId: doc.hookId,
|
|
621
|
+
correlationId: doc.correlationId,
|
|
622
|
+
dataJson: doc.dataJson,
|
|
623
|
+
createdAt: doc.createdAt,
|
|
624
|
+
}));
|
|
625
|
+
},
|
|
626
|
+
});
|
|
627
|
+
export const getHook = query({
|
|
628
|
+
args: { orgId: v.string(), hookId: v.string() },
|
|
629
|
+
returns: v.union(hookResult, v.null()),
|
|
630
|
+
handler: async (ctx, args) => {
|
|
631
|
+
const orgId = requireOrgId(args.orgId);
|
|
632
|
+
const doc = await ctx.db
|
|
633
|
+
.query("worldHooks")
|
|
634
|
+
.withIndex("by_org_hook", (q) => q.eq("orgId", orgId).eq("hookId", args.hookId))
|
|
635
|
+
.unique();
|
|
636
|
+
if (!doc || doc.orgId !== orgId) {
|
|
637
|
+
return null;
|
|
638
|
+
}
|
|
639
|
+
return {
|
|
640
|
+
runId: doc.runId,
|
|
641
|
+
hookId: doc.hookId,
|
|
642
|
+
token: doc.token,
|
|
643
|
+
status: doc.status,
|
|
644
|
+
createdAt: doc.createdAt,
|
|
645
|
+
updatedAt: doc.updatedAt,
|
|
646
|
+
};
|
|
647
|
+
},
|
|
648
|
+
});
|
|
649
|
+
export const getHookByToken = query({
|
|
650
|
+
args: { orgId: v.string(), token: v.string() },
|
|
651
|
+
returns: v.union(hookResult, v.null()),
|
|
652
|
+
handler: async (ctx, args) => {
|
|
653
|
+
const orgId = requireOrgId(args.orgId);
|
|
654
|
+
const doc = await ctx.db
|
|
655
|
+
.query("worldHooks")
|
|
656
|
+
.withIndex("by_org_token", (q) => q.eq("orgId", orgId).eq("token", args.token))
|
|
657
|
+
.unique();
|
|
658
|
+
if (!doc || doc.orgId !== orgId) {
|
|
659
|
+
return null;
|
|
660
|
+
}
|
|
661
|
+
return {
|
|
662
|
+
runId: doc.runId,
|
|
663
|
+
hookId: doc.hookId,
|
|
664
|
+
token: doc.token,
|
|
665
|
+
status: doc.status,
|
|
666
|
+
createdAt: doc.createdAt,
|
|
667
|
+
updatedAt: doc.updatedAt,
|
|
668
|
+
};
|
|
669
|
+
},
|
|
670
|
+
});
|
|
671
|
+
export const listHooks = query({
|
|
672
|
+
args: { orgId: v.string(), runId: v.string() },
|
|
673
|
+
returns: v.array(hookResult),
|
|
674
|
+
handler: async (ctx, args) => {
|
|
675
|
+
const orgId = requireOrgId(args.orgId);
|
|
676
|
+
const docs = await ctx.db
|
|
677
|
+
.query("worldHooks")
|
|
678
|
+
.withIndex("by_org_run", (q) => q.eq("orgId", orgId).eq("runId", args.runId))
|
|
679
|
+
.collect();
|
|
680
|
+
return docs.map((doc) => ({
|
|
681
|
+
runId: doc.runId,
|
|
682
|
+
hookId: doc.hookId,
|
|
683
|
+
token: doc.token,
|
|
684
|
+
status: doc.status,
|
|
685
|
+
createdAt: doc.createdAt,
|
|
686
|
+
updatedAt: doc.updatedAt,
|
|
687
|
+
}));
|
|
688
|
+
},
|
|
689
|
+
});
|