@vantageos/agent-engine 0.3.0-alpha.0 → 0.4.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  import { type DatabaseReader, type MutationCtx } from "../_generated/server.js";
2
- import type { Doc, Id } from "../_generated/dataModel.js";
2
+ import type { Id } from "../_generated/dataModel.js";
3
3
  declare function slotToEventId(seq: number): string;
4
4
  export type AppendEventArgs = {
5
5
  orgId: string;
@@ -24,7 +24,7 @@ export declare function appendEventCore(ctx: MutationCtx, args: AppendEventArgs)
24
24
  };
25
25
  run: {
26
26
  runId: string;
27
- status: "running" | "completed" | "cancelled" | "failed";
27
+ status: "completed" | "failed" | "cancelled" | "running";
28
28
  eventCount: number;
29
29
  createdAt: number;
30
30
  updatedAt: number;
@@ -32,7 +32,7 @@ export declare function appendEventCore(ctx: MutationCtx, args: AppendEventArgs)
32
32
  step: {
33
33
  runId: string;
34
34
  stepId: string;
35
- status: "running" | "completed" | "failed";
35
+ status: "completed" | "failed" | "running";
36
36
  dataJson: string | undefined;
37
37
  createdAt: number;
38
38
  updatedAt: number;
@@ -68,7 +68,7 @@ export declare const appendEvent: import("convex/server").RegisteredMutation<"pu
68
68
  };
69
69
  run: {
70
70
  runId: string;
71
- status: "running" | "completed" | "cancelled" | "failed";
71
+ status: "completed" | "failed" | "cancelled" | "running";
72
72
  eventCount: number;
73
73
  createdAt: number;
74
74
  updatedAt: number;
@@ -76,7 +76,7 @@ export declare const appendEvent: import("convex/server").RegisteredMutation<"pu
76
76
  step: {
77
77
  runId: string;
78
78
  stepId: string;
79
- status: "running" | "completed" | "failed";
79
+ status: "completed" | "failed" | "running";
80
80
  dataJson: string | undefined;
81
81
  createdAt: number;
82
82
  updatedAt: number;
@@ -90,40 +90,38 @@ export declare const appendEvent: import("convex/server").RegisteredMutation<"pu
90
90
  updatedAt: number;
91
91
  } | null;
92
92
  }>>;
93
- export declare function legacyCorrelationId(table: "sessions" | "durableJobs", legacyId: string): string;
94
- export declare function findRunIdByCorrelation(ctx: MutationCtx, orgId: string, correlationId: string): Promise<string | null>;
95
- export declare function openLegacyRun(ctx: MutationCtx, args: {
93
+ export declare const RUN_KIND_PREFIX = "run:";
94
+ export declare const LEGACY_KIND_PREFIX = "legacy:";
95
+ export type RunKind = "session" | "job";
96
+ export type RunStatus = "running" | "completed" | "failed" | "cancelled";
97
+ export declare function legacyCorrelationId(kind: RunKind, legacyId: string): string;
98
+ export declare function openRun(ctx: MutationCtx, args: {
96
99
  orgId: string;
97
- table: "sessions" | "durableJobs";
98
- legacyId: string;
100
+ kind: RunKind;
99
101
  data: Record<string, unknown>;
100
102
  }): Promise<string>;
101
- export declare function mirrorToRun(ctx: MutationCtx, args: {
103
+ export declare const RUN_WORKFLOW_BOUND = "run_workflow_bound";
104
+ export declare function bindWorkflowToRun(ctx: MutationCtx, args: {
102
105
  orgId: string;
103
- table: "sessions" | "durableJobs";
104
- legacyId: string;
105
- eventType: string;
106
- stepId?: string;
107
- dataJson?: string;
108
- }): Promise<boolean>;
109
- export type RunKind = "sessions" | "durableJobs";
110
- export type RunStatus = "running" | "completed" | "failed" | "cancelled";
106
+ runId: string;
107
+ workflowId: string;
108
+ workflowVersion: string;
109
+ }): Promise<void>;
110
+ export declare function workflowOfRun(ctx: {
111
+ db: DatabaseReader;
112
+ }, orgId: string, runId: string): Promise<{
113
+ workflowId: string;
114
+ workflowVersion: string;
115
+ } | null>;
111
116
  export type LoadedRun = {
112
117
  runId: string;
113
118
  status: RunStatus;
114
119
  eventCount: number;
115
- legacyId: string | null;
116
120
  created: Record<string, unknown>;
117
121
  };
118
122
  export declare function loadRun(ctx: {
119
123
  db: DatabaseReader;
120
124
  }, orgId: string, runId: string, kind: RunKind, notFoundMessage: string): Promise<LoadedRun>;
121
- export declare function legacySessionOf(ctx: {
122
- db: DatabaseReader;
123
- }, run: LoadedRun, orgId: string): Promise<Doc<"sessions"> | null>;
124
- export declare function legacyJobOf(ctx: {
125
- db: DatabaseReader;
126
- }, run: LoadedRun, orgId: string): Promise<Doc<"durableJobs"> | null>;
127
125
  export declare function agentIdOfRun(ctx: {
128
126
  db: DatabaseReader;
129
127
  }, run: LoadedRun): Id<"agents">;
@@ -134,7 +132,7 @@ export declare const getRun: import("convex/server").RegisteredQuery<"public", {
134
132
  runId: string;
135
133
  }, Promise<{
136
134
  runId: string;
137
- status: "running" | "completed" | "cancelled" | "failed";
135
+ status: "completed" | "failed" | "cancelled" | "running";
138
136
  eventCount: number;
139
137
  createdAt: number;
140
138
  updatedAt: number;
@@ -143,7 +141,7 @@ export declare const listRuns: import("convex/server").RegisteredQuery<"public",
143
141
  orgId: string;
144
142
  }, Promise<{
145
143
  runId: string;
146
- status: "running" | "completed" | "cancelled" | "failed";
144
+ status: "completed" | "failed" | "cancelled" | "running";
147
145
  eventCount: number;
148
146
  createdAt: number;
149
147
  updatedAt: number;
@@ -155,7 +153,7 @@ export declare const getStep: import("convex/server").RegisteredQuery<"public",
155
153
  }, Promise<{
156
154
  runId: string;
157
155
  stepId: string;
158
- status: "running" | "completed" | "failed";
156
+ status: "completed" | "failed" | "running";
159
157
  dataJson: string | undefined;
160
158
  createdAt: number;
161
159
  updatedAt: number;
@@ -166,7 +164,7 @@ export declare const listSteps: import("convex/server").RegisteredQuery<"public"
166
164
  }, Promise<{
167
165
  runId: string;
168
166
  stepId: string;
169
- status: "running" | "completed" | "failed";
167
+ status: "completed" | "failed" | "running";
170
168
  dataJson: string | undefined;
171
169
  createdAt: number;
172
170
  updatedAt: number;
@@ -1,5 +1,6 @@
1
1
  import { v } from "convex/values";
2
2
  import { mutation, query, } from "../_generated/server.js";
3
+ import { internal } from "../_generated/api.js";
3
4
  import { assertSameOrg, requireOrgId } from "../lib/auth.js";
4
5
  function pad26(n) {
5
6
  return String(n).padStart(26, "0");
@@ -243,15 +244,31 @@ export async function appendEventCore(ctx, args) {
243
244
  if (!parsed.token) {
244
245
  throw new Error("Validation: hook_created payload requires token");
245
246
  }
247
+ const hasDeadline = parsed.expiresAt !== undefined;
248
+ if (hasDeadline) {
249
+ if (!Number.isFinite(parsed.expiresAt)) {
250
+ throw new Error("Validation: hook_created expiresAt must be a number");
251
+ }
252
+ if (!parsed.awaitedEventName) {
253
+ throw new Error("Validation: a hook with expiresAt requires awaitedEventName");
254
+ }
255
+ await loadRun(ctx, orgId, runId, "session", "Validation: a hook with a deadline requires a session run");
256
+ }
246
257
  const hookDocId = await ctx.db.insert("worldHooks", {
247
258
  orgId,
248
259
  runId,
249
260
  hookId: args.hookId,
250
261
  token: parsed.token,
251
262
  status: "pending",
263
+ expiresAt: hasDeadline ? parsed.expiresAt : undefined,
264
+ awaitedEventName: hasDeadline ? parsed.awaitedEventName : undefined,
252
265
  createdAt: now,
253
266
  updatedAt: now,
254
267
  });
268
+ if (hasDeadline) {
269
+ const scheduledFunctionId = await ctx.scheduler.runAt(parsed.expiresAt, internal.engine.awaitEventTimeout.expire, { orgId, hookId: args.hookId });
270
+ await ctx.db.patch(hookDocId, { scheduledFunctionId });
271
+ }
255
272
  hookDoc = await ctx.db.get(hookDocId);
256
273
  break;
257
274
  }
@@ -264,6 +281,9 @@ export async function appendEventCore(ctx, args) {
264
281
  .withIndex("by_org_hook", (q) => q.eq("orgId", orgId).eq("hookId", args.hookId))
265
282
  .unique();
266
283
  assertSameOrg(orgId, existing, "Hook not found");
284
+ if (existing.scheduledFunctionId !== undefined) {
285
+ await ctx.scheduler.cancel(existing.scheduledFunctionId);
286
+ }
267
287
  await ctx.db.delete(existing._id);
268
288
  hookDoc = null;
269
289
  break;
@@ -346,44 +366,81 @@ export const appendEvent = mutation({
346
366
  hook: v.union(hookResult, v.null()),
347
367
  }),
348
368
  handler: async (ctx, args) => {
349
- if (args.correlationId?.startsWith("legacy:")) {
369
+ if (args.correlationId?.startsWith(RUN_KIND_PREFIX) ||
370
+ args.correlationId?.startsWith(LEGACY_KIND_PREFIX)) {
350
371
  throw new Error("Validation: invalid correlationId");
351
372
  }
373
+ if (args.eventType === RUN_WORKFLOW_BOUND) {
374
+ throw new Error("Validation: invalid eventType");
375
+ }
352
376
  return appendEventCore(ctx, args);
353
377
  },
354
378
  });
355
- export function legacyCorrelationId(table, legacyId) {
356
- return `legacy:${table}:${legacyId}`;
379
+ export const RUN_KIND_PREFIX = "run:";
380
+ export const LEGACY_KIND_PREFIX = "legacy:";
381
+ const LEGACY_KIND_NAME = {
382
+ session: "sessions",
383
+ job: "durableJobs",
384
+ };
385
+ function runKindPrefix(kind) {
386
+ return `${RUN_KIND_PREFIX}${kind}:`;
357
387
  }
358
- export async function findRunIdByCorrelation(ctx, orgId, correlationId) {
359
- const events = await ctx.db
360
- .query("worldEvents")
361
- .withIndex("by_org_correlation", (q) => q.eq("orgId", orgId).eq("correlationId", correlationId))
362
- .collect();
363
- return events.find((e) => e.eventType === "run_created")?.runId ?? null;
388
+ function legacyKindPrefix(kind) {
389
+ return `${LEGACY_KIND_PREFIX}${LEGACY_KIND_NAME[kind]}:`;
364
390
  }
365
- export async function openLegacyRun(ctx, args) {
391
+ export function legacyCorrelationId(kind, legacyId) {
392
+ return `${legacyKindPrefix(kind)}${legacyId}`;
393
+ }
394
+ export async function openRun(ctx, args) {
366
395
  const result = await appendEventCore(ctx, {
367
396
  orgId: args.orgId,
368
397
  runId: null,
369
398
  eventType: "run_created",
370
- correlationId: legacyCorrelationId(args.table, args.legacyId),
399
+ correlationId: `${runKindPrefix(args.kind)}${crypto.randomUUID()}`,
371
400
  dataJson: JSON.stringify(args.data),
372
401
  });
373
402
  return result.event.runId;
374
403
  }
375
- export async function mirrorToRun(ctx, args) {
376
- const runId = await findRunIdByCorrelation(ctx, args.orgId, legacyCorrelationId(args.table, args.legacyId));
377
- if (runId === null)
378
- return false;
404
+ export const RUN_WORKFLOW_BOUND = "run_workflow_bound";
405
+ function workflowCorrelationId(runId) {
406
+ return `workflow:${runId}`;
407
+ }
408
+ export async function bindWorkflowToRun(ctx, args) {
379
409
  await appendEventCore(ctx, {
380
410
  orgId: args.orgId,
381
- runId,
382
- eventType: args.eventType,
383
- stepId: args.stepId,
384
- dataJson: args.dataJson ?? "{}",
411
+ runId: args.runId,
412
+ eventType: RUN_WORKFLOW_BOUND,
413
+ correlationId: workflowCorrelationId(args.runId),
414
+ dataJson: JSON.stringify({
415
+ workflowId: args.workflowId,
416
+ workflowVersion: args.workflowVersion,
417
+ }),
385
418
  });
386
- return true;
419
+ }
420
+ export async function workflowOfRun(ctx, orgId, runId) {
421
+ const events = await ctx.db
422
+ .query("worldEvents")
423
+ .withIndex("by_org_correlation", (q) => q.eq("orgId", orgId).eq("correlationId", workflowCorrelationId(runId)))
424
+ .collect();
425
+ let latest = null;
426
+ for (const e of events) {
427
+ if (e.eventType !== RUN_WORKFLOW_BOUND || e.runId !== runId)
428
+ continue;
429
+ if (latest === null || e.seq > latest.seq)
430
+ latest = e;
431
+ }
432
+ if (latest === null)
433
+ return null;
434
+ try {
435
+ const data = JSON.parse(latest.dataJson);
436
+ if (typeof data.workflowId !== "string" || typeof data.workflowVersion !== "string") {
437
+ return null;
438
+ }
439
+ return { workflowId: data.workflowId, workflowVersion: data.workflowVersion };
440
+ }
441
+ catch {
442
+ return null;
443
+ }
387
444
  }
388
445
  export async function loadRun(ctx, orgId, runId, kind, notFoundMessage) {
389
446
  const run = await ctx.db
@@ -395,11 +452,11 @@ export async function loadRun(ctx, orgId, runId, kind, notFoundMessage) {
395
452
  .query("worldEvents")
396
453
  .withIndex("by_org_run_seq", (q) => q.eq("orgId", orgId).eq("runId", runId).eq("seq", 1))
397
454
  .unique();
398
- const prefix = `legacy:${kind}:`;
399
455
  if (!first ||
400
456
  first.eventType !== "run_created" ||
401
457
  first.correlationId === undefined ||
402
- !first.correlationId.startsWith(prefix)) {
458
+ !(first.correlationId.startsWith(runKindPrefix(kind)) ||
459
+ first.correlationId.startsWith(legacyKindPrefix(kind)))) {
403
460
  throw new Error(notFoundMessage);
404
461
  }
405
462
  let created = {};
@@ -412,33 +469,13 @@ export async function loadRun(ctx, orgId, runId, kind, notFoundMessage) {
412
469
  catch {
413
470
  created = {};
414
471
  }
415
- const legacyId = first.correlationId.slice(prefix.length);
416
472
  return {
417
473
  runId: run.runId,
418
474
  status: run.status,
419
475
  eventCount: run.eventCount,
420
- legacyId: legacyId.length > 0 ? legacyId : null,
421
476
  created,
422
477
  };
423
478
  }
424
- export async function legacySessionOf(ctx, run, orgId) {
425
- if (run.legacyId === null)
426
- return null;
427
- const id = ctx.db.normalizeId("sessions", run.legacyId);
428
- if (id === null)
429
- return null;
430
- const doc = await ctx.db.get(id);
431
- return doc !== null && doc.orgId === orgId ? doc : null;
432
- }
433
- export async function legacyJobOf(ctx, run, orgId) {
434
- if (run.legacyId === null)
435
- return null;
436
- const id = ctx.db.normalizeId("durableJobs", run.legacyId);
437
- if (id === null)
438
- return null;
439
- const doc = await ctx.db.get(id);
440
- return doc !== null && doc.orgId === orgId ? doc : null;
441
- }
442
479
  export function agentIdOfRun(ctx, run) {
443
480
  const raw = run.created.agentId;
444
481
  const id = typeof raw === "string" ? ctx.db.normalizeId("agents", raw) : null;