@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,75 +1,16 @@
1
1
  import { defineSchema, defineTable } from "convex/server";
2
2
  import { v } from "convex/values";
3
- // @vantageos/agent-engine — COMPONENT schema (T2-QUATER).
4
- //
5
- // This is the schema that actually ships INSIDE the published package and
6
- // is deployed under the component boundary when a consumer registers
7
- // `app.use(agentEngine)` — as opposed to `convex/schema.ts` at the repo
8
- // root, which only backs THIS repo's own dev/test deployment.
9
- //
10
- // SCOPE (T2-QUATER: durableJob only. T2-QUINQUIES, this pass: WIDENS the
11
- // boundary to the SESSIONS surface a downstream conversation-thread
12
- // consumer needs — session start/turn, streamed reasoning/tool-call events,
13
- // toolApprovals response, replay on reconnect, dynamic resolver — see
14
- // README "What's callable"). Still DELIBERATELY NARROW: `skills`,
15
- // `connections`, `subagents`, `habits`, `sandboxConfig`, `modelKeys`,
16
- // `mandates`, `awaitEventTimeouts`, `wakeableWork` are NOT re-hosted here —
17
- // each of those needs its own component-level test harness first (Day 128:
18
- // never ship unverified). `agents` is included, minimally, because a
19
- // session cannot exist without an owning agent row the component itself
20
- // can read (component isolation: this package cannot read a consumer app's
21
- // OWN `agents` table across the component boundary).
22
3
  export default defineSchema({
23
- // durableJobs — GENERIC durable step-batch primitive. See
24
- // engine/durableJob.ts for the full contract description.
25
- durableJobs: defineTable({
26
- orgId: v.string(),
27
- kind: v.string(),
28
- stepFunctionHandle: v.string(),
29
- totalSteps: v.number(),
30
- maxAttemptsPerStep: v.number(),
31
- cursor: v.number(),
32
- status: v.union(v.literal("running"), v.literal("completed"), v.literal("abandoned"), v.literal("cancelled")),
33
- workflowId: v.optional(v.string()),
34
- workflowVersion: v.string(),
35
- lastError: v.optional(v.string()),
36
- abandonedAtStep: v.optional(v.number()),
37
- createdAt: v.number(),
38
- updatedAt: v.number(),
39
- })
40
- .index("by_org", ["orgId"])
41
- .index("by_org_status", ["orgId", "status"]),
42
- // callerStepInvocations — TEST-ONLY fixture table, see
43
- // convex/component/testFixtures/callerSteps.ts. Not part of the public
44
- // contract; kept so the component-level test harness can prove the
45
- // deterministic-replay / abandon-after-N properties the same way the
46
- // repo-root test suite does.
47
- callerStepInvocations: defineTable({
48
- orgId: v.string(),
49
- jobId: v.id("durableJobs"),
50
- stepIndex: v.number(),
51
- createdAt: v.number(),
52
- })
53
- .index("by_org", ["orgId"])
54
- .index("by_org_job", ["orgId", "jobId"]),
55
- // ---------------------------------------------------------------------
56
- // T2-QUINQUIES — SESSIONS surface (EveVantage V4 conversation thread).
57
- // ---------------------------------------------------------------------
58
- // agents — minimal root sheet, ONLY the fields the sessions surface
59
- // needs to resolve a manifest (name/instructions/model) + gate tool
60
- // calls (via toolApprovals below). The full agent aggregate (skills,
61
- // connections, subagents, habits, sandboxConfig, modelKeys) stays out of
62
- // this component boundary for this pass — see the SCOPE note above.
63
4
  agents: defineTable({
64
5
  orgId: v.string(),
65
6
  name: v.string(),
66
- description: v.optional(v.string()), // eve: agent-definition.ts:291
7
+ description: v.optional(v.string()),
67
8
  instructions: v.string(),
68
9
  model: v.string(),
69
- reasoningEffort: v.optional(v.union(v.literal("none"), v.literal("low"), v.literal("medium"), v.literal("high"))), // eve: agent-definition.ts:315-319
70
- modelProvider: v.optional(v.union(v.literal("gateway"), v.literal("openrouter"))), // intentional divergence from Eve (bring-your-own-key); optional so it stays additive/backwards-compatible with rows created before this field existed
71
- maxSubagents: v.optional(v.number()), // eve: agent-definition.ts:163-179, default 100
72
- maxInputTokensPerSession: v.optional(v.union(v.number(), v.literal(false))), // eve: :195, false = unlimited
10
+ reasoningEffort: v.optional(v.union(v.literal("none"), v.literal("low"), v.literal("medium"), v.literal("high"))),
11
+ modelProvider: v.optional(v.union(v.literal("gateway"), v.literal("openrouter"))),
12
+ maxSubagents: v.optional(v.number()),
13
+ maxInputTokensPerSession: v.optional(v.union(v.number(), v.literal(false))),
73
14
  status: v.union(v.literal("draft"), v.literal("live"), v.literal("archived")),
74
15
  createdBy: v.string(),
75
16
  createdAt: v.number(),
@@ -77,11 +18,6 @@ export default defineSchema({
77
18
  })
78
19
  .index("by_org", ["orgId"])
79
20
  .index("by_org_status", ["orgId", "status"]),
80
- // toolApprovals — approval gate per (agent, tool), eve parity
81
- // (public/definitions/tool.ts:117-125). "never" (default) means a tool
82
- // call never pauses for human approval; "always"/"once" gate every /
83
- // first call of a session via a `tool.approval.requested` sessionEvent
84
- // that `toolApprovals.respond` resolves.
85
21
  toolApprovals: defineTable({
86
22
  orgId: v.string(),
87
23
  agentId: v.id("agents"),
@@ -93,102 +29,18 @@ export default defineSchema({
93
29
  .index("by_org", ["orgId"])
94
30
  .index("by_org_agent", ["orgId", "agentId"])
95
31
  .index("by_org_agent_tool", ["orgId", "agentId", "toolName"]),
96
- // sessions — one conversation container per `sessions.start` call.
97
- // "intentional divergence from Eve": a session is piloted by a VERSIONED
98
- // `@convex-dev/workflow` run (engine/workflows.ts: sessionWorkflowV1), so
99
- // a turn survives a deploy. `workflowId`/`workflowVersion` are optional:
100
- // rows written before that workflow existed stay valid without a backfill,
101
- // and a consumer driving turns itself may leave them unset.
102
- sessions: defineTable({
103
- orgId: v.string(),
104
- agentId: v.id("agents"),
105
- status: v.union(v.literal("running"), v.literal("completed"), v.literal("cancelled")),
106
- createdBy: v.string(),
107
- workflowId: v.optional(v.string()),
108
- workflowVersion: v.optional(v.string()),
109
- createdAt: v.number(),
110
- updatedAt: v.number(),
111
- })
112
- .index("by_org", ["orgId"])
113
- .index("by_org_agent", ["orgId", "agentId"]),
114
- // sessionEvents — the append-only event log a client replays on
115
- // reconnect (`sessions.listEvents`) and that IS the streaming transport:
116
- // Convex query subscriptions push new rows to every subscriber in real
117
- // time, so "streaming the reasoning and tool calls" is simply inserting
118
- // rows here as they happen and letting reactivity carry them to the
119
- // client — no separate transport needed. Event names mirror eve's
120
- // session/turn boundaries (`session.started`, `turn.started`,
121
- // dynamic-tool-definition.ts:40-46) plus the turn-body events this pass
122
- // adds (reasoning chunks, tool-call lifecycle, approval lifecycle).
123
- sessionEvents: defineTable({
124
- orgId: v.string(),
125
- sessionId: v.id("sessions"),
126
- name: v.union(v.literal("session.started"), v.literal("turn.started"), v.literal("turn.reasoning"), v.literal("turn.toolCall.requested"), v.literal("turn.toolCall.completed"),
127
- // "intentional divergence from Eve" (VP k17ea6x3tht8xa3rxm139t23gx8ajkf3, defaut 2):
128
- // explicit terminal state for a one-turn approval REFUSAL, distinct
129
- // from "turn.toolCall.completed" (tool actually ran). Without this,
130
- // a denied tool call had no event of its own -- a consumer had no
131
- // way to tell "refused this turn" from "skipped, turn completed
132
- // anyway", which is the wrong default a consumer had to code around.
133
- v.literal("turn.toolCall.denied"), v.literal("turn.completed"), v.literal("tool.approval.requested"), v.literal("tool.approval.resolved"),
134
- // Blank event auto-inserted by `engine/awaitEventTimeout.fire`
135
- // (workflow#177: Convex provides no native timeout for
136
- // `step.awaitEvent`). See engine/awaitEventTimeout.ts.
137
- v.literal("sandbox.timeout")),
138
- payload: v.optional(v.string()), // JSON-serialized, free contract per event name
139
- createdAt: v.number(),
140
- })
141
- .index("by_org", ["orgId"])
142
- .index("by_org_session", ["orgId", "sessionId"]),
143
- // ---------------------------------------------------------------------
144
- // WAKE-UP WITHOUT POLLING. See engine/wakeup.ts and
145
- // engine/awaitEventTimeout.ts for the mechanism itself.
146
- // ---------------------------------------------------------------------
147
- // wakeableWork — GENERIC wake-up primitive. `enqueue` puts caller-supplied
148
- // work (an action, referenced by an opaque function-handle string) into a
149
- // dedicated `@convex-dev/workpool`; that pool's `onComplete` callback
150
- // calls `workflow.sendEvent(...)` against the CALLER-supplied
151
- // `workflowId` the instant the work resolves — success, failure, OR
152
- // cancellation alike. Zero setInterval, zero cron.
153
- wakeableWork: defineTable({
154
- orgId: v.string(),
155
- workflowId: v.string(), // target: the workflow parked on step.awaitEvent
156
- awaitedEventName: v.string(), // event name that workflow is awaiting
157
- workId: v.string(), // @convex-dev/workpool WorkId, opaque string
158
- status: v.union(v.literal("pending"), v.literal("completed"), // work succeeded
159
- v.literal("failed"), // work threw
160
- v.literal("cancelled")),
161
- resultSummary: v.optional(v.string()), // error message when status=="failed"
162
- createdAt: v.number(),
163
- updatedAt: v.number(),
164
- })
165
- .index("by_org", ["orgId"])
166
- .index("by_org_workflow", ["orgId", "workflowId"]),
167
- // awaitEventTimeouts — self-built timeout for `step.awaitEvent`
168
- // (workflow#177: "Convex does not provide a native timeout for
169
- // awaitEvent"). A scheduled mutation (`ctx.scheduler.runAfter`) fires a
170
- // blank `sandbox.timeout` sessionEvent if the awaited event never arrives
171
- // before `deadlineAt`.
172
32
  awaitEventTimeouts: defineTable({
173
33
  orgId: v.string(),
174
- sessionId: v.id("sessions"),
34
+ runId: v.optional(v.string()),
35
+ sessionId: v.optional(v.string()),
175
36
  awaitedEventName: v.string(),
176
37
  deadlineAt: v.number(),
177
- // optional: patched right after insert, once the scheduled function
178
- // itself has been created (it needs this row's id as its argument).
179
38
  scheduledFunctionId: v.optional(v.id("_scheduled_functions")),
180
39
  firedAt: v.optional(v.number()),
181
40
  cancelledAt: v.optional(v.number()),
182
41
  })
183
42
  .index("by_org", ["orgId"])
184
- .index("by_org_session", ["orgId", "sessionId"]),
185
- // wakeupDemoResults — TEST-ONLY fixture, see
186
- // component/testFixtures/wakeupDemoWorkflow.ts. Not part of the public
187
- // contract; a reference recipient workflow parked on `step.awaitEvent`
188
- // patches `wokenAt`/`observedStatus` here ONLY after
189
- // `engine/wakeup.onWorkComplete` has called `sendEvent` -- its presence
190
- // is the test-observable proof that the wake mechanism (not a poll)
191
- // resumed the workflow. Same discipline as `callerStepInvocations` above.
43
+ .index("by_org_run", ["orgId", "runId"]),
192
44
  wakeupDemoResults: defineTable({
193
45
  orgId: v.string(),
194
46
  enqueuedAt: v.number(),
@@ -196,23 +48,13 @@ export default defineSchema({
196
48
  observedStatus: v.optional(v.union(v.literal("completed"), v.literal("failed"), v.literal("cancelled"))),
197
49
  observedResultSummary: v.optional(v.string()),
198
50
  }).index("by_org", ["orgId"]),
199
- // ---------------------------------------------------------------------
200
- // MANDATES — "intentional divergence from Eve": bounded authority. See
201
- // engine/mandates.ts and engine/wakeup.ts, where `enqueue` funds through
202
- // `mandates.spendInternal` before any work can enter the wakeup pool.
203
- // ---------------------------------------------------------------------
204
- // mandates — a cap is a DATA row scoped by orgId + agentId + scope,
205
- // never a constant read from code. `kind`/`rate`/`period`/`capacity` are
206
- // the inline contract of @convex-dev/rate-limiter ("dynamic config"): the
207
- // COST (`count`) is a WEIGHTED count (tokens/euros/calls), never a plain
208
- // call counter. Changing a cap is a row mutation, never a deployment.
209
51
  mandates: defineTable({
210
52
  orgId: v.string(),
211
53
  agentId: v.id("agents"),
212
- scope: v.string(), // free-form business name (e.g. "llm-tokens") -- DATA, not a constant
54
+ scope: v.string(),
213
55
  kind: v.union(v.literal("token bucket"), v.literal("fixed window")),
214
56
  rate: v.number(),
215
- period: v.number(), // ms
57
+ period: v.number(),
216
58
  capacity: v.optional(v.number()),
217
59
  createdAt: v.number(),
218
60
  updatedAt: v.number(),
@@ -220,9 +62,6 @@ export default defineSchema({
220
62
  .index("by_org", ["orgId"])
221
63
  .index("by_org_agent", ["orgId", "agentId"])
222
64
  .index("by_org_agent_scope", ["orgId", "agentId", "scope"]),
223
- // approvalRequests — created whenever a mandate is exceeded. NEVER a
224
- // silent bypass: an overrun always becomes a persisted HUMAN approval
225
- // request, never a silent refusal with no trace.
226
65
  approvalRequests: defineTable({
227
66
  orgId: v.string(),
228
67
  agentId: v.id("agents"),
@@ -237,77 +76,15 @@ export default defineSchema({
237
76
  .index("by_org", ["orgId"])
238
77
  .index("by_org_agent", ["orgId", "agentId"])
239
78
  .index("by_org_status", ["orgId", "status"]),
240
- // ---------------------------------------------------------------------
241
- // HABITS — "intentional divergence from Eve": a habit is a DYNAMIC cron
242
- // per agent/org, which is what turns a chat into an employee. See
243
- // engine/habits.ts; `crons` is a sub-component of THIS component,
244
- // registered in component/convex.config.ts.
245
- // ---------------------------------------------------------------------
246
- // habits — a schedule is a DATA row scoped by orgId + agentId, never a
247
- // constant read from a deployed `convex/crons.ts`. `cronExpression` is
248
- // the DATA that drives the runtime cron registered via
249
- // `@convex-dev/crons`; create/updateSchedule/enable/disable never
250
- // redeploy anything.
251
- habits: defineTable({
252
- orgId: v.string(),
253
- agentId: v.id("agents"),
254
- name: v.string(),
255
- cronExpression: v.string(),
256
- prompt: v.string(),
257
- enabled: v.boolean(),
258
- // id of the runtime-registered @convex-dev/crons job, present only
259
- // while `enabled` is true (cleared by `disable`, reassigned by
260
- // `enable` / `updateSchedule` -- no redeploy involved in any transition).
261
- cronJobId: v.optional(v.string()),
262
- lastRunAt: v.optional(v.number()),
263
- createdAt: v.number(),
264
- updatedAt: v.number(),
265
- })
266
- .index("by_org", ["orgId"])
267
- .index("by_org_agent", ["orgId", "agentId"]),
268
- // habitRuns — proof of execution of a habit (MUST_PASS/MUST_BLOCK probe,
269
- // component/habits.componentPackaging.test.ts). Every tick of the runtime
270
- // cron registered via @convex-dev/crons writes a row here -- this is what
271
- // makes "fires its first run without a deploy" PROVABLE rather than
272
- // asserted on trust.
273
- habitRuns: defineTable({
274
- orgId: v.string(),
275
- habitId: v.id("habits"),
276
- agentId: v.id("agents"),
277
- ranAt: v.number(),
278
- })
279
- .index("by_org", ["orgId"])
280
- .index("by_org_habit", ["orgId", "habitId"]),
281
- // ---------------------------------------------------------------------
282
- // SANDBOX BRIDGE — the same pause/wake mechanism serves both the sandbox
283
- // and agent-to-agent delegation. See engine/sandboxCallback.ts.
284
- //
285
- // BOUNDARY, and why it is where it is: the "use node" action
286
- // that actually talks to `@vercel/sandbox` CANNOT live inside this
287
- // component — the
288
- // Convex CLI's own component bundler HARD-CRASHES on any "use node" file
289
- // found in a child component directory ("use node directive is not
290
- // supported in components", node_modules/convex/dist/cjs/cli/lib/
291
- // components/definition/bundle.js). It stays app-level and calls INTO
292
- // this component's public `engine.sandboxCallback.startRun` /
293
- // `attachSandboxId` (and `engine.mandates.spend`) instead. Likewise, no
294
- // official `@convex-dev/*` component ships an `http.ts` or reads
295
- // `process.env` directly (measured: zero hits across all 7 installed
296
- // `@convex-dev/*` component dist trees) — the signed callback's public
297
- // HTTP route (`/sandbox/callback`) and its `SANDBOX_CALLBACK_SECRET`
298
- // read + HMAC verification stay in the CONSUMER app's `convex/http.ts`,
299
- // which delegates to this component's public
300
- // `engine.sandboxCallback.processCallback` /
301
- // `engine.sandboxCallback.deadLetterMalformed` ONLY after the signature
302
- // has already checked out.
303
79
  sandboxConfig: defineTable({
304
80
  orgId: v.string(),
305
81
  agentId: v.id("agents"),
306
- provider: v.literal("vercel-sandbox"),
82
+ provider: v.union(v.literal("vercel-sandbox"), v.literal("e2b"), v.literal("daytona"), v.literal("box")),
307
83
  image: v.optional(v.string()),
308
84
  timeoutSeconds: v.number(),
309
85
  egressPolicy: v.union(v.literal("allow-all"), v.literal("deny-all-allowlist")),
310
86
  allowlistDomains: v.array(v.string()),
87
+ boxId: v.optional(v.string()),
311
88
  createdAt: v.number(),
312
89
  updatedAt: v.number(),
313
90
  })
@@ -315,9 +92,9 @@ export default defineSchema({
315
92
  .index("by_org_agent", ["orgId", "agentId"]),
316
93
  sandboxRuns: defineTable({
317
94
  orgId: v.string(),
318
- workflowId: v.string(), // target: the workflow parked on step.awaitEvent
319
- awaitedEventName: v.string(), // event name that workflow is awaiting
320
- sandboxId: v.string(), // Vercel Sandbox id (opaque, from Sandbox.create())
95
+ workflowId: v.string(),
96
+ awaitedEventName: v.string(),
97
+ sandboxId: v.string(),
321
98
  status: v.union(v.literal("pending"), v.literal("completed"), v.literal("failed"), v.literal("timedOut")),
322
99
  resultSummary: v.optional(v.string()),
323
100
  createdAt: v.number(),
@@ -329,39 +106,24 @@ export default defineSchema({
329
106
  sandboxCallbackDeliveries: defineTable({
330
107
  orgId: v.string(),
331
108
  sandboxRunId: v.id("sandboxRuns"),
332
- deliveryId: v.string(), // caller-supplied, opaque, unique per attempt-family
333
- outcome: v.union(v.literal("woke"), // first valid delivery for this id -- sendEvent fired
334
- v.literal("duplicate")),
109
+ deliveryId: v.string(),
110
+ outcome: v.union(v.literal("woke"), v.literal("duplicate")),
335
111
  receivedAt: v.number(),
336
112
  })
337
113
  .index("by_org", ["orgId"])
338
114
  .index("by_org_delivery", ["orgId", "deliveryId"])
339
115
  .index("by_org_run", ["orgId", "sandboxRunId"]),
340
116
  sandboxCallbackDeadLetters: defineTable({
341
- orgId: v.optional(v.string()), // absent when the payload itself couldn't be parsed
117
+ orgId: v.optional(v.string()),
342
118
  deliveryId: v.string(),
343
119
  reason: v.string(),
344
120
  rawBody: v.string(),
345
121
  createdAt: v.number(),
346
122
  }).index("by_org", ["orgId"]),
347
- // ---------------------------------------------------------------------
348
- // DELEGATION — an agent delegating to another agent uses THE SAME
349
- // pause/wake system as the sandbox bridge: one system to maintain, not
350
- // two. See engine/delegation.ts and engine/vpConnection.ts: every
351
- // delegation/clone path terminates in exactly one call to this
352
- // component's own public `engine/wakeup.enqueue`, never in a second
353
- // onComplete/sendEvent path.
354
- // ---------------------------------------------------------------------
355
- // subagents — declared subagents, RECURSIVE (the child is a full
356
- // `agents` row with its own aggregate). Isolation is by construction
357
- // (eve.dev/docs/subagents: "inherits nothing from the root's authored
358
- // slots") -- this table ONLY declares the parent->child link and exposes
359
- // it as a named tool; it never copies a single field out of the child's
360
- // own skills/connections/toolApprovals onto the parent.
361
123
  subagents: defineTable({
362
124
  orgId: v.string(),
363
125
  parentAgentId: v.id("agents"),
364
- childAgentId: v.id("agents"), // full agent, own aggregate -- strict isolation
126
+ childAgentId: v.id("agents"),
365
127
  toolName: v.string(),
366
128
  description: v.optional(v.string()),
367
129
  enabled: v.boolean(),
@@ -370,16 +132,7 @@ export default defineSchema({
370
132
  .index("by_org", ["orgId"])
371
133
  .index("by_org_parent", ["orgId", "parentAgentId"])
372
134
  .index("by_org_child", ["orgId", "childAgentId"])
373
- // Build-time collision guard (engine/delegation.declareSubagent):
374
- // exact-match lookup of an existing (parent, toolName) pair, no filter().
375
135
  .index("by_org_parent_tool", ["orgId", "parentAgentId", "toolName"]),
376
- // delegationRuns — one row per A->B delegation through a declared
377
- // subagent (`subagents` table above). `workflowId`/`awaitedEventName` are
378
- // the SAME two fields `engine/wakeup.enqueue` already threads through to
379
- // `workflow.sendEvent` -- this table records WHICH delegation a given
380
- // `wakeableWork` row belongs to; it never duplicates the wake mechanism
381
- // itself (no second onComplete/sendEvent path exists anywhere in this
382
- // engine).
383
136
  delegationRuns: defineTable({
384
137
  orgId: v.string(),
385
138
  parentAgentId: v.id("agents"),
@@ -396,17 +149,9 @@ export default defineSchema({
396
149
  .index("by_org_parent", ["orgId", "parentAgentId"])
397
150
  .index("by_org_workflow", ["orgId", "workflowId"])
398
151
  .index("by_org_awaitedEvent", ["orgId", "awaitedEventName"]),
399
- // agentCloneRuns — the built-in `agent` tool (eve.dev/docs/subagents: "the
400
- // built-in `agent` tool clones the ROOT, root-only, NOT recursive -- it
401
- // cannot call itself"). `viaAgentTool` is the recursion guard: a clone
402
- // invocation started BY a clone invocation (viaAgentTool: true passed in)
403
- // is refused at `engine/delegation.invokeAgentClone` BEFORE any row here
404
- // is written -- so every row in this table by construction has
405
- // `viaAgentTool === false` on entry. Wakes through the SAME
406
- // `engine/wakeup.enqueue` mechanism as `delegationRuns`.
407
152
  agentCloneRuns: defineTable({
408
153
  orgId: v.string(),
409
- agentId: v.id("agents"), // the root being cloned
154
+ agentId: v.id("agents"),
410
155
  workflowId: v.string(),
411
156
  awaitedEventName: v.string(),
412
157
  status: v.union(v.literal("pending"), v.literal("completed"), v.literal("failed"), v.literal("cancelled")),
@@ -416,14 +161,6 @@ export default defineSchema({
416
161
  })
417
162
  .index("by_org", ["orgId"])
418
163
  .index("by_org_agent", ["orgId", "agentId"]),
419
- // vpConnections — teamwork. The
420
- // four VantagePeers tools (memory, tasks, messages, notes) wired as a
421
- // CONNECTION of the agent aggregate, an intentional divergence from Eve. 0..1 row per agent;
422
- // absence means "use the defaults" (all four ON) -- see
423
- // `engine/vpConnection.ts:defaultToggles`. DIRECT access: invocation
424
- // goes through a caller-supplied Convex function handle
425
- // (`createFunctionHandle`, same discipline as `wakeableWork.workId` /
426
- // `durableJobs.stepFunctionHandle`), never a `fetch()` call.
427
164
  vpConnections: defineTable({
428
165
  orgId: v.string(),
429
166
  agentId: v.id("agents"),
@@ -436,9 +173,6 @@ export default defineSchema({
436
173
  })
437
174
  .index("by_org", ["orgId"])
438
175
  .index("by_org_agent", ["orgId", "agentId"]),
439
- // vpToolInvocations — audit trail of every VantagePeers tool call an
440
- // agent made, ALWAYS written by `engine/vpConnection.invokeVpTool` in the
441
- // SAME transaction family as the call itself.
442
176
  vpToolInvocations: defineTable({
443
177
  orgId: v.string(),
444
178
  agentId: v.id("agents"),
@@ -447,18 +181,124 @@ export default defineSchema({
447
181
  })
448
182
  .index("by_org", ["orgId"])
449
183
  .index("by_org_agent", ["orgId", "agentId"]),
450
- // vpTenantFixture — TEST-ONLY stand-in for "VantagePeers' own tenant
451
- // table" (component/testFixtures/vpTool.ts). This engine never owns
452
- // VantagePeers' schema (component isolation) -- this fixture plays the
453
- // CALLER-side function a real `vpFunctionHandle` would point to, so
454
- // `component/engine/vpConnection.test.ts` can prove a VP tool write
455
- // lands in the invoking agent's own org tenant (and that a mismatched
456
- // orgId is refused) without needing a second real Convex deployment in
457
- // the test harness. Not part of the public contract.
184
+ connections: defineTable({
185
+ orgId: v.string(),
186
+ agentId: v.id("agents"),
187
+ type: v.union(v.literal("mcp"), v.literal("openapi")),
188
+ name: v.string(),
189
+ serverConfig: v.string(),
190
+ authMode: v.union(v.literal("app-token"), v.literal("user-oauth"), v.literal("none"), v.literal("header")),
191
+ encryptedSecretCiphertext: v.optional(v.string()),
192
+ encryptedSecretIv: v.optional(v.string()),
193
+ status: v.union(v.literal("active"), v.literal("pending_authorization"), v.literal("error")),
194
+ createdAt: v.number(),
195
+ updatedAt: v.number(),
196
+ })
197
+ .index("by_org", ["orgId"])
198
+ .index("by_org_agent", ["orgId", "agentId"]),
199
+ connectionAuthorizations: defineTable({
200
+ orgId: v.string(),
201
+ connectionId: v.id("connections"),
202
+ workflowId: v.string(),
203
+ awaitedEventName: v.string(),
204
+ url: v.string(),
205
+ userCode: v.string(),
206
+ status: v.union(v.literal("pending"), v.literal("authorized"), v.literal("denied")),
207
+ resultSummary: v.optional(v.string()),
208
+ createdAt: v.number(),
209
+ updatedAt: v.number(),
210
+ resolvedAt: v.optional(v.number()),
211
+ })
212
+ .index("by_org", ["orgId"])
213
+ .index("by_org_connection", ["orgId", "connectionId"]),
458
214
  vpTenantFixture: defineTable({
459
215
  orgId: v.string(),
460
216
  tool: v.union(v.literal("memory"), v.literal("tasks"), v.literal("messages"), v.literal("notes")),
461
217
  argsJson: v.string(),
462
218
  createdAt: v.number(),
463
219
  }).index("by_org", ["orgId"]),
220
+ worldRuns: defineTable({
221
+ orgId: v.string(),
222
+ runId: v.string(),
223
+ status: v.union(v.literal("running"), v.literal("completed"), v.literal("failed"), v.literal("cancelled")),
224
+ eventCount: v.number(),
225
+ createdAt: v.number(),
226
+ updatedAt: v.number(),
227
+ })
228
+ .index("by_org", ["orgId"])
229
+ .index("by_org_run", ["orgId", "runId"]),
230
+ worldSteps: defineTable({
231
+ orgId: v.string(),
232
+ runId: v.string(),
233
+ stepId: v.string(),
234
+ status: v.union(v.literal("running"), v.literal("completed"), v.literal("failed")),
235
+ dataJson: v.optional(v.string()),
236
+ createdAt: v.number(),
237
+ updatedAt: v.number(),
238
+ })
239
+ .index("by_org_run", ["orgId", "runId"])
240
+ .index("by_org_run_step", ["orgId", "runId", "stepId"]),
241
+ worldHooks: defineTable({
242
+ orgId: v.string(),
243
+ runId: v.string(),
244
+ hookId: v.string(),
245
+ token: v.string(),
246
+ status: v.union(v.literal("pending"), v.literal("disposed")),
247
+ createdAt: v.number(),
248
+ updatedAt: v.number(),
249
+ })
250
+ .index("by_org_run", ["orgId", "runId"])
251
+ .index("by_org_hook", ["orgId", "hookId"])
252
+ .index("by_org_token", ["orgId", "token"]),
253
+ worldEvents: defineTable({
254
+ orgId: v.string(),
255
+ runId: v.string(),
256
+ seq: v.number(),
257
+ eventType: v.string(),
258
+ stepId: v.optional(v.string()),
259
+ hookId: v.optional(v.string()),
260
+ correlationId: v.optional(v.string()),
261
+ dataJson: v.string(),
262
+ createdAt: v.number(),
263
+ })
264
+ .index("by_org_run", ["orgId", "runId"])
265
+ .index("by_org_run_seq", ["orgId", "runId", "seq"])
266
+ .index("by_org_correlation", ["orgId", "correlationId"]),
267
+ worldQueueMessages: defineTable({
268
+ orgId: v.string(),
269
+ queueName: v.string(),
270
+ messageId: v.string(),
271
+ payloadJson: v.string(),
272
+ idempotencyKey: v.optional(v.string()),
273
+ attempt: v.number(),
274
+ status: v.union(v.literal("pending"), v.literal("claimed"), v.literal("delivered"), v.literal("failed")),
275
+ deliverAt: v.number(),
276
+ createdAt: v.number(),
277
+ updatedAt: v.number(),
278
+ })
279
+ .index("by_org", ["orgId"])
280
+ .index("by_org_queue", ["orgId", "queueName"])
281
+ .index("by_org_message", ["orgId", "messageId"])
282
+ .index("by_org_queue_idempotencyKey", ["orgId", "queueName", "idempotencyKey"])
283
+ .index("by_org_queue_status_deliverAt", ["orgId", "queueName", "status", "deliverAt"]),
284
+ worldStreams: defineTable({
285
+ orgId: v.string(),
286
+ runId: v.string(),
287
+ name: v.string(),
288
+ chunkCount: v.number(),
289
+ done: v.boolean(),
290
+ createdAt: v.number(),
291
+ updatedAt: v.number(),
292
+ })
293
+ .index("by_org", ["orgId"])
294
+ .index("by_org_run", ["orgId", "runId"])
295
+ .index("by_org_run_name", ["orgId", "runId", "name"]),
296
+ worldStreamChunks: defineTable({
297
+ orgId: v.string(),
298
+ runId: v.string(),
299
+ name: v.string(),
300
+ index: v.number(),
301
+ dataBase64: v.string(),
302
+ createdAt: v.number(),
303
+ }).index("by_org_run_name_index", ["orgId", "runId", "name", "index"]),
464
304
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vantageos/agent-engine",
3
- "version": "0.1.0-alpha.8",
3
+ "version": "0.3.0-alpha.1",
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": "FSL-1.1-Apache-2.0",
@@ -19,6 +19,7 @@
19
19
  },
20
20
  "files": [
21
21
  "dist",
22
+ "!dist/component/testFixtures",
22
23
  "README.md",
23
24
  "LICENSE"
24
25
  ],
@@ -38,14 +39,21 @@
38
39
  },
39
40
  "scripts": {
40
41
  "dev": "convex dev",
41
- "test": "vitest run",
42
+ "test": "npm run typecheck:build && npm run check:consumer-types && npm run check:dts-jsdoc && vitest run && npm run test:guard && npm run test:world-convex && npm run test:first-orchestrator",
43
+ "test:guard": "node --test evidence-skills/evals/guard.test.mjs",
44
+ "test:world-convex": "npm ci --prefix world-convex && npm test --prefix world-convex",
45
+ "test:first-orchestrator": "npm ci --prefix first-orchestrator && npm test --prefix first-orchestrator",
42
46
  "test:watch": "vitest",
47
+ "validate:agent-standard": "vitest run convex/lib/agentStandardValidator.test.ts",
43
48
  "typecheck": "tsc --noEmit",
44
- "build": "tsc -p tsconfig.build.json",
45
- "prepublishOnly": "npm run build && npm run check:public",
49
+ "typecheck:build": "tsc -p tsconfig.build.json --noEmit",
50
+ "check:consumer-types": "node scripts/check-consumer-types.mjs",
51
+ "build": "tsc -p tsconfig.build.json && tsc -p tsconfig.build.types.json",
52
+ "prepublishOnly": "npm run build && npm run check:public && SKIP_BUILD=1 npm run check:dts-jsdoc",
46
53
  "check:generated": "node scripts/check-generated-freshness.mjs",
47
54
  "check:generated:content": "node scripts/check-generated-content.mjs",
48
- "check:public": "node scripts/check-public-package.mjs"
55
+ "check:public": "node scripts/check-public-package.mjs",
56
+ "check:dts-jsdoc": "node scripts/check-dts-jsdoc.mjs"
49
57
  },
50
58
  "dependencies": {
51
59
  "@convex-dev/agent": "0.6.4",
@@ -55,6 +63,7 @@
55
63
  "@convex-dev/sharded-counter": "^0.2.1",
56
64
  "@convex-dev/workflow": "0.4.4",
57
65
  "@convex-dev/workpool": "0.4.7",
66
+ "@vantageos/cloud-identity": "0.21.3",
58
67
  "@vercel/sandbox": "^2.6.1",
59
68
  "ai": "6.0.35",
60
69
  "convex": "1.42.1",
@@ -62,8 +71,14 @@
62
71
  },
63
72
  "devDependencies": {
64
73
  "@edge-runtime/vm": "5.0.0",
74
+ "@types/node": "^26.1.2",
65
75
  "convex-test": "0.0.54",
66
76
  "typescript": "5.7.3",
67
- "vitest": "2.1.9"
77
+ "vitest": "3.2.6"
78
+ },
79
+ "overrides": {
80
+ "nanoid": "^3.3.18",
81
+ "tinypool": "^2.1.2",
82
+ "vite": "^6.4.4"
68
83
  }
69
84
  }