@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.
Files changed (62) hide show
  1. package/README.md +33 -8
  2. package/dist/component/_generated/api.d.ts +14 -3
  3. package/dist/component/_generated/api.js +0 -25
  4. package/dist/component/_generated/component.d.ts +369 -73
  5. package/dist/component/_generated/component.js +0 -9
  6. package/dist/component/_generated/dataModel.js +0 -9
  7. package/dist/component/_generated/server.js +0 -69
  8. package/dist/component/convex.config.js +0 -44
  9. package/dist/component/engine/agents.d.ts +3 -3
  10. package/dist/component/engine/agents.js +0 -7
  11. package/dist/component/engine/awaitEventTimeout.d.ts +1 -1
  12. package/dist/component/engine/awaitEventTimeout.js +16 -26
  13. package/dist/component/engine/connections.d.ts +74 -0
  14. package/dist/component/engine/connections.js +254 -0
  15. package/dist/component/engine/delegation.d.ts +5 -5
  16. package/dist/component/engine/delegation.js +0 -74
  17. package/dist/component/engine/directory.js +0 -18
  18. package/dist/component/engine/durableJob.d.ts +24 -17
  19. package/dist/component/engine/durableJob.js +182 -138
  20. package/dist/component/engine/mandates.d.ts +2 -2
  21. package/dist/component/engine/mandates.js +0 -59
  22. package/dist/component/engine/queue.d.ts +97 -0
  23. package/dist/component/engine/queue.js +201 -0
  24. package/dist/component/engine/resolvers.d.ts +1 -1
  25. package/dist/component/engine/resolvers.js +6 -18
  26. package/dist/component/engine/sandboxCallback.d.ts +2 -2
  27. package/dist/component/engine/sandboxCallback.js +2 -79
  28. package/dist/component/engine/sandboxConfigProvider.d.ts +9 -0
  29. package/dist/component/engine/sandboxConfigProvider.js +32 -0
  30. package/dist/component/engine/sessions.d.ts +28 -10
  31. package/dist/component/engine/sessions.js +130 -170
  32. package/dist/component/engine/streams.d.ts +47 -0
  33. package/dist/component/engine/streams.js +162 -0
  34. package/dist/component/engine/toolApprovals.d.ts +1 -1
  35. package/dist/component/engine/toolApprovals.js +12 -37
  36. package/dist/component/engine/vpConnection.js +0 -40
  37. package/dist/component/engine/wakeup.d.ts +7 -9
  38. package/dist/component/engine/wakeup.js +54 -127
  39. package/dist/component/engine/workflows.d.ts +26 -8
  40. package/dist/component/engine/workflows.js +59 -64
  41. package/dist/component/engine/world.d.ts +250 -0
  42. package/dist/component/engine/world.js +689 -0
  43. package/dist/component/lib/auth.js +0 -26
  44. package/dist/component/lib/connectionSecret.d.ts +5 -0
  45. package/dist/component/lib/connectionSecret.js +32 -0
  46. package/dist/component/schema.d.ts +240 -175
  47. package/dist/component/schema.js +134 -294
  48. package/package.json +21 -6
  49. package/dist/component/engine/habits.d.ts +0 -70
  50. package/dist/component/engine/habits.js +0 -218
  51. package/dist/component/testFixtures/callerSteps.d.ts +0 -20
  52. package/dist/component/testFixtures/callerSteps.js +0 -90
  53. package/dist/component/testFixtures/delegationDemoWorkflow.d.ts +0 -18
  54. package/dist/component/testFixtures/delegationDemoWorkflow.js +0 -75
  55. package/dist/component/testFixtures/sandboxDemoWorkflow.d.ts +0 -19
  56. package/dist/component/testFixtures/sandboxDemoWorkflow.js +0 -69
  57. package/dist/component/testFixtures/vpTool.d.ts +0 -4
  58. package/dist/component/testFixtures/vpTool.js +0 -25
  59. package/dist/component/testFixtures/wakeupDemoWorkflow.d.ts +0 -19
  60. package/dist/component/testFixtures/wakeupDemoWorkflow.js +0 -77
  61. package/dist/component/testFixtures/wakeupWork.d.ts +0 -8
  62. package/dist/component/testFixtures/wakeupWork.js +0 -21
@@ -1,70 +0,0 @@
1
- export declare const create: import("convex/server").RegisteredMutation<"public", {
2
- name: string;
3
- orgId: string;
4
- agentId: import("convex/values").GenericId<"agents">;
5
- cronExpression: string;
6
- prompt: string;
7
- }, Promise<import("convex/values").GenericId<"habits">>>;
8
- export declare const get: import("convex/server").RegisteredQuery<"public", {
9
- orgId: string;
10
- habitId: import("convex/values").GenericId<"habits">;
11
- }, Promise<{
12
- _id: import("convex/values").GenericId<"habits">;
13
- _creationTime: number;
14
- cronJobId?: string | undefined;
15
- lastRunAt?: number | undefined;
16
- name: string;
17
- orgId: string;
18
- createdAt: number;
19
- updatedAt: number;
20
- agentId: import("convex/values").GenericId<"agents">;
21
- cronExpression: string;
22
- prompt: string;
23
- enabled: boolean;
24
- } | null>>;
25
- export declare const listForAgent: import("convex/server").RegisteredQuery<"public", {
26
- orgId: string;
27
- agentId: import("convex/values").GenericId<"agents">;
28
- }, Promise<{
29
- _id: import("convex/values").GenericId<"habits">;
30
- _creationTime: number;
31
- cronJobId?: string | undefined;
32
- lastRunAt?: number | undefined;
33
- name: string;
34
- orgId: string;
35
- createdAt: number;
36
- updatedAt: number;
37
- agentId: import("convex/values").GenericId<"agents">;
38
- cronExpression: string;
39
- prompt: string;
40
- enabled: boolean;
41
- }[]>>;
42
- export declare const updateSchedule: import("convex/server").RegisteredMutation<"public", {
43
- name?: string | undefined;
44
- cronExpression?: string | undefined;
45
- prompt?: string | undefined;
46
- orgId: string;
47
- habitId: import("convex/values").GenericId<"habits">;
48
- }, Promise<null>>;
49
- export declare const disable: import("convex/server").RegisteredMutation<"public", {
50
- orgId: string;
51
- habitId: import("convex/values").GenericId<"habits">;
52
- }, Promise<null>>;
53
- export declare const enable: import("convex/server").RegisteredMutation<"public", {
54
- orgId: string;
55
- habitId: import("convex/values").GenericId<"habits">;
56
- }, Promise<null>>;
57
- export declare const run: import("convex/server").RegisteredMutation<"internal", {
58
- habitId: import("convex/values").GenericId<"habits">;
59
- }, Promise<null>>;
60
- export declare const listRuns: import("convex/server").RegisteredQuery<"public", {
61
- orgId: string;
62
- habitId: import("convex/values").GenericId<"habits">;
63
- }, Promise<{
64
- _id: import("convex/values").GenericId<"habitRuns">;
65
- _creationTime: number;
66
- orgId: string;
67
- agentId: import("convex/values").GenericId<"agents">;
68
- habitId: import("convex/values").GenericId<"habits">;
69
- ranAt: number;
70
- }[]>>;
@@ -1,218 +0,0 @@
1
- import { v } from "convex/values";
2
- import { Crons } from "@convex-dev/crons";
3
- import { internal, components } from "../_generated/api.js";
4
- import { internalMutation, mutation, query } from "../_generated/server.js";
5
- import { assertSameOrg, requireOrgId } from "../lib/auth.js";
6
- // engine/habits — "intentional divergence from Eve": a habit is what turns
7
- // a chat into an employee. A habit is a DYNAMIC cron per agent/org,
8
- // registered at runtime via @convex-dev/crons — never a static
9
- // `convex/crons.ts`. Creating, running, editing or disabling one deploys
10
- // nothing: the `cronExpression` is DATA carried by the `habits` row, and
11
- // the runtime cron is merely a reflection of it.
12
- //
13
- // orgId discipline (component/lib/auth.ts): every PUBLIC function takes
14
- // `orgId` as an explicit ARGUMENT (auth does not propagate across a
15
- // component boundary), same as engine/mandates.ts. The cron runtime
16
- // target is `internal.engine.habits.run` -- the component-boundary path
17
- // for this module, mirroring `internal.habits.run` at the app level.
18
- const crons = new Crons(components.crons);
19
- const habitDoc = v.object({
20
- _id: v.id("habits"),
21
- _creationTime: v.number(),
22
- orgId: v.string(),
23
- agentId: v.id("agents"),
24
- name: v.string(),
25
- cronExpression: v.string(),
26
- prompt: v.string(),
27
- enabled: v.boolean(),
28
- cronJobId: v.optional(v.string()),
29
- lastRunAt: v.optional(v.number()),
30
- createdAt: v.number(),
31
- updatedAt: v.number(),
32
- });
33
- // create — writes the habit row AND registers its runtime cron in the same
34
- // transaction. @convex-dev/crons guarantees the cron "is guaranteed to
35
- // exist after the mutation that creates them has run" (README) -- so the
36
- // very next tick can fire it, with zero deploy.
37
- export const create = mutation({
38
- args: {
39
- orgId: v.string(),
40
- agentId: v.id("agents"),
41
- name: v.string(),
42
- cronExpression: v.string(),
43
- prompt: v.string(),
44
- },
45
- returns: v.id("habits"),
46
- handler: async (ctx, args) => {
47
- const orgId = requireOrgId(args.orgId);
48
- const agent = await ctx.db.get(args.agentId);
49
- assertSameOrg(orgId, agent, "Agent not found");
50
- if (args.name.trim().length === 0) {
51
- throw new Error("Validation: name must not be empty");
52
- }
53
- if (args.cronExpression.trim().length === 0) {
54
- throw new Error("Validation: cronExpression must not be empty");
55
- }
56
- const now = Date.now();
57
- const habitId = await ctx.db.insert("habits", {
58
- orgId,
59
- agentId: args.agentId,
60
- name: args.name,
61
- cronExpression: args.cronExpression,
62
- prompt: args.prompt,
63
- enabled: true,
64
- createdAt: now,
65
- updatedAt: now,
66
- });
67
- const cronJobId = await crons.register(ctx, { kind: "cron", cronspec: args.cronExpression }, internal.engine.habits.run, { habitId }, `habit:${habitId}`);
68
- await ctx.db.patch(habitId, { cronJobId, updatedAt: Date.now() });
69
- return habitId;
70
- },
71
- });
72
- export const get = query({
73
- args: { orgId: v.string(), habitId: v.id("habits") },
74
- returns: v.union(habitDoc, v.null()),
75
- handler: async (ctx, args) => {
76
- const orgId = requireOrgId(args.orgId);
77
- const doc = await ctx.db.get(args.habitId);
78
- if (!doc || doc.orgId !== orgId)
79
- return null;
80
- return doc;
81
- },
82
- });
83
- export const listForAgent = query({
84
- args: { orgId: v.string(), agentId: v.id("agents") },
85
- returns: v.array(habitDoc),
86
- handler: async (ctx, args) => {
87
- const orgId = requireOrgId(args.orgId);
88
- return await ctx.db
89
- .query("habits")
90
- .withIndex("by_org_agent", (q) => q.eq("orgId", orgId).eq("agentId", args.agentId))
91
- .collect();
92
- },
93
- });
94
- // updateSchedule — edits the habit AT EXECUTION TIME. Deletes the old
95
- // runtime cron and registers a fresh one from the new `cronExpression`,
96
- // still inside a single Convex mutation transaction. No redeploy, no
97
- // downtime window: @convex-dev/crons crons are transactional (README).
98
- export const updateSchedule = mutation({
99
- args: {
100
- orgId: v.string(),
101
- habitId: v.id("habits"),
102
- cronExpression: v.optional(v.string()),
103
- prompt: v.optional(v.string()),
104
- name: v.optional(v.string()),
105
- },
106
- returns: v.null(),
107
- handler: async (ctx, args) => {
108
- const orgId = requireOrgId(args.orgId);
109
- const existing = await ctx.db.get(args.habitId);
110
- assertSameOrg(orgId, existing, "Habit not found");
111
- const { orgId: _orgId, habitId, cronExpression, ...rest } = args;
112
- await ctx.db.patch(habitId, { ...rest, updatedAt: Date.now() });
113
- if (cronExpression !== undefined && cronExpression !== existing.cronExpression) {
114
- if (cronExpression.trim().length === 0) {
115
- throw new Error("Validation: cronExpression must not be empty");
116
- }
117
- if (existing.enabled && existing.cronJobId) {
118
- await crons.delete(ctx, { id: existing.cronJobId });
119
- }
120
- await ctx.db.patch(habitId, { cronExpression, updatedAt: Date.now() });
121
- if (existing.enabled) {
122
- const cronJobId = await crons.register(ctx, { kind: "cron", cronspec: cronExpression }, internal.engine.habits.run, { habitId }, `habit:${habitId}:${Date.now()}`);
123
- await ctx.db.patch(habitId, { cronJobId, updatedAt: Date.now() });
124
- }
125
- }
126
- return null;
127
- },
128
- });
129
- // disable — stops future runs by DELETING the runtime cron registration.
130
- // This is a hard stop, not a soft "skip if disabled" flag: after `disable`
131
- // resolves, there is no scheduled function left that could fire this habit.
132
- export const disable = mutation({
133
- args: { orgId: v.string(), habitId: v.id("habits") },
134
- returns: v.null(),
135
- handler: async (ctx, args) => {
136
- const orgId = requireOrgId(args.orgId);
137
- const existing = await ctx.db.get(args.habitId);
138
- assertSameOrg(orgId, existing, "Habit not found");
139
- if (existing.enabled && existing.cronJobId) {
140
- await crons.delete(ctx, { id: existing.cronJobId });
141
- }
142
- await ctx.db.patch(args.habitId, {
143
- enabled: false,
144
- cronJobId: undefined,
145
- updatedAt: Date.now(),
146
- });
147
- return null;
148
- },
149
- });
150
- // enable — re-registers the runtime cron from the habit's own
151
- // `cronExpression` DATA. No redeploy: the schedule was never lost, only its
152
- // runtime registration.
153
- export const enable = mutation({
154
- args: { orgId: v.string(), habitId: v.id("habits") },
155
- returns: v.null(),
156
- handler: async (ctx, args) => {
157
- const orgId = requireOrgId(args.orgId);
158
- const existing = await ctx.db.get(args.habitId);
159
- assertSameOrg(orgId, existing, "Habit not found");
160
- if (existing.enabled)
161
- return null;
162
- const cronJobId = await crons.register(ctx, { kind: "cron", cronspec: existing.cronExpression }, internal.engine.habits.run, { habitId: args.habitId }, `habit:${args.habitId}:${Date.now()}`);
163
- await ctx.db.patch(args.habitId, {
164
- enabled: true,
165
- cronJobId,
166
- updatedAt: Date.now(),
167
- });
168
- return null;
169
- },
170
- });
171
- // run — the cron-invoked target (internal only, never client-facing).
172
- // Writes a `habitRuns` proof row and bumps `lastRunAt`. This is what makes
173
- // "a habit fires its first run without a deploy" and "a disabled habit does
174
- // not fire again" PROVABLE rather than asserted on trust
175
- // (component/habits.componentPackaging.test.ts). No `orgId` argument: this
176
- // mutation is reachable ONLY by the runtime cron scheduler (never a
177
- // client), and it derives `orgId` from the habit row itself, which is the
178
- // same trust boundary `spendInternal` uses in engine/mandates.ts.
179
- export const run = internalMutation({
180
- args: { habitId: v.id("habits") },
181
- returns: v.null(),
182
- handler: async (ctx, args) => {
183
- const habit = await ctx.db.get(args.habitId);
184
- if (!habit || !habit.enabled) {
185
- // Defence in depth: `disable` already removes the cron registration,
186
- // but if a run was already in flight when disable landed, refuse to
187
- // record it as having run.
188
- return null;
189
- }
190
- const now = Date.now();
191
- await ctx.db.insert("habitRuns", {
192
- orgId: habit.orgId,
193
- habitId: args.habitId,
194
- agentId: habit.agentId,
195
- ranAt: now,
196
- });
197
- await ctx.db.patch(args.habitId, { lastRunAt: now });
198
- return null;
199
- },
200
- });
201
- export const listRuns = query({
202
- args: { orgId: v.string(), habitId: v.id("habits") },
203
- returns: v.array(v.object({
204
- _id: v.id("habitRuns"),
205
- _creationTime: v.number(),
206
- orgId: v.string(),
207
- habitId: v.id("habits"),
208
- agentId: v.id("agents"),
209
- ranAt: v.number(),
210
- })),
211
- handler: async (ctx, args) => {
212
- const orgId = requireOrgId(args.orgId);
213
- return await ctx.db
214
- .query("habitRuns")
215
- .withIndex("by_org_habit", (q) => q.eq("orgId", orgId).eq("habitId", args.habitId))
216
- .collect();
217
- },
218
- });
@@ -1,20 +0,0 @@
1
- export declare const getAttemptCount: import("convex/server").RegisteredQuery<"internal", {
2
- jobId: import("convex/values").GenericId<"durableJobs">;
3
- stepIndex: number;
4
- }, Promise<number>>;
5
- export declare const resetAttemptCounts: import("convex/server").RegisteredMutation<"internal", {}, Promise<null>>;
6
- export declare const alwaysSucceeds: import("convex/server").RegisteredMutation<"internal", {
7
- orgId: string;
8
- jobId: import("convex/values").GenericId<"durableJobs">;
9
- stepIndex: number;
10
- }, Promise<null>>;
11
- export declare const alwaysFails: import("convex/server").RegisteredMutation<"internal", {
12
- orgId: string;
13
- jobId: import("convex/values").GenericId<"durableJobs">;
14
- stepIndex: number;
15
- }, Promise<never>>;
16
- export declare const failsFirstAttemptThenSucceeds: import("convex/server").RegisteredMutation<"internal", {
17
- orgId: string;
18
- jobId: import("convex/values").GenericId<"durableJobs">;
19
- stepIndex: number;
20
- }, Promise<null>>;
@@ -1,90 +0,0 @@
1
- import { v } from "convex/values";
2
- import { internalMutation, internalQuery } from "../_generated/server.js";
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 abandonment after N failures. 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
- });
@@ -1,18 +0,0 @@
1
- import { WorkflowManager } from "@convex-dev/workflow";
2
- export declare const workflow: WorkflowManager;
3
- export declare const start: import("convex/server").RegisteredMutation<"public", {
4
- orgId: string;
5
- awaitedEventName: string;
6
- }, Promise<{
7
- workflowId: string;
8
- }>>;
9
- export declare const delegationDemoWorkflowV1: import("convex/server").RegisteredMutation<"internal", import("@convex-dev/workflow").WorkflowArgs<{
10
- orgId: import("convex/values").VString<string, "required">;
11
- awaitedEventName: import("convex/values").VString<string, "required">;
12
- }>, import("@convex-dev/workflow").WorkflowId>;
13
- export declare const recordDelegationResolvedByEventInternal: import("convex/server").RegisteredMutation<"internal", {
14
- resultSummary?: string | undefined;
15
- orgId: string;
16
- status: "completed" | "cancelled" | "failed";
17
- awaitedEventName: string;
18
- }, Promise<null>>;
@@ -1,75 +0,0 @@
1
- import { WorkflowManager } from "@convex-dev/workflow";
2
- import { v } from "convex/values";
3
- import { components, internal } from "../_generated/api.js";
4
- import { internalMutation, mutation } from "../_generated/server.js";
5
- import { requireOrgId } from "../lib/auth.js";
6
- // testFixtures/delegationDemoWorkflow — TEST-ONLY reference PARENT-side
7
- // recipient for delegation. This is exactly the "bespoke
8
- // workflow" shape `engine/wakeup.ts`'s own comment describes ("any
9
- // workflow -- a session, a durable job, or a bespoke one -- can be the wake
10
- // target"): it parks on `step.awaitEvent` and, once
11
- // `engine.wakeup.onWorkComplete` fires `sendEvent` (via
12
- // `engine.delegation.delegateToSubagent` -> `engine.wakeup.enqueue`, the
13
- // same public action this component ships), it patches the SAME `delegationRuns`
14
- // row `delegateToSubagent` created -- proving A paused and was woken by
15
- // B's completion, with zero polling.
16
- export const workflow = new WorkflowManager(components.workflow);
17
- export const start = mutation({
18
- args: { orgId: v.string(), awaitedEventName: v.string() },
19
- returns: v.object({ workflowId: v.string() }),
20
- handler: async (ctx, args) => {
21
- const orgId = requireOrgId(args.orgId);
22
- const workflowId = await workflow.start(ctx, internal.testFixtures.delegationDemoWorkflow.delegationDemoWorkflowV1, { orgId, awaitedEventName: args.awaitedEventName });
23
- return { workflowId };
24
- },
25
- });
26
- export const delegationDemoWorkflowV1 = workflow.define({
27
- args: { orgId: v.string(), awaitedEventName: v.string() },
28
- returns: v.null(),
29
- handler: async (step, args) => {
30
- // A pauses here. Resumes ONLY when engine.wakeup.onWorkComplete calls
31
- // sendEvent with this exact name -- no loop, no timer.
32
- const result = await step.awaitEvent({
33
- name: args.awaitedEventName,
34
- validator: v.object({
35
- status: v.union(v.literal("completed"), v.literal("failed"), v.literal("cancelled")),
36
- resultSummary: v.optional(v.string()),
37
- }),
38
- });
39
- await step.runMutation(internal.testFixtures.delegationDemoWorkflow.recordDelegationResolvedByEventInternal, {
40
- orgId: args.orgId,
41
- awaitedEventName: args.awaitedEventName,
42
- status: result.status,
43
- resultSummary: result.resultSummary,
44
- });
45
- return null;
46
- },
47
- });
48
- // recordDelegationResolvedByEventInternal — looks up the delegationRuns row
49
- // created for THIS (orgId, awaitedEventName) pair via `by_org_awaitedEvent`
50
- // (no `.filter()`) and patches it. Kept as one mutation (rather than two
51
- // round trips from the workflow step) so the lookup and patch land in the
52
- // same transaction.
53
- export const recordDelegationResolvedByEventInternal = internalMutation({
54
- args: {
55
- orgId: v.string(),
56
- awaitedEventName: v.string(),
57
- status: v.union(v.literal("completed"), v.literal("failed"), v.literal("cancelled")),
58
- resultSummary: v.optional(v.string()),
59
- },
60
- returns: v.null(),
61
- handler: async (ctx, args) => {
62
- const row = await ctx.db
63
- .query("delegationRuns")
64
- .withIndex("by_org_awaitedEvent", (q) => q.eq("orgId", args.orgId).eq("awaitedEventName", args.awaitedEventName))
65
- .first();
66
- if (!row)
67
- return null;
68
- await ctx.db.patch(row._id, {
69
- status: args.status,
70
- resultSummary: args.resultSummary,
71
- updatedAt: Date.now(),
72
- });
73
- return null;
74
- },
75
- });
@@ -1,19 +0,0 @@
1
- import { WorkflowManager } from "@convex-dev/workflow";
2
- import type { Id } from "../_generated/dataModel.js";
3
- export declare const workflow: WorkflowManager;
4
- export declare const start: import("convex/server").RegisteredMutation<"public", {
5
- orgId: string;
6
- awaitedEventName: string;
7
- }, Promise<{
8
- workflowId: string;
9
- resultId: Id<"wakeupDemoResults">;
10
- }>>;
11
- export declare const recordWokenInternal: import("convex/server").RegisteredMutation<"internal", {
12
- resultSummary?: string | undefined;
13
- status: "completed" | "failed";
14
- resultId: import("convex/values").GenericId<"wakeupDemoResults">;
15
- }, Promise<null>>;
16
- export declare const sandboxDemoWorkflowV1: import("convex/server").RegisteredMutation<"internal", import("@convex-dev/workflow").WorkflowArgs<{
17
- resultId: import("convex/values").VId<import("convex/values").GenericId<"wakeupDemoResults">, "required">;
18
- awaitedEventName: import("convex/values").VString<string, "required">;
19
- }>, import("@convex-dev/workflow").WorkflowId>;
@@ -1,69 +0,0 @@
1
- import { WorkflowManager } from "@convex-dev/workflow";
2
- import { v } from "convex/values";
3
- import { components, internal } from "../_generated/api.js";
4
- import { internalMutation, mutation } from "../_generated/server.js";
5
- import { requireOrgId } from "../lib/auth.js";
6
- // testFixtures/sandboxDemoWorkflow — TEST-ONLY reference recipient for the
7
- // sandbox bridge, same shape/role as testFixtures/wakeupDemoWorkflow.ts.
8
- // A real consumer's
9
- // own workflow parks on `step.awaitEvent({ name: <awaitedEventName passed
10
- // to engine/sandboxCallback.startRun> })` and does nothing else. Proves
11
- // (component/sandboxCallback.componentPackaging.test.ts) the ONLY way this
12
- // resumes is a signed, deduplicated callback reaching
13
- // `engine/sandboxCallback.processCallback` (or the self-built timeout) --
14
- // no poll, no interval, no direct call.
15
- export const workflow = new WorkflowManager(components.workflow);
16
- export const start = mutation({
17
- args: { orgId: v.string(), awaitedEventName: v.string() },
18
- returns: v.object({
19
- workflowId: v.string(),
20
- resultId: v.id("wakeupDemoResults"),
21
- }),
22
- handler: async (ctx, args) => {
23
- const orgId = requireOrgId(args.orgId);
24
- // Reuses wakeupDemoResults -- same test-observation shape (enqueuedAt /
25
- // wokenAt / observedStatus), decoupled from the wake-up-pool path by
26
- // definition since nothing here touches wakeableWork.
27
- const resultId = await ctx.db.insert("wakeupDemoResults", { orgId, enqueuedAt: Date.now() });
28
- const workflowId = await workflow.start(ctx, internal.testFixtures.sandboxDemoWorkflow.sandboxDemoWorkflowV1, { resultId, awaitedEventName: args.awaitedEventName });
29
- return { workflowId, resultId };
30
- },
31
- });
32
- export const recordWokenInternal = internalMutation({
33
- args: {
34
- resultId: v.id("wakeupDemoResults"),
35
- status: v.union(v.literal("completed"), v.literal("failed")),
36
- resultSummary: v.optional(v.string()),
37
- },
38
- returns: v.null(),
39
- handler: async (ctx, args) => {
40
- await ctx.db.patch(args.resultId, {
41
- wokenAt: Date.now(),
42
- observedStatus: args.status,
43
- observedResultSummary: args.resultSummary,
44
- });
45
- return null;
46
- },
47
- });
48
- export const sandboxDemoWorkflowV1 = workflow.define({
49
- args: { resultId: v.id("wakeupDemoResults"), awaitedEventName: v.string() },
50
- returns: v.null(),
51
- handler: async (step, args) => {
52
- // Blocks here -- resumes ONLY when a signed, non-duplicate sandbox
53
- // callback (or the self-built timeout) calls `workflow.sendEvent` with
54
- // this exact name. No loop, no timer polling this workflow's state.
55
- const result = await step.awaitEvent({
56
- name: args.awaitedEventName,
57
- validator: v.object({
58
- status: v.union(v.literal("completed"), v.literal("failed")),
59
- resultSummary: v.optional(v.string()),
60
- }),
61
- });
62
- await step.runMutation(internal.testFixtures.sandboxDemoWorkflow.recordWokenInternal, {
63
- resultId: args.resultId,
64
- status: result.status,
65
- resultSummary: result.resultSummary,
66
- });
67
- return null;
68
- },
69
- });
@@ -1,4 +0,0 @@
1
- export declare const writeToTenant: import("convex/server").RegisteredMutation<"internal", {
2
- orgId: string;
3
- argsJson: string;
4
- }, Promise<null>>;
@@ -1,25 +0,0 @@
1
- import { v } from "convex/values";
2
- import { internalMutation } from "../_generated/server.js";
3
- // testFixtures/vpTool — stand-in for a VantagePeers-owned mutation reached
4
- // via `createFunctionHandle` (same discipline as testFixtures/wakeupWork.ts
5
- // and durableJob's caller-step fixtures). Ported from
6
- // convex/testFixtures/vpTool.ts. This engine never owns VantagePeers'
7
- // schema, so its own `vpTenantFixture` table plays the role of "VP's own
8
- // tenant table" for `component/engine/vpConnection.test.ts`). Writes
9
- // EXACTLY the orgId it was handed -- it does not derive one from
10
- // `ctx.auth` -- so the caller (`engine.vpConnection.invokeVpTool`) is what
11
- // determines tenant isolation, which is precisely the property under test.
12
- export const writeToTenant = internalMutation({
13
- args: { orgId: v.string(), argsJson: v.string() },
14
- returns: v.null(),
15
- handler: async (ctx, args) => {
16
- const parsed = JSON.parse(args.argsJson);
17
- await ctx.db.insert("vpTenantFixture", {
18
- orgId: args.orgId,
19
- tool: parsed.tool,
20
- argsJson: args.argsJson,
21
- createdAt: Date.now(),
22
- });
23
- return null;
24
- },
25
- });
@@ -1,19 +0,0 @@
1
- import { WorkflowManager } from "@convex-dev/workflow";
2
- import type { Id } from "../_generated/dataModel.js";
3
- export declare const workflow: WorkflowManager;
4
- export declare const start: import("convex/server").RegisteredMutation<"public", {
5
- orgId: string;
6
- awaitedEventName: string;
7
- }, Promise<{
8
- workflowId: string;
9
- resultId: Id<"wakeupDemoResults">;
10
- }>>;
11
- export declare const recordWokenInternal: import("convex/server").RegisteredMutation<"internal", {
12
- resultSummary?: string | undefined;
13
- status: "completed" | "cancelled" | "failed";
14
- resultId: import("convex/values").GenericId<"wakeupDemoResults">;
15
- }, Promise<null>>;
16
- export declare const wakeupDemoWorkflowV1: import("convex/server").RegisteredMutation<"internal", import("@convex-dev/workflow").WorkflowArgs<{
17
- resultId: import("convex/values").VId<import("convex/values").GenericId<"wakeupDemoResults">, "required">;
18
- awaitedEventName: import("convex/values").VString<string, "required">;
19
- }>, import("@convex-dev/workflow").WorkflowId>;