@vantageos/agent-engine 0.1.0-alpha.4 → 0.1.0-alpha.6

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 (41) hide show
  1. package/README.md +285 -46
  2. package/dist/component/_generated/api.d.ts +31 -0
  3. package/dist/component/_generated/component.d.ts +365 -3
  4. package/dist/component/convex.config.js +19 -0
  5. package/dist/component/engine/agents.d.ts +10 -0
  6. package/dist/component/engine/agents.js +21 -0
  7. package/dist/component/engine/awaitEventTimeout.d.ts +13 -0
  8. package/dist/component/engine/awaitEventTimeout.js +82 -0
  9. package/dist/component/engine/delegation.d.ts +75 -0
  10. package/dist/component/engine/delegation.js +329 -0
  11. package/dist/component/engine/directory.d.ts +9 -0
  12. package/dist/component/engine/directory.js +45 -0
  13. package/dist/component/engine/habits.d.ts +70 -0
  14. package/dist/component/engine/habits.js +219 -0
  15. package/dist/component/engine/mandates.d.ts +74 -0
  16. package/dist/component/engine/mandates.js +272 -0
  17. package/dist/component/engine/sandboxCallback.d.ts +36 -0
  18. package/dist/component/engine/sandboxCallback.js +262 -0
  19. package/dist/component/engine/sessions.d.ts +7 -2
  20. package/dist/component/engine/sessions.js +75 -15
  21. package/dist/component/engine/toolApprovals.d.ts +2 -1
  22. package/dist/component/engine/toolApprovals.js +64 -26
  23. package/dist/component/engine/vpConnection.d.ts +69 -0
  24. package/dist/component/engine/vpConnection.js +221 -0
  25. package/dist/component/engine/wakeup.d.ts +33 -0
  26. package/dist/component/engine/wakeup.js +230 -0
  27. package/dist/component/engine/workflows.d.ts +11 -0
  28. package/dist/component/engine/workflows.js +87 -0
  29. package/dist/component/schema.d.ts +365 -4
  30. package/dist/component/schema.js +360 -1
  31. package/dist/component/testFixtures/delegationDemoWorkflow.d.ts +18 -0
  32. package/dist/component/testFixtures/delegationDemoWorkflow.js +76 -0
  33. package/dist/component/testFixtures/sandboxDemoWorkflow.d.ts +19 -0
  34. package/dist/component/testFixtures/sandboxDemoWorkflow.js +69 -0
  35. package/dist/component/testFixtures/vpTool.d.ts +4 -0
  36. package/dist/component/testFixtures/vpTool.js +25 -0
  37. package/dist/component/testFixtures/wakeupDemoWorkflow.d.ts +19 -0
  38. package/dist/component/testFixtures/wakeupDemoWorkflow.js +77 -0
  39. package/dist/component/testFixtures/wakeupWork.d.ts +8 -0
  40. package/dist/component/testFixtures/wakeupWork.js +21 -0
  41. package/package.json +2 -1
package/README.md CHANGED
@@ -37,12 +37,17 @@ ligne"** below, in this table and in the code comments next to the field.
37
37
  | `dynamicTools` / tools | `manifest.ts:722-725` | resolved at read-time from `connections` + `toolApprovals` | direct parity |
38
38
  | Tool `approval: Approval<...>` (`always`/`once`/`never`) | `public/definitions/tool.ts:117-125` | `toolApprovals` child table | direct parity |
39
39
  | MCP/OpenAPI connections (`defineMcpClientConnection`) | `public/definitions/connections/mcp.ts:23-48` | `connections` child table | direct parity. Our VantagePeers tools (memory, tasks, messages, notes) are wired here as an MCP connection, on by default — **notre ligne**, mvp §2.2 |
40
- | Declared subagents, root-only `agent` tool | https://eve.dev/docs/subagents | `subagents` child table (`parentAgentId` -> `childAgentId`, recursive) | direct parity. **Isolation strict, "inherits nothing from the root's authored slots"** — a subagent row only declares the link + exposed tool name; it never inlines the parent's skills/connections/approvals |
40
+ | Declared subagents (named tool, strict isolation) | https://eve.dev/docs/subagents ("a declared subagent inherits nothing from the root's authored slots") | `subagents` child table (`parentAgentId` -> `childAgentId`, recursive) + `engine/delegation.declareSubagent` / `listSubagents` (I6) | direct parity. Isolation is by construction: `declareSubagent` writes ONLY the link + `toolName`; the child's own slots are only ever read through `engine/resolvers.resolveManifest` against a session on `childAgentId` — never copied onto the parent (proven: `engineDelegation.test.ts`, "manifest carries ONLY its own skills") |
41
+ | Subagent tool-name collision -> **build rejected** | https://eve.dev/docs/subagents ("eve rejects the build rather than picking a winner") | `engine/delegation.declareSubagent`'s build-time collision guard (`by_org_parent_tool` index + reserved `"agent"` name + `toolApprovals` lookup) | direct parity — a colliding `declareSubagent` call throws and writes nothing, never picks a silent winner |
42
+ | Built-in `agent` tool — clone of the root, root-only, **NOT recursive** | https://eve.dev/docs/subagents | `engine/delegation.invokeAgentClone`, `agentCloneRuns` child table, `viaAgentTool` recursion guard | direct parity — a clone invocation cannot invoke the `agent` tool again; refused before any row is written or the wake-up pool is touched |
43
+ | Inter-agent delegation mechanism | not modeled explicitly in `vercel/eve`'s own docs | `engine/delegation.delegateToSubagent` / `invokeAgentClone`, BOTH terminate in the SAME public `engine.wakeup.enqueue` (T4/I2) — one pause/wake system, not two (SYNTHESE §4.d) | **notre ligne (mechanism)** — delegation IS a wake-up: A parks on `step.awaitEvent`, B's work is enqueued through `wakeupPool`, `onComplete` -> `sendEvent` wakes A. No second `onComplete`/`sendEvent` path exists anywhere in this engine |
41
44
  | `sandbox` | `agent-definition.ts:150-160` | `sandboxConfig` child table (Vercel Sandbox, egress policy, allowlist) | direct parity, I0 §8 |
42
45
  | `schedules` / hooks -> "habits" | `manifest.ts:143`, `:726` | `habits` + `habitRuns` child tables (dynamic cron per agent/org, `@convex-dev/crons`, see "Mandates & habits (T4)" below) | **notre ligne (naming + mechanism)** — Eve calls these schedules/hooks; we call them habits (product decision) and back them with runtime-registered crons instead of static config |
43
46
  | *(no equivalent)* — bounded per-agent authority | not present in `vercel/eve` | `mandates` + `approvalRequests` child tables (`@convex-dev/rate-limiter` + `@convex-dev/sharded-counter`, see "Mandates & habits (T4)" below) | **notre ligne** — SYNTHESE §2 "la réponse à la peur de lâcher les commandes"; a weighted, data-driven spending cap per org+agent+scope, overrun => human approval request, never a silent workaround |
44
47
  | Session/turn resolver boundaries (`ALLOWED_DYNAMIC_INSTRUCTION_EVENTS = {session.started, turn.started}`) | `dynamic-tool-definition.ts:40-46` | `sessionEvents.name` union (`session.started`, `turn.started`, ...) + `engine/resolvers.ts:resolveManifest` | direct parity — same event names, same "reread the aggregate, never cache" contract |
45
- | `subagents` used for inter-agent collaboration | eve docs | **not used** — collaboration goes through VantagePeers MCP tools instead | **notre ligne** — explicit Laurent decision (mvp-evevantage.md §2.5): "la collaboration entre agents n'arrive pas par les `subagents` d'eve" |
48
+ | VantagePeers tools (memory, tasks, messages, notes) as a connection | not in `vercel/eve` (no equivalent first-party integration) | `vpConnections` child table (4 booleans, ON by default) + `engine/vpConnection.invokeVpTool` (I6, mvp §2.2 "Travail d'équipe") | **notre ligne** — direct access, NOT network: dispatch is a caller-supplied Convex function handle (`createFunctionHandle`, same discipline as `wakeableWork.workId` / `durableJobs.stepFunctionHandle`), never a `fetch()` call. `orgId` is threaded explicitly into the target handle so a VP write always lands in the invoking agent's own org tenant (`engineDelegation.test.ts` proves both the write and the cross-org refusal) |
49
+ | Agent directory, scoped by org | not in `vercel/eve` | `directory.listByOrg` (I6) | **notre ligne** — same `by_org` index discipline as every other list in this engine; reports each agent's subagent/connection counts alongside `agents.list` |
50
+ | ~~`subagents` NOT used for inter-agent collaboration~~ | eve docs / mvp-evevantage.md §2.5 (T4-era decision) | superseded by I6 — see the delegation rows above | **REVISED, T6/I6**: the T4-era README claimed "la collaboration entre agents n'arrive pas par les `subagents` d'eve" (VantagePeers-only). I6 wires `subagents` up for real, by the SAME pause/wake mechanism as everything else in this engine (SYNTHESE §4.d) — VantagePeers connection tools (row above) remain available IN ADDITION, not as a replacement |
46
51
  | `outputSchema`, `compaction`, `modelOptions`, `channels` | `agent-definition.ts:124-146,313-330`, `manifest.ts:56-77` | out of scope this lot | left for a later I-cycle, documented as deliberately deferred (mvp §2.5) |
47
52
 
48
53
  ## Data aggregate — schema (`convex/schema.ts`)
@@ -59,8 +64,10 @@ Child tables (>= 7, every index starts with `orgId`):
59
64
  6. `toolApprovals` — `always` / `once` / `never` per tool
60
65
  7. `modelKeys` — BYOK, server-only, split from `agents`
61
66
  8. `mandates` (+ `approvalRequests`) — bounded authority (notre ligne, T4)
67
+ 9. `vpConnections` (+ `vpToolInvocations`) — VantagePeers connection toggles + audit trail (notre ligne, T6/I6)
62
68
 
63
- Engine tables: `sessions`, `sessionEvents`, `awaitEventTimeouts`.
69
+ Engine tables: `sessions`, `sessionEvents`, `awaitEventTimeouts`,
70
+ `delegationRuns`, `agentCloneRuns` (I6 — see "Delegation (T6/I6)" below).
64
71
 
65
72
  **Hard rule**: `orgId` is derived server-side from the authenticated
66
73
  identity (`convex/lib/auth.ts:requireOrgId`, reads the Clerk `org_id`
@@ -194,6 +201,64 @@ recurrence is a **dynamically registered `@convex-dev/crons` job**
194
201
  its first run without a deploy" and "a disabled habit does not fire
195
202
  again" PROVABLE in `habits.test.ts` rather than asserted on trust.
196
203
 
204
+ ## Delegation (T6/I6) — `engine/delegation.ts`, `engine/vpConnection.ts`, `directory.ts`
205
+
206
+ Bible: SYNTHESE §4.d — "un agent qui délègue à un autre agent = LE MÊME
207
+ système pause/réveil (awaitEvent/sendEvent) que le pont sandbox — un seul
208
+ système à écrire, pas deux".
209
+
210
+ ### (a) Two delegation semantics, both reusing `engine.wakeup.enqueue`
211
+
212
+ - **Declared subagents** (`subagents` table, `engine/delegation.ts`):
213
+ strict isolation — `declareSubagent` writes ONLY the parent->child link +
214
+ the named tool (`toolName`); the child's own aggregate is the sole place
215
+ its skills/connections/toolApprovals ever live (read via
216
+ `engine/resolvers.resolveManifest` against a session on `childAgentId`).
217
+ **Build-time collision guard**: `toolName` must be unique against (1) the
218
+ reserved built-in `"agent"` tool name, (2) every other declared subagent
219
+ of the same parent (`by_org_parent_tool` index), (3) an existing
220
+ `toolApprovals` row on the parent — any collision REJECTS the mutation,
221
+ never a silent "last one wins" (eve.dev/docs/subagents, verbatim).
222
+ - **Built-in `agent` tool** (`invokeAgentClone`, `agentCloneRuns` table):
223
+ a clone of the ROOT, root-only, NOT recursive. `viaAgentTool: true`
224
+ (a recursive attempt) is refused BEFORE any row is written or
225
+ `engine.wakeup.enqueue` is ever called.
226
+ - **The reuse point**: `delegateToSubagent` and `invokeAgentClone` BOTH
227
+ terminate in exactly one call to the SAME public `engine.wakeup.enqueue`
228
+ action T4/I2 already ships (`ctx.runAction(api.engine.wakeup.enqueue,
229
+ ...)`) — no bespoke `onComplete`/`sendEvent` path exists in
230
+ `engine/delegation.ts`. `engine/delegationDemoWorkflow.ts` is the
231
+ reference parent-side recipient (same shape as
232
+ `engine/wakeupDemoWorkflow.ts`): it parks on `step.awaitEvent` and
233
+ resumes only when the delegate's work completes.
234
+
235
+ ### (b) VantagePeers connection — `engine/vpConnection.ts`
236
+
237
+ Four toggles (`memoryEnabled`, `tasksEnabled`, `messagesEnabled`,
238
+ `notesEnabled`), ON by default (mvp §2.2 "Travail d'équipe", "quatre
239
+ interrupteurs cochés par défaut" — a missing `vpConnections` row means "use
240
+ the defaults"). `invokeVpTool` dispatches via a caller-supplied Convex
241
+ function handle (`createFunctionHandle`, same discipline as
242
+ `wakeableWork.workId` / `durableJobs.stepFunctionHandle`) — **direct
243
+ access, zero network in the hot path**:
244
+
245
+ ```
246
+ $ grep -n "fetch(" convex/engine/vpConnection.ts convex/engine/delegation.ts
247
+ (no matches)
248
+ ```
249
+
250
+ `orgId` is threaded explicitly into the target handle's args (never a
251
+ client-supplied value, never the target's own `ctx.auth`), so a VP tool
252
+ call always writes into the CALLING agent's own org tenant — proven in
253
+ `engineDelegation.test.ts` alongside the cross-org refusal (an agent
254
+ belonging to another org is "not found", same deny-by-default posture as
255
+ everywhere else in this engine).
256
+
257
+ ### (c) Agent directory — `directory.ts`
258
+
259
+ `directory.listByOrg` — every agent of the caller's own org (server-derived
260
+ `orgId`, `by_org` index), plus each agent's subagent/connection counts.
261
+
197
262
  ## `convex/_generated/` freshness -- two gates, not one
198
263
 
199
264
  `convex/_generated/api.d.ts` carries an "AUTOMATICALLY GENERATED" header
@@ -214,10 +279,31 @@ Two gates now exist, and they check different things:
214
279
  hand-edit of existing generated content.**
215
280
  2. **Content, requires a live deployment** --
216
281
  `scripts/check-generated-content.mjs` (`npm run
217
- check:generated:content`). Actually runs `npx convex codegen` against
218
- the dev deployment and diffs `convex/_generated/` byte-for-byte
219
- (`git diff --exit-code`). Catches everything, including a single hand-
220
- edited character.
282
+ check:generated:content`). Runs `npx convex codegen` against the dev
283
+ deployment for BOTH `convex/_generated/` (the host repo's own dev
284
+ deployment bindings) AND, since 0.1.0-alpha.5, `component/_generated/`
285
+ via `npx convex codegen --component-dir component` -- the checked-in
286
+ file `tsc -p tsconfig.build.json` copies verbatim into the PUBLISHED
287
+ `dist/component/_generated/component.d.ts`, i.e. the exact type a
288
+ consuming app checks `components.agentEngine.*` calls against. Diffs
289
+ each byte-for-byte (`git diff --exit-code`). Catches everything,
290
+ including a single hand-edited character.
291
+
292
+ **`component/_generated/` was NOT covered before 0.1.0-alpha.5** — that
293
+ gap is why `0.1.0-alpha.4` shipped a `component.d.ts` stale against the
294
+ real component schema (missing `orgId`/`createdBy` on
295
+ sessions/agents/durableJob/toolApprovals args), which broke
296
+ `tsc --noEmit` for any consumer calling `sessions.start`/`durableJob.start`
297
+ with `orgId` (Hestia, VP k17ea6x3tht8xa3rxm139t23gx8ajkf3, defaut 1).
298
+ The visibility (`"internal"`) on every entry is, by measured comparison
299
+ against `@convex-dev/rate-limiter`'s own published `component.d.ts`,
300
+ CORRECT and unrelated to that defect — every official Convex component
301
+ types its cross-component `ComponentApi` as `"internal"` regardless of
302
+ whether the underlying function is declared `mutation`/`query` or
303
+ `internalMutation`/`internalQuery`; `ctx.runMutation`'s own signature
304
+ (`convex/server`) accepts `FunctionReference<"mutation", "public" |
305
+ "internal">` either way. The root cause was staleness of the args
306
+ shape, not visibility.
221
307
 
222
308
  **Three-state exit contract** (never two -- a two-state gate cannot
223
309
  distinguish "I measured, it's clean" from "I could not measure at
@@ -299,6 +385,28 @@ that all contributors run) is Pi's to make, not this PR's.
299
385
  against `component/_generated/api` — the exact source tree compiled to
300
386
  `dist/component/` and published — proving the migrated, packaged code
301
387
  itself, not only the repo-root dev app.
388
+ - `convex/engineWakeup.test.ts` — I2 wake-up primitive (`enqueue` ->
389
+ `onComplete` -> `sendEvent`), three-pole probe + org isolation.
390
+ - `convex/engineSandboxCallback.test.ts` — I5 sandbox bridge (signed
391
+ callback, dedup, timeout, dead-letter).
392
+ - `convex/engineDelegation.test.ts` — I6 delegation, three-pole probe:
393
+ MUST_PASS (a declared subagent's manifest carries only its own skills;
394
+ `declareSubagent` succeeds on a fresh name; `invokeAgentClone` succeeds
395
+ for a first-level call; VP toggles default ON; `invokeVpTool` writes
396
+ into the calling org's tenant; `directory.listByOrg` scoped to the
397
+ caller's org), MUST_BLOCK (a subagent tool-name collision with an
398
+ existing tool OR the reserved `"agent"` name is rejected and writes
399
+ nothing; a recursive `invokeAgentClone` call is refused before any row
400
+ is written; a cross-org `invokeVpTool` call is refused; a disabled VP
401
+ tool is refused; zero `fetch(` calls in the VP connection hot path),
402
+ MUST_REFUSE (every mutation/action above with no identity names
403
+ "Unauthenticated" rather than crashing unreadably).
404
+ - `convex/engineDelegationDemo.test.ts` — the real pause/wake delegation
405
+ demo (A delegates to B via `engine.delegation.delegateToSubagent`, B's
406
+ work completes through `wakeupPool`, A's parked workflow resumes),
407
+ console-logged verbatim (`[i6-demo]` prefix) and kept in its own file
408
+ because it drives `vi.useFakeTimers()` through more event-loop pumps
409
+ than the rest of the suite.
302
410
 
303
411
  Run: `npx vitest run` (or `npm test`).
304
412
 
@@ -351,24 +459,56 @@ orgId-first-index, no-cross-org-reads are unchanged.
351
459
 
352
460
  `component/` (sibling of `convex/`, NOT nested under it — see "Why
353
461
  `component/` and not `convex/component/`" below) hosts the **actual,
354
- compiled, published functions** for a real slice of the engine, not just
355
- the component's identity:
462
+ compiled, published functions** for the FULL agent aggregate as of
463
+ 0.1.0-alpha.6 (C1-C6, all merged) — not just the component's identity:
356
464
 
357
465
  - `component/schema.ts` — `durableJobs`, `agents`, `toolApprovals`,
358
- `sessions`, `sessionEvents` (+ a test-only `callerStepInvocations`
359
- fixture table).
466
+ `sessions`, `sessionEvents`, `wakeableWork`, `awaitEventTimeouts`,
467
+ `mandates`, `approvalRequests`, `habits`, `habitRuns`, `sandboxConfig`,
468
+ `sandboxRuns`, `sandboxCallbackDeliveries`, `sandboxCallbackDeadLetters`,
469
+ `subagents`, `delegationRuns`, `agentCloneRuns`, `vpConnections`,
470
+ `vpToolInvocations` (+ test-only fixture tables: `callerStepInvocations`,
471
+ `wakeupDemoResults`, `vpTenantFixture`).
360
472
  - `component/engine/durableJob.ts` — `start` / `getStatus` / `cancel`
361
473
  (public contract) + `durableJobWorkflowV1` (the versioned pause/resume/
362
474
  replay workflow) + its internal step-runner mutations.
363
- - `component/engine/agents.ts` — `create` / `get` (minimal root-sheet
364
- bootstrap; no `update`/`list`/skills/connections in this pass).
475
+ - `component/engine/agents.ts` — `create` / `get` (root-sheet bootstrap,
476
+ now including `description`/`reasoningEffort`/`modelProvider`/
477
+ `maxSubagents`/`maxInputTokensPerSession`; `update`/skills/connections
478
+ CRUD remain out of scope for this pass).
479
+ - `component/engine/directory.ts` — `listByOrg` (agent directory listing).
365
480
  - `component/engine/resolvers.ts` — `resolveManifest` (dynamic reread at
366
481
  session/turn boundaries; returns `agentId`/`name`/`instructions`/`model`/
367
482
  `toolApprovals` — `skills`/`connections` NOT included in this pass).
368
483
  - `component/engine/sessions.ts` — the conversation thread contract:
369
484
  `start` / `sendTurn` / `appendReasoning` / `requestToolCall` /
370
- `completeToolCall` / `completeTurn` / `listEvents`.
485
+ `completeToolCall` / `completeTurn` / `listEvents`, now piloted by a
486
+ versioned `@convex-dev/workflow` run (`sessionWorkflowV1`,
487
+ `component/engine/workflows.ts` — C1.5).
488
+ `sessions.start` returns `{ sessionId, workflowId }`.
371
489
  - `component/engine/toolApprovals.ts` — `respond`.
490
+ - `component/engine/wakeup.ts` — `enqueue` / `cancel` / `getStatus` +
491
+ `onWorkComplete` (the wake-without-polling primitive; funds through
492
+ `mandates.spendInternal` before any work enters the pool — C2/C3 seam).
493
+ - `component/engine/awaitEventTimeout.ts` — self-built `step.awaitEvent`
494
+ timeout (workflow#177: Convex has no native timeout for it).
495
+ - `component/engine/mandates.ts` — `set` / `get` / `spend` /
496
+ `listApprovals` / `resolveApproval` / `usageCount` (autorité bornée,
497
+ fails-closed spend gate; "notre ligne").
498
+ - `component/engine/habits.ts` — `create` / `get` / `listForAgent` /
499
+ `updateSchedule` / `disable` / `enable` / `listRuns` (per-agent dynamic
500
+ cron, zero-redeploy lifecycle via `@convex-dev/crons`; "notre ligne").
501
+ - `component/engine/sandboxCallback.ts` — `startRun` / `attachSandboxId` /
502
+ `getStatus` / `deadLetterMalformed` / `processCallback` (the DB-side
503
+ half of the Vercel Sandbox bridge: dedup + wake + timeout, reusing C2's
504
+ `wakeup` mechanism).
505
+ - `component/engine/delegation.ts` — `declareSubagent` / `listSubagents` /
506
+ `delegateToSubagent` / `invokeAgentClone` (inter-agent delegation and the
507
+ built-in `agent` clone tool, both reusing `wakeup.enqueue` — the SAME
508
+ pause/wake mechanism as the sandbox bridge, one system not two).
509
+ - `component/engine/vpConnection.ts` — `get` / `getToggles` / `setToggles`
510
+ / `invokeVpTool` (the four VantagePeers tools — memory/tasks/messages/
511
+ notes — wired as a connection of the agent aggregate; "notre ligne").
372
512
  - `component/lib/auth.ts` — `requireOrgId` / `assertSameOrg`: see "Trust
373
513
  model" above.
374
514
 
@@ -381,49 +521,141 @@ components.agentEngine.engine.durableJob.start({ orgId, kind, stepFunctionHandle
381
521
  components.agentEngine.engine.durableJob.getStatus({ orgId, jobId }) -> { status, cursor, totalSteps, lastError?, abandonedAtStep? }
382
522
  components.agentEngine.engine.durableJob.cancel({ orgId, jobId }) -> null
383
523
 
384
- components.agentEngine.engine.agents.create({ orgId, createdBy, name, instructions, model }) -> Id<"agents">
524
+ components.agentEngine.engine.agents.create({ orgId, createdBy, name, description?, instructions, model, reasoningEffort?, modelProvider?, maxSubagents?, maxInputTokensPerSession? }) -> Id<"agents">
385
525
  components.agentEngine.engine.agents.get({ orgId, agentId }) -> agent | null
386
526
 
527
+ components.agentEngine.engine.directory.listByOrg({ orgId }) -> { _id, name, status, model, createdAt }[]
528
+
387
529
  components.agentEngine.engine.resolvers.resolveManifest({ orgId, sessionId }) -> { agentId, name, instructions, model, toolApprovals[] }
388
530
 
389
- components.agentEngine.engine.sessions.start({ orgId, agentId, createdBy }) -> Id<"sessions">
531
+ components.agentEngine.engine.sessions.start({ orgId, agentId, createdBy }) -> { sessionId: Id<"sessions">, workflowId: string }
390
532
  components.agentEngine.engine.sessions.sendTurn({ orgId, sessionId, input }) -> null
391
533
  components.agentEngine.engine.sessions.appendReasoning({ orgId, sessionId, chunk }) -> null
392
- components.agentEngine.engine.sessions.requestToolCall({ orgId, sessionId, toolName, argsJson }) -> { authorized, approvalPending }
534
+ components.agentEngine.engine.sessions.requestToolCall({ orgId, sessionId, toolName, argsJson }) -> { authorized, approvalPending, denied }
393
535
  components.agentEngine.engine.sessions.completeToolCall({ orgId, sessionId, toolName, resultJson }) -> null
394
536
  components.agentEngine.engine.sessions.completeTurn({ orgId, sessionId }) -> null
395
537
  components.agentEngine.engine.sessions.listEvents({ orgId, sessionId }) -> sessionEvent[]
396
538
 
397
- components.agentEngine.engine.toolApprovals.respond({ orgId, sessionId, toolName, approval: "always"|"once"|"never" }) -> null
539
+ components.agentEngine.engine.toolApprovals.respond({ orgId, sessionId, toolName, decision: "approved"|"denied", updatePolicy?: "always"|"once"|"never" }) -> null
540
+
541
+ components.agentEngine.engine.wakeup.enqueue({ orgId, agentId, mandateScope, cost, reason, workFunctionHandle, workflowId, awaitedEventName }) -> Id<"wakeableWork"> (action)
542
+ components.agentEngine.engine.wakeup.cancel({ orgId, wakeableWorkId }) -> null
543
+ components.agentEngine.engine.wakeup.getStatus({ orgId, wakeableWorkId }) -> { status, resultSummary?, ... }
544
+
545
+ components.agentEngine.engine.mandates.set({ orgId, agentId, scope, kind, rate, period, capacity? }) -> Id<"mandates">
546
+ components.agentEngine.engine.mandates.get({ orgId, agentId, scope }) -> mandate | null
547
+ components.agentEngine.engine.mandates.spend({ orgId, agentId, scope, cost, reason }) -> { ok, approvalId? }
548
+ components.agentEngine.engine.mandates.listApprovals({ orgId, agentId }) -> approvalRequest[]
549
+ components.agentEngine.engine.mandates.resolveApproval({ orgId, approvalId, resolvedBy, status: "approved"|"denied" }) -> null
550
+ components.agentEngine.engine.mandates.usageCount({ orgId, agentId, scope }) -> number
551
+
552
+ components.agentEngine.engine.habits.create({ orgId, agentId, name, cronExpression, prompt }) -> Id<"habits">
553
+ components.agentEngine.engine.habits.get({ orgId, habitId }) -> habit | null
554
+ components.agentEngine.engine.habits.listForAgent({ orgId, agentId }) -> habit[]
555
+ components.agentEngine.engine.habits.updateSchedule({ orgId, habitId, cronExpression }) -> null
556
+ components.agentEngine.engine.habits.disable({ orgId, habitId }) -> null
557
+ components.agentEngine.engine.habits.enable({ orgId, habitId }) -> null
558
+ components.agentEngine.engine.habits.listRuns({ orgId, habitId }) -> habitRun[]
559
+
560
+ components.agentEngine.engine.sandboxCallback.startRun({ orgId, workflowId, awaitedEventName, timeoutMs }) -> Id<"sandboxRuns">
561
+ components.agentEngine.engine.sandboxCallback.attachSandboxId({ orgId, sandboxRunId, sandboxId }) -> null
562
+ components.agentEngine.engine.sandboxCallback.getStatus({ orgId, sandboxRunId }) -> { status, resultSummary?, ... }
563
+ components.agentEngine.engine.sandboxCallback.processCallback({ orgId, sandboxRunId, deliveryId, status, resultSummary? }) -> { outcome: "woke"|"duplicate" } (called ONLY after the app-level HTTP route has verified the HMAC signature — see "notre ligne" below)
564
+ components.agentEngine.engine.sandboxCallback.deadLetterMalformed({ orgId?, deliveryId, reason, rawBody }) -> null
565
+
566
+ components.agentEngine.engine.delegation.declareSubagent({ orgId, parentAgentId, childAgentId, toolName, description? }) -> Id<"subagents">
567
+ components.agentEngine.engine.delegation.listSubagents({ orgId, parentAgentId }) -> subagent[]
568
+ components.agentEngine.engine.delegation.delegateToSubagent({ orgId, subagentId, ... }) -> null (action; reuses `wakeup.enqueue` end to end)
569
+ components.agentEngine.engine.delegation.invokeAgentClone({ orgId, agentId, ... }) -> null (action; the built-in `agent` clone tool, root-only, non-recursive by construction)
570
+
571
+ components.agentEngine.engine.vpConnection.get({ orgId, agentId }) -> vpConnection | null
572
+ components.agentEngine.engine.vpConnection.getToggles({ orgId, agentId }) -> { memoryEnabled, tasksEnabled, messagesEnabled, notesEnabled }
573
+ components.agentEngine.engine.vpConnection.setToggles({ orgId, agentId, memoryEnabled?, tasksEnabled?, messagesEnabled?, notesEnabled? }) -> Id<"vpConnections">
574
+ components.agentEngine.engine.vpConnection.invokeVpTool({ orgId, agentId, tool, vpFunctionHandle, argsJson }) -> null (action; direct access via `createFunctionHandle`, never `fetch()`)
398
575
  ```
399
576
 
577
+ **Deliberately app-level, NOT ported into the component boundary** — this
578
+ is a measured platform boundary (C5 recherche-d'abord), not an unfinished
579
+ migration:
580
+
581
+ - The public HTTP route (`/sandbox/callback`) that reads
582
+ `SANDBOX_CALLBACK_SECRET` and verifies the HMAC signature over the raw
583
+ request body. No official `@convex-dev/*` component ships an `http.ts`
584
+ or reads `process.env` directly (measured: zero hits across all 7
585
+ installed `@convex-dev/*` component dist trees) — a component cannot
586
+ own this. It stays in the consumer app's own `convex/http.ts`, which
587
+ calls `sandboxCallback.processCallback` / `deadLetterMalformed` ONLY
588
+ after the signature has already checked out.
589
+ - The `"use node"` action that actually talks to `@vercel/sandbox`
590
+ (`Sandbox.create()` / `sandbox.runCommand()`). The Convex CLI's own
591
+ component bundler hard-crashes on any `"use node"` file found inside a
592
+ child component directory ("use node directive is not supported in
593
+ components"). It stays app-level and calls INTO this component's public
594
+ `sandboxCallback.startRun` / `attachSandboxId` (and `mandates.spend`)
595
+ instead.
596
+
597
+ Everything else that was previously listed as "not yet callable" —
598
+ `skills`/`subagents`/`habits`/`sandboxConfig`/`mandates`/
599
+ `approvalRequests`/`awaitEventTimeouts`/`wakeableWork`/`engine/wakeup.ts`/
600
+ `engine/workflows.ts`/`vpConnections` — is now hosted under `component/`
601
+ and callable as listed above (C1 through C6, this pass). `skills` and
602
+ `connections` (the agent's own tool-definition and third-party-connection
603
+ tables, distinct from the `subagents`/`vpConnections` tables above) remain
604
+ the one genuinely deferred slice: porting them needs their own
605
+ component-level `convex-test` harness first (Day 128: never ship
606
+ unverified), and is left for the next, separately-scoped migration.
607
+
608
+ ### `toolApprovals.respond` — `decision` vs `updatePolicy` (0.1.0-alpha.5)
609
+
610
+ Two DISTINCT axes were conflated pre-alpha.5 (VP
611
+ k17ea6x3tht8xa3rxm139t23gx8ajkf3, defaut 2) — a single `approval:
612
+ "always"|"once"|"never"` argument was overloaded to mean both "the standing
613
+ policy for this tool" AND "the answer to this one pending request", and
614
+ there was no literal for "refuse this turn only". Fixed by splitting them:
615
+
616
+ - **`decision`** (required) — the response to THIS ONE pending
617
+ `tool.approval.requested` event, for THIS turn only. `"approved"` |
618
+ `"denied"`. **Eve parity, direct** — vocabulary matches Eve's
619
+ `ApprovalStatus` (`packages/eve/src/public/definitions/approval.ts`:
620
+ `"approved" | "denied" | "not-applicable" | "user-approval" | ...`); we
621
+ did not invent a `deny` literal, we aligned with the one Eve already has.
622
+ `"denied"` is a terminal refusal for the turn: `sessions.requestToolCall`
623
+ reports back `{ authorized: false, approvalPending: false, denied: true
624
+ }` (never re-authorized on retry within the session), and a dedicated
625
+ `turn.toolCall.denied` sessionEvent is appended — there is NO
626
+ `turn.toolCall.completed` for a denied call, i.e. the tool was never run
627
+ and the turn is NOT silently treated as done.
628
+ - **`updatePolicy`** (optional) — persists a NEW standing gate
629
+ (`"always"|"once"|"never"`, eve parity: `eve/tools/approval` helpers) for
630
+ this `(agentId, toolName)` pair going forward, independent of `decision`.
631
+ A human can deny this turn's call and leave the policy alone (ask again
632
+ next time), or approve this turn and simultaneously flip the policy to
633
+ `"always"`/`"never"` so future calls stop pausing. `"never"` still means
634
+ Eve's "never require approval" (auto-authorize) — that policy literal is
635
+ unchanged and is NOT the same thing as a `"denied"` decision.
636
+
400
637
  `createdBy` (on `agents.create` / `sessions.start`) is, for the same
401
638
  platform reason as `orgId`, no longer read from `ctx.auth.getUserIdentity()`
402
639
  — the consumer passes the acting user's own identifier.
403
640
 
404
- Verified two ways: (1) `component/durableJob.componentPackaging.test.ts` +
405
- `component/sessions.componentPackaging.test.ts` run `convex-test` directly
406
- against `component/_generated/api` (the exact source tree
407
- `tsc -p tsconfig.build.json` compiles into `dist/component/`, which is
408
- what `npm pack` ships), so the compiled artifact has a real green test
409
- suite behind it, not just the repo-root dev app's; (2) a from-scratch
410
- consumer probe (install the published tarball into a bare `npx convex dev`
411
- project, register the component, call `durableJob.start` + `sessions.start`
412
- from the consumer's own function, passing `orgId` explicitly) — see the
413
- completion report for the verbatim transcript.
414
-
415
- **NOT yet callable through the published component** (still only exists in
416
- this repo's OWN dev/test deployment under `convex/schema.ts` +
417
- `convex/agents.ts` + `convex/skills.ts`, used by `npx convex dev` and the
418
- repo-root `convex-test` suite): `skills`, `connections`, `subagents`,
419
- `habits`, `sandboxConfig`, `modelKeys`, `mandates`, `approvalRequests`,
420
- `awaitEventTimeouts`, `wakeableWork`, `engine/wakeup.ts`,
421
- `engine/workflows.ts` (`sessionWorkflowV1` — this pass's `sessions` is
422
- deliberately NOT tied to a `@convex-dev/workflow` run; see
423
- `component/schema.ts`'s `sessions` table comment for why). Porting each of
424
- those under `component/` without a working component-level `convex-test`
425
- harness behind it would ship unverified code — Day 128 forbids that; it is
426
- deliberately left for the next, separately-scoped migration.
641
+ Verified two ways: (1) every `component/*.componentPackaging.test.ts` file
642
+ (one per migrated module: durableJob, sessions, directory, wakeup,
643
+ awaitEventTimeout, mandates, habits, sandboxCallback, delegation,
644
+ vpConnection) runs `convex-test` directly against `component/_generated/api`
645
+ (the exact source tree `tsc -p tsconfig.build.json` compiles into
646
+ `dist/component/`, which is what `npm pack` ships), so the compiled
647
+ artifact has a real green test suite behind it, not just the repo-root dev
648
+ app's; (2) a from-scratch global consumer probe (install the REAL
649
+ `npm pack` tarball into a bare Convex project outside this repo, register
650
+ the component, call one function from EACH migrated capability from the
651
+ consumer's own function, passing `orgId` explicitly) — see the C7
652
+ completion report for the verbatim transcript and, honestly, whichever
653
+ parts of it required deployment provisioning this repo does not have.
654
+
655
+ Only `skills` and `connections` (the agent's own tool-definition and
656
+ third-party-connection tables) remain genuinely NOT ported — see
657
+ "Deliberately app-level" and the closing paragraph above for the full,
658
+ current boundary.
427
659
 
428
660
  ### Why `component/` and not `convex/component/`
429
661
 
@@ -447,8 +679,15 @@ T1 (schema + durable session engine, PR #1) merged @
447
679
  `9f3401ba63e6e663af95e9e4f5da42bc30b3550b`. I1 (long-task survival
448
680
  contract, `engine/durableJob.ts`) — not merged, Eta reviews. T4 (mandates
449
681
  + habits) — **not merged** — Eta reviews, merge on Pi token. T2-TER (npm
450
- packaging + component identity + publication) and T2-QUATER (this pass:
451
- migrated `durableJob` + its schema + `lib/auth.ts` under the actual
452
- component boundary, `component/`, with its own `_generated/` and its own
453
- green `convex-test` suite) — see "Packaging & publication" above; **not
454
- merged**.
682
+ packaging + component identity + publication) through C6 (agents,
683
+ sessions/workflow, directory, wakeup, awaitEventTimeout, mandates, habits,
684
+ sandbox bridge DB-side, delegation, VantagePeers connection — all ported
685
+ under `component/` with their own green `convex-test` suite) — see
686
+ "Packaging & publication" above.
687
+
688
+ **M-COMPLETION C7** (this pass): version bump to `0.1.0-alpha.6`
689
+ republishing the FULL C1-C6 surface + a from-scratch global consumer probe
690
+ against the real `npm pack` tarball proving packaged surface == built
691
+ surface. npm `latest`/`alpha` tags are NOT repointed by this PR — that is
692
+ a post-merge deployment step, Pi token protocol, same as every prior
693
+ publish. See the C7 completion report for the probe transcript.
@@ -7,21 +7,49 @@
7
7
  * @module
8
8
  */
9
9
  import type * as engine_agents from "../engine/agents.js";
10
+ import type * as engine_awaitEventTimeout from "../engine/awaitEventTimeout.js";
11
+ import type * as engine_delegation from "../engine/delegation.js";
12
+ import type * as engine_directory from "../engine/directory.js";
10
13
  import type * as engine_durableJob from "../engine/durableJob.js";
14
+ import type * as engine_habits from "../engine/habits.js";
15
+ import type * as engine_mandates from "../engine/mandates.js";
11
16
  import type * as engine_resolvers from "../engine/resolvers.js";
17
+ import type * as engine_sandboxCallback from "../engine/sandboxCallback.js";
12
18
  import type * as engine_sessions from "../engine/sessions.js";
13
19
  import type * as engine_toolApprovals from "../engine/toolApprovals.js";
20
+ import type * as engine_vpConnection from "../engine/vpConnection.js";
21
+ import type * as engine_wakeup from "../engine/wakeup.js";
22
+ import type * as engine_workflows from "../engine/workflows.js";
14
23
  import type * as lib_auth from "../lib/auth.js";
15
24
  import type * as testFixtures_callerSteps from "../testFixtures/callerSteps.js";
25
+ import type * as testFixtures_delegationDemoWorkflow from "../testFixtures/delegationDemoWorkflow.js";
26
+ import type * as testFixtures_sandboxDemoWorkflow from "../testFixtures/sandboxDemoWorkflow.js";
27
+ import type * as testFixtures_vpTool from "../testFixtures/vpTool.js";
28
+ import type * as testFixtures_wakeupDemoWorkflow from "../testFixtures/wakeupDemoWorkflow.js";
29
+ import type * as testFixtures_wakeupWork from "../testFixtures/wakeupWork.js";
16
30
  import type { ApiFromModules, FilterApi, FunctionReference } from "convex/server";
17
31
  declare const fullApi: ApiFromModules<{
18
32
  "engine/agents": typeof engine_agents;
33
+ "engine/awaitEventTimeout": typeof engine_awaitEventTimeout;
34
+ "engine/delegation": typeof engine_delegation;
35
+ "engine/directory": typeof engine_directory;
19
36
  "engine/durableJob": typeof engine_durableJob;
37
+ "engine/habits": typeof engine_habits;
38
+ "engine/mandates": typeof engine_mandates;
20
39
  "engine/resolvers": typeof engine_resolvers;
40
+ "engine/sandboxCallback": typeof engine_sandboxCallback;
21
41
  "engine/sessions": typeof engine_sessions;
22
42
  "engine/toolApprovals": typeof engine_toolApprovals;
43
+ "engine/vpConnection": typeof engine_vpConnection;
44
+ "engine/wakeup": typeof engine_wakeup;
45
+ "engine/workflows": typeof engine_workflows;
23
46
  "lib/auth": typeof lib_auth;
24
47
  "testFixtures/callerSteps": typeof testFixtures_callerSteps;
48
+ "testFixtures/delegationDemoWorkflow": typeof testFixtures_delegationDemoWorkflow;
49
+ "testFixtures/sandboxDemoWorkflow": typeof testFixtures_sandboxDemoWorkflow;
50
+ "testFixtures/vpTool": typeof testFixtures_vpTool;
51
+ "testFixtures/wakeupDemoWorkflow": typeof testFixtures_wakeupDemoWorkflow;
52
+ "testFixtures/wakeupWork": typeof testFixtures_wakeupWork;
25
53
  }>;
26
54
  /**
27
55
  * A utility for referencing Convex functions in your app's public API.
@@ -46,5 +74,8 @@ export declare const components: {
46
74
  agentTurnPool: import("@convex-dev/workpool/_generated/component.js").ComponentApi<"agentTurnPool">;
47
75
  wakeupPool: import("@convex-dev/workpool/_generated/component.js").ComponentApi<"wakeupPool">;
48
76
  agent: import("@convex-dev/agent/_generated/component.js").ComponentApi<"agent">;
77
+ rateLimiter: import("@convex-dev/rate-limiter/_generated/component.js").ComponentApi<"rateLimiter">;
78
+ shardedCounter: import("@convex-dev/sharded-counter/_generated/component.js").ComponentApi<"shardedCounter">;
79
+ crons: import("@convex-dev/crons/_generated/component.js").ComponentApi<"crons">;
49
80
  };
50
81
  export {};