@vantageos/agent-engine 0.1.0-alpha.7 → 0.3.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.
Files changed (62) hide show
  1. package/README.md +153 -50
  2. package/dist/component/_generated/api.d.ts +14 -27
  3. package/dist/component/_generated/api.js +0 -25
  4. package/dist/component/_generated/component.d.ts +369 -92
  5. package/dist/component/_generated/component.js +0 -9
  6. package/dist/component/_generated/dataModel.d.ts +0 -38
  7. package/dist/component/_generated/dataModel.js +0 -9
  8. package/dist/component/_generated/server.d.ts +0 -106
  9. package/dist/component/_generated/server.js +0 -69
  10. package/dist/component/convex.config.js +0 -44
  11. package/dist/component/engine/agents.js +0 -7
  12. package/dist/component/engine/awaitEventTimeout.d.ts +1 -1
  13. package/dist/component/engine/awaitEventTimeout.js +18 -26
  14. package/dist/component/engine/connections.d.ts +74 -0
  15. package/dist/component/engine/connections.js +254 -0
  16. package/dist/component/engine/delegation.d.ts +1 -1
  17. package/dist/component/engine/delegation.js +0 -74
  18. package/dist/component/engine/directory.js +0 -18
  19. package/dist/component/engine/durableJob.d.ts +34 -4
  20. package/dist/component/engine/durableJob.js +333 -86
  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 +1 -1
  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 +153 -165
  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 +5 -7
  38. package/dist/component/engine/wakeup.js +54 -127
  39. package/dist/component/engine/workflows.d.ts +42 -0
  40. package/dist/component/engine/workflows.js +100 -41
  41. package/dist/component/engine/world.d.ts +249 -0
  42. package/dist/component/engine/world.js +665 -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 +227 -77
  47. package/dist/component/schema.js +137 -250
  48. package/package.json +17 -3
  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,27 +1,6 @@
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
4
  durableJobs: defineTable({
26
5
  orgId: v.string(),
27
6
  kind: v.string(),
@@ -39,11 +18,6 @@ export default defineSchema({
39
18
  })
40
19
  .index("by_org", ["orgId"])
41
20
  .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
21
  callerStepInvocations: defineTable({
48
22
  orgId: v.string(),
49
23
  jobId: v.id("durableJobs"),
@@ -52,24 +26,16 @@ export default defineSchema({
52
26
  })
53
27
  .index("by_org", ["orgId"])
54
28
  .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
29
  agents: defineTable({
64
30
  orgId: v.string(),
65
31
  name: v.string(),
66
- description: v.optional(v.string()), // eve: agent-definition.ts:291
32
+ description: v.optional(v.string()),
67
33
  instructions: v.string(),
68
34
  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
35
+ reasoningEffort: v.optional(v.union(v.literal("none"), v.literal("low"), v.literal("medium"), v.literal("high"))),
36
+ modelProvider: v.optional(v.union(v.literal("gateway"), v.literal("openrouter"))),
37
+ maxSubagents: v.optional(v.number()),
38
+ maxInputTokensPerSession: v.optional(v.union(v.number(), v.literal(false))),
73
39
  status: v.union(v.literal("draft"), v.literal("live"), v.literal("archived")),
74
40
  createdBy: v.string(),
75
41
  createdAt: v.number(),
@@ -77,11 +43,6 @@ export default defineSchema({
77
43
  })
78
44
  .index("by_org", ["orgId"])
79
45
  .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
46
  toolApprovals: defineTable({
86
47
  orgId: v.string(),
87
48
  agentId: v.id("agents"),
@@ -93,12 +54,6 @@ export default defineSchema({
93
54
  .index("by_org", ["orgId"])
94
55
  .index("by_org_agent", ["orgId", "agentId"])
95
56
  .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
57
  sessions: defineTable({
103
58
  orgId: v.string(),
104
59
  agentId: v.id("agents"),
@@ -111,84 +66,28 @@ export default defineSchema({
111
66
  })
112
67
  .index("by_org", ["orgId"])
113
68
  .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
69
  sessionEvents: defineTable({
124
70
  orgId: v.string(),
125
71
  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
72
+ 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"), v.literal("turn.toolCall.denied"), v.literal("turn.completed"), v.literal("tool.approval.requested"), v.literal("tool.approval.resolved"), v.literal("sandbox.timeout")),
73
+ payload: v.optional(v.string()),
139
74
  createdAt: v.number(),
140
75
  })
141
76
  .index("by_org", ["orgId"])
142
77
  .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
78
  awaitEventTimeouts: defineTable({
173
79
  orgId: v.string(),
174
- sessionId: v.id("sessions"),
80
+ runId: v.optional(v.string()),
81
+ sessionId: v.optional(v.id("sessions")),
175
82
  awaitedEventName: v.string(),
176
83
  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
84
  scheduledFunctionId: v.optional(v.id("_scheduled_functions")),
180
85
  firedAt: v.optional(v.number()),
181
86
  cancelledAt: v.optional(v.number()),
182
87
  })
183
88
  .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.
89
+ .index("by_org_session", ["orgId", "sessionId"])
90
+ .index("by_org_run", ["orgId", "runId"]),
192
91
  wakeupDemoResults: defineTable({
193
92
  orgId: v.string(),
194
93
  enqueuedAt: v.number(),
@@ -196,23 +95,13 @@ export default defineSchema({
196
95
  observedStatus: v.optional(v.union(v.literal("completed"), v.literal("failed"), v.literal("cancelled"))),
197
96
  observedResultSummary: v.optional(v.string()),
198
97
  }).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
98
  mandates: defineTable({
210
99
  orgId: v.string(),
211
100
  agentId: v.id("agents"),
212
- scope: v.string(), // free-form business name (e.g. "llm-tokens") -- DATA, not a constant
101
+ scope: v.string(),
213
102
  kind: v.union(v.literal("token bucket"), v.literal("fixed window")),
214
103
  rate: v.number(),
215
- period: v.number(), // ms
104
+ period: v.number(),
216
105
  capacity: v.optional(v.number()),
217
106
  createdAt: v.number(),
218
107
  updatedAt: v.number(),
@@ -220,9 +109,6 @@ export default defineSchema({
220
109
  .index("by_org", ["orgId"])
221
110
  .index("by_org_agent", ["orgId", "agentId"])
222
111
  .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
112
  approvalRequests: defineTable({
227
113
  orgId: v.string(),
228
114
  agentId: v.id("agents"),
@@ -237,77 +123,15 @@ export default defineSchema({
237
123
  .index("by_org", ["orgId"])
238
124
  .index("by_org_agent", ["orgId", "agentId"])
239
125
  .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
126
  sandboxConfig: defineTable({
304
127
  orgId: v.string(),
305
128
  agentId: v.id("agents"),
306
- provider: v.literal("vercel-sandbox"),
129
+ provider: v.union(v.literal("vercel-sandbox"), v.literal("e2b"), v.literal("daytona"), v.literal("box")),
307
130
  image: v.optional(v.string()),
308
131
  timeoutSeconds: v.number(),
309
132
  egressPolicy: v.union(v.literal("allow-all"), v.literal("deny-all-allowlist")),
310
133
  allowlistDomains: v.array(v.string()),
134
+ boxId: v.optional(v.string()),
311
135
  createdAt: v.number(),
312
136
  updatedAt: v.number(),
313
137
  })
@@ -315,9 +139,9 @@ export default defineSchema({
315
139
  .index("by_org_agent", ["orgId", "agentId"]),
316
140
  sandboxRuns: defineTable({
317
141
  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())
142
+ workflowId: v.string(),
143
+ awaitedEventName: v.string(),
144
+ sandboxId: v.string(),
321
145
  status: v.union(v.literal("pending"), v.literal("completed"), v.literal("failed"), v.literal("timedOut")),
322
146
  resultSummary: v.optional(v.string()),
323
147
  createdAt: v.number(),
@@ -329,39 +153,24 @@ export default defineSchema({
329
153
  sandboxCallbackDeliveries: defineTable({
330
154
  orgId: v.string(),
331
155
  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")),
156
+ deliveryId: v.string(),
157
+ outcome: v.union(v.literal("woke"), v.literal("duplicate")),
335
158
  receivedAt: v.number(),
336
159
  })
337
160
  .index("by_org", ["orgId"])
338
161
  .index("by_org_delivery", ["orgId", "deliveryId"])
339
162
  .index("by_org_run", ["orgId", "sandboxRunId"]),
340
163
  sandboxCallbackDeadLetters: defineTable({
341
- orgId: v.optional(v.string()), // absent when the payload itself couldn't be parsed
164
+ orgId: v.optional(v.string()),
342
165
  deliveryId: v.string(),
343
166
  reason: v.string(),
344
167
  rawBody: v.string(),
345
168
  createdAt: v.number(),
346
169
  }).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
170
  subagents: defineTable({
362
171
  orgId: v.string(),
363
172
  parentAgentId: v.id("agents"),
364
- childAgentId: v.id("agents"), // full agent, own aggregate -- strict isolation
173
+ childAgentId: v.id("agents"),
365
174
  toolName: v.string(),
366
175
  description: v.optional(v.string()),
367
176
  enabled: v.boolean(),
@@ -370,16 +179,7 @@ export default defineSchema({
370
179
  .index("by_org", ["orgId"])
371
180
  .index("by_org_parent", ["orgId", "parentAgentId"])
372
181
  .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
182
  .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
183
  delegationRuns: defineTable({
384
184
  orgId: v.string(),
385
185
  parentAgentId: v.id("agents"),
@@ -396,17 +196,9 @@ export default defineSchema({
396
196
  .index("by_org_parent", ["orgId", "parentAgentId"])
397
197
  .index("by_org_workflow", ["orgId", "workflowId"])
398
198
  .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
199
  agentCloneRuns: defineTable({
408
200
  orgId: v.string(),
409
- agentId: v.id("agents"), // the root being cloned
201
+ agentId: v.id("agents"),
410
202
  workflowId: v.string(),
411
203
  awaitedEventName: v.string(),
412
204
  status: v.union(v.literal("pending"), v.literal("completed"), v.literal("failed"), v.literal("cancelled")),
@@ -416,14 +208,6 @@ export default defineSchema({
416
208
  })
417
209
  .index("by_org", ["orgId"])
418
210
  .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
211
  vpConnections: defineTable({
428
212
  orgId: v.string(),
429
213
  agentId: v.id("agents"),
@@ -436,9 +220,6 @@ export default defineSchema({
436
220
  })
437
221
  .index("by_org", ["orgId"])
438
222
  .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
223
  vpToolInvocations: defineTable({
443
224
  orgId: v.string(),
444
225
  agentId: v.id("agents"),
@@ -447,18 +228,124 @@ export default defineSchema({
447
228
  })
448
229
  .index("by_org", ["orgId"])
449
230
  .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.
231
+ connections: defineTable({
232
+ orgId: v.string(),
233
+ agentId: v.id("agents"),
234
+ type: v.union(v.literal("mcp"), v.literal("openapi")),
235
+ name: v.string(),
236
+ serverConfig: v.string(),
237
+ authMode: v.union(v.literal("app-token"), v.literal("user-oauth"), v.literal("none"), v.literal("header")),
238
+ encryptedSecretCiphertext: v.optional(v.string()),
239
+ encryptedSecretIv: v.optional(v.string()),
240
+ status: v.union(v.literal("active"), v.literal("pending_authorization"), v.literal("error")),
241
+ createdAt: v.number(),
242
+ updatedAt: v.number(),
243
+ })
244
+ .index("by_org", ["orgId"])
245
+ .index("by_org_agent", ["orgId", "agentId"]),
246
+ connectionAuthorizations: defineTable({
247
+ orgId: v.string(),
248
+ connectionId: v.id("connections"),
249
+ workflowId: v.string(),
250
+ awaitedEventName: v.string(),
251
+ url: v.string(),
252
+ userCode: v.string(),
253
+ status: v.union(v.literal("pending"), v.literal("authorized"), v.literal("denied")),
254
+ resultSummary: v.optional(v.string()),
255
+ createdAt: v.number(),
256
+ updatedAt: v.number(),
257
+ resolvedAt: v.optional(v.number()),
258
+ })
259
+ .index("by_org", ["orgId"])
260
+ .index("by_org_connection", ["orgId", "connectionId"]),
458
261
  vpTenantFixture: defineTable({
459
262
  orgId: v.string(),
460
263
  tool: v.union(v.literal("memory"), v.literal("tasks"), v.literal("messages"), v.literal("notes")),
461
264
  argsJson: v.string(),
462
265
  createdAt: v.number(),
463
266
  }).index("by_org", ["orgId"]),
267
+ worldRuns: defineTable({
268
+ orgId: v.string(),
269
+ runId: v.string(),
270
+ status: v.union(v.literal("running"), v.literal("completed"), v.literal("failed"), v.literal("cancelled")),
271
+ eventCount: v.number(),
272
+ createdAt: v.number(),
273
+ updatedAt: v.number(),
274
+ })
275
+ .index("by_org", ["orgId"])
276
+ .index("by_org_run", ["orgId", "runId"]),
277
+ worldSteps: defineTable({
278
+ orgId: v.string(),
279
+ runId: v.string(),
280
+ stepId: v.string(),
281
+ status: v.union(v.literal("running"), v.literal("completed"), v.literal("failed")),
282
+ dataJson: v.optional(v.string()),
283
+ createdAt: v.number(),
284
+ updatedAt: v.number(),
285
+ })
286
+ .index("by_org_run", ["orgId", "runId"])
287
+ .index("by_org_run_step", ["orgId", "runId", "stepId"]),
288
+ worldHooks: defineTable({
289
+ orgId: v.string(),
290
+ runId: v.string(),
291
+ hookId: v.string(),
292
+ token: v.string(),
293
+ status: v.union(v.literal("pending"), v.literal("disposed")),
294
+ createdAt: v.number(),
295
+ updatedAt: v.number(),
296
+ })
297
+ .index("by_org_run", ["orgId", "runId"])
298
+ .index("by_org_hook", ["orgId", "hookId"])
299
+ .index("by_org_token", ["orgId", "token"]),
300
+ worldEvents: defineTable({
301
+ orgId: v.string(),
302
+ runId: v.string(),
303
+ seq: v.number(),
304
+ eventType: v.string(),
305
+ stepId: v.optional(v.string()),
306
+ hookId: v.optional(v.string()),
307
+ correlationId: v.optional(v.string()),
308
+ dataJson: v.string(),
309
+ createdAt: v.number(),
310
+ })
311
+ .index("by_org_run", ["orgId", "runId"])
312
+ .index("by_org_run_seq", ["orgId", "runId", "seq"])
313
+ .index("by_org_correlation", ["orgId", "correlationId"]),
314
+ worldQueueMessages: defineTable({
315
+ orgId: v.string(),
316
+ queueName: v.string(),
317
+ messageId: v.string(),
318
+ payloadJson: v.string(),
319
+ idempotencyKey: v.optional(v.string()),
320
+ attempt: v.number(),
321
+ status: v.union(v.literal("pending"), v.literal("claimed"), v.literal("delivered"), v.literal("failed")),
322
+ deliverAt: v.number(),
323
+ createdAt: v.number(),
324
+ updatedAt: v.number(),
325
+ })
326
+ .index("by_org", ["orgId"])
327
+ .index("by_org_queue", ["orgId", "queueName"])
328
+ .index("by_org_message", ["orgId", "messageId"])
329
+ .index("by_org_queue_idempotencyKey", ["orgId", "queueName", "idempotencyKey"])
330
+ .index("by_org_queue_status_deliverAt", ["orgId", "queueName", "status", "deliverAt"]),
331
+ worldStreams: defineTable({
332
+ orgId: v.string(),
333
+ runId: v.string(),
334
+ name: v.string(),
335
+ chunkCount: v.number(),
336
+ done: v.boolean(),
337
+ createdAt: v.number(),
338
+ updatedAt: v.number(),
339
+ })
340
+ .index("by_org", ["orgId"])
341
+ .index("by_org_run", ["orgId", "runId"])
342
+ .index("by_org_run_name", ["orgId", "runId", "name"]),
343
+ worldStreamChunks: defineTable({
344
+ orgId: v.string(),
345
+ runId: v.string(),
346
+ name: v.string(),
347
+ index: v.number(),
348
+ dataBase64: v.string(),
349
+ createdAt: v.number(),
350
+ }).index("by_org_run_name_index", ["orgId", "runId", "name", "index"]),
464
351
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vantageos/agent-engine",
3
- "version": "0.1.0-alpha.7",
3
+ "version": "0.3.0-alpha.0",
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,9 +39,15 @@
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 && 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",
49
+ "typecheck:build": "tsc -p tsconfig.build.json --noEmit",
50
+ "check:consumer-types": "node scripts/check-consumer-types.mjs",
44
51
  "build": "tsc -p tsconfig.build.json",
45
52
  "prepublishOnly": "npm run build && npm run check:public",
46
53
  "check:generated": "node scripts/check-generated-freshness.mjs",
@@ -55,6 +62,7 @@
55
62
  "@convex-dev/sharded-counter": "^0.2.1",
56
63
  "@convex-dev/workflow": "0.4.4",
57
64
  "@convex-dev/workpool": "0.4.7",
65
+ "@vantageos/cloud-identity": "0.21.3",
58
66
  "@vercel/sandbox": "^2.6.1",
59
67
  "ai": "6.0.35",
60
68
  "convex": "1.42.1",
@@ -62,8 +70,14 @@
62
70
  },
63
71
  "devDependencies": {
64
72
  "@edge-runtime/vm": "5.0.0",
73
+ "@types/node": "^26.1.2",
65
74
  "convex-test": "0.0.54",
66
75
  "typescript": "5.7.3",
67
- "vitest": "2.1.9"
76
+ "vitest": "3.2.6"
77
+ },
78
+ "overrides": {
79
+ "nanoid": "^3.3.18",
80
+ "tinypool": "^2.1.2",
81
+ "vite": "^6.4.4"
68
82
  }
69
83
  }