@vercel/factory 0.0.15 → 0.0.16

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 (214) hide show
  1. package/CHANGELOG.md +356 -0
  2. package/README.md +49 -261
  3. package/dist/agent-routes.d.mts +47 -3
  4. package/dist/agent-routes.mjs +28 -1
  5. package/dist/agent-routes.mjs.map +1 -1
  6. package/dist/api-contracts.d.mts +20 -1
  7. package/dist/api-contracts.mjs +2 -1
  8. package/dist/api-contracts.mjs.map +1 -1
  9. package/dist/api.d.mts +45 -2
  10. package/dist/api.mjs +199 -10
  11. package/dist/api.mjs.map +1 -1
  12. package/dist/approval-contracts.d.mts +6 -0
  13. package/dist/blob/index.d.mts +51 -14
  14. package/dist/blob/index.mjs +26 -10
  15. package/dist/blob/index.mjs.map +1 -1
  16. package/dist/budget.d.mts +7 -0
  17. package/dist/budget.mjs +6 -0
  18. package/dist/budget.mjs.map +1 -1
  19. package/dist/build-factory.d.mts +1 -0
  20. package/dist/change-verification/dispatch.d.mts +16 -3
  21. package/dist/change-verification/dispatch.mjs +45 -7
  22. package/dist/change-verification/dispatch.mjs.map +1 -1
  23. package/dist/change-verification/eve-tool.d.mts +2 -1
  24. package/dist/change-verification/eve-tool.mjs +35 -47
  25. package/dist/change-verification/eve-tool.mjs.map +1 -1
  26. package/dist/change-verification/result.mjs +130 -0
  27. package/dist/change-verification/result.mjs.map +1 -0
  28. package/dist/changes/eve-record-change.d.mts +2 -2
  29. package/dist/changes/eve-record-change.mjs +43 -9
  30. package/dist/changes/eve-record-change.mjs.map +1 -1
  31. package/dist/changes.d.mts +4 -3
  32. package/dist/changes.mjs +2 -2
  33. package/dist/changes.mjs.map +1 -1
  34. package/dist/client-events.d.mts +10 -3
  35. package/dist/client-events.mjs +6 -2
  36. package/dist/client-events.mjs.map +1 -1
  37. package/dist/client-stream.mjs +8 -2
  38. package/dist/client-stream.mjs.map +1 -1
  39. package/dist/client-transcript.mjs +5 -1
  40. package/dist/client-transcript.mjs.map +1 -1
  41. package/dist/client.d.mts +121 -12
  42. package/dist/client.mjs +117 -9
  43. package/dist/client.mjs.map +1 -1
  44. package/dist/code-review/contracts.d.mts +1 -0
  45. package/dist/code-review/eve-post-review.d.mts +4 -4
  46. package/dist/code-review/eve-post-review.mjs +29 -17
  47. package/dist/code-review/eve-post-review.mjs.map +1 -1
  48. package/dist/code-review/eve-review-comments.d.mts +13 -2
  49. package/dist/code-review/eve-review-comments.mjs +45 -11
  50. package/dist/code-review/eve-review-comments.mjs.map +1 -1
  51. package/dist/code-review/github-reporter.d.mts +2 -0
  52. package/dist/code-review/github-reporter.mjs +7 -5
  53. package/dist/code-review/github-reporter.mjs.map +1 -1
  54. package/dist/code-review.d.mts +3 -2
  55. package/dist/deepsec/eve-tool.mjs +3 -1
  56. package/dist/deepsec/eve-tool.mjs.map +1 -1
  57. package/dist/dispatch.d.mts +79 -8
  58. package/dist/dispatch.mjs +68 -9
  59. package/dist/dispatch.mjs.map +1 -1
  60. package/dist/eve/index.d.mts +70 -10
  61. package/dist/eve/index.mjs +93 -22
  62. package/dist/eve/index.mjs.map +1 -1
  63. package/dist/eve/invoke.mjs +24 -11
  64. package/dist/eve/invoke.mjs.map +1 -1
  65. package/dist/eve/session-client.d.mts +122 -4
  66. package/dist/eve/session-client.mjs +127 -13
  67. package/dist/eve/session-client.mjs.map +1 -1
  68. package/dist/eve/task-execution.d.mts +380 -0
  69. package/dist/eve/task-execution.mjs +57 -2
  70. package/dist/eve/task-execution.mjs.map +1 -1
  71. package/dist/eve/task-session.d.mts +44 -2
  72. package/dist/eve/task-session.mjs +44 -2
  73. package/dist/eve/task-session.mjs.map +1 -1
  74. package/dist/eve/transcript.mjs +5 -1
  75. package/dist/eve/transcript.mjs.map +1 -1
  76. package/dist/execution.d.mts +117 -9
  77. package/dist/execution.mjs +76 -6
  78. package/dist/execution.mjs.map +1 -1
  79. package/dist/finding-remediation/admission.d.mts +2 -0
  80. package/dist/finding-remediation/admission.mjs +4 -1
  81. package/dist/finding-remediation/admission.mjs.map +1 -1
  82. package/dist/findings.d.mts +1 -0
  83. package/dist/github-publication.d.mts +1 -0
  84. package/dist/github-publication.mjs +97 -84
  85. package/dist/github-publication.mjs.map +1 -1
  86. package/dist/github-transfer.d.mts +15 -6
  87. package/dist/github-transfer.mjs +214 -66
  88. package/dist/github-transfer.mjs.map +1 -1
  89. package/dist/github.d.mts +51 -11
  90. package/dist/github.mjs +126 -24
  91. package/dist/github.mjs.map +1 -1
  92. package/dist/inbox-activity.d.mts +53 -0
  93. package/dist/inbox-activity.mjs +41 -0
  94. package/dist/inbox-activity.mjs.map +1 -0
  95. package/dist/index.d.mts +3 -1
  96. package/dist/index.mjs +3 -2
  97. package/dist/intake-contracts.d.mts +0 -1
  98. package/dist/integrations/deepsec.d.mts +1 -0
  99. package/dist/integrations/github.d.mts +2 -2
  100. package/dist/integrations/github.mjs +2 -2
  101. package/dist/integrations/slack.d.mts +3 -1
  102. package/dist/integrations/slack.mjs +3 -1
  103. package/dist/integrations/vercel.d.mts +4 -2
  104. package/dist/integrations/vercel.mjs +3 -2
  105. package/dist/merge-resolution/eve-tools.d.mts +1 -0
  106. package/dist/merge-resolution/eve-tools.mjs +7 -2
  107. package/dist/merge-resolution/eve-tools.mjs.map +1 -1
  108. package/dist/model-settings.d.mts +41 -0
  109. package/dist/model-settings.mjs +35 -0
  110. package/dist/model-settings.mjs.map +1 -0
  111. package/dist/planning/reconcile.mjs +6 -0
  112. package/dist/planning/reconcile.mjs.map +1 -1
  113. package/dist/postgres/index.d.mts +43 -2
  114. package/dist/postgres/index.mjs +40 -2
  115. package/dist/postgres/index.mjs.map +1 -1
  116. package/dist/presets/software-development/dispatch.d.mts +4 -1
  117. package/dist/presets/software-development/dispatch.mjs +2 -1
  118. package/dist/presets/software-development/dispatch.mjs.map +1 -1
  119. package/dist/presets/software-development/task-communication.d.mts +1 -0
  120. package/dist/presets/software-development/task-communication.mjs +48 -11
  121. package/dist/presets/software-development/task-communication.mjs.map +1 -1
  122. package/dist/presets/software-development.d.mts +1 -0
  123. package/dist/pull-requests/github-publisher.d.mts +15 -1
  124. package/dist/pull-requests/github-publisher.mjs +61 -1
  125. package/dist/pull-requests/github-publisher.mjs.map +1 -1
  126. package/dist/pull-requests.d.mts +1 -0
  127. package/dist/sandbox/index.d.mts +1 -0
  128. package/dist/schema/agent-route.d.mts +19 -1
  129. package/dist/schema/agent-route.mjs +19 -1
  130. package/dist/schema/agent-route.mjs.map +1 -1
  131. package/dist/schema/factory-config.d.mts +27 -0
  132. package/dist/schema/factory-config.mjs +33 -3
  133. package/dist/schema/factory-config.mjs.map +1 -1
  134. package/dist/schema/repository.d.mts +4 -0
  135. package/dist/schema/repository.mjs +5 -1
  136. package/dist/schema/repository.mjs.map +1 -1
  137. package/dist/schema/session.d.mts +1 -0
  138. package/dist/schema/session.mjs +1 -0
  139. package/dist/schema/session.mjs.map +1 -1
  140. package/dist/schema/slack-pr-notifications.d.mts +12 -0
  141. package/dist/schema/slack-pr-notifications.mjs +11 -0
  142. package/dist/schema/slack-pr-notifications.mjs.map +1 -0
  143. package/dist/schema/task-graph.d.mts +39 -0
  144. package/dist/schema/task.d.mts +1 -0
  145. package/dist/schema/task.mjs +2 -1
  146. package/dist/schema/task.mjs.map +1 -1
  147. package/dist/schema/transcript.d.mts +6 -0
  148. package/dist/schema/transcript.mjs +2 -1
  149. package/dist/schema/transcript.mjs.map +1 -1
  150. package/dist/schema/work.d.mts +52 -3
  151. package/dist/schema/work.mjs.map +1 -1
  152. package/dist/session-previews.d.mts +76 -0
  153. package/dist/session-previews.mjs +55 -0
  154. package/dist/session-previews.mjs.map +1 -0
  155. package/dist/session-review.d.mts +120 -0
  156. package/dist/session-review.mjs +79 -0
  157. package/dist/session-review.mjs.map +1 -0
  158. package/dist/signal-triage.mjs +1 -1
  159. package/dist/signals.d.mts +1 -0
  160. package/dist/stall.d.mts +4 -1
  161. package/dist/stall.mjs +6 -2
  162. package/dist/stall.mjs.map +1 -1
  163. package/dist/store/driver.d.mts +1 -1
  164. package/dist/store/driver.mjs.map +1 -1
  165. package/dist/store/engine.d.mts +206 -8
  166. package/dist/store/engine.mjs +147 -13
  167. package/dist/store/engine.mjs.map +1 -1
  168. package/dist/store/memory.d.mts +18 -1
  169. package/dist/store/memory.mjs +18 -1
  170. package/dist/store/memory.mjs.map +1 -1
  171. package/dist/store/slack-pr-notifications.d.mts +44 -0
  172. package/dist/store/slack-pr-notifications.mjs +121 -0
  173. package/dist/store/slack-pr-notifications.mjs.map +1 -0
  174. package/dist/store/task-work.d.mts +121 -6
  175. package/dist/store/task-work.mjs +7 -4
  176. package/dist/store/task-work.mjs.map +1 -1
  177. package/dist/sweep.d.mts +28 -6
  178. package/dist/sweep.mjs +34 -6
  179. package/dist/sweep.mjs.map +1 -1
  180. package/dist/task-graph-view.d.mts +3 -0
  181. package/dist/tasks.d.mts +3 -3
  182. package/dist/tasks.mjs +3 -3
  183. package/dist/vercel-git.d.mts +35 -3
  184. package/dist/vercel-git.mjs +265 -33
  185. package/dist/vercel-git.mjs.map +1 -1
  186. package/dist/vercel-github-api.d.mts +103 -0
  187. package/dist/vercel-github-api.mjs +363 -0
  188. package/dist/vercel-github-api.mjs.map +1 -0
  189. package/dist/vercel.d.mts +3 -2
  190. package/dist/vercel.mjs +3 -2
  191. package/dist/vercel.mjs.map +1 -1
  192. package/dist/work-triage.d.mts +1 -0
  193. package/dist/workflows.d.mts +102 -4
  194. package/dist/workflows.mjs +55 -2
  195. package/dist/workflows.mjs.map +1 -1
  196. package/dist/workspace-files-git.d.mts +15 -0
  197. package/dist/workspace-files-git.mjs +61 -0
  198. package/dist/workspace-files-git.mjs.map +1 -0
  199. package/dist/workspace-files.d.mts +107 -0
  200. package/dist/workspace-files.mjs +74 -0
  201. package/dist/workspace-files.mjs.map +1 -0
  202. package/docs/getting-started.md +104 -0
  203. package/docs/index.md +100 -0
  204. package/docs/recipes/cancellation.md +215 -0
  205. package/docs/recipes/custom-workflow.md +153 -0
  206. package/docs/recipes/dependent-tasks.md +207 -0
  207. package/docs/recipes/eve-agent.md +277 -0
  208. package/docs/recipes/human-input.md +204 -0
  209. package/docs/recipes/persistence-recovery.md +268 -0
  210. package/docs/recipes/retry-recovery.md +241 -0
  211. package/docs/recipes/task-messaging.md +215 -0
  212. package/docs/recipes/typed-eve-result.md +161 -0
  213. package/docs/runtime-integration.md +137 -0
  214. package/package.json +17 -6
package/README.md CHANGED
@@ -8,8 +8,45 @@ state transitions, retries, and external effects while agents and coding harness
8
8
  results. The V1 reference runtime uses Blob, AI SDK `HarnessAgent`, and Vercel Sandbox,
9
9
  with independent verification and human-owned merges.
10
10
 
11
- See the [repository README](../../README.md) for development instructions and the
12
- [architecture](../../docs/factory-architecture.md) for the complete contract.
11
+ ## Requirements and installation
12
+
13
+ Factory requires Node.js 24 or newer and publishes ESM. Install the package with its required
14
+ `eve` and `zod` peers:
15
+
16
+ ```sh
17
+ pnpm add @vercel/factory eve zod
18
+ ```
19
+
20
+ Optional storage, sandbox, and harness integrations declare additional peer dependencies. Install
21
+ them only when using the corresponding capability path.
22
+
23
+ The published declarations are checked with TypeScript 5.9.3 and 7.0.2. Core workflows and the Blob
24
+ adapter use `nodeNext` resolution with library checking enabled; the browser-safe `/client` entry
25
+ point uses `bundler` resolution without loading Node, Eve, Blob, or Drizzle types. The Postgres
26
+ adapter is checked with its Drizzle peer and `skipLibCheck` because Drizzle's declaration graph
27
+ includes declarations for optional database drivers.
28
+
29
+ Start with the shipped [Eve agent integration recipe](docs/recipes/eve-agent.md), which shows how
30
+ Factory dispatches a route-bound Task into Eve and how the agent mounts Factory authentication,
31
+ tools, and hooks. The [local Task quickstart](docs/getting-started.md) isolates the durable state
32
+ model without running an agent. The [consumer documentation index](docs/index.md) maps common goals
33
+ to import paths and version-matched declaration files. For complete runtime assembly boundaries,
34
+ read [runtime composition](docs/runtime-integration.md).
35
+
36
+ Runnable, credential-free recipes cover [Eve agent integration](docs/recipes/eve-agent.md),
37
+ [typed Eve workflow results](docs/recipes/typed-eve-result.md),
38
+ [human input and resumption](docs/recipes/human-input.md),
39
+ [dependent Eve agents](docs/recipes/dependent-tasks.md),
40
+ [custom workflows and local execution](docs/recipes/custom-workflow.md),
41
+ [persistent restart recovery](docs/recipes/persistence-recovery.md),
42
+ [cancellation](docs/recipes/cancellation.md), [retry recovery](docs/recipes/retry-recovery.md), and
43
+ [typed Task messaging](docs/recipes/task-messaging.md). Each published example is compiled and
44
+ executed against the package before release.
45
+
46
+ The optional Vercel-managed Git transport uses fresh trusted Sandboxes for reads and signed
47
+ publication. These transfer Sandboxes run no repository code or models; agent and harness Sandboxes
48
+ remain credential-free. The managed transport returns the reconciled signed commit SHA. Initial Git Data API
49
+ publication remains an application-selected adapter.
13
50
 
14
51
  ## Public import paths
15
52
 
@@ -46,10 +83,9 @@ their semantic owner instead of under a parallel `/eve` namespace.
46
83
  | `@vercel/factory/sandbox` | Optional isolated execution integration |
47
84
 
48
85
  The package root owns common configuration, cross-cutting primitives, and generic Eve integration
49
- that have no narrower capability owner. This package is pre-1.0: replaced paths and names are
50
- removed during alpha API cleanup rather than retained as deprecated aliases. Each public
51
- declaration has one canonical owner. The browser client and server API re-export shared HTTP wire
52
- contracts at both consumption boundaries from the same source declarations.
86
+ that have no narrower capability owner. Each public declaration has one canonical owner. The
87
+ browser client and server API re-export shared HTTP wire contracts at both consumption boundaries
88
+ from the same source declarations.
53
89
 
54
90
  Ordinary public values and functions use `camelCase`; type-like declarations, constructors, and UI
55
91
  components use `PascalCase`; fixed constant-style values may use `UPPER_CASE`. Schemas use a
@@ -59,260 +95,12 @@ registries use `define*`. Provider and protocol payload keys retain their requir
59
95
  Oxlint is the repository's sole JavaScript and TypeScript linter and enforces the allowed
60
96
  identifier shapes. Declaration-specific casing follows the conventions above in code and review.
61
97
 
62
- ## Reusable runtime integration
63
-
64
- The package root exposes provider-neutral integration helpers that have no narrower capability owner:
65
-
66
- - `repositoryIdForSlug` and `ensureConfiguredRepository` provide deterministic repository identity
67
- and allowlist-bound admission. The caller supplies normalized `defaultBranch` and `dependsOn`
68
- metadata from its own configuration; the package does not infer either value.
69
- - `redactedErrorMessage` and `redactedErrorDetails` remove common credential forms and supplied
70
- secret values from diagnostics.
71
-
72
- The supplied software-development runtime is an explicit composition rather than part of the
73
- kernel. Domain operations and their Eve tools live under their owning capability paths. The
74
- `@vercel/factory/presets/software-development` entry point contains the supplied approval,
75
- completion, communication, recovery, dispatch, and delegation behavior plus the DeepSec scan brief.
76
- It also composes generic Task tools with the Change outcome tool. Applications inject repository
77
- policy, planner behavior, launchers, credentials, budget, wakeup publication, and retry and batch
78
- limits.
79
-
80
- Capability APIs use their semantic owner paths, while external-provider APIs stay opt-in under
81
- `@vercel/factory/integrations/<provider>`:
82
-
83
- - `@vercel/factory` binds Task or conversation scope to authenticated Eve sessions through
84
- semantic readers, owns the generic start, forward, cancel, and completed-turn lifecycle, and
85
- supplies generic delegation and Task tools. It also materializes browser-safe transcript
86
- projections through injected origin and header policy.
87
- - Capability entry points expose agent tools beside the corresponding domain operation. For
88
- example, `@vercel/factory/changes` records a Change and `@vercel/factory/code-review` exposes
89
- review tools. The software-development preset composes the common Task and Change tools.
90
- - Provider-specific Eve tool factories that implement a Factory capability stay on that
91
- capability path and identify the provider in their symbol name.
92
- - `@vercel/factory/storage/blob` includes content-addressed, Task-bound artifact storage with digest
93
- verification in addition to the Blob store driver. Applications inject or namespace its narrow
94
- Blob client when one project contains multiple factory state domains.
95
- - `@vercel/factory/sandbox` supplies the retry-safe execution ledger, abort propagation, harness
96
- continuation adapter, workspace contract, and scoped Vercel Sandbox lifecycle. Applications
97
- inject prompt construction, evidence interpretation, pricing, credentials, repository seeding,
98
- and the configured harness/model registry.
99
- - `@vercel/factory/integrations/github` normalizes addressed review comments, question replies,
100
- and repository identity alongside the GitHub API helpers. It also validates GitHub App token
101
- scope; supplies bounded committed-tree publication, a Git Data/pull-request client, and checkout
102
- primitives; and owns the standalone GitHub adapters for communication delivery, review
103
- reporting, pull-request publication and labels, and existing-work triage. Applications still
104
- choose credentials and authorize repositories.
105
- - `@vercel/factory/integrations/slack` delivers communication events through an injected Slack
106
- client.
107
- - `@vercel/factory/integrations/deepsec` owns DeepSec output normalization and its provider-centric
108
- Eve scanner tool.
109
- - `@vercel/factory/integrations/vercel` configures hosted Vercel Queue wakeups. It validates
110
- messages, provides stable idempotency keys and fixture isolation, and exports
111
- `createVercelGitHubGitTransport` to read and publish Git bundles through fresh trusted sandboxes,
112
- keeping credentials out of agent sandboxes.
113
- Reads require read-only tokens; bundles include full history with a 50 MiB limit.
114
- The application supplies queue transport, logging, and repository-scoped credentials.
115
- - `@vercel/factory/api` exports the bounded sandbox snapshot and workspace-diff assembly used by
116
- optional inspection adapters; sandbox discovery, base-blob retrieval, and repository
117
- authorization remain application-owned.
118
-
119
- These APIs deliberately stop before application policy: configured repositories, credentials,
120
- agent routes, budgets, prompts, approval rules, and provider account choices remain in the concrete
121
- factory.
122
-
123
- The communication and execution capabilities supply policy injection points such as
124
- `createTaskCommunicationReconciler` and the stall sweep's optional cancellation callback. Omitting
125
- `approval` from the configuration selects a neutral allow-by-default policy; factories should
126
- select a policy explicitly. A software factory opts into
127
- `defaultSoftwareDevelopmentApprovalPolicy`, the supplied delegation and completion policies, and
128
- the software presentation and dispatch behavior from
129
- `@vercel/factory/presets/software-development`. Generated projects make those choices explicitly.
130
-
131
- Every Task requires `work` (title, bounded JSON input, completion criteria, exact workflow
132
- ID/version, and optional exact agent route), plus engine-owned root and parent membership.
133
- `taskWork` defaults to the generic `task@1` JSON contract; explicit workflow registrations supply
134
- Zod input/output contracts. `stores.graphs` owns per-root dependencies, and `stores.work.complete`
135
- records validated output. Task kind is descriptive, not execution or success authority.
136
-
137
- `stores.messages` and `processTaskMessage` provide authorized immutable messages, bounded inbox
138
- leases, and durable decisions before idempotent effects. Access defaults to deny; supplied
139
- assignment/handoff protocols and task-bound policy require explicit application configuration.
140
- These are direct trusted APIs, not HTTP endpoints or an enabled recursive planner. See the
141
- [Task contract](../../docs/factory-architecture.md#task) for bounds, recovery, and race limitations.
142
-
143
- This is a breaking Task contract with no compatibility path. Old stored Task snapshots fail the
144
- new required schema validation; there is no separate legacy-rejection mechanism. No live data is changed by this source update; rollout must explicitly
145
- choose data migration or reset before deployment.
146
-
147
- ## Local Task primitives
148
-
149
- Copy the following three blocks together into a TypeScript ES module with top-level await. This isolated example
150
- uses an in-memory driver and a synthetic repository ID: no model, provider, HTTP endpoint, or live
151
- planner is called. Production applications supply persistent storage and authenticated principals;
152
- never derive a principal's Task binding from a message payload.
153
-
154
- ### Register a workflow
155
-
156
- `taskWork({ title, input })` constructs generic `task@1` work. A custom contract requires explicit
157
- registration and an exact binding; including `defaultTaskWorkflow` keeps generic work available
158
- in the same registry. Registration alone starts nothing.
159
-
160
- ```ts
161
- import { z } from "zod";
162
- import {
163
- assignmentPayloadSchema,
164
- assignmentTaskMessageProtocol,
165
- createDefaultTaskMessageProtocols,
166
- processTaskMessage,
167
- taskBoundMessagePolicy,
168
- } from "@vercel/factory/tasks";
169
- import { createInMemoryDriver, createStores } from "@vercel/factory/storage";
170
- import {
171
- defaultTaskWorkflow,
172
- defineWorkflow,
173
- defineWorkflows,
174
- taskWork,
175
- } from "@vercel/factory/workflows";
176
-
177
- const countWords = defineWorkflow({
178
- id: "word-count",
179
- version: 1,
180
- input: z.strictObject({ text: z.string().min(1).describe("Text to count.") }),
181
- output: z.strictObject({ words: z.number().int().nonnegative().describe("Word count.") }),
182
- });
183
- const driver = createInMemoryDriver();
184
- const stores = createStores({
185
- driver,
186
- workflows: defineWorkflows([defaultTaskWorkflow, countWords]),
187
- messages: { protocols: createDefaultTaskMessageProtocols(), authorize: taskBoundMessagePolicy },
188
- });
189
- const scope = { repositoryIds: ["repo_example"], replyTo: { channel: "local", address: "demo" } };
190
- const parent = await stores.tasks.create({
191
- ...scope,
192
- kind: "analysis",
193
- origin: { operator: "local:demo" },
194
- work: taskWork({ title: "Count words in supplied text" }),
195
- dedupeKey: "demo:root",
196
- });
197
- ```
198
-
199
- ### Create children and canonical dependencies
200
-
201
- The engine derives each child's root from its one parent. Dependencies belong to `stores.graphs`,
202
- not Task snapshots. Editing requires the observed graph revision and a queued, unsealed Task.
203
- Transitioning to running seals edges and requires every prerequisite to have succeeded.
204
-
205
- ```ts
206
- const childScope = { ...scope, origin: {}, parentTaskId: parent.id };
207
- const preparation = await stores.tasks.create({
208
- ...childScope,
209
- kind: "analysis",
210
- work: taskWork({ title: "Prepare input" }),
211
- dedupeKey: "demo:prepare",
212
- });
213
- const worker = await stores.tasks.create({
214
- ...childScope,
215
- kind: "analysis",
216
- dedupeKey: "demo:count",
217
- work: {
218
- ...taskWork({
219
- title: "Count words",
220
- input: { text: "Hello from Factory" },
221
- workflow: countWords,
222
- }),
223
- completionCriteria: ["Return the number of whitespace-separated words"],
224
- },
225
- });
226
- const graph = await stores.graphs.get(parent.rootTaskId);
227
- if (!graph) throw new Error("Task graph must exist after admission");
228
- await stores.graphs.setDependencies({
229
- rootTaskId: parent.rootTaskId,
230
- taskId: worker.id,
231
- dependencies: [preparation.id],
232
- expectedRevision: graph.revision,
233
- });
234
- await stores.tasks.transition(parent.id, "running");
235
- await stores.work.waitForMessages({
236
- task: { taskId: parent.id, attempt: parent.attempt },
237
- phase: "children",
238
- });
239
- await stores.tasks.transition(preparation.id, "running");
240
- await stores.work.complete({
241
- task: { taskId: preparation.id, attempt: preparation.attempt },
242
- output: { prepared: true },
243
- });
244
- await stores.tasks.transition(worker.id, "running");
245
- ```
246
-
247
- These Tasks have no agent route: local workflow code owns execution. For agent work,
248
- `routeTaskWork({ route, title, input })` adds an exact route but still selects generic `task@1`.
249
- For typed agent work, add `route: parseAgentRouteBinding({ id, version })` to work constructed with
250
- the custom workflow binding, or use the exact route returned by a registry. See the
251
- [route example](../../docs/factory-architecture.md#task); recovery must use that exact route, not
252
- kind or latest version.
253
-
254
- ### Send and process one message
255
-
256
- The explicit policy above permits parent-child sends and recipient-local processing, not discard.
257
- Stable operation IDs deduplicate sends. `processTaskMessage` stores a validated decision before
258
- `apply`; this example's only effect is idempotent Task completion with that output.
259
-
260
- ```ts
261
- await stores.messages.send({
262
- principal: { id: "local:parent", taskId: parent.id },
263
- message: {
264
- operationId: "assign-count",
265
- fromTaskId: parent.id,
266
- toTaskId: worker.id,
267
- expectAttempt: parent.attempt,
268
- type: assignmentTaskMessageProtocol.binding,
269
- payload: { instructions: "Count the words in your work input." },
270
- },
271
- });
272
- const result = await processTaskMessage({
273
- stores,
274
- principal: { id: "local:worker", taskId: worker.id },
275
- task: { taskId: worker.id, attempt: worker.attempt },
276
- leaseMs: 30_000,
277
- workflow: countWords.binding,
278
- protocol: assignmentTaskMessageProtocol,
279
- decisionSchema: countWords.output,
280
- async decide({ task, message }) {
281
- if (message.type.id !== "assignment" || message.type.version !== 1) {
282
- throw new Error("Expected assignment@1");
283
- }
284
- assignmentPayloadSchema.parse(message.payload);
285
- const { text } = countWords.input.parse(task.work.input);
286
- return { words: text.trim() ? text.trim().split(/\s+/u).length : 0 };
287
- },
288
- async apply({ task, decision }) {
289
- await stores.work.complete({
290
- task: { taskId: task.id, attempt: task.attempt },
291
- output: decision,
292
- });
293
- },
294
- });
295
- if (result.status === "processed") {
296
- await stores.work.complete({
297
- task: { taskId: parent.id, attempt: parent.attempt },
298
- output: { childTaskId: worker.id, output: result.decision },
299
- });
300
- }
301
- ```
302
-
303
- `idle` means empty or already leased, not necessarily drained. In durable application code, retry
304
- processing against the same store and stable identities; a whole-script rerun is not a recovery loop.
305
- External writes in `apply` must honor its `effectKey` (derive stable subkeys for multiple writes).
306
- Lease assertions cannot fence external systems or eliminate cancellation races. Recovery after
307
- success/failure requires the exact durable decision; cancelled Tasks cannot replay effects.
308
- Inboxes hold at most 256 pending messages and leases last at most five minutes. Supplied
309
- `assignment@1` / `handoff@1` protocols do not install a recursive planner or change lifecycle
310
- on receipt. Without explicit message configuration, access is denied. See the
311
- [message contract](../../docs/factory-architecture.md#task-messages) for recovery and discard rules.
98
+ ## API stability and repository development
312
99
 
313
- ## Operator clients
100
+ This package is pre-1.0: replaced paths and names can be removed during alpha API cleanup rather
101
+ than retained as deprecated aliases. Check the shipped [changelog](CHANGELOG.md) before upgrading
102
+ persisted factories.
314
103
 
315
- `@vercel/factory/client` exposes the browser-safe `FactoryClient`, operator request/response
316
- schemas, durable `taskStates`, `Transcript`, and `followSessionStream`. It is shared by the
317
- reference web UI and TUI. Supply a fixed factory origin and, for non-browser callers, an async `headers` callback for
318
- authentication. Server handlers and their dependencies remain under `@vercel/factory/api`.
104
+ Repository setup, architecture, and contribution instructions live in the
105
+ [Agent Factory repository](https://github.com/vercel-labs/agent-factory). They are maintainer
106
+ documentation rather than prerequisites for using the installed package.
@@ -20,11 +20,17 @@ type AgentRoute = Readonly<Omit<z.infer<typeof agentRouteSchema>, "permissions">
20
20
  }>;
21
21
  /** Caller input accepted when registering an agent route, before defaults are applied. */
22
22
  interface AgentRouteInput {
23
+ /** Stable lowercase route identifier selected by workflows. */
23
24
  readonly id: string;
25
+ /** Positive exact version. Defaults to `1` during registration. */
24
26
  readonly version?: number;
27
+ /** Internally addressable Eve agent name used by the application launcher. */
25
28
  readonly agent: string;
29
+ /** Descriptive Task kind created for work on this route; it does not control routing. */
26
30
  readonly taskKind: TaskKind;
31
+ /** Model-facing explanation of when this route is appropriate. */
27
32
  readonly description: string;
33
+ /** Inspectable capability labels; the application still enforces actual authorization. */
28
34
  readonly permissions?: readonly string[];
29
35
  }
30
36
  /** Task work with a required exact agent-route binding. */
@@ -53,16 +59,54 @@ type RegisteredAgentRoute<Input extends AgentRouteInput> = Input extends AgentRo
53
59
  }> : never;
54
60
  /** Immutable registry for exact agent-route lookup and unambiguous admission selection. */
55
61
  interface AgentRouteRegistry<out Route extends AgentRoute = AgentRoute> {
62
+ /** Immutable validated routes in registration order. */
56
63
  readonly routes: readonly Route[];
57
- /** Returns only the exact route binding supplied; never defaults a version. */
64
+ /**
65
+ * Returns only the exact route binding supplied; never defaults a version.
66
+ *
67
+ * @param binding - Exact route ID and version to resolve.
68
+ * @returns The registered route, or `undefined` when that exact version is absent.
69
+ */
58
70
  get<const Binding extends AgentRouteBinding>(binding: Binding): (Route & Readonly<Binding>) | undefined;
59
- /** Lists every registered route for a descriptive Task kind in registration order. */
71
+ /**
72
+ * Lists every registered route for a descriptive Task kind in registration order.
73
+ *
74
+ * @param kind - Descriptive Task kind to filter by.
75
+ * @returns Matching routes; an empty array when none were registered.
76
+ */
60
77
  listForTaskKind<const Kind extends string>(kind: Kind): readonly (Route & {
61
78
  readonly taskKind: Kind;
62
79
  })[];
63
80
  }
64
81
  type RegisteredAgentRoutes<Input extends readonly AgentRouteInput[]> = [Input[number]] extends [never] ? AgentRoute : RegisteredAgentRoute<Input[number]>;
65
- /** Descriptions inform judgment; exact registered bindings remain the authority. */
82
+ /**
83
+ * Builds an immutable registry whose exact bindings are the routing authority.
84
+ *
85
+ * @remarks
86
+ * Descriptions and permissions are inspectable guidance. They do not authorize execution and a
87
+ * Task kind never selects a route. Persist the exact route binding on work and recover that same
88
+ * binding after retries. Omitted versions and permissions become `1` and an empty array.
89
+ *
90
+ * @param inputs - Application-owned routes available to workflow and agent policy.
91
+ * @returns A registry supporting exact lookup and descriptive Task-kind listing.
92
+ * @throws A Zod validation error for an invalid route or an error for duplicate `id@version` keys.
93
+ *
94
+ * @example
95
+ * ```ts
96
+ * import { defineAgentRoutes } from "@vercel/factory/workflows";
97
+ *
98
+ * const routes = defineAgentRoutes([
99
+ * {
100
+ * id: "incident-worker",
101
+ * agent: "worker",
102
+ * taskKind: "incident",
103
+ * description: "Investigate one admitted incident.",
104
+ * permissions: ["read-repository"],
105
+ * },
106
+ * ]);
107
+ * const route = routes.routes[0];
108
+ * ```
109
+ */
66
110
  declare function defineAgentRoutes<const Input extends readonly AgentRouteInput[]>(inputs: Input): AgentRouteRegistry<RegisteredAgentRoutes<Input>>;
67
111
  //#endregion
68
112
  export { AgentRoute, type AgentRouteBinding, type AgentRouteBindingInput, AgentRouteInput, AgentRouteKey, AgentRouteRegistry, RouteTaskWorkOptions, RoutedTaskWork, agentRouteBindingSchema, agentRouteSchema, defineAgentRoutes, parseAgentRouteBinding, routeKey, routeTaskWork };
@@ -30,7 +30,34 @@ function routeTaskWork(options) {
30
30
  route: routeBinding(route)
31
31
  };
32
32
  }
33
- /** Descriptions inform judgment; exact registered bindings remain the authority. */
33
+ /**
34
+ * Builds an immutable registry whose exact bindings are the routing authority.
35
+ *
36
+ * @remarks
37
+ * Descriptions and permissions are inspectable guidance. They do not authorize execution and a
38
+ * Task kind never selects a route. Persist the exact route binding on work and recover that same
39
+ * binding after retries. Omitted versions and permissions become `1` and an empty array.
40
+ *
41
+ * @param inputs - Application-owned routes available to workflow and agent policy.
42
+ * @returns A registry supporting exact lookup and descriptive Task-kind listing.
43
+ * @throws A Zod validation error for an invalid route or an error for duplicate `id@version` keys.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * import { defineAgentRoutes } from "@vercel/factory/workflows";
48
+ *
49
+ * const routes = defineAgentRoutes([
50
+ * {
51
+ * id: "incident-worker",
52
+ * agent: "worker",
53
+ * taskKind: "incident",
54
+ * description: "Investigate one admitted incident.",
55
+ * permissions: ["read-repository"],
56
+ * },
57
+ * ]);
58
+ * const route = routes.routes[0];
59
+ * ```
60
+ */
34
61
  function defineAgentRoutes(inputs) {
35
62
  const routes = Object.freeze(inputs.map((input) => {
36
63
  const route = agentRouteSchema.parse(input);
@@ -1 +1 @@
1
- {"version":3,"file":"agent-routes.mjs","names":[],"sources":["../src/agent-routes.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { parseAgentRouteBinding, type AgentRouteBinding } from \"./schema/agent-route\";\nimport { taskKindSchema, type TaskKind } from \"./schema/task\";\nimport { taskWork, type JsonValue, type TaskWork } from \"./schema/work\";\n\nexport {\n agentRouteBindingSchema,\n parseAgentRouteBinding,\n type AgentRouteBinding,\n type AgentRouteBindingInput,\n} from \"./schema/agent-route\";\n\n/** Validates an exact-version route from workflow work to one configured agent. */\nexport const agentRouteSchema = z\n .strictObject({\n id: z\n .string()\n .regex(/^[a-z][a-z0-9-]{0,63}$/u)\n .describe(\"Stable route selected by a conversational agent or workflow.\"),\n version: z\n .number()\n .int()\n .positive()\n .default(1)\n .describe(\"Exact version of this agent route; defaults to 1 at registration.\"),\n agent: z\n .string()\n .regex(/^[a-z][a-z0-9-]{0,31}$/u)\n .describe(\"Internally addressable Eve agent that executes this route.\"),\n taskKind: taskKindSchema.describe(\"Factory task kind created for this route.\"),\n description: z\n .string()\n .min(1)\n .max(500)\n .describe(\"Model-facing guidance for when this route is appropriate.\"),\n permissions: z\n .array(z.string().min(1))\n .readonly()\n .default([])\n .describe(\"Inspectable capabilities granted to the target agent.\"),\n })\n .brand<\"AgentRouteBinding\">();\n\n/** Canonical `id@version` lookup key for one exact agent route. */\nexport type AgentRouteKey<\n Id extends string = string,\n Version extends number = number,\n> = `${Id}@${Version}`;\n/** Validated immutable route describing an agent, Task kind, purpose, and permissions. */\nexport type AgentRoute = Readonly<\n Omit<z.infer<typeof agentRouteSchema>, \"permissions\"> & {\n readonly permissions: readonly string[];\n }\n>;\n/** Caller input accepted when registering an agent route, before defaults are applied. */\nexport interface AgentRouteInput {\n readonly id: string;\n readonly version?: number;\n readonly agent: string;\n readonly taskKind: TaskKind;\n readonly description: string;\n readonly permissions?: readonly string[];\n}\n/** Task work with a required exact agent-route binding. */\nexport type RoutedTaskWork<\n Input extends JsonValue = JsonValue,\n Route extends AgentRouteBinding = AgentRouteBinding,\n> = Omit<TaskWork<Input>, \"route\"> & { readonly route: Route };\n\n/** Named fields for constructing generic work bound to an exact agent route. */\nexport interface RouteTaskWorkOptions<\n Route extends AgentRouteBinding = AgentRouteBinding,\n Input extends JsonValue = JsonValue,\n> {\n readonly route: Route;\n readonly title: string;\n readonly input?: Input;\n readonly workflow?: never;\n readonly completionCriteria?: never;\n}\n\n/** Produces the canonical `id@version` lookup key for a route binding. */\nexport function routeKey<const Binding extends AgentRouteBinding>(\n binding: Binding,\n): AgentRouteKey<Binding[\"id\"], Binding[\"version\"]> {\n return `${binding.id}@${binding.version}`;\n}\n\nconst routeBinding = <const Route extends AgentRouteBinding>(\n route: Route,\n): AgentRouteBinding<Route[\"id\"], Route[\"version\"]> =>\n Object.freeze(parseAgentRouteBinding({ id: route.id, version: route.version }));\n\n/** Constructs generic Task work bound to the selected exact agent route. */\nexport function routeTaskWork<\n const Route extends AgentRouteBinding,\n const Input extends JsonValue = JsonValue,\n>(\n options: RouteTaskWorkOptions<Route, Input>,\n): RoutedTaskWork<Input, AgentRouteBinding<Route[\"id\"], Route[\"version\"]>>;\nexport function routeTaskWork(options: RouteTaskWorkOptions): RoutedTaskWork {\n const { route, title, input = {} } = options;\n return {\n ...taskWork({ title, input }),\n route: routeBinding(route),\n };\n}\n\ntype RegisteredAgentRoute<Input extends AgentRouteInput> = Input extends AgentRouteInput\n ? AgentRouteBinding<\n Input[\"id\"],\n Input extends { readonly version: infer Version extends number } ? Version : 1\n > &\n Readonly<\n Omit<Input, \"version\" | \"permissions\"> & {\n readonly version: Input extends { readonly version: infer Version extends number }\n ? Version\n : 1;\n readonly permissions: readonly string[];\n }\n >\n : never;\n\n/** Immutable registry for exact agent-route lookup and unambiguous admission selection. */\nexport interface AgentRouteRegistry<out Route extends AgentRoute = AgentRoute> {\n readonly routes: readonly Route[];\n /** Returns only the exact route binding supplied; never defaults a version. */\n get<const Binding extends AgentRouteBinding>(\n binding: Binding,\n ): (Route & Readonly<Binding>) | undefined;\n /** Lists every registered route for a descriptive Task kind in registration order. */\n listForTaskKind<const Kind extends string>(\n kind: Kind,\n ): readonly (Route & { readonly taskKind: Kind })[];\n}\n\ntype RegisteredAgentRoutes<Input extends readonly AgentRouteInput[]> = [Input[number]] extends [\n never,\n]\n ? AgentRoute\n : RegisteredAgentRoute<Input[number]>;\n\n/** Descriptions inform judgment; exact registered bindings remain the authority. */\nexport function defineAgentRoutes<const Input extends readonly AgentRouteInput[]>(\n inputs: Input,\n): AgentRouteRegistry<RegisteredAgentRoutes<Input>> {\n type Route = RegisteredAgentRoutes<Input>;\n const routes = Object.freeze(\n inputs.map((input) => {\n const route = agentRouteSchema.parse(input);\n return Object.freeze({ ...route, permissions: Object.freeze([...route.permissions]) });\n }),\n ) as readonly Route[];\n const byBinding = new Map<string, Route>();\n const byTaskKind = new Map<string, readonly Route[]>();\n for (const route of routes) {\n const key = routeKey(route);\n if (byBinding.has(key)) {\n throw new Error(`Agent route ${key} is registered more than once.`);\n }\n byBinding.set(key, route);\n const matchingRoutes = byTaskKind.get(route.taskKind) ?? [];\n byTaskKind.set(route.taskKind, Object.freeze([...matchingRoutes, route]));\n }\n return Object.freeze({\n routes,\n get<const Binding extends AgentRouteBinding>(binding: Binding) {\n return byBinding.get(routeKey(routeBinding(binding))) as\n | (Route & Readonly<Binding>)\n | undefined;\n },\n listForTaskKind<const Kind extends string>(kind: Kind) {\n return (byTaskKind.get(kind) ?? Object.freeze([])) as readonly (Route & {\n readonly taskKind: Kind;\n })[];\n },\n });\n}\n"],"mappings":";;;;;;AAaA,MAAa,mBAAmB,EAC7B,aAAa;CACZ,IAAI,EACD,OAAO,CAAC,CACR,MAAM,yBAAyB,CAAC,CAChC,SAAS,8DAA8D;CAC1E,SAAS,EACN,OAAO,CAAC,CACR,IAAI,CAAC,CACL,SAAS,CAAC,CACV,QAAQ,CAAC,CAAC,CACV,SAAS,mEAAmE;CAC/E,OAAO,EACJ,OAAO,CAAC,CACR,MAAM,yBAAyB,CAAC,CAChC,SAAS,4DAA4D;CACxE,UAAU,eAAe,SAAS,2CAA2C;CAC7E,aAAa,EACV,OAAO,CAAC,CACR,IAAI,CAAC,CAAC,CACN,IAAI,GAAG,CAAC,CACR,SAAS,2DAA2D;CACvE,aAAa,EACV,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CACxB,SAAS,CAAC,CACV,QAAQ,CAAC,CAAC,CAAC,CACX,SAAS,uDAAuD;AACrE,CAAC,CAAC,CACD,MAA2B;;AAyC9B,SAAgB,SACd,SACkD;CAClD,OAAO,GAAG,QAAQ,GAAG,GAAG,QAAQ;AAClC;AAEA,MAAM,gBACJ,UAEA,OAAO,OAAO,uBAAuB;CAAE,IAAI,MAAM;CAAI,SAAS,MAAM;AAAQ,CAAC,CAAC;AAShF,SAAgB,cAAc,SAA+C;CAC3E,MAAM,EAAE,OAAO,OAAO,QAAQ,CAAC,MAAM;CACrC,OAAO;EACL,GAAG,SAAS;GAAE;GAAO;EAAM,CAAC;EAC5B,OAAO,aAAa,KAAK;CAC3B;AACF;;AAqCA,SAAgB,kBACd,QACkD;CAElD,MAAM,SAAS,OAAO,OACpB,OAAO,KAAK,UAAU;EACpB,MAAM,QAAQ,iBAAiB,MAAM,KAAK;EAC1C,OAAO,OAAO,OAAO;GAAE,GAAG;GAAO,aAAa,OAAO,OAAO,CAAC,GAAG,MAAM,WAAW,CAAC;EAAE,CAAC;CACvF,CAAC,CACH;CACA,MAAM,4BAAY,IAAI,IAAmB;CACzC,MAAM,6BAAa,IAAI,IAA8B;CACrD,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,MAAM,SAAS,KAAK;EAC1B,IAAI,UAAU,IAAI,GAAG,GACnB,MAAM,IAAI,MAAM,eAAe,IAAI,+BAA+B;EAEpE,UAAU,IAAI,KAAK,KAAK;EACxB,MAAM,iBAAiB,WAAW,IAAI,MAAM,QAAQ,KAAK,CAAC;EAC1D,WAAW,IAAI,MAAM,UAAU,OAAO,OAAO,CAAC,GAAG,gBAAgB,KAAK,CAAC,CAAC;CAC1E;CACA,OAAO,OAAO,OAAO;EACnB;EACA,IAA6C,SAAkB;GAC7D,OAAO,UAAU,IAAI,SAAS,aAAa,OAAO,CAAC,CAAC;EAGtD;EACA,gBAA2C,MAAY;GACrD,OAAQ,WAAW,IAAI,IAAI,KAAK,OAAO,OAAO,CAAC,CAAC;EAGlD;CACF,CAAC;AACH"}
1
+ {"version":3,"file":"agent-routes.mjs","names":[],"sources":["../src/agent-routes.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { parseAgentRouteBinding, type AgentRouteBinding } from \"./schema/agent-route\";\nimport { taskKindSchema, type TaskKind } from \"./schema/task\";\nimport { taskWork, type JsonValue, type TaskWork } from \"./schema/work\";\n\nexport {\n agentRouteBindingSchema,\n parseAgentRouteBinding,\n type AgentRouteBinding,\n type AgentRouteBindingInput,\n} from \"./schema/agent-route\";\n\n/** Validates an exact-version route from workflow work to one configured agent. */\nexport const agentRouteSchema = z\n .strictObject({\n id: z\n .string()\n .regex(/^[a-z][a-z0-9-]{0,63}$/u)\n .describe(\"Stable route selected by a conversational agent or workflow.\"),\n version: z\n .number()\n .int()\n .positive()\n .default(1)\n .describe(\"Exact version of this agent route; defaults to 1 at registration.\"),\n agent: z\n .string()\n .regex(/^[a-z][a-z0-9-]{0,31}$/u)\n .describe(\"Internally addressable Eve agent that executes this route.\"),\n taskKind: taskKindSchema.describe(\"Factory task kind created for this route.\"),\n description: z\n .string()\n .min(1)\n .max(500)\n .describe(\"Model-facing guidance for when this route is appropriate.\"),\n permissions: z\n .array(z.string().min(1))\n .readonly()\n .default([])\n .describe(\"Inspectable capabilities granted to the target agent.\"),\n })\n .brand<\"AgentRouteBinding\">();\n\n/** Canonical `id@version` lookup key for one exact agent route. */\nexport type AgentRouteKey<\n Id extends string = string,\n Version extends number = number,\n> = `${Id}@${Version}`;\n/** Validated immutable route describing an agent, Task kind, purpose, and permissions. */\nexport type AgentRoute = Readonly<\n Omit<z.infer<typeof agentRouteSchema>, \"permissions\"> & {\n readonly permissions: readonly string[];\n }\n>;\n/** Caller input accepted when registering an agent route, before defaults are applied. */\nexport interface AgentRouteInput {\n /** Stable lowercase route identifier selected by workflows. */\n readonly id: string;\n /** Positive exact version. Defaults to `1` during registration. */\n readonly version?: number;\n /** Internally addressable Eve agent name used by the application launcher. */\n readonly agent: string;\n /** Descriptive Task kind created for work on this route; it does not control routing. */\n readonly taskKind: TaskKind;\n /** Model-facing explanation of when this route is appropriate. */\n readonly description: string;\n /** Inspectable capability labels; the application still enforces actual authorization. */\n readonly permissions?: readonly string[];\n}\n/** Task work with a required exact agent-route binding. */\nexport type RoutedTaskWork<\n Input extends JsonValue = JsonValue,\n Route extends AgentRouteBinding = AgentRouteBinding,\n> = Omit<TaskWork<Input>, \"route\"> & { readonly route: Route };\n\n/** Named fields for constructing generic work bound to an exact agent route. */\nexport interface RouteTaskWorkOptions<\n Route extends AgentRouteBinding = AgentRouteBinding,\n Input extends JsonValue = JsonValue,\n> {\n readonly route: Route;\n readonly title: string;\n readonly input?: Input;\n readonly workflow?: never;\n readonly completionCriteria?: never;\n}\n\n/** Produces the canonical `id@version` lookup key for a route binding. */\nexport function routeKey<const Binding extends AgentRouteBinding>(\n binding: Binding,\n): AgentRouteKey<Binding[\"id\"], Binding[\"version\"]> {\n return `${binding.id}@${binding.version}`;\n}\n\nconst routeBinding = <const Route extends AgentRouteBinding>(\n route: Route,\n): AgentRouteBinding<Route[\"id\"], Route[\"version\"]> =>\n Object.freeze(parseAgentRouteBinding({ id: route.id, version: route.version }));\n\n/** Constructs generic Task work bound to the selected exact agent route. */\nexport function routeTaskWork<\n const Route extends AgentRouteBinding,\n const Input extends JsonValue = JsonValue,\n>(\n options: RouteTaskWorkOptions<Route, Input>,\n): RoutedTaskWork<Input, AgentRouteBinding<Route[\"id\"], Route[\"version\"]>>;\nexport function routeTaskWork(options: RouteTaskWorkOptions): RoutedTaskWork {\n const { route, title, input = {} } = options;\n return {\n ...taskWork({ title, input }),\n route: routeBinding(route),\n };\n}\n\ntype RegisteredAgentRoute<Input extends AgentRouteInput> = Input extends AgentRouteInput\n ? AgentRouteBinding<\n Input[\"id\"],\n Input extends { readonly version: infer Version extends number } ? Version : 1\n > &\n Readonly<\n Omit<Input, \"version\" | \"permissions\"> & {\n readonly version: Input extends { readonly version: infer Version extends number }\n ? Version\n : 1;\n readonly permissions: readonly string[];\n }\n >\n : never;\n\n/** Immutable registry for exact agent-route lookup and unambiguous admission selection. */\nexport interface AgentRouteRegistry<out Route extends AgentRoute = AgentRoute> {\n /** Immutable validated routes in registration order. */\n readonly routes: readonly Route[];\n /**\n * Returns only the exact route binding supplied; never defaults a version.\n *\n * @param binding - Exact route ID and version to resolve.\n * @returns The registered route, or `undefined` when that exact version is absent.\n */\n get<const Binding extends AgentRouteBinding>(\n binding: Binding,\n ): (Route & Readonly<Binding>) | undefined;\n /**\n * Lists every registered route for a descriptive Task kind in registration order.\n *\n * @param kind - Descriptive Task kind to filter by.\n * @returns Matching routes; an empty array when none were registered.\n */\n listForTaskKind<const Kind extends string>(\n kind: Kind,\n ): readonly (Route & { readonly taskKind: Kind })[];\n}\n\ntype RegisteredAgentRoutes<Input extends readonly AgentRouteInput[]> = [Input[number]] extends [\n never,\n]\n ? AgentRoute\n : RegisteredAgentRoute<Input[number]>;\n\n/**\n * Builds an immutable registry whose exact bindings are the routing authority.\n *\n * @remarks\n * Descriptions and permissions are inspectable guidance. They do not authorize execution and a\n * Task kind never selects a route. Persist the exact route binding on work and recover that same\n * binding after retries. Omitted versions and permissions become `1` and an empty array.\n *\n * @param inputs - Application-owned routes available to workflow and agent policy.\n * @returns A registry supporting exact lookup and descriptive Task-kind listing.\n * @throws A Zod validation error for an invalid route or an error for duplicate `id@version` keys.\n *\n * @example\n * ```ts\n * import { defineAgentRoutes } from \"@vercel/factory/workflows\";\n *\n * const routes = defineAgentRoutes([\n * {\n * id: \"incident-worker\",\n * agent: \"worker\",\n * taskKind: \"incident\",\n * description: \"Investigate one admitted incident.\",\n * permissions: [\"read-repository\"],\n * },\n * ]);\n * const route = routes.routes[0];\n * ```\n */\nexport function defineAgentRoutes<const Input extends readonly AgentRouteInput[]>(\n inputs: Input,\n): AgentRouteRegistry<RegisteredAgentRoutes<Input>> {\n type Route = RegisteredAgentRoutes<Input>;\n const routes = Object.freeze(\n inputs.map((input) => {\n const route = agentRouteSchema.parse(input);\n return Object.freeze({ ...route, permissions: Object.freeze([...route.permissions]) });\n }),\n ) as readonly Route[];\n const byBinding = new Map<string, Route>();\n const byTaskKind = new Map<string, readonly Route[]>();\n for (const route of routes) {\n const key = routeKey(route);\n if (byBinding.has(key)) {\n throw new Error(`Agent route ${key} is registered more than once.`);\n }\n byBinding.set(key, route);\n const matchingRoutes = byTaskKind.get(route.taskKind) ?? [];\n byTaskKind.set(route.taskKind, Object.freeze([...matchingRoutes, route]));\n }\n return Object.freeze({\n routes,\n get<const Binding extends AgentRouteBinding>(binding: Binding) {\n return byBinding.get(routeKey(routeBinding(binding))) as\n | (Route & Readonly<Binding>)\n | undefined;\n },\n listForTaskKind<const Kind extends string>(kind: Kind) {\n return (byTaskKind.get(kind) ?? Object.freeze([])) as readonly (Route & {\n readonly taskKind: Kind;\n })[];\n },\n });\n}\n"],"mappings":";;;;;;AAaA,MAAa,mBAAmB,EAC7B,aAAa;CACZ,IAAI,EACD,OAAO,CAAC,CACR,MAAM,yBAAyB,CAAC,CAChC,SAAS,8DAA8D;CAC1E,SAAS,EACN,OAAO,CAAC,CACR,IAAI,CAAC,CACL,SAAS,CAAC,CACV,QAAQ,CAAC,CAAC,CACV,SAAS,mEAAmE;CAC/E,OAAO,EACJ,OAAO,CAAC,CACR,MAAM,yBAAyB,CAAC,CAChC,SAAS,4DAA4D;CACxE,UAAU,eAAe,SAAS,2CAA2C;CAC7E,aAAa,EACV,OAAO,CAAC,CACR,IAAI,CAAC,CAAC,CACN,IAAI,GAAG,CAAC,CACR,SAAS,2DAA2D;CACvE,aAAa,EACV,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CACxB,SAAS,CAAC,CACV,QAAQ,CAAC,CAAC,CAAC,CACX,SAAS,uDAAuD;AACrE,CAAC,CAAC,CACD,MAA2B;;AA+C9B,SAAgB,SACd,SACkD;CAClD,OAAO,GAAG,QAAQ,GAAG,GAAG,QAAQ;AAClC;AAEA,MAAM,gBACJ,UAEA,OAAO,OAAO,uBAAuB;CAAE,IAAI,MAAM;CAAI,SAAS,MAAM;AAAQ,CAAC,CAAC;AAShF,SAAgB,cAAc,SAA+C;CAC3E,MAAM,EAAE,OAAO,OAAO,QAAQ,CAAC,MAAM;CACrC,OAAO;EACL,GAAG,SAAS;GAAE;GAAO;EAAM,CAAC;EAC5B,OAAO,aAAa,KAAK;CAC3B;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2EA,SAAgB,kBACd,QACkD;CAElD,MAAM,SAAS,OAAO,OACpB,OAAO,KAAK,UAAU;EACpB,MAAM,QAAQ,iBAAiB,MAAM,KAAK;EAC1C,OAAO,OAAO,OAAO;GAAE,GAAG;GAAO,aAAa,OAAO,OAAO,CAAC,GAAG,MAAM,WAAW,CAAC;EAAE,CAAC;CACvF,CAAC,CACH;CACA,MAAM,4BAAY,IAAI,IAAmB;CACzC,MAAM,6BAAa,IAAI,IAA8B;CACrD,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,MAAM,SAAS,KAAK;EAC1B,IAAI,UAAU,IAAI,GAAG,GACnB,MAAM,IAAI,MAAM,eAAe,IAAI,+BAA+B;EAEpE,UAAU,IAAI,KAAK,KAAK;EACxB,MAAM,iBAAiB,WAAW,IAAI,MAAM,QAAQ,KAAK,CAAC;EAC1D,WAAW,IAAI,MAAM,UAAU,OAAO,OAAO,CAAC,GAAG,gBAAgB,KAAK,CAAC,CAAC;CAC1E;CACA,OAAO,OAAO,OAAO;EACnB;EACA,IAA6C,SAAkB;GAC7D,OAAO,UAAU,IAAI,SAAS,aAAa,OAAO,CAAC,CAAC;EAGtD;EACA,gBAA2C,MAAY;GACrD,OAAQ,WAAW,IAAI,IAAI,KAAK,OAAO,OAAO,CAAC,CAAC;EAGlD;CACF,CAAC;AACH"}
@@ -39,6 +39,7 @@ interface ApiAgentInput {
39
39
  declare const createSessionRequestSchema: z.ZodObject<{
40
40
  operationId: z.ZodString;
41
41
  agent: z.ZodString;
42
+ model: z.ZodOptional<z.ZodString>;
42
43
  repository: z.ZodOptional<z.ZodType<`${string}/${string}`, string, z.core.$ZodTypeInternals<`${string}/${string}`, string>>>;
43
44
  changeId: z.ZodOptional<z.ZodType<`chg_${string}`, string, z.core.$ZodTypeInternals<`chg_${string}`, string>>>;
44
45
  message: z.ZodString;
@@ -138,6 +139,7 @@ declare const sessionViewSchema: z.ZodObject<{
138
139
  id: z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>;
139
140
  repositoryIds: z.ZodReadonly<z.ZodTuple<[z.ZodType<`repo_${string}`, string, z.core.$ZodTypeInternals<`repo_${string}`, string>>], z.ZodType<`repo_${string}`, string, z.core.$ZodTypeInternals<`repo_${string}`, string>>>>;
140
141
  kind: z.ZodType<TaskKind, unknown, z.core.$ZodTypeInternals<TaskKind, unknown>>;
142
+ model: z.ZodOptional<z.ZodString>;
141
143
  area: z.ZodOptional<z.ZodString>;
142
144
  state: z.ZodEnum<{
143
145
  queued: "queued";
@@ -254,6 +256,7 @@ declare const sessionViewSchema: z.ZodObject<{
254
256
  attempt: number;
255
257
  createdAt: string;
256
258
  updatedAt: string;
259
+ model?: string | undefined;
257
260
  area?: string | undefined;
258
261
  approval?: "required" | "granted" | "denied" | undefined;
259
262
  workResult?: {
@@ -331,6 +334,7 @@ declare const sessionViewSchema: z.ZodObject<{
331
334
  attempt: number;
332
335
  createdAt: string;
333
336
  updatedAt: string;
337
+ model?: string | undefined;
334
338
  area?: string | undefined;
335
339
  approval?: "required" | "granted" | "denied" | undefined;
336
340
  workResult?: {
@@ -426,6 +430,7 @@ declare const sessionViewSchema: z.ZodObject<{
426
430
  id: z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>;
427
431
  repositoryIds: z.ZodReadonly<z.ZodTuple<[z.ZodType<`repo_${string}`, string, z.core.$ZodTypeInternals<`repo_${string}`, string>>], z.ZodType<`repo_${string}`, string, z.core.$ZodTypeInternals<`repo_${string}`, string>>>>;
428
432
  kind: z.ZodType<TaskKind, unknown, z.core.$ZodTypeInternals<TaskKind, unknown>>;
433
+ model: z.ZodOptional<z.ZodString>;
429
434
  area: z.ZodOptional<z.ZodString>;
430
435
  state: z.ZodEnum<{
431
436
  queued: "queued";
@@ -542,6 +547,7 @@ declare const sessionViewSchema: z.ZodObject<{
542
547
  attempt: number;
543
548
  createdAt: string;
544
549
  updatedAt: string;
550
+ model?: string | undefined;
545
551
  area?: string | undefined;
546
552
  approval?: "required" | "granted" | "denied" | undefined;
547
553
  workResult?: {
@@ -619,6 +625,7 @@ declare const sessionViewSchema: z.ZodObject<{
619
625
  attempt: number;
620
626
  createdAt: string;
621
627
  updatedAt: string;
628
+ model?: string | undefined;
622
629
  area?: string | undefined;
623
630
  approval?: "required" | "granted" | "denied" | undefined;
624
631
  workResult?: {
@@ -671,6 +678,7 @@ declare const sessionViewSchema: z.ZodObject<{
671
678
  id: z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>;
672
679
  repositoryIds: z.ZodReadonly<z.ZodTuple<[z.ZodType<`repo_${string}`, string, z.core.$ZodTypeInternals<`repo_${string}`, string>>], z.ZodType<`repo_${string}`, string, z.core.$ZodTypeInternals<`repo_${string}`, string>>>>;
673
680
  kind: z.ZodType<TaskKind, unknown, z.core.$ZodTypeInternals<TaskKind, unknown>>;
681
+ model: z.ZodOptional<z.ZodString>;
674
682
  area: z.ZodOptional<z.ZodString>;
675
683
  state: z.ZodEnum<{
676
684
  queued: "queued";
@@ -787,6 +795,7 @@ declare const sessionViewSchema: z.ZodObject<{
787
795
  attempt: number;
788
796
  createdAt: string;
789
797
  updatedAt: string;
798
+ model?: string | undefined;
790
799
  area?: string | undefined;
791
800
  approval?: "required" | "granted" | "denied" | undefined;
792
801
  workResult?: {
@@ -864,6 +873,7 @@ declare const sessionViewSchema: z.ZodObject<{
864
873
  attempt: number;
865
874
  createdAt: string;
866
875
  updatedAt: string;
876
+ model?: string | undefined;
867
877
  area?: string | undefined;
868
878
  approval?: "required" | "granted" | "denied" | undefined;
869
879
  workResult?: {
@@ -923,6 +933,7 @@ declare const sessionPageSchema: z.ZodObject<{
923
933
  id: z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>;
924
934
  repositoryIds: z.ZodReadonly<z.ZodTuple<[z.ZodType<`repo_${string}`, string, z.core.$ZodTypeInternals<`repo_${string}`, string>>], z.ZodType<`repo_${string}`, string, z.core.$ZodTypeInternals<`repo_${string}`, string>>>>;
925
935
  kind: z.ZodType<TaskKind, unknown, z.core.$ZodTypeInternals<TaskKind, unknown>>;
936
+ model: z.ZodOptional<z.ZodString>;
926
937
  area: z.ZodOptional<z.ZodString>;
927
938
  state: z.ZodEnum<{
928
939
  queued: "queued";
@@ -1039,6 +1050,7 @@ declare const sessionPageSchema: z.ZodObject<{
1039
1050
  attempt: number;
1040
1051
  createdAt: string;
1041
1052
  updatedAt: string;
1053
+ model?: string | undefined;
1042
1054
  area?: string | undefined;
1043
1055
  approval?: "required" | "granted" | "denied" | undefined;
1044
1056
  workResult?: {
@@ -1116,6 +1128,7 @@ declare const sessionPageSchema: z.ZodObject<{
1116
1128
  attempt: number;
1117
1129
  createdAt: string;
1118
1130
  updatedAt: string;
1131
+ model?: string | undefined;
1119
1132
  area?: string | undefined;
1120
1133
  approval?: "required" | "granted" | "denied" | undefined;
1121
1134
  workResult?: {
@@ -1211,6 +1224,7 @@ declare const sessionPageSchema: z.ZodObject<{
1211
1224
  id: z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>;
1212
1225
  repositoryIds: z.ZodReadonly<z.ZodTuple<[z.ZodType<`repo_${string}`, string, z.core.$ZodTypeInternals<`repo_${string}`, string>>], z.ZodType<`repo_${string}`, string, z.core.$ZodTypeInternals<`repo_${string}`, string>>>>;
1213
1226
  kind: z.ZodType<TaskKind, unknown, z.core.$ZodTypeInternals<TaskKind, unknown>>;
1227
+ model: z.ZodOptional<z.ZodString>;
1214
1228
  area: z.ZodOptional<z.ZodString>;
1215
1229
  state: z.ZodEnum<{
1216
1230
  queued: "queued";
@@ -1327,6 +1341,7 @@ declare const sessionPageSchema: z.ZodObject<{
1327
1341
  attempt: number;
1328
1342
  createdAt: string;
1329
1343
  updatedAt: string;
1344
+ model?: string | undefined;
1330
1345
  area?: string | undefined;
1331
1346
  approval?: "required" | "granted" | "denied" | undefined;
1332
1347
  workResult?: {
@@ -1404,6 +1419,7 @@ declare const sessionPageSchema: z.ZodObject<{
1404
1419
  attempt: number;
1405
1420
  createdAt: string;
1406
1421
  updatedAt: string;
1422
+ model?: string | undefined;
1407
1423
  area?: string | undefined;
1408
1424
  approval?: "required" | "granted" | "denied" | undefined;
1409
1425
  workResult?: {
@@ -1456,6 +1472,7 @@ declare const sessionPageSchema: z.ZodObject<{
1456
1472
  id: z.ZodType<`task_${string}`, string, z.core.$ZodTypeInternals<`task_${string}`, string>>;
1457
1473
  repositoryIds: z.ZodReadonly<z.ZodTuple<[z.ZodType<`repo_${string}`, string, z.core.$ZodTypeInternals<`repo_${string}`, string>>], z.ZodType<`repo_${string}`, string, z.core.$ZodTypeInternals<`repo_${string}`, string>>>>;
1458
1474
  kind: z.ZodType<TaskKind, unknown, z.core.$ZodTypeInternals<TaskKind, unknown>>;
1475
+ model: z.ZodOptional<z.ZodString>;
1459
1476
  area: z.ZodOptional<z.ZodString>;
1460
1477
  state: z.ZodEnum<{
1461
1478
  queued: "queued";
@@ -1572,6 +1589,7 @@ declare const sessionPageSchema: z.ZodObject<{
1572
1589
  attempt: number;
1573
1590
  createdAt: string;
1574
1591
  updatedAt: string;
1592
+ model?: string | undefined;
1575
1593
  area?: string | undefined;
1576
1594
  approval?: "required" | "granted" | "denied" | undefined;
1577
1595
  workResult?: {
@@ -1649,6 +1667,7 @@ declare const sessionPageSchema: z.ZodObject<{
1649
1667
  attempt: number;
1650
1668
  createdAt: string;
1651
1669
  updatedAt: string;
1670
+ model?: string | undefined;
1652
1671
  area?: string | undefined;
1653
1672
  approval?: "required" | "granted" | "denied" | undefined;
1654
1673
  workResult?: {
@@ -1697,7 +1716,7 @@ declare const sessionPageSchema: z.ZodObject<{
1697
1716
  reportRef?: string | undefined;
1698
1717
  }>>>;
1699
1718
  }, z.core.$strict>>>;
1700
- nextCursor: z.ZodDefault<z.ZodNullable<z.ZodType<`ses_${string}`, string, z.core.$ZodTypeInternals<`ses_${string}`, string>>>>;
1719
+ nextCursor: z.ZodDefault<z.ZodNullable<z.ZodString>>;
1701
1720
  totalCount: z.ZodOptional<z.ZodNumber>;
1702
1721
  }, z.core.$strict>;
1703
1722
  /** Cursor-paginated operator-facing sessions. */