@vantageos/agent-engine 0.1.0-alpha.1 → 0.1.0-alpha.3

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.
@@ -0,0 +1,90 @@
1
+ import { v } from "convex/values";
2
+ import { internalMutation, internalQuery } from "../_generated/server";
3
+ // testFixtures/callerSteps — stand-ins for a CALLER'S OWN step function
4
+ // (the thing a real consumer, e.g. VantagePeers, would define in ITS OWN
5
+ // app and pass to `engine/durableJob.start` as `stepFunctionHandle` via
6
+ // `createFunctionHandle`). These exist ONLY to drive the contract tests in
7
+ // convex/engineDurableJob.test.ts -- they are not part of the public
8
+ // surface (see examples/vantage-peers-long-task for the real, documented
9
+ // integration a consumer would write).
10
+ //
11
+ // Attempt counts are tracked IN-MEMORY (module-level Map), not via a DB
12
+ // write, because a mutation that throws rolls back every write made in its
13
+ // OWN transaction -- including a "record that I was called" insert made
14
+ // just before the throw. In-memory state survives the rollback since it is
15
+ // plain JS state, not part of the Convex transaction.
16
+ const attemptCounts = new Map();
17
+ function bump(jobId, stepIndex) {
18
+ const key = `${jobId}:${stepIndex}`;
19
+ const next = (attemptCounts.get(key) ?? 0) + 1;
20
+ attemptCounts.set(key, next);
21
+ return next;
22
+ }
23
+ export const getAttemptCount = internalQuery({
24
+ args: { jobId: v.id("durableJobs"), stepIndex: v.number() },
25
+ returns: v.number(),
26
+ handler: async (_ctx, args) => {
27
+ return attemptCounts.get(`${args.jobId}:${args.stepIndex}`) ?? 0;
28
+ },
29
+ });
30
+ // TEST-ONLY: `convexTest()` reuses deterministic, low-cardinality document
31
+ // IDs across SEPARATE test instances within the same process, so this
32
+ // module-level Map must be cleared between tests to avoid one test's
33
+ // counters leaking into the next via a colliding `${jobId}:${stepIndex}`
34
+ // key. Called from `beforeEach` in engineDurableJob.test.ts.
35
+ export const resetAttemptCounts = internalMutation({
36
+ args: {},
37
+ returns: v.null(),
38
+ handler: async () => {
39
+ attemptCounts.clear();
40
+ return null;
41
+ },
42
+ });
43
+ // Records one row per SUCCESSFUL invocation so tests can assert exactly
44
+ // which step indices actually completed (idempotent-replay assertions).
45
+ export const alwaysSucceeds = internalMutation({
46
+ args: { orgId: v.string(), jobId: v.id("durableJobs"), stepIndex: v.number() },
47
+ returns: v.null(),
48
+ handler: async (ctx, args) => {
49
+ bump(args.jobId, args.stepIndex);
50
+ await ctx.db.insert("callerStepInvocations", {
51
+ orgId: args.orgId,
52
+ jobId: args.jobId,
53
+ stepIndex: args.stepIndex,
54
+ createdAt: Date.now(),
55
+ });
56
+ return null;
57
+ },
58
+ });
59
+ // Fails on every attempt -- used to prove "abandon after N échecs". Attempt
60
+ // count is observable via `getAttemptCount` even though every DB write this
61
+ // function might otherwise make would be rolled back by the throw.
62
+ export const alwaysFails = internalMutation({
63
+ args: { orgId: v.string(), jobId: v.id("durableJobs"), stepIndex: v.number() },
64
+ returns: v.null(),
65
+ handler: async (_ctx, args) => {
66
+ bump(args.jobId, args.stepIndex);
67
+ throw new Error(`simulated failure at step ${args.stepIndex}`);
68
+ },
69
+ });
70
+ // Fails on the FIRST attempt of ANY step, then succeeds on every
71
+ // subsequent attempt of that same step -- used to prove retry-then-succeed
72
+ // still advances the cursor (not just outright failure), exercising the
73
+ // `maxAttemptsPerStep > 1` path distinctly from `alwaysFails`.
74
+ export const failsFirstAttemptThenSucceeds = internalMutation({
75
+ args: { orgId: v.string(), jobId: v.id("durableJobs"), stepIndex: v.number() },
76
+ returns: v.null(),
77
+ handler: async (ctx, args) => {
78
+ const attemptNumber = bump(args.jobId, args.stepIndex);
79
+ if (attemptNumber === 1) {
80
+ throw new Error(`simulated single failure at step ${args.stepIndex}`);
81
+ }
82
+ await ctx.db.insert("callerStepInvocations", {
83
+ orgId: args.orgId,
84
+ jobId: args.jobId,
85
+ stepIndex: args.stepIndex,
86
+ createdAt: Date.now(),
87
+ });
88
+ return null;
89
+ },
90
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vantageos/agent-engine",
3
- "version": "0.1.0-alpha.1",
3
+ "version": "0.1.0-alpha.3",
4
4
  "description": "Reusable Convex component: durable agent engine. Agent = data aggregate (root sheet + child tables), never compiled code. Built on @convex-dev/workflow + @convex-dev/workpool (+ @convex-dev/agent). Eve parity by design.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -21,6 +21,9 @@
21
21
  "./convex.config.js": {
22
22
  "types": "./dist/component/convex.config.d.ts",
23
23
  "default": "./dist/component/convex.config.js"
24
+ },
25
+ "./_generated/component.js": {
26
+ "types": "./dist/component/_generated/component.d.ts"
24
27
  }
25
28
  },
26
29
  "scripts": {
@@ -35,6 +38,10 @@
35
38
  },
36
39
  "dependencies": {
37
40
  "@convex-dev/agent": "0.6.4",
41
+ "@convex-dev/aggregate": "^0.2.2",
42
+ "@convex-dev/crons": "^0.2.2",
43
+ "@convex-dev/rate-limiter": "^0.3.2",
44
+ "@convex-dev/sharded-counter": "^0.2.1",
38
45
  "@convex-dev/workflow": "0.4.4",
39
46
  "@convex-dev/workpool": "0.4.7",
40
47
  "ai": "6.0.35",