@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.
- package/README.md +33 -8
- package/dist/component/_generated/api.d.ts +14 -3
- package/dist/component/_generated/api.js +0 -25
- package/dist/component/_generated/component.d.ts +369 -73
- package/dist/component/_generated/component.js +0 -9
- package/dist/component/_generated/dataModel.js +0 -9
- package/dist/component/_generated/server.js +0 -69
- package/dist/component/convex.config.js +0 -44
- package/dist/component/engine/agents.d.ts +3 -3
- package/dist/component/engine/agents.js +0 -7
- package/dist/component/engine/awaitEventTimeout.d.ts +1 -1
- package/dist/component/engine/awaitEventTimeout.js +16 -26
- package/dist/component/engine/connections.d.ts +74 -0
- package/dist/component/engine/connections.js +254 -0
- package/dist/component/engine/delegation.d.ts +5 -5
- package/dist/component/engine/delegation.js +0 -74
- package/dist/component/engine/directory.js +0 -18
- package/dist/component/engine/durableJob.d.ts +24 -17
- package/dist/component/engine/durableJob.js +182 -138
- package/dist/component/engine/mandates.d.ts +2 -2
- package/dist/component/engine/mandates.js +0 -59
- package/dist/component/engine/queue.d.ts +97 -0
- package/dist/component/engine/queue.js +201 -0
- package/dist/component/engine/resolvers.d.ts +1 -1
- package/dist/component/engine/resolvers.js +6 -18
- package/dist/component/engine/sandboxCallback.d.ts +2 -2
- package/dist/component/engine/sandboxCallback.js +2 -79
- package/dist/component/engine/sandboxConfigProvider.d.ts +9 -0
- package/dist/component/engine/sandboxConfigProvider.js +32 -0
- package/dist/component/engine/sessions.d.ts +28 -10
- package/dist/component/engine/sessions.js +130 -170
- package/dist/component/engine/streams.d.ts +47 -0
- package/dist/component/engine/streams.js +162 -0
- package/dist/component/engine/toolApprovals.d.ts +1 -1
- package/dist/component/engine/toolApprovals.js +12 -37
- package/dist/component/engine/vpConnection.js +0 -40
- package/dist/component/engine/wakeup.d.ts +7 -9
- package/dist/component/engine/wakeup.js +54 -127
- package/dist/component/engine/workflows.d.ts +26 -8
- package/dist/component/engine/workflows.js +59 -64
- package/dist/component/engine/world.d.ts +250 -0
- package/dist/component/engine/world.js +689 -0
- package/dist/component/lib/auth.js +0 -26
- package/dist/component/lib/connectionSecret.d.ts +5 -0
- package/dist/component/lib/connectionSecret.js +32 -0
- package/dist/component/schema.d.ts +240 -175
- package/dist/component/schema.js +134 -294
- package/package.json +21 -6
- package/dist/component/engine/habits.d.ts +0 -70
- package/dist/component/engine/habits.js +0 -218
- package/dist/component/testFixtures/callerSteps.d.ts +0 -20
- package/dist/component/testFixtures/callerSteps.js +0 -90
- package/dist/component/testFixtures/delegationDemoWorkflow.d.ts +0 -18
- package/dist/component/testFixtures/delegationDemoWorkflow.js +0 -75
- package/dist/component/testFixtures/sandboxDemoWorkflow.d.ts +0 -19
- package/dist/component/testFixtures/sandboxDemoWorkflow.js +0 -69
- package/dist/component/testFixtures/vpTool.d.ts +0 -4
- package/dist/component/testFixtures/vpTool.js +0 -25
- package/dist/component/testFixtures/wakeupDemoWorkflow.d.ts +0 -19
- package/dist/component/testFixtures/wakeupDemoWorkflow.js +0 -77
- package/dist/component/testFixtures/wakeupWork.d.ts +0 -8
- package/dist/component/testFixtures/wakeupWork.js +0 -21
package/dist/component/schema.js
CHANGED
|
@@ -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()),
|
|
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"))),
|
|
70
|
-
modelProvider: v.optional(v.union(v.literal("gateway"), v.literal("openrouter"))),
|
|
71
|
-
maxSubagents: v.optional(v.number()),
|
|
72
|
-
maxInputTokensPerSession: v.optional(v.union(v.number(), v.literal(false))),
|
|
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
|
-
|
|
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("
|
|
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(),
|
|
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(),
|
|
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(),
|
|
319
|
-
awaitedEventName: v.string(),
|
|
320
|
-
sandboxId: v.string(),
|
|
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(),
|
|
333
|
-
outcome: v.union(v.literal("woke"),
|
|
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()),
|
|
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"),
|
|
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"),
|
|
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
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
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.
|
|
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
|
-
"
|
|
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.
|
|
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
|
}
|