@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
@@ -19,6 +19,7 @@ const apiAgentSchema = z.strictObject({
19
19
  const createSessionRequestSchema = z.strictObject({
20
20
  operationId: z.string().trim().min(1).max(200).describe("Client-generated idempotency key; retain when retrying creation."),
21
21
  agent: apiAgentSchema.shape.id,
22
+ model: z.string().trim().min(1).max(200).optional().describe("Explicit supported model; absent uses the repository or application default."),
22
23
  repository: repositorySlugSchema.optional().describe("Configured owner/repo slug."),
23
24
  changeId: changeIdSchema.optional().describe("Existing change to review, verify, or continue."),
24
25
  message: agentSessionMessageSchema,
@@ -90,7 +91,7 @@ const sessionViewSchema = z.strictObject({
90
91
  /** Validates a cursor-paginated page of operator-facing sessions. */
91
92
  const sessionPageSchema = z.strictObject({
92
93
  sessions: z.array(sessionViewSchema).readonly().describe("Sessions in this page."),
93
- nextCursor: agentSessionIdSchema.nullable().default(null).describe("Session ID that starts the next page, or null after the final page."),
94
+ nextCursor: z.string().min(1).nullable().default(null).describe("Opaque cursor to pass unchanged for the next page, or null after the final page."),
94
95
  totalCount: z.number().int().nonnegative().optional().describe("Total matching sessions when explicitly requested.")
95
96
  });
96
97
  /** Validates one configured repository exposed by the operator API. */
@@ -1 +1 @@
1
- {"version":3,"file":"api-contracts.mjs","names":[],"sources":["../src/api-contracts.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { hasMessageContent, imageAttachmentsSchema, type ImageAttachment } from \"./image-input\";\nimport {\n changeIdSchema,\n repositoryIdSchema,\n taskIdSchema,\n type ChangeId,\n type TaskId,\n} from \"./schema/id\";\nimport { repositorySlugSchema, type RepositorySlug } from \"./schema/repository\";\nimport {\n agentRouteBindingSchema,\n type AgentRouteBinding,\n type AgentRouteBindingInput,\n} from \"./schema/agent-route\";\nimport {\n agentSessionIdSchema,\n agentSessionMessageSchema,\n agentSessionSchema,\n} from \"./schema/session\";\nimport { taskSchema, executionReferenceSchema } from \"./schema/task\";\n\n/** Validates an agent exposed by the operator API. */\nexport const apiAgentSchema = z.strictObject({\n id: z\n .string()\n .regex(/^[a-z][a-z0-9-]{0,31}$/u)\n .describe(\"Agent name used for discovery, filtering, and session creation when allowed.\"),\n description: z.string().describe(\"What this agent does.\"),\n route: agentRouteBindingSchema\n .optional()\n .describe(\"Exact registered specialist route; absent for conversations.\"),\n canStart: z\n .boolean()\n .describe(\"Whether an operator may start this agent directly through the session API.\"),\n requiresRepository: z.boolean().describe(\"Whether creation requires a configured repository.\"),\n requiresChange: z.boolean().describe(\"Whether creation requires an existing change ID.\"),\n});\n/** An agent exposed for operator discovery, filtering, or session creation. */\nexport type ApiAgent = Readonly<\n Omit<z.infer<typeof apiAgentSchema>, \"route\"> & {\n readonly route?: AgentRouteBinding;\n }\n>;\n\n/** Caller input for exposing an agent through the operator API before validation and branding. */\nexport interface ApiAgentInput {\n readonly id: string;\n readonly description: string;\n readonly route?: AgentRouteBindingInput;\n readonly canStart: boolean;\n readonly requiresRepository: boolean;\n readonly requiresChange: boolean;\n}\n\n/** Validates an idempotent request to create an operator session. */\nexport const createSessionRequestSchema = z\n .strictObject({\n operationId: z\n .string()\n .trim()\n .min(1)\n .max(200)\n .describe(\"Client-generated idempotency key; retain when retrying creation.\"),\n agent: apiAgentSchema.shape.id,\n repository: repositorySlugSchema.optional().describe(\"Configured owner/repo slug.\"),\n changeId: changeIdSchema.optional().describe(\"Existing change to review, verify, or continue.\"),\n message: agentSessionMessageSchema,\n attachments: imageAttachmentsSchema\n .optional()\n .describe(\"Uploaded images accompanying this message.\"),\n })\n .refine(hasMessageContent, \"Enter a message or attach an image\");\n/** An idempotent request to create an operator session. */\nexport type CreateSessionRequest = Readonly<\n Omit<z.input<typeof createSessionRequestSchema>, \"attachments\" | \"repository\" | \"changeId\">\n> & {\n readonly repository?: RepositorySlug;\n readonly changeId?: ChangeId;\n readonly attachments?: readonly ImageAttachment[];\n};\n\n/** Validates a message sent to an existing session with optimistic execution matching. */\nexport const sendSessionMessageRequestSchema = z\n .strictObject({\n message: agentSessionMessageSchema,\n attachments: imageAttachmentsSchema\n .optional()\n .describe(\"Uploaded images accompanying this message.\"),\n operationId: z\n .string()\n .trim()\n .min(1)\n .max(200)\n .optional()\n .describe(\"Required for a follow-up after completion; retain unchanged for retries.\"),\n turnPolicy: z\n .enum([\"steer\", \"queue\"])\n .default(\"steer\")\n .describe(\"Whether an active turn is steered immediately or the message is queued.\"),\n executionId: z\n .string()\n .min(1)\n .nullable()\n .describe(\"Observed execution ID; stale writes are rejected.\"),\n })\n .refine(hasMessageContent, \"Enter a message or attach an image\");\n\n/** Message sent to an existing session with optimistic execution matching. */\nexport type SendSessionMessageRequest = Readonly<\n Omit<z.input<typeof sendSessionMessageRequestSchema>, \"attachments\">\n> & {\n readonly attachments?: readonly ImageAttachment[];\n};\n\nconst sessionInputResponseBase = {\n requestId: z.string().min(1).describe(\"Pending provider input request ID.\"),\n};\n\n/** Validates an option selection or freeform answer to one provider input request. */\nexport const sessionInputResponseSchema = z.union([\n z.strictObject({\n ...sessionInputResponseBase,\n optionId: z.string().min(1).describe(\"Selected option ID.\"),\n text: z.never().optional().describe(\"Absent for an option selection.\"),\n }),\n z.strictObject({\n ...sessionInputResponseBase,\n optionId: z.never().optional().describe(\"Absent for a freeform answer.\"),\n text: z.string().min(1).describe(\"Freeform answer text.\"),\n }),\n]);\n/** A response to one pending provider input request. */\nexport type SessionInputResponse = Readonly<z.input<typeof sessionInputResponseSchema>>;\n\n/** Validates responses submitted to pending provider input requests. */\nexport const submitSessionInputRequestSchema = z.strictObject({\n executionId: z\n .string()\n .min(1)\n .describe(\"Observed provider session ID; stale input responses are rejected.\"),\n inputResponses: z\n .array(sessionInputResponseSchema)\n .min(1)\n .readonly()\n .describe(\"Responses to pending human-input requests.\"),\n});\n/** Responses submitted to pending provider input requests. */\nexport interface SubmitSessionInputRequest {\n readonly executionId: string;\n readonly inputResponses: readonly SessionInputResponse[];\n}\n\n/** Validates an execution-aware session control request. */\nexport const controlSessionRequestSchema = z.strictObject({\n executionId: z\n .string()\n .min(1)\n .optional()\n .describe(\"Observed provider session ID; guards against a replaced execution.\"),\n turnId: z.string().min(1).optional().describe(\"Observed turn ID for cooperative interruption.\"),\n});\n/** Execution and turn observed before requesting a session control operation. */\nexport type ControlSessionRequest = Readonly<z.input<typeof controlSessionRequestSchema>>;\n\n/** Validates an authenticated answer to a workflow question. */\nexport const answerSessionQuestionRequestSchema = z.strictObject({\n questionId: taskIdSchema.describe(\"Question Task belonging to this operator session.\"),\n answer: z.string().min(1).max(16000).describe(\"Authenticated immutable human answer.\"),\n});\n/** Authenticated answer to a workflow question in a session. */\nexport interface AnswerSessionQuestionRequest {\n readonly questionId: TaskId;\n readonly answer: string;\n}\n\n/** Validates the current operator-facing projection of a Factory session. */\nexport const sessionViewSchema = z.strictObject({\n session: agentSessionSchema.describe(\"Durable interaction binding.\"),\n task: taskSchema.nullable().describe(\"Current task state, if workflow-backed.\"),\n dependencyTaskIds: z\n .array(taskIdSchema)\n .readonly()\n .optional()\n .describe(\"Read-only current Task dependencies projected from its authoritative task graph.\"),\n execution: executionReferenceSchema\n .nullable()\n .describe(\"Current execution; may change after recovery.\"),\n activeAgent: z\n .string()\n .optional()\n .describe(\"Agent executing the selected work in this conversation.\"),\n source: z.string().optional().describe(\"Original trigger of automatically started work.\"),\n prUrl: z.string().optional().describe(\"Pull request associated with the conversation.\"),\n attention: z\n .enum([\"question\", \"failed\", \"stalled\", \"review\", \"conversation\"])\n .optional()\n .describe(\n \"Current human action: answer a question, inspect failed or stalled work, review a ready Change, or continue an external conversation.\",\n ),\n canFollowUp: z\n .boolean()\n .optional()\n .describe(\"Whether completed work can start a budgeted discussion in this conversation.\"),\n hasWorkflowFailure: z\n .boolean()\n .optional()\n .describe(\"Whether delegated workflow work has failed, independently of a pending question.\"),\n canCancel: z\n .boolean()\n .optional()\n .describe(\n \"Whether cancellation is available for the conversation or its delegated workflow work.\",\n ),\n history: z\n .array(\n z.strictObject({\n taskId: taskIdSchema.optional().describe(\"Task owning this execution, if any.\"),\n agent: z.string().describe(\"Agent that owns the provider execution.\"),\n message: z.string().describe(\"Initial request displayed for this execution.\"),\n attachments: imageAttachmentsSchema\n .optional()\n .describe(\"Images accompanying this execution's initial user request.\"),\n execution: executionReferenceSchema.describe(\"Authorized provider execution to replay.\"),\n }),\n )\n .readonly()\n .optional()\n .describe(\"Earlier executions, in conversation order, including replacement attempts.\"),\n relatedTasks: z\n .array(taskSchema)\n .readonly()\n .optional()\n .describe(\"Work linked to this conversation, including specialist delegations.\"),\n question: taskSchema\n .nullable()\n .describe(\"Open Factory question requiring an authenticated answer.\"),\n});\n/** The current operator-facing projection of a Factory session. */\nexport type SessionView = z.infer<typeof sessionViewSchema>;\n\n/** Validates a cursor-paginated page of operator-facing sessions. */\nexport const sessionPageSchema = z.strictObject({\n sessions: z.array(sessionViewSchema).readonly().describe(\"Sessions in this page.\"),\n nextCursor: agentSessionIdSchema\n .nullable()\n .default(null)\n .describe(\"Session ID that starts the next page, or null after the final page.\"),\n totalCount: z\n .number()\n .int()\n .nonnegative()\n .optional()\n .describe(\"Total matching sessions when explicitly requested.\"),\n});\n/** Cursor-paginated operator-facing sessions. */\nexport type SessionPage = z.infer<typeof sessionPageSchema>;\n\n/** Validates one configured repository exposed by the operator API. */\nexport const repositorySummarySchema = z.strictObject({\n id: repositoryIdSchema.describe(\"Stable configured repository identifier.\"),\n slug: repositorySlugSchema.describe(\"Configured owner/repository slug.\"),\n});\n/** Configured repository exposed by the operator API. */\nexport type RepositorySummary = z.infer<typeof repositorySummarySchema>;\n\n/** Validates repository slugs and their stable identifiers. */\nexport const repositoryDetailsSchema = z.strictObject({\n repositories: z\n .array(repositorySlugSchema)\n .readonly()\n .describe(\"Configured owner/repository slugs.\"),\n details: z\n .array(repositorySummarySchema)\n .readonly()\n .default([])\n .describe(\"Stable identifiers and slugs for configured repositories.\"),\n});\n/** Repository slugs and their stable configured identifiers. */\nexport type RepositoryDetails = z.infer<typeof repositoryDetailsSchema>;\n\n/** Validates a locally retained follow-up awaiting confirmed API acceptance. */\nexport const pendingFollowUpSchema = z\n .strictObject({\n sessionId: agentSessionIdSchema.describe(\"Conversation containing this follow-up.\"),\n operationId: z\n .string()\n .min(1)\n .max(200)\n .describe(\"Stable operation id retained until acceptance is confirmed.\"),\n executionId: z\n .string()\n .nullable()\n .describe(\"Execution observed before sending, or null before execution starts.\"),\n message: agentSessionMessageSchema,\n attachments: imageAttachmentsSchema\n .optional()\n .describe(\"Uploaded images accompanying this message.\"),\n turnPolicy: z.enum([\"steer\", \"queue\"]).describe(\"Requested turn policy.\"),\n })\n .refine(hasMessageContent, \"Enter a message or attach an image\");\n/** A locally retained follow-up awaiting confirmed API acceptance. */\nexport type PendingFollowUp = Readonly<\n Omit<z.output<typeof pendingFollowUpSchema>, \"attachments\">\n> & {\n readonly attachments?: readonly ImageAttachment[];\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"api-contracts.mjs","names":[],"sources":["../src/api-contracts.ts"],"sourcesContent":["import { z } from \"zod\";\nimport { hasMessageContent, imageAttachmentsSchema, type ImageAttachment } from \"./image-input\";\nimport {\n changeIdSchema,\n repositoryIdSchema,\n taskIdSchema,\n type ChangeId,\n type TaskId,\n} from \"./schema/id\";\nimport { repositorySlugSchema, type RepositorySlug } from \"./schema/repository\";\nimport {\n agentRouteBindingSchema,\n type AgentRouteBinding,\n type AgentRouteBindingInput,\n} from \"./schema/agent-route\";\nimport {\n agentSessionIdSchema,\n agentSessionMessageSchema,\n agentSessionSchema,\n} from \"./schema/session\";\nimport { taskSchema, executionReferenceSchema } from \"./schema/task\";\n\n/** Validates an agent exposed by the operator API. */\nexport const apiAgentSchema = z.strictObject({\n id: z\n .string()\n .regex(/^[a-z][a-z0-9-]{0,31}$/u)\n .describe(\"Agent name used for discovery, filtering, and session creation when allowed.\"),\n description: z.string().describe(\"What this agent does.\"),\n route: agentRouteBindingSchema\n .optional()\n .describe(\"Exact registered specialist route; absent for conversations.\"),\n canStart: z\n .boolean()\n .describe(\"Whether an operator may start this agent directly through the session API.\"),\n requiresRepository: z.boolean().describe(\"Whether creation requires a configured repository.\"),\n requiresChange: z.boolean().describe(\"Whether creation requires an existing change ID.\"),\n});\n/** An agent exposed for operator discovery, filtering, or session creation. */\nexport type ApiAgent = Readonly<\n Omit<z.infer<typeof apiAgentSchema>, \"route\"> & {\n readonly route?: AgentRouteBinding;\n }\n>;\n\n/** Caller input for exposing an agent through the operator API before validation and branding. */\nexport interface ApiAgentInput {\n readonly id: string;\n readonly description: string;\n readonly route?: AgentRouteBindingInput;\n readonly canStart: boolean;\n readonly requiresRepository: boolean;\n readonly requiresChange: boolean;\n}\n\n/** Validates an idempotent request to create an operator session. */\nexport const createSessionRequestSchema = z\n .strictObject({\n operationId: z\n .string()\n .trim()\n .min(1)\n .max(200)\n .describe(\"Client-generated idempotency key; retain when retrying creation.\"),\n agent: apiAgentSchema.shape.id,\n model: z\n .string()\n .trim()\n .min(1)\n .max(200)\n .optional()\n .describe(\"Explicit supported model; absent uses the repository or application default.\"),\n repository: repositorySlugSchema.optional().describe(\"Configured owner/repo slug.\"),\n changeId: changeIdSchema.optional().describe(\"Existing change to review, verify, or continue.\"),\n message: agentSessionMessageSchema,\n attachments: imageAttachmentsSchema\n .optional()\n .describe(\"Uploaded images accompanying this message.\"),\n })\n .refine(hasMessageContent, \"Enter a message or attach an image\");\n/** An idempotent request to create an operator session. */\nexport type CreateSessionRequest = Readonly<\n Omit<z.input<typeof createSessionRequestSchema>, \"attachments\" | \"repository\" | \"changeId\">\n> & {\n readonly repository?: RepositorySlug;\n readonly changeId?: ChangeId;\n readonly attachments?: readonly ImageAttachment[];\n};\n\n/** Validates a message sent to an existing session with optimistic execution matching. */\nexport const sendSessionMessageRequestSchema = z\n .strictObject({\n message: agentSessionMessageSchema,\n attachments: imageAttachmentsSchema\n .optional()\n .describe(\"Uploaded images accompanying this message.\"),\n operationId: z\n .string()\n .trim()\n .min(1)\n .max(200)\n .optional()\n .describe(\"Required for a follow-up after completion; retain unchanged for retries.\"),\n turnPolicy: z\n .enum([\"steer\", \"queue\"])\n .default(\"steer\")\n .describe(\"Whether an active turn is steered immediately or the message is queued.\"),\n executionId: z\n .string()\n .min(1)\n .nullable()\n .describe(\"Observed execution ID; stale writes are rejected.\"),\n })\n .refine(hasMessageContent, \"Enter a message or attach an image\");\n\n/** Message sent to an existing session with optimistic execution matching. */\nexport type SendSessionMessageRequest = Readonly<\n Omit<z.input<typeof sendSessionMessageRequestSchema>, \"attachments\">\n> & {\n readonly attachments?: readonly ImageAttachment[];\n};\n\nconst sessionInputResponseBase = {\n requestId: z.string().min(1).describe(\"Pending provider input request ID.\"),\n};\n\n/** Validates an option selection or freeform answer to one provider input request. */\nexport const sessionInputResponseSchema = z.union([\n z.strictObject({\n ...sessionInputResponseBase,\n optionId: z.string().min(1).describe(\"Selected option ID.\"),\n text: z.never().optional().describe(\"Absent for an option selection.\"),\n }),\n z.strictObject({\n ...sessionInputResponseBase,\n optionId: z.never().optional().describe(\"Absent for a freeform answer.\"),\n text: z.string().min(1).describe(\"Freeform answer text.\"),\n }),\n]);\n/** A response to one pending provider input request. */\nexport type SessionInputResponse = Readonly<z.input<typeof sessionInputResponseSchema>>;\n\n/** Validates responses submitted to pending provider input requests. */\nexport const submitSessionInputRequestSchema = z.strictObject({\n executionId: z\n .string()\n .min(1)\n .describe(\"Observed provider session ID; stale input responses are rejected.\"),\n inputResponses: z\n .array(sessionInputResponseSchema)\n .min(1)\n .readonly()\n .describe(\"Responses to pending human-input requests.\"),\n});\n/** Responses submitted to pending provider input requests. */\nexport interface SubmitSessionInputRequest {\n readonly executionId: string;\n readonly inputResponses: readonly SessionInputResponse[];\n}\n\n/** Validates an execution-aware session control request. */\nexport const controlSessionRequestSchema = z.strictObject({\n executionId: z\n .string()\n .min(1)\n .optional()\n .describe(\"Observed provider session ID; guards against a replaced execution.\"),\n turnId: z.string().min(1).optional().describe(\"Observed turn ID for cooperative interruption.\"),\n});\n/** Execution and turn observed before requesting a session control operation. */\nexport type ControlSessionRequest = Readonly<z.input<typeof controlSessionRequestSchema>>;\n\n/** Validates an authenticated answer to a workflow question. */\nexport const answerSessionQuestionRequestSchema = z.strictObject({\n questionId: taskIdSchema.describe(\"Question Task belonging to this operator session.\"),\n answer: z.string().min(1).max(16000).describe(\"Authenticated immutable human answer.\"),\n});\n/** Authenticated answer to a workflow question in a session. */\nexport interface AnswerSessionQuestionRequest {\n readonly questionId: TaskId;\n readonly answer: string;\n}\n\n/** Validates the current operator-facing projection of a Factory session. */\nexport const sessionViewSchema = z.strictObject({\n session: agentSessionSchema.describe(\"Durable interaction binding.\"),\n task: taskSchema.nullable().describe(\"Current task state, if workflow-backed.\"),\n dependencyTaskIds: z\n .array(taskIdSchema)\n .readonly()\n .optional()\n .describe(\"Read-only current Task dependencies projected from its authoritative task graph.\"),\n execution: executionReferenceSchema\n .nullable()\n .describe(\"Current execution; may change after recovery.\"),\n activeAgent: z\n .string()\n .optional()\n .describe(\"Agent executing the selected work in this conversation.\"),\n source: z.string().optional().describe(\"Original trigger of automatically started work.\"),\n prUrl: z.string().optional().describe(\"Pull request associated with the conversation.\"),\n attention: z\n .enum([\"question\", \"failed\", \"stalled\", \"review\", \"conversation\"])\n .optional()\n .describe(\n \"Current human action: answer a question, inspect failed or stalled work, review a ready Change, or continue an external conversation.\",\n ),\n canFollowUp: z\n .boolean()\n .optional()\n .describe(\"Whether completed work can start a budgeted discussion in this conversation.\"),\n hasWorkflowFailure: z\n .boolean()\n .optional()\n .describe(\"Whether delegated workflow work has failed, independently of a pending question.\"),\n canCancel: z\n .boolean()\n .optional()\n .describe(\n \"Whether cancellation is available for the conversation or its delegated workflow work.\",\n ),\n history: z\n .array(\n z.strictObject({\n taskId: taskIdSchema.optional().describe(\"Task owning this execution, if any.\"),\n agent: z.string().describe(\"Agent that owns the provider execution.\"),\n message: z.string().describe(\"Initial request displayed for this execution.\"),\n attachments: imageAttachmentsSchema\n .optional()\n .describe(\"Images accompanying this execution's initial user request.\"),\n execution: executionReferenceSchema.describe(\"Authorized provider execution to replay.\"),\n }),\n )\n .readonly()\n .optional()\n .describe(\"Earlier executions, in conversation order, including replacement attempts.\"),\n relatedTasks: z\n .array(taskSchema)\n .readonly()\n .optional()\n .describe(\"Work linked to this conversation, including specialist delegations.\"),\n question: taskSchema\n .nullable()\n .describe(\"Open Factory question requiring an authenticated answer.\"),\n});\n/** The current operator-facing projection of a Factory session. */\nexport type SessionView = z.infer<typeof sessionViewSchema>;\n\n/** Validates a cursor-paginated page of operator-facing sessions. */\nexport const sessionPageSchema = z.strictObject({\n sessions: z.array(sessionViewSchema).readonly().describe(\"Sessions in this page.\"),\n nextCursor: z\n .string()\n .min(1)\n .nullable()\n .default(null)\n .describe(\"Opaque cursor to pass unchanged for the next page, or null after the final page.\"),\n totalCount: z\n .number()\n .int()\n .nonnegative()\n .optional()\n .describe(\"Total matching sessions when explicitly requested.\"),\n});\n/** Cursor-paginated operator-facing sessions. */\nexport type SessionPage = z.infer<typeof sessionPageSchema>;\n\n/** Validates one configured repository exposed by the operator API. */\nexport const repositorySummarySchema = z.strictObject({\n id: repositoryIdSchema.describe(\"Stable configured repository identifier.\"),\n slug: repositorySlugSchema.describe(\"Configured owner/repository slug.\"),\n});\n/** Configured repository exposed by the operator API. */\nexport type RepositorySummary = z.infer<typeof repositorySummarySchema>;\n\n/** Validates repository slugs and their stable identifiers. */\nexport const repositoryDetailsSchema = z.strictObject({\n repositories: z\n .array(repositorySlugSchema)\n .readonly()\n .describe(\"Configured owner/repository slugs.\"),\n details: z\n .array(repositorySummarySchema)\n .readonly()\n .default([])\n .describe(\"Stable identifiers and slugs for configured repositories.\"),\n});\n/** Repository slugs and their stable configured identifiers. */\nexport type RepositoryDetails = z.infer<typeof repositoryDetailsSchema>;\n\n/** Validates a locally retained follow-up awaiting confirmed API acceptance. */\nexport const pendingFollowUpSchema = z\n .strictObject({\n sessionId: agentSessionIdSchema.describe(\"Conversation containing this follow-up.\"),\n operationId: z\n .string()\n .min(1)\n .max(200)\n .describe(\"Stable operation id retained until acceptance is confirmed.\"),\n executionId: z\n .string()\n .nullable()\n .describe(\"Execution observed before sending, or null before execution starts.\"),\n message: agentSessionMessageSchema,\n attachments: imageAttachmentsSchema\n .optional()\n .describe(\"Uploaded images accompanying this message.\"),\n turnPolicy: z.enum([\"steer\", \"queue\"]).describe(\"Requested turn policy.\"),\n })\n .refine(hasMessageContent, \"Enter a message or attach an image\");\n/** A locally retained follow-up awaiting confirmed API acceptance. */\nexport type PendingFollowUp = Readonly<\n Omit<z.output<typeof pendingFollowUpSchema>, \"attachments\">\n> & {\n readonly attachments?: readonly ImageAttachment[];\n};\n"],"mappings":";;;;;;;;;AAuBA,MAAa,iBAAiB,EAAE,aAAa;CAC3C,IAAI,EACD,OAAO,CAAC,CACR,MAAM,yBAAyB,CAAC,CAChC,SAAS,8EAA8E;CAC1F,aAAa,EAAE,OAAO,CAAC,CAAC,SAAS,uBAAuB;CACxD,OAAO,wBACJ,SAAS,CAAC,CACV,SAAS,8DAA8D;CAC1E,UAAU,EACP,QAAQ,CAAC,CACT,SAAS,4EAA4E;CACxF,oBAAoB,EAAE,QAAQ,CAAC,CAAC,SAAS,oDAAoD;CAC7F,gBAAgB,EAAE,QAAQ,CAAC,CAAC,SAAS,kDAAkD;AACzF,CAAC;;AAmBD,MAAa,6BAA6B,EACvC,aAAa;CACZ,aAAa,EACV,OAAO,CAAC,CACR,KAAK,CAAC,CACN,IAAI,CAAC,CAAC,CACN,IAAI,GAAG,CAAC,CACR,SAAS,kEAAkE;CAC9E,OAAO,eAAe,MAAM;CAC5B,OAAO,EACJ,OAAO,CAAC,CACR,KAAK,CAAC,CACN,IAAI,CAAC,CAAC,CACN,IAAI,GAAG,CAAC,CACR,SAAS,CAAC,CACV,SAAS,8EAA8E;CAC1F,YAAY,qBAAqB,SAAS,CAAC,CAAC,SAAS,6BAA6B;CAClF,UAAU,eAAe,SAAS,CAAC,CAAC,SAAS,iDAAiD;CAC9F,SAAS;CACT,aAAa,uBACV,SAAS,CAAC,CACV,SAAS,4CAA4C;AAC1D,CAAC,CAAC,CACD,OAAO,mBAAmB,oCAAoC;;AAWjE,MAAa,kCAAkC,EAC5C,aAAa;CACZ,SAAS;CACT,aAAa,uBACV,SAAS,CAAC,CACV,SAAS,4CAA4C;CACxD,aAAa,EACV,OAAO,CAAC,CACR,KAAK,CAAC,CACN,IAAI,CAAC,CAAC,CACN,IAAI,GAAG,CAAC,CACR,SAAS,CAAC,CACV,SAAS,0EAA0E;CACtF,YAAY,EACT,KAAK,CAAC,SAAS,OAAO,CAAC,CAAC,CACxB,QAAQ,OAAO,CAAC,CAChB,SAAS,yEAAyE;CACrF,aAAa,EACV,OAAO,CAAC,CACR,IAAI,CAAC,CAAC,CACN,SAAS,CAAC,CACV,SAAS,mDAAmD;AACjE,CAAC,CAAC,CACD,OAAO,mBAAmB,oCAAoC;AASjE,MAAM,2BAA2B,EAC/B,WAAW,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,oCAAoC,EAC5E;;AAGA,MAAa,6BAA6B,EAAE,MAAM,CAChD,EAAE,aAAa;CACb,GAAG;CACH,UAAU,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,qBAAqB;CAC1D,MAAM,EAAE,MAAM,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,iCAAiC;AACvE,CAAC,GACD,EAAE,aAAa;CACb,GAAG;CACH,UAAU,EAAE,MAAM,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,+BAA+B;CACvE,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,uBAAuB;AAC1D,CAAC,CACH,CAAC;;AAKD,MAAa,kCAAkC,EAAE,aAAa;CAC5D,aAAa,EACV,OAAO,CAAC,CACR,IAAI,CAAC,CAAC,CACN,SAAS,mEAAmE;CAC/E,gBAAgB,EACb,MAAM,0BAA0B,CAAC,CACjC,IAAI,CAAC,CAAC,CACN,SAAS,CAAC,CACV,SAAS,4CAA4C;AAC1D,CAAC;;AAQD,MAAa,8BAA8B,EAAE,aAAa;CACxD,aAAa,EACV,OAAO,CAAC,CACR,IAAI,CAAC,CAAC,CACN,SAAS,CAAC,CACV,SAAS,oEAAoE;CAChF,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,gDAAgD;AAChG,CAAC;;AAKD,MAAa,qCAAqC,EAAE,aAAa;CAC/D,YAAY,aAAa,SAAS,mDAAmD;CACrF,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAK,CAAC,CAAC,SAAS,uCAAuC;AACvF,CAAC;;AAQD,MAAa,oBAAoB,EAAE,aAAa;CAC9C,SAAS,mBAAmB,SAAS,8BAA8B;CACnE,MAAM,WAAW,SAAS,CAAC,CAAC,SAAS,yCAAyC;CAC9E,mBAAmB,EAChB,MAAM,YAAY,CAAC,CACnB,SAAS,CAAC,CACV,SAAS,CAAC,CACV,SAAS,kFAAkF;CAC9F,WAAW,yBACR,SAAS,CAAC,CACV,SAAS,+CAA+C;CAC3D,aAAa,EACV,OAAO,CAAC,CACR,SAAS,CAAC,CACV,SAAS,yDAAyD;CACrE,QAAQ,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,iDAAiD;CACxF,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,gDAAgD;CACtF,WAAW,EACR,KAAK;EAAC;EAAY;EAAU;EAAW;EAAU;CAAc,CAAC,CAAC,CACjE,SAAS,CAAC,CACV,SACC,uIACF;CACF,aAAa,EACV,QAAQ,CAAC,CACT,SAAS,CAAC,CACV,SAAS,8EAA8E;CAC1F,oBAAoB,EACjB,QAAQ,CAAC,CACT,SAAS,CAAC,CACV,SAAS,kFAAkF;CAC9F,WAAW,EACR,QAAQ,CAAC,CACT,SAAS,CAAC,CACV,SACC,wFACF;CACF,SAAS,EACN,MACC,EAAE,aAAa;EACb,QAAQ,aAAa,SAAS,CAAC,CAAC,SAAS,qCAAqC;EAC9E,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS,yCAAyC;EACpE,SAAS,EAAE,OAAO,CAAC,CAAC,SAAS,+CAA+C;EAC5E,aAAa,uBACV,SAAS,CAAC,CACV,SAAS,4DAA4D;EACxE,WAAW,yBAAyB,SAAS,0CAA0C;CACzF,CAAC,CACH,CAAC,CACA,SAAS,CAAC,CACV,SAAS,CAAC,CACV,SAAS,4EAA4E;CACxF,cAAc,EACX,MAAM,UAAU,CAAC,CACjB,SAAS,CAAC,CACV,SAAS,CAAC,CACV,SAAS,qEAAqE;CACjF,UAAU,WACP,SAAS,CAAC,CACV,SAAS,0DAA0D;AACxE,CAAC;;AAKD,MAAa,oBAAoB,EAAE,aAAa;CAC9C,UAAU,EAAE,MAAM,iBAAiB,CAAC,CAAC,SAAS,CAAC,CAAC,SAAS,wBAAwB;CACjF,YAAY,EACT,OAAO,CAAC,CACR,IAAI,CAAC,CAAC,CACN,SAAS,CAAC,CACV,QAAQ,IAAI,CAAC,CACb,SAAS,kFAAkF;CAC9F,YAAY,EACT,OAAO,CAAC,CACR,IAAI,CAAC,CACL,YAAY,CAAC,CACb,SAAS,CAAC,CACV,SAAS,oDAAoD;AAClE,CAAC;;AAKD,MAAa,0BAA0B,EAAE,aAAa;CACpD,IAAI,mBAAmB,SAAS,0CAA0C;CAC1E,MAAM,qBAAqB,SAAS,mCAAmC;AACzE,CAAC;;AAKD,MAAa,0BAA0B,EAAE,aAAa;CACpD,cAAc,EACX,MAAM,oBAAoB,CAAC,CAC3B,SAAS,CAAC,CACV,SAAS,oCAAoC;CAChD,SAAS,EACN,MAAM,uBAAuB,CAAC,CAC9B,SAAS,CAAC,CACV,QAAQ,CAAC,CAAC,CAAC,CACX,SAAS,2DAA2D;AACzE,CAAC;;AAKD,MAAa,wBAAwB,EAClC,aAAa;CACZ,WAAW,qBAAqB,SAAS,yCAAyC;CAClF,aAAa,EACV,OAAO,CAAC,CACR,IAAI,CAAC,CAAC,CACN,IAAI,GAAG,CAAC,CACR,SAAS,6DAA6D;CACzE,aAAa,EACV,OAAO,CAAC,CACR,SAAS,CAAC,CACV,SAAS,qEAAqE;CACjF,SAAS;CACT,aAAa,uBACV,SAAS,CAAC,CACV,SAAS,4CAA4C;CACxD,YAAY,EAAE,KAAK,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,SAAS,wBAAwB;AAC1E,CAAC,CAAC,CACD,OAAO,mBAAmB,oCAAoC"}
package/dist/api.d.mts CHANGED
@@ -1,3 +1,9 @@
1
+ import { InboxActivity, InboxActivityItem, inboxActivityItemSchema, inboxActivitySchema } from "./inbox-activity.mjs";
2
+ import { AgentModelConfig, ModelSettings, UpdateModelDefaults, agentModelConfigSchema, modelSettingsSchema, updateModelDefaultsSchema } from "./model-settings.mjs";
3
+ import { SessionPreview, SessionPreviews, sessionPreviewSchema, sessionPreviewsSchema } from "./session-previews.mjs";
4
+ import { SessionReview, SessionReviewCheck, SessionReviewIssue, SessionReviewScreenshot, sessionReviewCheckSchema, sessionReviewIssueSchema, sessionReviewSchema, sessionReviewScreenshotSchema } from "./session-review.mjs";
5
+ import { WorkspaceGitFilesOptions, workspaceGitFilesScript } from "./workspace-files-git.mjs";
6
+ import { WorkspaceFileEntry, WorkspaceFiles, WorkspaceFilesRequest, workspaceFileContentSchema, workspaceFileEntrySchema, workspaceFilesRequestSchema, workspaceFilesSchema, workspacePathSchema } from "./workspace-files.mjs";
1
7
  import { Signal } from "./schema/signal.mjs";
2
8
  import { Repository, RepositorySlug } from "./schema/repository.mjs";
3
9
  import { ExecutionReference, Task } from "./schema/task.mjs";
@@ -28,14 +34,43 @@ interface FactoryApiForwardInput {
28
34
  readonly path: string;
29
35
  readonly request: Request;
30
36
  }
37
+ /** Identifies a file inspection in an authorized execution workspace. */
38
+ interface FactoryApiWorkspaceFilesInput extends FactoryApiWorkspaceDiffInput {
39
+ readonly query: WorkspaceFilesRequest;
40
+ }
31
41
  /** Identifies the active execution whose workspace diff the Factory API requests. */
32
42
  interface FactoryApiWorkspaceDiffInput {
33
43
  readonly session: AgentSession;
34
44
  readonly execution: ExecutionReference;
35
45
  readonly signal: AbortSignal;
36
46
  }
47
+ /** Authorized session projection supplied to an application-owned review reader. */
48
+ interface FactoryApiSessionReviewInput {
49
+ readonly current: SessionView;
50
+ /** Authorized current Task snapshots, including delegated workflow-owned work. */
51
+ readonly tasks: readonly Task[];
52
+ readonly signal: AbortSignal;
53
+ }
54
+ /** Authorized work supplied to an application-owned preview reader. */
55
+ interface FactoryApiSessionPreviewsInput {
56
+ readonly current: SessionView;
57
+ readonly tasks: readonly Task[];
58
+ readonly signal: AbortSignal;
59
+ }
60
+ /** Authenticated caller and repository scope supplied to an external activity reader. */
61
+ interface FactoryApiInboxActivityInput {
62
+ /** Authenticated caller; the reader must apply its application's source access policy. */
63
+ readonly principal: ApiPrincipal;
64
+ /** Configured repositories that have not been disabled. Never expand this scope. */
65
+ readonly repositories: readonly RepositorySlug[];
66
+ readonly signal: AbortSignal;
67
+ }
37
68
  /** Application-owned dependencies and authorization policies for the Factory HTTP API. */
38
69
  interface FactoryApiOptions {
70
+ /** Optional application-owned catalogs; the start adapter must honor each persisted model choice. */
71
+ agentModels?: readonly AgentModelConfig[];
72
+ /** Explicit policy for saving shared repository defaults; absent denies mutations. */
73
+ canEditModelDefaults?(principal: ApiPrincipal, repository: Repository): Promise<boolean>;
39
74
  stores: FactoryStores;
40
75
  agents: readonly ApiAgentInput[];
41
76
  routes: AgentRouteRegistry;
@@ -50,7 +85,7 @@ interface FactoryApiOptions {
50
85
  }, task: Task): Promise<boolean>;
51
86
  /** Exposed conversational agent used for separately budgeted follow-up investigations. */
52
87
  continuationAgent?: string;
53
- /** Application-owned launcher for an explicitly retried, stopped intake attempt. */
88
+ /** Application-owned validation and launch for a stopped intake attempt, including supported Signal sources. */
54
89
  retryIntake?(signal: Signal, options: {
55
90
  expectedAttempt: number;
56
91
  by: string;
@@ -59,7 +94,15 @@ interface FactoryApiOptions {
59
94
  ensureRepository(slug: RepositorySlug): Promise<Repository>;
60
95
  start(input: FactoryApiStartInput): Promise<ExecutionReference>;
61
96
  forward(input: FactoryApiForwardInput): Promise<Response>;
97
+ /** Optional evidence reader; owns source selection, image access, and response limits. */
98
+ sessionReview?(input: FactoryApiSessionReviewInput): Promise<SessionReview>;
99
+ /** Optional read-only preview reader; owns source selection, link access, expiry, and limits. */
100
+ sessionPreviews?(input: FactoryApiSessionPreviewsInput): Promise<SessionPreviews>;
101
+ /** Optional external attention reader; owns caller authorization, sources, caching, and limits. */
102
+ inboxActivity?(input: FactoryApiInboxActivityInput): Promise<InboxActivity>;
62
103
  workspaceDiff?(input: FactoryApiWorkspaceDiffInput): Promise<WorkspaceDiff>;
104
+ /** Optional provider-neutral reader for files and directories at immutable revisions. */
105
+ workspaceFiles?(input: FactoryApiWorkspaceFilesInput): Promise<WorkspaceFiles>;
63
106
  reportError?(error: unknown, context: {
64
107
  method: string;
65
108
  path: string;
@@ -73,5 +116,5 @@ interface FactoryApi {
73
116
  /** Creates the authenticated request handler for the public Factory HTTP API. */
74
117
  declare function createFactoryApi(options: FactoryApiOptions): FactoryApi;
75
118
  //#endregion
76
- export { type AgentSession, type AgentSessionId, type AnswerSessionQuestionRequest, type ApiAgent, type ApiAgentInput, type ApiPrincipal, type ControlSessionRequest, type CreateSessionRequest, type DirectAgentSession, type ExternalAgentSession, FactoryApi, FactoryApiForwardInput, FactoryApiOptions, FactoryApiStartInput, FactoryApiWorkspaceDiffInput, ImageInputError, type RepositoryDetails, type RepositorySummary, type ResolveImageMessageInput, type ResolveImageMessageStores, type ResolvedImageMessage, type ResolvedImageMessagePart, type SendSessionMessageRequest, SessionConflictError, type SessionInputResponse, type SessionPage, type SessionView, type SubmitSessionInputRequest, type TaskAgentSession, type UnavailableWorkspaceDiffInput, type WorkspaceBaseBlob, type WorkspaceBaseEntry, type WorkspaceDiff, type WorkspaceDiffAssemblyOptions, type WorkspaceDiffFile, type WorkspaceSnapshot, agentSessionIdSchema, agentSessionSchema, answerSessionQuestionRequestSchema, apiAgentSchema, assembleWorkspaceDiff, controlSessionRequestSchema, createFactoryApi, createSessionRequestSchema, imagePartFilename, repositoryDetailsSchema, repositorySummarySchema, resolveImageMessage, sendSessionMessageRequestSchema, sessionInputResponseSchema, sessionPageSchema, sessionViewSchema, submitSessionInputRequestSchema, unavailableWorkspaceDiff, workspaceDiffFileSchema, workspaceDiffSchema, workspaceSnapshotSchema, workspaceSnapshotScript };
119
+ export { type AgentModelConfig, type AgentSession, type AgentSessionId, type AnswerSessionQuestionRequest, type ApiAgent, type ApiAgentInput, type ApiPrincipal, type ControlSessionRequest, type CreateSessionRequest, type DirectAgentSession, type ExternalAgentSession, FactoryApi, FactoryApiForwardInput, FactoryApiInboxActivityInput, FactoryApiOptions, FactoryApiSessionPreviewsInput, FactoryApiSessionReviewInput, FactoryApiStartInput, FactoryApiWorkspaceDiffInput, FactoryApiWorkspaceFilesInput, ImageInputError, type InboxActivity, type InboxActivityItem, type ModelSettings, type RepositoryDetails, type RepositorySummary, type ResolveImageMessageInput, type ResolveImageMessageStores, type ResolvedImageMessage, type ResolvedImageMessagePart, type SendSessionMessageRequest, SessionConflictError, type SessionInputResponse, type SessionPage, type SessionPreview, type SessionPreviews, type SessionReview, type SessionReviewCheck, type SessionReviewIssue, type SessionReviewScreenshot, type SessionView, type SubmitSessionInputRequest, type TaskAgentSession, type UnavailableWorkspaceDiffInput, type UpdateModelDefaults, type WorkspaceBaseBlob, type WorkspaceBaseEntry, type WorkspaceDiff, type WorkspaceDiffAssemblyOptions, type WorkspaceDiffFile, type WorkspaceFileEntry, type WorkspaceFiles, type WorkspaceFilesRequest, type WorkspaceGitFilesOptions, type WorkspaceSnapshot, agentModelConfigSchema, agentSessionIdSchema, agentSessionSchema, answerSessionQuestionRequestSchema, apiAgentSchema, assembleWorkspaceDiff, controlSessionRequestSchema, createFactoryApi, createSessionRequestSchema, imagePartFilename, inboxActivityItemSchema, inboxActivitySchema, modelSettingsSchema, repositoryDetailsSchema, repositorySummarySchema, resolveImageMessage, sendSessionMessageRequestSchema, sessionInputResponseSchema, sessionPageSchema, sessionPreviewSchema, sessionPreviewsSchema, sessionReviewCheckSchema, sessionReviewIssueSchema, sessionReviewSchema, sessionReviewScreenshotSchema, sessionViewSchema, submitSessionInputRequestSchema, unavailableWorkspaceDiff, updateModelDefaultsSchema, workspaceDiffFileSchema, workspaceDiffSchema, workspaceFileContentSchema, workspaceFileEntrySchema, workspaceFilesRequestSchema, workspaceFilesSchema, workspaceGitFilesScript, workspacePathSchema, workspaceSnapshotSchema, workspaceSnapshotScript };
77
120
  //# sourceMappingURL=api.d.mts.map
package/dist/api.mjs CHANGED
@@ -1,5 +1,6 @@
1
- import { signalIdSchema, taskIdSchema } from "./schema/id.mjs";
1
+ import { repositoryIdSchema, signalIdSchema, taskIdSchema } from "./schema/id.mjs";
2
2
  import { repositorySlugSchema } from "./schema/repository.mjs";
3
+ import { parseAgentRouteBinding } from "./schema/agent-route.mjs";
3
4
  import { taskStateSchema } from "./schema/task.mjs";
4
5
  import { InvalidTransitionError } from "./schema/transitions.mjs";
5
6
  import { readEvents } from "./client-events.mjs";
@@ -11,10 +12,16 @@ import { BudgetExhaustedError, DispatchRefusedError, dispatchTask } from "./disp
11
12
  import { ApprovalAccessError, createApprovalAccess } from "./approval-access.mjs";
12
13
  import { agentSessionIdSchema, agentSessionSchema } from "./schema/session.mjs";
13
14
  import { ApprovalError, ImageInputError, SessionConflictError } from "./store/engine.mjs";
15
+ import { inboxActivityItemSchema, inboxActivitySchema } from "./inbox-activity.mjs";
16
+ import { agentModelConfigSchema, modelSettingsSchema, updateModelDefaultsSchema } from "./model-settings.mjs";
17
+ import { sessionPreviewSchema, sessionPreviewsSchema } from "./session-previews.mjs";
18
+ import { sessionReviewCheckSchema, sessionReviewIssueSchema, sessionReviewSchema, sessionReviewScreenshotSchema } from "./session-review.mjs";
19
+ import { workspaceFileContentSchema, workspaceFileEntrySchema, workspaceFilesRequestSchema, workspaceFilesSchema, workspacePathSchema } from "./workspace-files.mjs";
14
20
  import { intakeViewSchema, retryIntakeSchema } from "./intake-contracts.mjs";
15
21
  import { answerSessionQuestionRequestSchema, apiAgentSchema, controlSessionRequestSchema, createSessionRequestSchema, repositoryDetailsSchema, repositorySummarySchema, sendSessionMessageRequestSchema, sessionInputResponseSchema, sessionPageSchema, sessionViewSchema, submitSessionInputRequestSchema } from "./api-contracts.mjs";
16
22
  import { unavailableWorkspaceDiff, workspaceDiffFileSchema, workspaceDiffSchema } from "./workspace-diff.mjs";
17
23
  import { taskGraphViewSchema } from "./task-graph-view.mjs";
24
+ import { workspaceGitFilesScript } from "./workspace-files-git.mjs";
18
25
  import { TaskGraphAccessError, readSessionGraph } from "./api-task-graph.mjs";
19
26
  import { resolveImageMessage } from "./api-images.mjs";
20
27
  import { workspaceSnapshotSchema, workspaceSnapshotScript } from "./workspace-snapshot.mjs";
@@ -41,6 +48,14 @@ function attention(task, change) {
41
48
  function createFactoryApi(options) {
42
49
  const { stores } = options;
43
50
  const agents = options.agents.map((agent) => apiAgentSchema.parse(agent));
51
+ const agentModels = (options.agentModels ?? []).map((entry) => agentModelConfigSchema.parse(entry));
52
+ if (new Set(agentModels.map((entry) => entry.agent)).size !== agentModels.length) throw new Error("Duplicate agent model catalog");
53
+ function selectModel(agent, requested, defaults) {
54
+ const catalog = agentModels.find((entry) => entry.agent === agent);
55
+ const model = requested ?? defaults?.[agent] ?? catalog?.defaultModel;
56
+ if (model !== void 0 && !catalog?.models.some((entry) => entry.id === model)) throw new ApiError(400, `Model is not available for ${agent}. Choose an advertised model.`);
57
+ return model;
58
+ }
44
59
  if (new Set(agents.map((agent) => agent.id)).size !== agents.length) throw new Error("Duplicate API agent");
45
60
  if (options.continuationAgent !== void 0 && !agents.some((agent) => agent.id === options.continuationAgent && agent.route === void 0 && agent.canStart)) throw new Error("continuationAgent must be a directly startable conversation agent");
46
61
  for (const agent of agents) if (agent.route !== void 0 && options.routes.get(agent.route)?.agent !== agent.id) throw new Error(`API agent ${agent.id} must select one of its registered routes`);
@@ -413,6 +428,7 @@ function createFactoryApi(options) {
413
428
  async function assertRepository(id) {
414
429
  const repository = await stores.repositories.get(id);
415
430
  if (repository === null || !repository.enabled || !options.repositories.includes(repository.slug)) throw new ApiError(403, "The session repository is no longer enabled and configured");
431
+ return repository;
416
432
  }
417
433
  async function owned(id, owner) {
418
434
  const session = await stores.sessions.get(agentSessionIdSchema.parse(id));
@@ -479,13 +495,14 @@ function createFactoryApi(options) {
479
495
  if (agent.requiresChange && input.changeId === void 0) throw new ApiError(400, `${agent.id} requires an existing changeId`);
480
496
  const slug = input.repository === void 0 ? void 0 : repositorySlugSchema.parse(input.repository.toLowerCase());
481
497
  if (slug !== void 0 && !options.repositories.includes(slug)) throw new ApiError(403, "Repository is not configured");
482
- const repository = slug === void 0 ? void 0 : await options.ensureRepository(slug);
483
- if (repository !== void 0) await assertRepository(repository.id);
498
+ const repository = slug === void 0 ? void 0 : await assertRepository((await options.ensureRepository(slug)).id);
484
499
  if (input.changeId !== void 0) {
485
500
  const change = await stores.changes.get(input.changeId);
486
501
  if (change === null || change.repositoryId !== repository?.id) throw new ApiError(400, "changeId must identify a change in the selected repository");
487
502
  if (change.state !== "verifying" && change.state !== "ready") throw new ApiError(409, "Change is no longer open for work");
488
503
  }
504
+ const existing = await stores.sessions.getByOperation(owner, input.operationId);
505
+ const model = existing && input.model === void 0 ? existing.model : selectModel(agent.id, input.model, repository?.modelDefaults?.agents);
489
506
  let session = await stores.sessions.create({
490
507
  operationId: input.operationId,
491
508
  owner,
@@ -494,6 +511,7 @@ function createFactoryApi(options) {
494
511
  route: agent.route,
495
512
  message: input.message,
496
513
  changeId: input.changeId,
514
+ model,
497
515
  ...input.attachments?.length ? { attachments: input.attachments } : {}
498
516
  });
499
517
  if (agent.route !== void 0 && session.taskId === void 0) {
@@ -539,10 +557,57 @@ function createFactoryApi(options) {
539
557
  changeId: input.changeId,
540
558
  dedupeKey: `api:${session.id}`
541
559
  });
560
+ if (session.model) await stores.tasks.pinModel(task.id, {
561
+ attempt: task.attempt,
562
+ model: session.model
563
+ });
542
564
  session = await stores.sessions.bind(session.id, { taskId: task.id });
543
565
  }
544
566
  return json(await launch(session), 202);
545
567
  }
568
+ /**
569
+ * Starts a deepsec security scan of a repository as a background Task,
570
+ * the same way `factory scan` does, or returns the already-active scan for
571
+ * that repository instead of starting a duplicate one.
572
+ */
573
+ async function startSecurityScan(rawId, owner) {
574
+ const repositoryId = repositoryIdSchema.parse(rawId);
575
+ const repository = await stores.repositories.get(repositoryId);
576
+ if (!repository) throw new ApiError(404, "Repository not found");
577
+ await assertRepository(repositoryId);
578
+ const active = (await stores.tasks.list()).find((task) => task.kind === "security_scan" && task.repositoryIds.includes(repository.id) && ![
579
+ "succeeded",
580
+ "failed",
581
+ "cancelled"
582
+ ].includes(task.state));
583
+ if (active) return json({
584
+ taskId: active.id,
585
+ reused: true
586
+ }, 202);
587
+ const route = options.routes.get(parseAgentRouteBinding({
588
+ id: "security-scan",
589
+ version: 1
590
+ }));
591
+ if (!route) throw new ApiError(409, "This factory has no security-scan route configured");
592
+ const task = await stores.tasks.create({
593
+ repositoryIds: [repository.id],
594
+ kind: "security_scan",
595
+ work: routeTaskWork({
596
+ route,
597
+ title: `Security scan of ${repository.slug}`,
598
+ input: { repositoryId: repository.id }
599
+ }),
600
+ origin: { operator: owner },
601
+ replyTo: {
602
+ channel: "factory",
603
+ address: repository.slug
604
+ }
605
+ });
606
+ return json({
607
+ taskId: task.id,
608
+ reused: false
609
+ }, 202);
610
+ }
546
611
  async function continuationBrief(session, current, message, taskId) {
547
612
  const continuation = session.continuations?.find((item) => item.taskId === taskId);
548
613
  const saved = continuation?.context;
@@ -625,21 +690,22 @@ function createFactoryApi(options) {
625
690
  }
626
691
  async function materializedTranscript(session, task, agent, execution, signal) {
627
692
  const cached = await stores.transcripts.get(execution);
628
- if (cached !== null) return cached;
693
+ if (cached !== null && cached.state !== "running" && cached.state !== "connecting") return cached;
629
694
  const transcript = new Transcript();
695
+ if (cached !== null) transcript.hydrate(cached);
630
696
  const path = `/stream?${new URLSearchParams({
631
- startIndex: "0",
697
+ startIndex: String(transcript.cursor),
632
698
  includeTailIndex: "1"
633
699
  })}`;
634
700
  const response = await options.forward({
635
701
  session: executionSession(session, task, agent),
636
702
  execution,
637
703
  path,
638
- request: new Request("http://factory/transcript", { signal })
704
+ request: new Request("http://factory/transcript", { signal: AbortSignal.any([signal, AbortSignal.timeout(15e3)]) })
639
705
  });
640
706
  if (!response.ok || !response.body) {
641
707
  await response.body?.cancel();
642
- throw new ApiError(response.status || 502, "Execution transcript is unavailable");
708
+ throw new ApiError(response.status || 502, response.status === 404 ? "Execution transcript is unavailable on this deployment. It may belong to another deployment or have expired." : "Execution transcript is unavailable");
643
709
  }
644
710
  const tailValue = response.headers.get("x-eve-stream-tail-index");
645
711
  const tail = tailValue === null ? NaN : Number(tailValue);
@@ -647,7 +713,11 @@ function createFactoryApi(options) {
647
713
  await response.body.cancel();
648
714
  throw new Error("Execution transcript did not include a valid durable cursor");
649
715
  }
650
- for await (const event of readEvents(response.body)) transcript.accept(event);
716
+ if (transcript.cursor <= tail) for await (const event of readEvents(response.body)) {
717
+ transcript.accept(event);
718
+ if (transcript.cursor === tail + 1) break;
719
+ }
720
+ else await response.body.cancel();
651
721
  if (transcript.cursor !== tail + 1) throw new Error("Execution transcript closed before its durable cursor");
652
722
  return stores.transcripts.put(execution, transcript.snapshot());
653
723
  }
@@ -708,6 +778,28 @@ function createFactoryApi(options) {
708
778
  const url = new URL(request.url);
709
779
  const path = url.pathname.replace(/^\/api\/v1\/?/u, "").split("/").filter(Boolean);
710
780
  const [resource, id, action] = path;
781
+ if (resource === "models" && path.length === 1 && ["GET", "POST"].includes(request.method)) {
782
+ const update = request.method === "POST" ? updateModelDefaultsSchema.parse(await body(request)) : null;
783
+ const slug = update?.repository ?? (url.searchParams.has("repository") ? repositorySlugSchema.parse(url.searchParams.get("repository")) : null);
784
+ if (slug && !options.repositories.includes(slug)) throw new ApiError(403, "Repository is not configured");
785
+ let repository = slug ? await assertRepository((await options.ensureRepository(slug)).id) : null;
786
+ const canEdit = repository !== null && agentModels.length > 0 && await options.canEditModelDefaults?.(auth, repository) === true;
787
+ if (update) {
788
+ if (!repository || !canEdit) throw new ApiError(403, "Saving model defaults requires an authorized operator");
789
+ for (const [agent, model] of Object.entries(update.defaults)) selectModel(agent, model);
790
+ repository = await stores.repositories.setModelDefaults(repository.id, {
791
+ expectedRevision: update.expectedRevision,
792
+ agents: update.defaults
793
+ });
794
+ }
795
+ return json(modelSettingsSchema.parse({
796
+ agents: agentModels,
797
+ repository: slug,
798
+ defaults: repository?.modelDefaults?.agents ?? {},
799
+ revision: repository?.modelDefaults?.revision ?? 0,
800
+ canEdit
801
+ }));
802
+ }
711
803
  if (path.length > 3) throw new ApiError(404, "API route not found");
712
804
  if (resource === "intake") {
713
805
  if (!auth.actor || !(options.operators ?? []).includes(auth.actor)) throw new ApiError(403, "Intake requires an individually authenticated configured operator");
@@ -739,7 +831,7 @@ function createFactoryApi(options) {
739
831
  if (!signal || !allowed.has(signal.repositoryId)) throw new ApiError(404, "Intake not found");
740
832
  const input = retryIntakeSchema.parse(await body(request));
741
833
  if (signal.outcome !== "pending" || signal.triage?.state !== "blocked" || signal.triage.attempt !== input.expectedAttempt || tasks.some((task) => task.origin.signalId === signal.id)) throw new ApiError(409, "Only the observed blocked attempt with no linked task can be retried; refresh intake");
742
- if (!options.retryIntake || signal.source !== "github_issue") throw new ApiError(409, "This factory has no retry launcher for this intake source");
834
+ if (!options.retryIntake) throw new ApiError(409, "This factory has no retry launcher for this intake source");
743
835
  return json(view(await options.retryIntake(signal, {
744
836
  ...input,
745
837
  by: auth.actor
@@ -765,6 +857,7 @@ function createFactoryApi(options) {
765
857
  input: await body(request)
766
858
  }));
767
859
  }
860
+ if (resource === "repositories" && id && action === "security-scan" && request.method === "POST") return await startSecurityScan(id, auth.id);
768
861
  if (resource === "images") {
769
862
  if (request.method === "POST" && path.length === 1) {
770
863
  const input = imageUploadSchema.parse(await body(request, 4 * Math.ceil(MAX_IMAGE_BYTES / 3) + 2048));
@@ -785,6 +878,33 @@ function createFactoryApi(options) {
785
878
  throw new ApiError(404, "API route not found");
786
879
  }
787
880
  if (request.method === "GET" && path.length === 1) {
881
+ if (resource === "inbox-activity") {
882
+ if (!options.inboxActivity) return json({
883
+ state: "unavailable",
884
+ capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
885
+ reason: "External activity is not configured."
886
+ });
887
+ const allowedRepositories = async () => {
888
+ const disabled = new Set((await stores.repositories.list()).filter((repo) => !repo.enabled).map((repo) => repo.slug));
889
+ return options.repositories.filter((slug) => !disabled.has(slug));
890
+ };
891
+ const repositories = await allowedRepositories();
892
+ const result = inboxActivitySchema.parse(await options.inboxActivity({
893
+ principal: auth,
894
+ repositories,
895
+ signal: request.signal
896
+ }));
897
+ if (result.state === "ready") {
898
+ const allowed = new Set(await allowedRepositories());
899
+ const ids = /* @__PURE__ */ new Set();
900
+ for (const item of result.items) {
901
+ if (!repositories.includes(item.repository) || !allowed.has(item.repository)) throw new ApiError(403, "Activity repository is not available");
902
+ if (ids.has(item.id)) throw new ApiError(502, "Activity contains duplicate identities");
903
+ ids.add(item.id);
904
+ }
905
+ }
906
+ return json(result);
907
+ }
788
908
  if (resource === "agents") return json({ agents });
789
909
  if (resource === "repositories") return json({
790
910
  repositories: options.repositories,
@@ -863,6 +983,75 @@ function createFactoryApi(options) {
863
983
  if (latest?.execution?.sessionId !== execution.sessionId || latest.execution.provider !== execution.provider) throw new ApiError(409, "Execution changed; refresh the graph.");
864
984
  return json(snapshot);
865
985
  }
986
+ if (request.method === "GET" && (action === "review" || action === "previews")) {
987
+ const current = await view(session, void 0, "summary");
988
+ const executionId = current.execution?.sessionId ?? null;
989
+ if (url.searchParams.get("executionId") !== (executionId ?? "")) throw new ApiError(409, "Execution changed; refresh the session before reviewing work");
990
+ const reader = action === "review" ? options.sessionReview : options.sessionPreviews;
991
+ if (!reader) return json({
992
+ state: "unavailable",
993
+ executionId,
994
+ capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
995
+ reason: action === "review" ? "Review evidence is not configured for this factory." : "Live previews are not configured for this factory."
996
+ });
997
+ const reviewTasks = async (value) => {
998
+ const delegated = value.session.external ? [] : workflowTasks(value.session, await stores.tasks.forReplyTo({
999
+ channel: "api",
1000
+ address: value.session.id
1001
+ }));
1002
+ return [...new Map([
1003
+ ...value.task ? [value.task] : [],
1004
+ ...value.relatedTasks ?? [],
1005
+ ...delegated
1006
+ ].map((task) => [task.id, task])).values()].sort((a, b) => a.createdAt.localeCompare(b.createdAt) || a.id.localeCompare(b.id));
1007
+ };
1008
+ const tasks = await reviewTasks(current);
1009
+ const identity = (value, work) => JSON.stringify([
1010
+ value.execution,
1011
+ value.task?.id,
1012
+ work.map((task) => [
1013
+ task.id,
1014
+ task.attempt,
1015
+ task.execution,
1016
+ task.changeId,
1017
+ task.repositoryIds
1018
+ ])
1019
+ ]);
1020
+ const result = (action === "review" ? sessionReviewSchema : sessionPreviewsSchema).parse(await reader({
1021
+ current,
1022
+ tasks,
1023
+ signal: request.signal
1024
+ }));
1025
+ const latest = await view(await owned(id, auth.id), void 0, "summary");
1026
+ if (identity(current, tasks) !== identity(latest, await reviewTasks(latest))) throw new ApiError(409, "Execution changed; refresh the session before reviewing work");
1027
+ if (result.executionId !== executionId) throw new Error("Review reader returned a different execution");
1028
+ return json(result);
1029
+ }
1030
+ if (request.method === "GET" && action === "files") {
1031
+ const query = workspaceFilesRequestSchema.parse({
1032
+ path: url.searchParams.get("path") ?? "",
1033
+ ...url.searchParams.has("revision") ? { revision: url.searchParams.get("revision") } : {}
1034
+ });
1035
+ const current = await view(session, void 0, "summary");
1036
+ const execution = current.execution;
1037
+ if (url.searchParams.get("executionId") !== (execution?.sessionId ?? "")) throw new ApiError(409, "Execution changed; refresh the session before browsing files");
1038
+ if (!execution || !options.workspaceFiles) return json({
1039
+ state: "unavailable",
1040
+ path: query.path,
1041
+ executionId: execution?.sessionId ?? null,
1042
+ reason: execution ? "File browsing is not configured for this factory." : "The workspace is not available yet."
1043
+ });
1044
+ const result = workspaceFilesSchema.parse(await options.workspaceFiles({
1045
+ session: executionSession(session, current.task),
1046
+ execution,
1047
+ query,
1048
+ signal: request.signal
1049
+ }));
1050
+ const latest = (await view(await stores.sessions.get(session.id) ?? session, void 0, "summary")).execution;
1051
+ if (latest?.sessionId !== execution.sessionId || latest.provider !== execution.provider) throw new ApiError(409, "Execution changed; refresh the session before browsing files");
1052
+ if (result.executionId !== execution.sessionId || result.path !== query.path || result.state !== "unavailable" && query.revision !== void 0 && result.revision !== query.revision) throw new Error("File inspection returned a different execution, path, or revision");
1053
+ return json(result);
1054
+ }
866
1055
  if (request.method === "GET" && action === "diff") {
867
1056
  const current = await view(session, void 0, "summary");
868
1057
  const execution = current.execution;
@@ -1082,6 +1271,6 @@ async function body(request, limit = 64e3) {
1082
1271
  }
1083
1272
  }
1084
1273
  //#endregion
1085
- export { ImageInputError, SessionConflictError, agentSessionIdSchema, agentSessionSchema, answerSessionQuestionRequestSchema, apiAgentSchema, assembleWorkspaceDiff, controlSessionRequestSchema, createFactoryApi, createSessionRequestSchema, imagePartFilename, repositoryDetailsSchema, repositorySummarySchema, resolveImageMessage, sendSessionMessageRequestSchema, sessionInputResponseSchema, sessionPageSchema, sessionViewSchema, submitSessionInputRequestSchema, unavailableWorkspaceDiff, workspaceDiffFileSchema, workspaceDiffSchema, workspaceSnapshotSchema, workspaceSnapshotScript };
1274
+ export { ImageInputError, SessionConflictError, agentModelConfigSchema, agentSessionIdSchema, agentSessionSchema, answerSessionQuestionRequestSchema, apiAgentSchema, assembleWorkspaceDiff, controlSessionRequestSchema, createFactoryApi, createSessionRequestSchema, imagePartFilename, inboxActivityItemSchema, inboxActivitySchema, modelSettingsSchema, repositoryDetailsSchema, repositorySummarySchema, resolveImageMessage, sendSessionMessageRequestSchema, sessionInputResponseSchema, sessionPageSchema, sessionPreviewSchema, sessionPreviewsSchema, sessionReviewCheckSchema, sessionReviewIssueSchema, sessionReviewSchema, sessionReviewScreenshotSchema, sessionViewSchema, submitSessionInputRequestSchema, unavailableWorkspaceDiff, updateModelDefaultsSchema, workspaceDiffFileSchema, workspaceDiffSchema, workspaceFileContentSchema, workspaceFileEntrySchema, workspaceFilesRequestSchema, workspaceFilesSchema, workspaceGitFilesScript, workspacePathSchema, workspaceSnapshotSchema, workspaceSnapshotScript };
1086
1275
 
1087
1276
  //# sourceMappingURL=api.mjs.map