@vercel/factory 0.0.16 → 0.0.18

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 (186) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/dist/api-contracts.d.mts +665 -715
  3. package/dist/api-contracts.mjs +91 -4
  4. package/dist/api-contracts.mjs.map +1 -1
  5. package/dist/api-task-graph.mjs +14 -5
  6. package/dist/api-task-graph.mjs.map +1 -1
  7. package/dist/api.d.mts +58 -11
  8. package/dist/api.mjs +715 -399
  9. package/dist/api.mjs.map +1 -1
  10. package/dist/approval-access.d.mts +1 -1
  11. package/dist/approval-contracts.d.mts +3 -4
  12. package/dist/approval-contracts.mjs +1 -2
  13. package/dist/approval-contracts.mjs.map +1 -1
  14. package/dist/blob/index.d.mts +1 -1
  15. package/dist/budget.mjs +20 -3
  16. package/dist/budget.mjs.map +1 -1
  17. package/dist/change-verification/brief.d.mts +1 -1
  18. package/dist/change-verification/dispatch.mjs +8 -1
  19. package/dist/change-verification/dispatch.mjs.map +1 -1
  20. package/dist/changes/eve-record-change.mjs +4 -1
  21. package/dist/changes/eve-record-change.mjs.map +1 -1
  22. package/dist/client-stream.d.mts +19 -2
  23. package/dist/client-stream.mjs +115 -53
  24. package/dist/client-stream.mjs.map +1 -1
  25. package/dist/client-transcript.d.mts +25 -2
  26. package/dist/client-transcript.mjs +95 -13
  27. package/dist/client-transcript.mjs.map +1 -1
  28. package/dist/client.d.mts +145 -19
  29. package/dist/client.mjs +89 -16
  30. package/dist/client.mjs.map +1 -1
  31. package/dist/code-review/contracts.d.mts +1 -0
  32. package/dist/code-review/eve-post-review.d.mts +4 -2
  33. package/dist/code-review/eve-post-review.mjs +8 -0
  34. package/dist/code-review/eve-post-review.mjs.map +1 -1
  35. package/dist/code-review/github-reporter.d.mts +6 -2
  36. package/dist/code-review/github-reporter.mjs +86 -35
  37. package/dist/code-review/github-reporter.mjs.map +1 -1
  38. package/dist/code-review/preparation.d.mts +2 -1
  39. package/dist/code-review/preparation.mjs +2 -1
  40. package/dist/code-review/preparation.mjs.map +1 -1
  41. package/dist/code-review/stall.mjs +11 -2
  42. package/dist/code-review/stall.mjs.map +1 -1
  43. package/dist/connectors/github.d.mts +1 -1
  44. package/dist/connectors/index.d.mts +1 -1
  45. package/dist/connectors/slack.d.mts +1 -1
  46. package/dist/delivery-metrics/metrics.d.mts +1 -1
  47. package/dist/dispatch.mjs +3 -3
  48. package/dist/dispatch.mjs.map +1 -1
  49. package/dist/eve/index.d.mts +1 -1
  50. package/dist/eve/index.mjs +8 -1
  51. package/dist/eve/index.mjs.map +1 -1
  52. package/dist/eve/task-execution.d.mts +4 -4
  53. package/dist/eve/transcript.d.mts +3 -1
  54. package/dist/eve/transcript.mjs.map +1 -1
  55. package/dist/finding-remediation/admission.d.mts +1 -1
  56. package/dist/finding-remediation/brief.d.mts +1 -1
  57. package/dist/github-app.d.mts +1 -1
  58. package/dist/github-publication.d.mts +1 -1
  59. package/dist/github.d.mts +9 -1
  60. package/dist/github.mjs +29 -11
  61. package/dist/github.mjs.map +1 -1
  62. package/dist/index.d.mts +1 -1
  63. package/dist/intake-contracts.d.mts +1 -0
  64. package/dist/intake-contracts.mjs +2 -1
  65. package/dist/intake-contracts.mjs.map +1 -1
  66. package/dist/integrations/github-issues.d.mts +53 -0
  67. package/dist/integrations/github-issues.mjs +170 -0
  68. package/dist/integrations/github-issues.mjs.map +1 -0
  69. package/dist/integrations/github.d.mts +2 -1
  70. package/dist/integrations/github.mjs +2 -1
  71. package/dist/multi-repository-changes/coordination.mjs +1 -1
  72. package/dist/planning/reconcile.mjs +15 -2
  73. package/dist/planning/reconcile.mjs.map +1 -1
  74. package/dist/postgres/index.d.mts +2 -1
  75. package/dist/postgres/index.mjs +11 -5
  76. package/dist/postgres/index.mjs.map +1 -1
  77. package/dist/postgres/indexed-codec.mjs +137 -0
  78. package/dist/postgres/indexed-codec.mjs.map +1 -0
  79. package/dist/postgres/indexed-schema.mjs +99 -0
  80. package/dist/postgres/indexed-schema.mjs.map +1 -0
  81. package/dist/postgres/indexed.d.mts +28 -0
  82. package/dist/postgres/indexed.mjs +277 -0
  83. package/dist/postgres/indexed.mjs.map +1 -0
  84. package/dist/postgres/record-predicates.mjs +23 -0
  85. package/dist/postgres/record-predicates.mjs.map +1 -0
  86. package/dist/presets/software-development/deepsec-brief.d.mts +1 -1
  87. package/dist/presets/software-development/dispatch.mjs +1 -1
  88. package/dist/presets/software-development/dispatch.mjs.map +1 -1
  89. package/dist/presets/software-development/recovery.mjs +9 -2
  90. package/dist/presets/software-development/recovery.mjs.map +1 -1
  91. package/dist/pull-requests/eve-fetch.d.mts +4 -1
  92. package/dist/pull-requests/eve-fetch.mjs +9 -0
  93. package/dist/pull-requests/eve-fetch.mjs.map +1 -1
  94. package/dist/pull-requests/github-publisher.d.mts +1 -1
  95. package/dist/pull-requests/github-publisher.mjs +19 -7
  96. package/dist/pull-requests/github-publisher.mjs.map +1 -1
  97. package/dist/repositories.mjs +20 -13
  98. package/dist/repositories.mjs.map +1 -1
  99. package/dist/sandbox/execution-run-ledger.d.mts +2 -2
  100. package/dist/sandbox/execution-run-ledger.mjs.map +1 -1
  101. package/dist/sandbox/execution.d.mts +2 -2
  102. package/dist/sandbox/execution.mjs.map +1 -1
  103. package/dist/schema/catalog.mjs +1 -1
  104. package/dist/schema/change.d.mts +1 -1
  105. package/dist/schema/communication.d.mts +2 -2
  106. package/dist/schema/factory-config.d.mts +1 -1
  107. package/dist/schema/outbox.d.mts +6 -6
  108. package/dist/schema/session-summary.d.mts +218 -0
  109. package/dist/schema/session-summary.mjs +68 -0
  110. package/dist/schema/session-summary.mjs.map +1 -0
  111. package/dist/schema/session.d.mts +39 -59
  112. package/dist/schema/session.mjs +6 -6
  113. package/dist/schema/session.mjs.map +1 -1
  114. package/dist/schema/task-graph.d.mts +24 -24
  115. package/dist/schema/transcript.d.mts +141 -3
  116. package/dist/schema/transcript.mjs +19 -3
  117. package/dist/schema/transcript.mjs.map +1 -1
  118. package/dist/schema/transitions.d.mts +1 -1
  119. package/dist/schema/transitions.mjs +1 -1
  120. package/dist/schema/transitions.mjs.map +1 -1
  121. package/dist/signals.d.mts +1 -1
  122. package/dist/stall.mjs +10 -2
  123. package/dist/stall.mjs.map +1 -1
  124. package/dist/storage.d.mts +10 -1
  125. package/dist/storage.mjs +7 -1
  126. package/dist/store/approvals.d.mts +14 -0
  127. package/dist/store/approvals.mjs +78 -0
  128. package/dist/store/approvals.mjs.map +1 -0
  129. package/dist/store/conversations.d.mts +59 -0
  130. package/dist/store/conversations.mjs +549 -0
  131. package/dist/store/conversations.mjs.map +1 -0
  132. package/dist/store/driver.d.mts +1 -1
  133. package/dist/store/driver.mjs.map +1 -1
  134. package/dist/store/engine.d.mts +38 -16
  135. package/dist/store/engine.mjs +229 -106
  136. package/dist/store/engine.mjs.map +1 -1
  137. package/dist/store/indexed-engine.d.mts +57 -0
  138. package/dist/store/indexed-engine.mjs +155 -0
  139. package/dist/store/indexed-engine.mjs.map +1 -0
  140. package/dist/store/indexed-memory.d.mts +7 -0
  141. package/dist/store/indexed-memory.mjs +200 -0
  142. package/dist/store/indexed-memory.mjs.map +1 -0
  143. package/dist/store/indexed-projections.d.mts +280 -0
  144. package/dist/store/indexed-projections.mjs +481 -0
  145. package/dist/store/indexed-projections.mjs.map +1 -0
  146. package/dist/store/indexed.d.mts +101 -0
  147. package/dist/store/indexed.mjs +19 -0
  148. package/dist/store/indexed.mjs.map +1 -0
  149. package/dist/store/iterate.d.mts +9 -0
  150. package/dist/store/iterate.mjs +23 -0
  151. package/dist/store/iterate.mjs.map +1 -0
  152. package/dist/store/query-scope.mjs +62 -0
  153. package/dist/store/query-scope.mjs.map +1 -0
  154. package/dist/store/query.d.mts +32 -0
  155. package/dist/store/query.mjs +103 -0
  156. package/dist/store/query.mjs.map +1 -0
  157. package/dist/store/session-history.mjs +24 -0
  158. package/dist/store/session-history.mjs.map +1 -0
  159. package/dist/store/slack-pr-notifications.d.mts +2 -2
  160. package/dist/store/slack-pr-notifications.mjs.map +1 -1
  161. package/dist/store/state.d.mts +26 -0
  162. package/dist/store/state.mjs +95 -0
  163. package/dist/store/state.mjs.map +1 -0
  164. package/dist/store/task-effects.d.mts +1 -1
  165. package/dist/store/task-graphs.d.mts +1 -1
  166. package/dist/store/task-messages.d.mts +1 -1
  167. package/dist/store/task-work.d.mts +1 -1
  168. package/dist/store/task-work.mjs +1 -1
  169. package/dist/store/transcripts.mjs +308 -0
  170. package/dist/store/transcripts.mjs.map +1 -0
  171. package/dist/sweep.d.mts +1 -1
  172. package/dist/sweep.mjs +12 -7
  173. package/dist/sweep.mjs.map +1 -1
  174. package/dist/task-communication.mjs +15 -1
  175. package/dist/task-communication.mjs.map +1 -1
  176. package/dist/task-protocols.d.mts +3 -3
  177. package/dist/tasks.d.mts +1 -1
  178. package/dist/vercel.d.mts +1 -1
  179. package/dist/work-triage/find-existing-work.d.mts +1 -1
  180. package/dist/work-triage/find-existing-work.mjs +12 -2
  181. package/dist/work-triage/find-existing-work.mjs.map +1 -1
  182. package/docs/index.md +4 -0
  183. package/docs/recipes/persistence-recovery.md +15 -9
  184. package/docs/recipes/postgres-indexed-engine.md +108 -0
  185. package/docs/recipes/postgres-indexed-storage.md +145 -0
  186. package/package.json +6 -1
@@ -1,3 +1,4 @@
1
+ import { collectFactoryRecords } from "../store/iterate.mjs";
1
2
  import { changeIdSchema, repositoryIdsSchema, signalIdSchema, taskIdSchema } from "../schema/id.mjs";
2
3
  import { taskWork } from "../schema/work.mjs";
3
4
  import { taskQuestionSchema } from "../schema/task.mjs";
@@ -451,7 +452,13 @@ function createFactoryHooks(stores, options) {
451
452
  }
452
453
  const address = ctx.channel.continuationToken;
453
454
  if (address === void 0) return;
454
- const abandoned = (await stores.tasks.list().catch(() => [])).filter((task) => task.id !== taskId && task.state === "running" && !task.waitingForMessages && task.replyTo.address === address);
455
+ const abandoned = (await collectFactoryRecords(stores.tasks.list, {
456
+ limit: 100,
457
+ match: {
458
+ state: "running",
459
+ replyTo: { address }
460
+ }
461
+ }).catch(() => [])).filter((task) => task.id !== taskId && task.state === "running" && !task.waitingForMessages && task.replyTo.address === address);
455
462
  for (const task of abandoned) await escalateUnresolvedTurn(task);
456
463
  }
457
464
  } }) };
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../src/eve/index.ts"],"sourcesContent":["import type { SessionContext } from \"eve/context\";\nimport { defineHook, type HookDefinition } from \"eve/hooks\";\nimport { defineTool, type ToolContext, type ToolDefinition } from \"eve/tools\";\nimport { z } from \"zod\";\nimport {\n changeIdSchema,\n repositoryIdsSchema,\n signalIdSchema,\n taskIdSchema,\n type ChangeId,\n type RepositoryIds,\n type SignalId,\n type TaskId,\n} from \"../schema/id\";\nimport {\n receiptKindSchema,\n receiptStateSchema,\n type Receipt,\n type ReceiptKind,\n type ReceiptState,\n} from \"../schema/receipt\";\nimport type { MaybePromise } from \"../schema/common\";\nimport type { TaskAttemptRef } from \"../schema/execution\";\nimport { taskQuestionSchema, type Task, type TaskQuestion } from \"../schema/task\";\nimport type { Signal, SignalOutcome } from \"../schema/signal\";\nimport { taskWork, type JsonValue } from \"../schema/work\";\nimport type { AnswerQuestionResult, FactoryStores } from \"../store/engine\";\nimport type { EveTranscriptMaterializer, TranscriptMaterializationInput } from \"./transcript\";\nimport { requireTaskExecution } from \"./task-execution\";\n\nexport {\n conversationBinding,\n conversationBindingForSession,\n conversationHeaders,\n factoryReplyAddressAttribute,\n factoryReplyAddressHeader,\n factoryReplyChannelAttribute,\n factoryReplyChannelHeader,\n factoryRepositoryIdsAttribute,\n factoryRepositoryIdsHeader,\n factoryTaskAttemptAttribute,\n factoryTaskAttemptHeader,\n factoryTaskAttribute,\n factoryTaskHeader,\n hasFactoryTaskBinding,\n optionalTaskIdForSession,\n requireTaskIdForSession,\n replyToForSession,\n repositoryIdsForSession,\n taskBindingForSession,\n taskHeaders,\n taskSessionAuth,\n withFactoryTask,\n withOptionalFactoryTask,\n type ConversationBinding,\n type FactoryConversationSessionBinding,\n} from \"./task-session\";\nexport {\n createEveTranscriptMaterializer,\n type EveTranscriptMaterializerOptions,\n type EveTranscriptMaterializer,\n type EveTranscriptRequestScope,\n type TranscriptMaterializationInput,\n} from \"./transcript\";\nexport {\n cancelEveSession,\n forwardEveSessionRequest,\n readCompletedEveTurn,\n startEveSession,\n type CancelEveSessionOptions,\n type EveSessionEvent,\n type EveSessionStream,\n type EveSessionStreamInput,\n type ForwardEveSessionRequestOptions,\n type ReadCompletedEveTurnOptions,\n type StartEveSessionOptions,\n} from \"./session-client\";\n\nconst decidedOutcomeSchema = z.enum([\n \"accepted\",\n \"duplicate\",\n \"unactionable\",\n \"low_confidence\",\n \"ignored\",\n]);\n\n/** Receipt, Signal, and Task capabilities used by the provider-neutral Eve tool bundle. */\nexport type TaskToolsStores = Pick<FactoryStores, \"receipts\" | \"signals\" | \"tasks\">;\n\n/** Task capability used to project Eve lifecycle events onto durable Factory state. */\nexport type FactoryHooksStores = Pick<FactoryStores, \"tasks\">;\n\n/** Configures the provider-neutral Task, signal, question, and receipt Eve tools. */\nexport interface TaskToolsOptions {\n /** Present a persisted human question through the application's Foreman communication boundary. */\n presentQuestion?: (task: Task) => Promise<void>;\n /** Return a refusal reason when this task must use a more specific outcome tool. */\n finishTaskPolicy?: (task: Task) => MaybePromise<string | null>;\n}\n\n/** Delivers a persisted human-question Task through an application-owned channel. */\nexport type PresentQuestion = NonNullable<TaskToolsOptions[\"presentQuestion\"]>;\n\n/** Identifies the Task to read through the provider-neutral task tool. */\nexport interface GetTaskToolInput {\n taskId: TaskId;\n}\n\n/** Explicit not-found result returned when a requested Task does not exist. */\nexport interface TaskNotFoundResult {\n readonly found: false;\n readonly taskId: TaskId;\n}\n\n/** Existing Task or explicit not-found result returned by the task lookup tool. */\nexport type GetTaskToolResult = Task | TaskNotFoundResult;\n\n/** Provider-neutral Eve tool for reading one Factory Task. */\nexport interface GetTaskTool extends ToolDefinition<GetTaskToolInput, GetTaskToolResult> {\n execute(input: GetTaskToolInput, ctx: ToolContext): Promise<GetTaskToolResult>;\n}\n\n/** One final triage decision submitted for a pending Signal. */\nexport interface DecideSignalToolInput {\n signalId: SignalId;\n outcome: Exclude<SignalOutcome, \"pending\">;\n reason: string;\n detail?: string;\n}\n\n/** Provider-neutral Eve tool for recording a Signal decision. */\nexport interface DecideSignalTool extends ToolDefinition<DecideSignalToolInput, Signal> {\n execute(input: DecideSignalToolInput, ctx: ToolContext): Promise<Signal>;\n}\n\n/** Running Task and summary supplied to the generic completion tool. */\nexport interface FinishTaskToolInput {\n taskId: TaskId;\n summary: string;\n}\n\n/** Successful generic Task completion. */\nexport interface FinishTaskSuccess {\n readonly finished: true;\n readonly taskState: \"succeeded\";\n}\n\n/** Policy or lifecycle refusal returned by the generic Task completion tool. */\nexport interface FinishTaskRefusal {\n readonly finished: false;\n readonly reason: string;\n}\n\n/** Completion or bounded refusal returned by the generic Task completion tool. */\nexport type FinishTaskToolResult = FinishTaskSuccess | FinishTaskRefusal;\n\n/** Provider-neutral Eve tool for completing a running Task. */\nexport interface FinishTaskTool extends ToolDefinition<FinishTaskToolInput, FinishTaskToolResult> {\n execute(input: FinishTaskToolInput, ctx: ToolContext): Promise<FinishTaskToolResult>;\n}\n\n/** Unanswered question shape accepted when a Task requests human input. */\nexport type HumanInputQuestion = Omit<TaskQuestion, \"answer\" | \"answeredBy\">;\n\n/** Currently running parent Task and its single human question for this execution. */\nexport interface RequestHumanInputToolInput {\n parentTaskId: TaskId;\n question: HumanInputQuestion;\n}\n\n/** Provider-neutral Eve tool for pausing one running execution on one persisted human question. */\nexport interface RequestHumanInputTool extends ToolDefinition<RequestHumanInputToolInput, Task> {\n execute(input: RequestHumanInputToolInput, ctx: ToolContext): Promise<Task>;\n}\n\n/** Question Task and authenticated answer supplied to the answer tool. */\nexport interface AnswerQuestionToolInput {\n questionTaskId: TaskId;\n answer: string;\n}\n\n/** Provider-neutral Eve tool for answering a persisted human question. */\nexport interface AnswerQuestionTool extends ToolDefinition<\n AnswerQuestionToolInput,\n AnswerQuestionResult\n> {\n execute(input: AnswerQuestionToolInput, ctx: ToolContext): Promise<AnswerQuestionResult>;\n}\n\n/** Immutable audit event accepted by the generic receipt tool. */\nexport interface AppendReceiptToolInput {\n repositoryIds: RepositoryIds;\n taskId?: TaskId;\n changeId?: ChangeId;\n kind: ReceiptKind;\n state: ReceiptState;\n reason?: string;\n dedupeKey?: string;\n}\n\n/** Provider-neutral Eve tool for appending one immutable audit Receipt. */\nexport interface AppendReceiptTool extends ToolDefinition<AppendReceiptToolInput, Receipt> {\n execute(input: AppendReceiptToolInput, ctx: ToolContext): Promise<Receipt>;\n}\n\n/** The provider-neutral Task, Signal, question, and Receipt tools exposed to an Eve agent. */\nexport interface TaskTools {\n /** Read one Task by ID; applications decide which agents may expose this cross-Task lookup. */\n readonly get_task: GetTaskTool;\n /** Record the one final decision for a pending Signal. */\n readonly decide_signal: DecideSignalTool;\n /** Complete generic `task@1` work after authenticating the current Eve execution. */\n readonly finish_task: FinishTaskTool;\n /** Persist and present one human question for the authenticated running Task. */\n readonly request_human_input: RequestHumanInputTool;\n /** Answer a question only from its authenticated reply conversation. */\n readonly answer_question: AnswerQuestionTool;\n /** Append immutable application audit evidence with optional deduplication. */\n readonly append_receipt: AppendReceiptTool;\n}\n\nasync function createAndPresentQuestion(\n stores: Pick<FactoryStores, \"tasks\">,\n parent: Task,\n question: z.infer<typeof taskQuestionSchema>,\n presentQuestion: PresentQuestion,\n completedSessionId?: string,\n): Promise<Task> {\n const needsHumanAction = question.text.trim();\n if (needsHumanAction === \"\") throw new Error(\"A human question must contain non-blank text.\");\n const task = await stores.tasks.create({\n repositoryIds: parent.repositoryIds,\n kind: \"question\",\n work: taskWork({\n title: needsHumanAction.slice(0, 256),\n input: {\n question,\n ...(completedSessionId ? { systemReason: \"unresolved_turn\", completedSessionId } : {}),\n },\n }),\n parentTaskId: parent.id,\n parentTaskAttempt: parent.attempt,\n origin: {},\n replyTo: parent.replyTo,\n question,\n });\n try {\n const waiting = await stores.tasks.transition(task.id, \"needs_human\", { needsHumanAction });\n if (parent.state === \"running\") {\n await stores.tasks.transition(parent.id, \"needs_human\", {\n expectFrom: \"running\",\n expectAttempt: parent.attempt,\n reason: `waiting for answer to question ${waiting.id}`,\n questionTaskId: waiting.id,\n needsHumanAction,\n });\n }\n await presentQuestion(waiting);\n return waiting;\n } catch (error) {\n const currentParent = await stores.tasks.get(parent.id);\n if (\n currentParent?.state === \"needs_human\" &&\n currentParent.attempt === parent.attempt &&\n currentParent.questionTaskId === task.id\n ) {\n try {\n await stores.tasks.transition(parent.id, \"queued\", {\n expectFrom: \"needs_human\",\n expectAttempt: currentParent.attempt,\n expectQuestionTaskId: task.id,\n reason: `delivery of question ${task.id} failed`,\n });\n } catch (cleanupError) {\n const latest = await stores.tasks.get(parent.id);\n if (\n latest?.state === \"needs_human\" &&\n latest.attempt === currentParent.attempt &&\n latest.questionTaskId === task.id\n )\n throw cleanupError;\n }\n }\n const currentQuestion = await stores.tasks.get(task.id);\n if (currentQuestion?.state === \"needs_human\" || currentQuestion?.state === \"queued\") {\n await stores.tasks.transition(task.id, \"cancelled\", {\n expectFrom: currentQuestion.state,\n expectAttempt: currentQuestion.attempt,\n reason: \"question delivery failed\",\n });\n }\n throw error;\n }\n}\n\n/**\n * Wraps provider-neutral Factory store operations as Eve tools.\n *\n * @remarks\n * Expose only the returned tools each agent role needs. Every write goes through the validating\n * engine, but tool visibility remains application authorization policy. `finish_task` and\n * `request_human_input` recheck the current authenticated Task attempt and Eve session before\n * mutating state. `get_task`, signal decisions, and generic receipts are broader capabilities and\n * should be mounted only for agents allowed to use them.\n *\n * The generic `finish_task` records `{ summary }` for `task@1`. Schema-specific workflows should\n * define a narrow tool that calls `requireTaskExecution` and `stores.work.completeWorkflow`, as in\n * the shipped `docs/recipes/typed-eve-result.md` recipe.\n *\n * @param stores - Receipt, Signal, and Task capabilities available to the tool bundle.\n * @param options - Optional question presentation and generic-completion policy.\n * @returns A new named Eve tool bundle ready for selective mounting under `agent/tools/`.\n *\n * @example\n * ```ts\n * import { createTaskTools, type TaskToolsStores } from \"@vercel/factory\";\n *\n * declare const stores: TaskToolsStores;\n * const tools = createTaskTools(stores);\n * export default tools.get_task;\n * ```\n */\nexport function createTaskTools(\n stores: TaskToolsStores,\n options: TaskToolsOptions = {},\n): TaskTools {\n return {\n get_task: defineTool({\n description:\n \"Read one factory task by id: its kind, state, repositories, origin, and results.\",\n inputSchema: z.object({ taskId: taskIdSchema }),\n async execute({ taskId }) {\n const task = await stores.tasks.get(taskId);\n return task ?? { found: false as const, taskId };\n },\n }),\n\n decide_signal: defineTool({\n description:\n \"Record the triage decision for a pending signal. A signal is decided exactly once.\",\n inputSchema: z.object({\n signalId: signalIdSchema,\n outcome: decidedOutcomeSchema,\n reason: z.string().min(1).describe(\"Why this outcome was chosen.\"),\n detail: z\n .string()\n .min(1)\n .optional()\n .describe(\"Severity if accepted, or the duplicate-of signal id.\"),\n }),\n execute({ signalId, outcome, reason, detail }) {\n return stores.signals.decide(signalId, { outcome: outcome, ...{ reason, detail } });\n },\n }),\n\n finish_task: defineTool({\n description:\n \"Mark your running task finished with a summary. The configured workflow policy may require a more specific outcome tool.\",\n inputSchema: z.object({\n taskId: taskIdSchema,\n summary: z.string().min(1).max(4000).describe(\"What you did and found, for the ledger.\"),\n }),\n async execute({ taskId, summary }, ctx) {\n const task = await requireTaskExecution(stores, ctx, taskId);\n const refusal = await options.finishTaskPolicy?.(task);\n if (refusal !== undefined && refusal !== null) {\n return {\n finished: false as const,\n reason: refusal,\n };\n }\n if (task.state !== \"running\") {\n return { finished: false as const, reason: `task is ${task.state}, not running` };\n }\n await stores.tasks.transition(taskId, \"succeeded\", {\n expectFrom: \"running\",\n expectAttempt: task.attempt,\n expectExecution: task.execution,\n reason: summary,\n output: { summary },\n });\n return { finished: true as const, taskState: \"succeeded\" as const };\n },\n }),\n\n request_human_input: defineTool({\n description:\n 'Request one human input or action and pause your currently running task until the human replies. A paused task cannot request another question; wait for the answer and replacement execution. The parent task determines the repositories and reply destination. Name any suggested reply in question.closingOptions when that exact answer means no further attempt can help (for example \"nothing to do, close it\"); Factory resolves the task instead of requeuing an identical retry when the human picks one.',\n inputSchema: z.object({\n parentTaskId: taskIdSchema.describe(\"Your running task, which the answer will unblock.\"),\n question: taskQuestionSchema.omit({ answer: true, answeredBy: true }),\n }),\n async execute({ parentTaskId, question }, ctx) {\n if (options.presentQuestion === undefined) {\n throw new Error(\"request_human_input requires a configured question presenter.\");\n }\n const parent = await requireTaskExecution(stores, ctx, parentTaskId);\n return createAndPresentQuestion(stores, parent, question, options.presentQuestion);\n },\n }),\n\n answer_question: defineTool({\n description:\n \"Record the authenticated user's answer to a waiting question and requeue the parent specialist task, unless the answer matches one of the question's closingOptions, which resolves the parent task instead. A blocked parent task id is also accepted and resolves its persisted QuestionTask id.\",\n inputSchema: z.object({\n questionTaskId: taskIdSchema,\n answer: z.string().min(1).max(4000),\n }),\n async execute({ questionTaskId, answer }, ctx) {\n const requestedTask = await stores.tasks.get(questionTaskId);\n const resolvedQuestionTaskId =\n requestedTask?.kind === \"question\" ? requestedTask.id : requestedTask?.questionTaskId;\n const questionTask =\n resolvedQuestionTaskId === undefined\n ? null\n : await stores.tasks.get(resolvedQuestionTaskId);\n if (\n questionTask === null ||\n questionTask.kind !== \"question\" ||\n questionTask.question === undefined\n ) {\n throw new Error(`Question task ${questionTaskId} does not exist.`);\n }\n const { initiator, current } = ctx.session.auth;\n if (\n !initiator ||\n !current ||\n [initiator, current].some(\n ({ attributes }) =>\n attributes.factoryReplyChannel !== questionTask.replyTo.channel ||\n attributes.factoryReplyAddress !== questionTask.replyTo.address,\n )\n ) {\n throw new Error(\"Question reply does not match the authenticated conversation.\");\n }\n const answeredBy = current.principalId;\n return stores.tasks.answerQuestion({\n questionTaskId: questionTask.id,\n answer,\n answeredBy,\n });\n },\n }),\n\n append_receipt: defineTool({\n description:\n \"Append one immutable audit receipt for an event worth remembering. Receipts can never be edited or deleted.\",\n inputSchema: z.object({\n repositoryIds: repositoryIdsSchema,\n taskId: taskIdSchema.optional(),\n changeId: changeIdSchema.optional(),\n kind: receiptKindSchema.describe(\"What the receipt is about, e.g. investigation.\"),\n state: receiptStateSchema.describe(\"What happened, e.g. flag_ramped.\"),\n reason: z.string().min(1).max(2000).optional(),\n dedupeKey: z\n .string()\n .min(1)\n .optional()\n .describe(\"Same key twice records one receipt instead of two.\"),\n }),\n execute(input) {\n return stores.receipts.append(input);\n },\n }),\n };\n}\n\n/** Completed conversation output, or `false` when the Task is not a conversation. */\nexport type ConversationCompletionResult = { readonly output: JsonValue } | false;\n\n/** Configures attempt recovery, conversation completion, and transcript projection for Eve hooks. */\nexport interface FactoryHooksOptions {\n /** Session auth attribute naming the task a dispatched session works on. Default `factoryTaskId`. */\n taskIdAttribute?: string;\n /** Session auth attribute naming the execution attempt. Default `factoryTaskAttempt`. */\n taskAttemptAttribute?: string;\n /** Attempts allowed before an explicit provider failure becomes terminal. Default: 3. */\n maxAttempts?: number;\n /**\n * Recover a saved workflow result for the exact bound execution before failure retry or\n * unresolved-turn escalation. Return true when handled; a throw defers generic lifecycle changes.\n */\n recoverResult?: (task: Task) => Promise<boolean>;\n /** Present the recovery question created when a task turn ends without an outcome. */\n presentQuestion: PresentQuestion;\n /** Classify discussion Tasks on failure or cancellation without collecting a successful response. */\n isConversation?: (task: Task) => MaybePromise<boolean>;\n /** Return the actual completed response for an opted-in discussion, or false for other work. */\n completeConversation?: (task: Task) => Promise<ConversationCompletionResult>;\n /** Persist the browser-safe transcript projection after Eve durably records a response boundary. */\n materializeTranscript?: EveTranscriptMaterializer;\n}\n\nfunction materializationInput(\n ctx: import(\"eve/hooks\").HookContext,\n): TranscriptMaterializationInput {\n return {\n agent: ctx.agent.name,\n execution: { provider: \"eve\", sessionId: ctx.session.id },\n attributes: ctx.session.auth.initiator?.attributes ?? {},\n };\n}\n\n/** Observe-only hook for agents that do not use the Factory task-lifecycle hook. */\nexport function createTranscriptSnapshotHook(\n materialize: EveTranscriptMaterializer,\n): TranscriptSnapshotHook {\n const snapshot = (_event: unknown, ctx: import(\"eve/hooks\").HookContext) =>\n materialize(materializationInput(ctx)).catch(() => undefined);\n return defineHook({\n events: {\n \"session.waiting\": snapshot,\n \"session.completed\": snapshot,\n \"session.failed\": snapshot,\n },\n });\n}\n\n/** Observe-only Eve hook that materializes transcript snapshots at response boundaries. */\nexport type TranscriptSnapshotHook = HookDefinition<\n \"session.waiting\" | \"session.completed\" | \"session.failed\"\n>;\n\nconst terminalTaskStates: ReadonlySet<Task[\"state\"]> = new Set([\n \"succeeded\",\n \"failed\",\n \"cancelled\",\n]);\n\ntype FactoryHookEvent =\n | \"step.completed\"\n | \"turn.failed\"\n | \"session.failed\"\n | \"session.waiting\"\n | \"session.completed\"\n | \"turn.cancelled\"\n | \"turn.completed\";\n\n/** The Eve lifecycle hook projected onto durable Factory Task state. */\nexport interface FactoryHooks {\n /** Hook definition to export from the target Eve agent's `hooks/` directory. */\n readonly factory: HookDefinition<FactoryHookEvent>;\n}\n\n/**\n * The turn contract's escalation question: a specialist turn ended cleanly\n * (eve durably recorded `turn.completed`) but the bound task is still\n * `running`, so no transition was recorded. That is never a \"worker died\n * mid-turn\" situation -- eve confirms the turn finished -- so retrying the\n * identical brief cannot succeed any differently. Escalating straight to\n * `needs_human` instead of waiting for the stall window (up to 45 minutes,\n * times `maxAttempts`) surfaces the dead end immediately. \"Close the task\"\n * is a declared closingOptions entry: answering with it resolves the task\n * instead of requeuing an identical retry.\n */\nconst UNRESOLVED_TURN_QUESTION: z.infer<typeof taskQuestionSchema> = {\n text: \"The specialist ended its turn without recording a result for this task. Reply to retry it, or close it if the work no longer applies (for example, a newer attempt already superseded it).\",\n options: [\"Retry the task\", \"Close the task\"],\n closingOptions: [\"Close the task\"],\n};\n\n/**\n * Creates observe-only Eve hooks that project session outcomes onto durable Factory state.\n *\n * @remarks\n * step.completed: records the step's tokens and AI Gateway cost on the bound\n * task. Delivery is at least once; recordUsage dedupes by (sessionId, turnId, stepIndex).\n *\n * turn.failed/session.failed: first recovers an opted-in saved workflow result, then re-queues the current attempt, or\n * fails it when its configured attempts are exhausted. turn.cancelled marks\n * the current Task cancelled. Late events from prior attempts are ignored.\n *\n * turn.completed: recovers an opted-in saved workflow result before generic escalation, and\n * settles the bound task's recorded usage if the task is\n * already terminal, and enforces the turn contract otherwise: a still-running\n * bound task (or a still-running task at this session's reply address, for\n * conversational continuations that never got their own bound session)\n * escalates immediately to a surfaced human question instead of waiting out\n * the stall window, because the agent finished without recording an outcome.\n * When the bound session matches the persisted execution, that question's work input includes\n * `systemReason: \"unresolved_turn\"` and `completedSessionId`. Application recovery can distinguish\n * this completed execution from an agent's genuine request for human input.\n *\n * Every store call is swallowed: a hook throw fails the turn.\n * Transcript projection writes at session response boundaries are likewise\n * best-effort; the API can rebuild a missing projection from durable events.\n *\n * Export the returned `factory` hook from the target agent's `hooks/` directory. The hook reads\n * the Task ID and attempt written by `withFactoryTask`; late events from replaced attempts cannot\n * advance current work. Provider failure recovery requeues below `maxAttempts` and fails the Task\n * at the limit. A clean turn that records no outcome creates a human question instead of silently\n * retrying the same brief.\n *\n * @param stores - Task store used for usage, recovery, cancellation, and completion projection.\n * @param options - Attempt limits plus application-owned question and conversation callbacks.\n * @returns An Eve hook definition under the `factory` property.\n * @see The shipped `docs/recipes/retry-recovery.md` recipe for bounded replacement attempts.\n *\n * @example\n * ```ts\n * import { createFactoryHooks, type FactoryHooksStores } from \"@vercel/factory\";\n *\n * declare const stores: FactoryHooksStores;\n * const hooks = createFactoryHooks(stores, {\n * presentQuestion: async (questionTask) => {\n * // Deliver the persisted question through the application's operator channel.\n * },\n * });\n * export default hooks.factory;\n * ```\n */\nexport function createFactoryHooks(\n stores: FactoryHooksStores,\n options: FactoryHooksOptions,\n): FactoryHooks {\n const taskIdAttribute = options.taskIdAttribute ?? \"factoryTaskId\";\n const taskAttemptAttribute = options.taskAttemptAttribute ?? \"factoryTaskAttempt\";\n const maxAttempts = z\n .number()\n .int()\n .min(1)\n .parse(options.maxAttempts ?? 3);\n\n function boundTaskId(ctx: Pick<SessionContext, \"session\">): TaskId | undefined {\n const parsed = taskIdSchema.safeParse(ctx.session.auth.initiator?.attributes[taskIdAttribute]);\n return parsed.success ? parsed.data : undefined;\n }\n\n function boundExecution(ctx: Pick<SessionContext, \"session\">): TaskAttemptRef | undefined {\n const attributes = ctx.session.auth.initiator?.attributes;\n const taskId = boundTaskId(ctx);\n const attempt = z.coerce.number().int().min(1).safeParse(attributes?.[taskAttemptAttribute]);\n return taskId !== undefined && attempt.success ? { taskId, attempt: attempt.data } : undefined;\n }\n\n async function recoverBoundResult(\n task: Task,\n ctx: Pick<SessionContext, \"session\">,\n ): Promise<boolean> {\n const binding = boundExecution(ctx);\n if (\n binding?.taskId !== task.id ||\n binding.attempt !== task.attempt ||\n task.execution?.provider !== \"eve\" ||\n task.execution.sessionId !== ctx.session.id\n )\n return false;\n return (await options.recoverResult?.(task)) ?? false;\n }\n\n async function recoverFailedAttempt(\n binding: TaskAttemptRef,\n failure: string,\n ctx: Pick<SessionContext, \"session\">,\n ): Promise<void> {\n const task = await stores.tasks.get(binding.taskId);\n if (\n !task ||\n task.attempt !== binding.attempt ||\n (options.recoverResult &&\n task.execution &&\n (task.execution.provider !== \"eve\" || task.execution.sessionId !== ctx.session.id))\n ) {\n return;\n }\n if (await recoverBoundResult(task, ctx)) {\n await settleIfTerminal(task.id);\n return;\n }\n if (task.state !== \"running\") return;\n const exhausted = task.attempt >= maxAttempts;\n await stores.tasks.transition(task.id, exhausted ? \"failed\" : \"queued\", {\n expectFrom: \"running\",\n expectAttempt: binding.attempt,\n phase: \"retry\",\n reason: exhausted\n ? `${failure} on attempt ${task.attempt} of ${maxAttempts}; attempts exhausted`\n : `${failure} on attempt ${task.attempt} of ${maxAttempts}; replacement queued`,\n });\n }\n\n async function cancelAttempt(binding: TaskAttemptRef, reason: string): Promise<void> {\n const task = await stores.tasks.get(binding.taskId);\n if (task?.state !== \"running\" || task.attempt !== binding.attempt) {\n return;\n }\n await stores.tasks.transition(task.id, \"cancelled\", {\n expectFrom: \"running\",\n expectAttempt: binding.attempt,\n phase: \"execution\",\n reason,\n });\n }\n\n async function isConversation(task: Task): Promise<boolean> {\n try {\n return (await options.isConversation?.(task)) ?? false;\n } catch {\n return false;\n }\n }\n\n async function settleIfTerminal(taskId: TaskId): Promise<void> {\n const task = await stores.tasks.get(taskId);\n if (\n task?.usage === undefined ||\n task.settledUsd !== undefined ||\n !terminalTaskStates.has(task.state)\n ) {\n return;\n }\n await stores.tasks.settle(task.id, {\n cost: { totalUsd: task.usage.costUsd },\n ...{\n reason: \"measured usage settled at turn end\",\n },\n });\n }\n\n async function escalateUnresolvedTurn(task: Task, completedSessionId?: string): Promise<void> {\n await createAndPresentQuestion(\n stores,\n task,\n UNRESOLVED_TURN_QUESTION,\n options.presentQuestion,\n completedSessionId,\n ).catch(() => undefined);\n }\n\n return {\n factory: defineHook({\n events: {\n async \"step.completed\"(event, ctx) {\n const taskId = boundTaskId(ctx);\n const usage = event.data.usage;\n if (taskId === undefined || usage === undefined) {\n return;\n }\n const binding = boundExecution(ctx);\n if (binding !== undefined) {\n const task = await stores.tasks.get(taskId).catch(() => null);\n if (task?.attempt !== binding.attempt) return;\n }\n await stores.tasks\n .recordUsage(taskId, {\n step: {\n turnId: JSON.stringify([ctx.session.id, event.data.turnId]),\n stepIndex: event.data.stepIndex,\n ...usage,\n },\n })\n .catch(() => undefined);\n },\n async \"turn.failed\"(event, ctx) {\n const binding = boundExecution(ctx);\n if (binding !== undefined) {\n await recoverFailedAttempt(\n binding,\n `provider turn failed (${event.data.code}): ${event.data.message}`,\n ctx,\n ).catch(() => undefined);\n return;\n }\n const taskId = boundTaskId(ctx);\n const task = taskId ? await stores.tasks.get(taskId).catch(() => null) : null;\n if (task?.state === \"running\" && (await isConversation(task))) {\n await stores.tasks\n .transition(task.id, \"failed\", {\n expectFrom: \"running\",\n reason: \"Conversation turn failed\",\n })\n .catch(() => undefined);\n await settleIfTerminal(task.id).catch(() => undefined);\n }\n },\n async \"session.failed\"(event, ctx) {\n const binding = boundExecution(ctx);\n if (binding !== undefined) {\n await recoverFailedAttempt(\n binding,\n `provider session failed (${event.data.code}): ${event.data.message}`,\n ctx,\n ).catch(() => undefined);\n }\n await options.materializeTranscript?.(materializationInput(ctx)).catch(() => undefined);\n },\n async \"session.waiting\"(_event, ctx) {\n await options.materializeTranscript?.(materializationInput(ctx)).catch(() => undefined);\n },\n async \"session.completed\"(_event, ctx) {\n await options.materializeTranscript?.(materializationInput(ctx)).catch(() => undefined);\n },\n async \"turn.cancelled\"(_event, ctx) {\n const binding = boundExecution(ctx);\n if (binding !== undefined) {\n await cancelAttempt(\n binding,\n `provider turn cancelled on attempt ${binding.attempt}`,\n ).catch(() => undefined);\n return;\n }\n const taskId = boundTaskId(ctx);\n const task = taskId ? await stores.tasks.get(taskId).catch(() => null) : null;\n if (task && (await isConversation(task))) {\n if (task.state === \"running\")\n await stores.tasks\n .transition(task.id, \"cancelled\", {\n expectFrom: \"running\",\n reason: \"Conversation turn interrupted\",\n })\n .catch(() => undefined);\n await settleIfTerminal(task.id).catch(() => undefined);\n }\n },\n async \"turn.completed\"(_event, ctx) {\n const taskId = boundTaskId(ctx);\n const binding = boundExecution(ctx);\n if (taskId !== undefined) {\n const task = await stores.tasks.get(taskId).catch(() => null);\n if (binding !== undefined && task?.attempt !== binding.attempt) return;\n if (\n binding &&\n options.recoverResult &&\n task?.execution &&\n (task.execution.provider !== \"eve\" || task.execution.sessionId !== ctx.session.id)\n )\n return;\n let recovered: boolean;\n try {\n recovered = task !== null && (await recoverBoundResult(task, ctx));\n } catch {\n // A saved outcome may exist; storage failure must not authorize escalation.\n return;\n }\n if (!recovered && task?.state === \"running\" && !task.waitingForMessages) {\n const completion = await options\n .completeConversation?.(task)\n .catch(() => false as const);\n if (completion) {\n await stores.tasks\n .transition(taskId, \"succeeded\", {\n expectFrom: \"running\",\n expectAttempt: task.attempt,\n reason: \"Conversation turn completed\",\n output: completion.output,\n })\n .catch(() => undefined);\n } else {\n // The turn genuinely finished (this is `turn.completed`, not a\n // crash or an interrupted turn.failed/turn.cancelled) with the\n // task still running: nothing was recorded, and an identical\n // retry cannot do better than this one did. Enforce the turn\n // contract now instead of leaving it to the 45-minute stall\n // window, which would otherwise burn up to `maxAttempts`\n // replacement attempts on a task no attempt can ever finish.\n await escalateUnresolvedTurn(\n task,\n binding &&\n task.execution?.provider === \"eve\" &&\n task.execution.sessionId === ctx.session.id\n ? ctx.session.id\n : undefined,\n );\n }\n }\n await settleIfTerminal(taskId).catch(() => undefined);\n }\n const address = ctx.channel.continuationToken;\n if (address === undefined) {\n return;\n }\n const tasks = await stores.tasks.list().catch(() => []);\n const abandoned = tasks.filter(\n (task) =>\n task.id !== taskId &&\n task.state === \"running\" &&\n !task.waitingForMessages &&\n task.replyTo.address === address,\n );\n for (const task of abandoned) {\n await escalateUnresolvedTurn(task);\n }\n },\n },\n }),\n };\n}\n\n/** The minimal command-running sandbox surface used by Eve tools. */\nexport {\n createDelegateAgentTool,\n createInvokeAgentTool,\n type DelegateAgentLaunchInput,\n type DelegateAgentTool,\n type DelegateAgentToolInput,\n type DelegateAgentToolOptions,\n type DelegateAgentToolStores,\n type DelegateWorkflowInput,\n type DelegateWorkflowPolicy,\n type DelegateWorkflowResult,\n type InvokeAgentTool,\n type InvokeAgentToolInput,\n type InvokeAgentToolOptions,\n type InvokeAgentToolResult,\n type InvokeAgentToolStores,\n} from \"./invoke\";\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/eve/index.ts"],"sourcesContent":["import { collectFactoryRecords } from \"../store/iterate\";\nimport type { SessionContext } from \"eve/context\";\nimport { defineHook, type HookDefinition } from \"eve/hooks\";\nimport { defineTool, type ToolContext, type ToolDefinition } from \"eve/tools\";\nimport { z } from \"zod\";\nimport {\n changeIdSchema,\n repositoryIdsSchema,\n signalIdSchema,\n taskIdSchema,\n type ChangeId,\n type RepositoryIds,\n type SignalId,\n type TaskId,\n} from \"../schema/id\";\nimport {\n receiptKindSchema,\n receiptStateSchema,\n type Receipt,\n type ReceiptKind,\n type ReceiptState,\n} from \"../schema/receipt\";\nimport type { MaybePromise } from \"../schema/common\";\nimport type { TaskAttemptRef } from \"../schema/execution\";\nimport { taskQuestionSchema, type Task, type TaskQuestion } from \"../schema/task\";\nimport type { Signal, SignalOutcome } from \"../schema/signal\";\nimport { taskWork, type JsonValue } from \"../schema/work\";\nimport type { AnswerQuestionResult, FactoryStores } from \"../store/engine\";\nimport type { EveTranscriptMaterializer, TranscriptMaterializationInput } from \"./transcript\";\nimport { requireTaskExecution } from \"./task-execution\";\n\nexport {\n conversationBinding,\n conversationBindingForSession,\n conversationHeaders,\n factoryReplyAddressAttribute,\n factoryReplyAddressHeader,\n factoryReplyChannelAttribute,\n factoryReplyChannelHeader,\n factoryRepositoryIdsAttribute,\n factoryRepositoryIdsHeader,\n factoryTaskAttemptAttribute,\n factoryTaskAttemptHeader,\n factoryTaskAttribute,\n factoryTaskHeader,\n hasFactoryTaskBinding,\n optionalTaskIdForSession,\n requireTaskIdForSession,\n replyToForSession,\n repositoryIdsForSession,\n taskBindingForSession,\n taskHeaders,\n taskSessionAuth,\n withFactoryTask,\n withOptionalFactoryTask,\n type ConversationBinding,\n type FactoryConversationSessionBinding,\n} from \"./task-session\";\nexport {\n createEveTranscriptMaterializer,\n type EveTranscriptMaterializerOptions,\n type EveTranscriptMaterializer,\n type EveTranscriptRequestScope,\n type TranscriptMaterializationInput,\n} from \"./transcript\";\nexport {\n cancelEveSession,\n forwardEveSessionRequest,\n readCompletedEveTurn,\n startEveSession,\n type CancelEveSessionOptions,\n type EveSessionEvent,\n type EveSessionStream,\n type EveSessionStreamInput,\n type ForwardEveSessionRequestOptions,\n type ReadCompletedEveTurnOptions,\n type StartEveSessionOptions,\n} from \"./session-client\";\n\nconst decidedOutcomeSchema = z.enum([\n \"accepted\",\n \"duplicate\",\n \"unactionable\",\n \"low_confidence\",\n \"ignored\",\n]);\n\n/** Receipt, Signal, and Task capabilities used by the provider-neutral Eve tool bundle. */\nexport type TaskToolsStores = Pick<FactoryStores, \"receipts\" | \"signals\" | \"tasks\">;\n\n/** Task capability used to project Eve lifecycle events onto durable Factory state. */\nexport type FactoryHooksStores = Pick<FactoryStores, \"tasks\">;\n\n/** Configures the provider-neutral Task, signal, question, and receipt Eve tools. */\nexport interface TaskToolsOptions {\n /** Present a persisted human question through the application's Foreman communication boundary. */\n presentQuestion?: (task: Task) => Promise<void>;\n /** Return a refusal reason when this task must use a more specific outcome tool. */\n finishTaskPolicy?: (task: Task) => MaybePromise<string | null>;\n}\n\n/** Delivers a persisted human-question Task through an application-owned channel. */\nexport type PresentQuestion = NonNullable<TaskToolsOptions[\"presentQuestion\"]>;\n\n/** Identifies the Task to read through the provider-neutral task tool. */\nexport interface GetTaskToolInput {\n taskId: TaskId;\n}\n\n/** Explicit not-found result returned when a requested Task does not exist. */\nexport interface TaskNotFoundResult {\n readonly found: false;\n readonly taskId: TaskId;\n}\n\n/** Existing Task or explicit not-found result returned by the task lookup tool. */\nexport type GetTaskToolResult = Task | TaskNotFoundResult;\n\n/** Provider-neutral Eve tool for reading one Factory Task. */\nexport interface GetTaskTool extends ToolDefinition<GetTaskToolInput, GetTaskToolResult> {\n execute(input: GetTaskToolInput, ctx: ToolContext): Promise<GetTaskToolResult>;\n}\n\n/** One final triage decision submitted for a pending Signal. */\nexport interface DecideSignalToolInput {\n signalId: SignalId;\n outcome: Exclude<SignalOutcome, \"pending\">;\n reason: string;\n detail?: string;\n}\n\n/** Provider-neutral Eve tool for recording a Signal decision. */\nexport interface DecideSignalTool extends ToolDefinition<DecideSignalToolInput, Signal> {\n execute(input: DecideSignalToolInput, ctx: ToolContext): Promise<Signal>;\n}\n\n/** Running Task and summary supplied to the generic completion tool. */\nexport interface FinishTaskToolInput {\n taskId: TaskId;\n summary: string;\n}\n\n/** Successful generic Task completion. */\nexport interface FinishTaskSuccess {\n readonly finished: true;\n readonly taskState: \"succeeded\";\n}\n\n/** Policy or lifecycle refusal returned by the generic Task completion tool. */\nexport interface FinishTaskRefusal {\n readonly finished: false;\n readonly reason: string;\n}\n\n/** Completion or bounded refusal returned by the generic Task completion tool. */\nexport type FinishTaskToolResult = FinishTaskSuccess | FinishTaskRefusal;\n\n/** Provider-neutral Eve tool for completing a running Task. */\nexport interface FinishTaskTool extends ToolDefinition<FinishTaskToolInput, FinishTaskToolResult> {\n execute(input: FinishTaskToolInput, ctx: ToolContext): Promise<FinishTaskToolResult>;\n}\n\n/** Unanswered question shape accepted when a Task requests human input. */\nexport type HumanInputQuestion = Omit<TaskQuestion, \"answer\" | \"answeredBy\">;\n\n/** Currently running parent Task and its single human question for this execution. */\nexport interface RequestHumanInputToolInput {\n parentTaskId: TaskId;\n question: HumanInputQuestion;\n}\n\n/** Provider-neutral Eve tool for pausing one running execution on one persisted human question. */\nexport interface RequestHumanInputTool extends ToolDefinition<RequestHumanInputToolInput, Task> {\n execute(input: RequestHumanInputToolInput, ctx: ToolContext): Promise<Task>;\n}\n\n/** Question Task and authenticated answer supplied to the answer tool. */\nexport interface AnswerQuestionToolInput {\n questionTaskId: TaskId;\n answer: string;\n}\n\n/** Provider-neutral Eve tool for answering a persisted human question. */\nexport interface AnswerQuestionTool extends ToolDefinition<\n AnswerQuestionToolInput,\n AnswerQuestionResult\n> {\n execute(input: AnswerQuestionToolInput, ctx: ToolContext): Promise<AnswerQuestionResult>;\n}\n\n/** Immutable audit event accepted by the generic receipt tool. */\nexport interface AppendReceiptToolInput {\n repositoryIds: RepositoryIds;\n taskId?: TaskId;\n changeId?: ChangeId;\n kind: ReceiptKind;\n state: ReceiptState;\n reason?: string;\n dedupeKey?: string;\n}\n\n/** Provider-neutral Eve tool for appending one immutable audit Receipt. */\nexport interface AppendReceiptTool extends ToolDefinition<AppendReceiptToolInput, Receipt> {\n execute(input: AppendReceiptToolInput, ctx: ToolContext): Promise<Receipt>;\n}\n\n/** The provider-neutral Task, Signal, question, and Receipt tools exposed to an Eve agent. */\nexport interface TaskTools {\n /** Read one Task by ID; applications decide which agents may expose this cross-Task lookup. */\n readonly get_task: GetTaskTool;\n /** Record the one final decision for a pending Signal. */\n readonly decide_signal: DecideSignalTool;\n /** Complete generic `task@1` work after authenticating the current Eve execution. */\n readonly finish_task: FinishTaskTool;\n /** Persist and present one human question for the authenticated running Task. */\n readonly request_human_input: RequestHumanInputTool;\n /** Answer a question only from its authenticated reply conversation. */\n readonly answer_question: AnswerQuestionTool;\n /** Append immutable application audit evidence with optional deduplication. */\n readonly append_receipt: AppendReceiptTool;\n}\n\nasync function createAndPresentQuestion(\n stores: Pick<FactoryStores, \"tasks\">,\n parent: Task,\n question: z.infer<typeof taskQuestionSchema>,\n presentQuestion: PresentQuestion,\n completedSessionId?: string,\n): Promise<Task> {\n const needsHumanAction = question.text.trim();\n if (needsHumanAction === \"\") throw new Error(\"A human question must contain non-blank text.\");\n const task = await stores.tasks.create({\n repositoryIds: parent.repositoryIds,\n kind: \"question\",\n work: taskWork({\n title: needsHumanAction.slice(0, 256),\n input: {\n question,\n ...(completedSessionId ? { systemReason: \"unresolved_turn\", completedSessionId } : {}),\n },\n }),\n parentTaskId: parent.id,\n parentTaskAttempt: parent.attempt,\n origin: {},\n replyTo: parent.replyTo,\n question,\n });\n try {\n const waiting = await stores.tasks.transition(task.id, \"needs_human\", { needsHumanAction });\n if (parent.state === \"running\") {\n await stores.tasks.transition(parent.id, \"needs_human\", {\n expectFrom: \"running\",\n expectAttempt: parent.attempt,\n reason: `waiting for answer to question ${waiting.id}`,\n questionTaskId: waiting.id,\n needsHumanAction,\n });\n }\n await presentQuestion(waiting);\n return waiting;\n } catch (error) {\n const currentParent = await stores.tasks.get(parent.id);\n if (\n currentParent?.state === \"needs_human\" &&\n currentParent.attempt === parent.attempt &&\n currentParent.questionTaskId === task.id\n ) {\n try {\n await stores.tasks.transition(parent.id, \"queued\", {\n expectFrom: \"needs_human\",\n expectAttempt: currentParent.attempt,\n expectQuestionTaskId: task.id,\n reason: `delivery of question ${task.id} failed`,\n });\n } catch (cleanupError) {\n const latest = await stores.tasks.get(parent.id);\n if (\n latest?.state === \"needs_human\" &&\n latest.attempt === currentParent.attempt &&\n latest.questionTaskId === task.id\n )\n throw cleanupError;\n }\n }\n const currentQuestion = await stores.tasks.get(task.id);\n if (currentQuestion?.state === \"needs_human\" || currentQuestion?.state === \"queued\") {\n await stores.tasks.transition(task.id, \"cancelled\", {\n expectFrom: currentQuestion.state,\n expectAttempt: currentQuestion.attempt,\n reason: \"question delivery failed\",\n });\n }\n throw error;\n }\n}\n\n/**\n * Wraps provider-neutral Factory store operations as Eve tools.\n *\n * @remarks\n * Expose only the returned tools each agent role needs. Every write goes through the validating\n * engine, but tool visibility remains application authorization policy. `finish_task` and\n * `request_human_input` recheck the current authenticated Task attempt and Eve session before\n * mutating state. `get_task`, signal decisions, and generic receipts are broader capabilities and\n * should be mounted only for agents allowed to use them.\n *\n * The generic `finish_task` records `{ summary }` for `task@1`. Schema-specific workflows should\n * define a narrow tool that calls `requireTaskExecution` and `stores.work.completeWorkflow`, as in\n * the shipped `docs/recipes/typed-eve-result.md` recipe.\n *\n * @param stores - Receipt, Signal, and Task capabilities available to the tool bundle.\n * @param options - Optional question presentation and generic-completion policy.\n * @returns A new named Eve tool bundle ready for selective mounting under `agent/tools/`.\n *\n * @example\n * ```ts\n * import { createTaskTools, type TaskToolsStores } from \"@vercel/factory\";\n *\n * declare const stores: TaskToolsStores;\n * const tools = createTaskTools(stores);\n * export default tools.get_task;\n * ```\n */\nexport function createTaskTools(\n stores: TaskToolsStores,\n options: TaskToolsOptions = {},\n): TaskTools {\n return {\n get_task: defineTool({\n description:\n \"Read one factory task by id: its kind, state, repositories, origin, and results.\",\n inputSchema: z.object({ taskId: taskIdSchema }),\n async execute({ taskId }) {\n const task = await stores.tasks.get(taskId);\n return task ?? { found: false as const, taskId };\n },\n }),\n\n decide_signal: defineTool({\n description:\n \"Record the triage decision for a pending signal. A signal is decided exactly once.\",\n inputSchema: z.object({\n signalId: signalIdSchema,\n outcome: decidedOutcomeSchema,\n reason: z.string().min(1).describe(\"Why this outcome was chosen.\"),\n detail: z\n .string()\n .min(1)\n .optional()\n .describe(\"Severity if accepted, or the duplicate-of signal id.\"),\n }),\n execute({ signalId, outcome, reason, detail }) {\n return stores.signals.decide(signalId, { outcome: outcome, ...{ reason, detail } });\n },\n }),\n\n finish_task: defineTool({\n description:\n \"Mark your running task finished with a summary. The configured workflow policy may require a more specific outcome tool.\",\n inputSchema: z.object({\n taskId: taskIdSchema,\n summary: z.string().min(1).max(4000).describe(\"What you did and found, for the ledger.\"),\n }),\n async execute({ taskId, summary }, ctx) {\n const task = await requireTaskExecution(stores, ctx, taskId);\n const refusal = await options.finishTaskPolicy?.(task);\n if (refusal !== undefined && refusal !== null) {\n return {\n finished: false as const,\n reason: refusal,\n };\n }\n if (task.state !== \"running\") {\n return { finished: false as const, reason: `task is ${task.state}, not running` };\n }\n await stores.tasks.transition(taskId, \"succeeded\", {\n expectFrom: \"running\",\n expectAttempt: task.attempt,\n expectExecution: task.execution,\n reason: summary,\n output: { summary },\n });\n return { finished: true as const, taskState: \"succeeded\" as const };\n },\n }),\n\n request_human_input: defineTool({\n description:\n 'Request one human input or action and pause your currently running task until the human replies. A paused task cannot request another question; wait for the answer and replacement execution. The parent task determines the repositories and reply destination. Name any suggested reply in question.closingOptions when that exact answer means no further attempt can help (for example \"nothing to do, close it\"); Factory resolves the task instead of requeuing an identical retry when the human picks one.',\n inputSchema: z.object({\n parentTaskId: taskIdSchema.describe(\"Your running task, which the answer will unblock.\"),\n question: taskQuestionSchema.omit({ answer: true, answeredBy: true }),\n }),\n async execute({ parentTaskId, question }, ctx) {\n if (options.presentQuestion === undefined) {\n throw new Error(\"request_human_input requires a configured question presenter.\");\n }\n const parent = await requireTaskExecution(stores, ctx, parentTaskId);\n return createAndPresentQuestion(stores, parent, question, options.presentQuestion);\n },\n }),\n\n answer_question: defineTool({\n description:\n \"Record the authenticated user's answer to a waiting question and requeue the parent specialist task, unless the answer matches one of the question's closingOptions, which resolves the parent task instead. A blocked parent task id is also accepted and resolves its persisted QuestionTask id.\",\n inputSchema: z.object({\n questionTaskId: taskIdSchema,\n answer: z.string().min(1).max(4000),\n }),\n async execute({ questionTaskId, answer }, ctx) {\n const requestedTask = await stores.tasks.get(questionTaskId);\n const resolvedQuestionTaskId =\n requestedTask?.kind === \"question\" ? requestedTask.id : requestedTask?.questionTaskId;\n const questionTask =\n resolvedQuestionTaskId === undefined\n ? null\n : await stores.tasks.get(resolvedQuestionTaskId);\n if (\n questionTask === null ||\n questionTask.kind !== \"question\" ||\n questionTask.question === undefined\n ) {\n throw new Error(`Question task ${questionTaskId} does not exist.`);\n }\n const { initiator, current } = ctx.session.auth;\n if (\n !initiator ||\n !current ||\n [initiator, current].some(\n ({ attributes }) =>\n attributes.factoryReplyChannel !== questionTask.replyTo.channel ||\n attributes.factoryReplyAddress !== questionTask.replyTo.address,\n )\n ) {\n throw new Error(\"Question reply does not match the authenticated conversation.\");\n }\n const answeredBy = current.principalId;\n return stores.tasks.answerQuestion({\n questionTaskId: questionTask.id,\n answer,\n answeredBy,\n });\n },\n }),\n\n append_receipt: defineTool({\n description:\n \"Append one immutable audit receipt for an event worth remembering. Receipts can never be edited or deleted.\",\n inputSchema: z.object({\n repositoryIds: repositoryIdsSchema,\n taskId: taskIdSchema.optional(),\n changeId: changeIdSchema.optional(),\n kind: receiptKindSchema.describe(\"What the receipt is about, e.g. investigation.\"),\n state: receiptStateSchema.describe(\"What happened, e.g. flag_ramped.\"),\n reason: z.string().min(1).max(2000).optional(),\n dedupeKey: z\n .string()\n .min(1)\n .optional()\n .describe(\"Same key twice records one receipt instead of two.\"),\n }),\n execute(input) {\n return stores.receipts.append(input);\n },\n }),\n };\n}\n\n/** Completed conversation output, or `false` when the Task is not a conversation. */\nexport type ConversationCompletionResult = { readonly output: JsonValue } | false;\n\n/** Configures attempt recovery, conversation completion, and transcript projection for Eve hooks. */\nexport interface FactoryHooksOptions {\n /** Session auth attribute naming the task a dispatched session works on. Default `factoryTaskId`. */\n taskIdAttribute?: string;\n /** Session auth attribute naming the execution attempt. Default `factoryTaskAttempt`. */\n taskAttemptAttribute?: string;\n /** Attempts allowed before an explicit provider failure becomes terminal. Default: 3. */\n maxAttempts?: number;\n /**\n * Recover a saved workflow result for the exact bound execution before failure retry or\n * unresolved-turn escalation. Return true when handled; a throw defers generic lifecycle changes.\n */\n recoverResult?: (task: Task) => Promise<boolean>;\n /** Present the recovery question created when a task turn ends without an outcome. */\n presentQuestion: PresentQuestion;\n /** Classify discussion Tasks on failure or cancellation without collecting a successful response. */\n isConversation?: (task: Task) => MaybePromise<boolean>;\n /** Return the actual completed response for an opted-in discussion, or false for other work. */\n completeConversation?: (task: Task) => Promise<ConversationCompletionResult>;\n /** Persist the browser-safe transcript projection after Eve durably records a response boundary. */\n materializeTranscript?: EveTranscriptMaterializer;\n}\n\nfunction materializationInput(\n ctx: import(\"eve/hooks\").HookContext,\n): TranscriptMaterializationInput {\n return {\n agent: ctx.agent.name,\n execution: { provider: \"eve\", sessionId: ctx.session.id },\n attributes: ctx.session.auth.initiator?.attributes ?? {},\n };\n}\n\n/** Observe-only hook for agents that do not use the Factory task-lifecycle hook. */\nexport function createTranscriptSnapshotHook(\n materialize: EveTranscriptMaterializer,\n): TranscriptSnapshotHook {\n const snapshot = (_event: unknown, ctx: import(\"eve/hooks\").HookContext) =>\n materialize(materializationInput(ctx)).catch(() => undefined);\n return defineHook({\n events: {\n \"session.waiting\": snapshot,\n \"session.completed\": snapshot,\n \"session.failed\": snapshot,\n },\n });\n}\n\n/** Observe-only Eve hook that materializes transcript snapshots at response boundaries. */\nexport type TranscriptSnapshotHook = HookDefinition<\n \"session.waiting\" | \"session.completed\" | \"session.failed\"\n>;\n\nconst terminalTaskStates: ReadonlySet<Task[\"state\"]> = new Set([\n \"succeeded\",\n \"failed\",\n \"cancelled\",\n]);\n\ntype FactoryHookEvent =\n | \"step.completed\"\n | \"turn.failed\"\n | \"session.failed\"\n | \"session.waiting\"\n | \"session.completed\"\n | \"turn.cancelled\"\n | \"turn.completed\";\n\n/** The Eve lifecycle hook projected onto durable Factory Task state. */\nexport interface FactoryHooks {\n /** Hook definition to export from the target Eve agent's `hooks/` directory. */\n readonly factory: HookDefinition<FactoryHookEvent>;\n}\n\n/**\n * The turn contract's escalation question: a specialist turn ended cleanly\n * (eve durably recorded `turn.completed`) but the bound task is still\n * `running`, so no transition was recorded. That is never a \"worker died\n * mid-turn\" situation -- eve confirms the turn finished -- so retrying the\n * identical brief cannot succeed any differently. Escalating straight to\n * `needs_human` instead of waiting for the stall window (up to 45 minutes,\n * times `maxAttempts`) surfaces the dead end immediately. \"Close the task\"\n * is a declared closingOptions entry: answering with it resolves the task\n * instead of requeuing an identical retry.\n */\nconst UNRESOLVED_TURN_QUESTION: z.infer<typeof taskQuestionSchema> = {\n text: \"The specialist ended its turn without recording a result for this task. Reply to retry it, or close it if the work no longer applies (for example, a newer attempt already superseded it).\",\n options: [\"Retry the task\", \"Close the task\"],\n closingOptions: [\"Close the task\"],\n};\n\n/**\n * Creates observe-only Eve hooks that project session outcomes onto durable Factory state.\n *\n * @remarks\n * step.completed: records the step's tokens and AI Gateway cost on the bound\n * task. Delivery is at least once; recordUsage dedupes by (sessionId, turnId, stepIndex).\n *\n * turn.failed/session.failed: first recovers an opted-in saved workflow result, then re-queues the current attempt, or\n * fails it when its configured attempts are exhausted. turn.cancelled marks\n * the current Task cancelled. Late events from prior attempts are ignored.\n *\n * turn.completed: recovers an opted-in saved workflow result before generic escalation, and\n * settles the bound task's recorded usage if the task is\n * already terminal, and enforces the turn contract otherwise: a still-running\n * bound task (or a still-running task at this session's reply address, for\n * conversational continuations that never got their own bound session)\n * escalates immediately to a surfaced human question instead of waiting out\n * the stall window, because the agent finished without recording an outcome.\n * When the bound session matches the persisted execution, that question's work input includes\n * `systemReason: \"unresolved_turn\"` and `completedSessionId`. Application recovery can distinguish\n * this completed execution from an agent's genuine request for human input.\n *\n * Every store call is swallowed: a hook throw fails the turn.\n * Transcript projection writes at session response boundaries are likewise\n * best-effort; the API can rebuild a missing projection from durable events.\n *\n * Export the returned `factory` hook from the target agent's `hooks/` directory. The hook reads\n * the Task ID and attempt written by `withFactoryTask`; late events from replaced attempts cannot\n * advance current work. Provider failure recovery requeues below `maxAttempts` and fails the Task\n * at the limit. A clean turn that records no outcome creates a human question instead of silently\n * retrying the same brief.\n *\n * @param stores - Task store used for usage, recovery, cancellation, and completion projection.\n * @param options - Attempt limits plus application-owned question and conversation callbacks.\n * @returns An Eve hook definition under the `factory` property.\n * @see The shipped `docs/recipes/retry-recovery.md` recipe for bounded replacement attempts.\n *\n * @example\n * ```ts\n * import { createFactoryHooks, type FactoryHooksStores } from \"@vercel/factory\";\n *\n * declare const stores: FactoryHooksStores;\n * const hooks = createFactoryHooks(stores, {\n * presentQuestion: async (questionTask) => {\n * // Deliver the persisted question through the application's operator channel.\n * },\n * });\n * export default hooks.factory;\n * ```\n */\nexport function createFactoryHooks(\n stores: FactoryHooksStores,\n options: FactoryHooksOptions,\n): FactoryHooks {\n const taskIdAttribute = options.taskIdAttribute ?? \"factoryTaskId\";\n const taskAttemptAttribute = options.taskAttemptAttribute ?? \"factoryTaskAttempt\";\n const maxAttempts = z\n .number()\n .int()\n .min(1)\n .parse(options.maxAttempts ?? 3);\n\n function boundTaskId(ctx: Pick<SessionContext, \"session\">): TaskId | undefined {\n const parsed = taskIdSchema.safeParse(ctx.session.auth.initiator?.attributes[taskIdAttribute]);\n return parsed.success ? parsed.data : undefined;\n }\n\n function boundExecution(ctx: Pick<SessionContext, \"session\">): TaskAttemptRef | undefined {\n const attributes = ctx.session.auth.initiator?.attributes;\n const taskId = boundTaskId(ctx);\n const attempt = z.coerce.number().int().min(1).safeParse(attributes?.[taskAttemptAttribute]);\n return taskId !== undefined && attempt.success ? { taskId, attempt: attempt.data } : undefined;\n }\n\n async function recoverBoundResult(\n task: Task,\n ctx: Pick<SessionContext, \"session\">,\n ): Promise<boolean> {\n const binding = boundExecution(ctx);\n if (\n binding?.taskId !== task.id ||\n binding.attempt !== task.attempt ||\n task.execution?.provider !== \"eve\" ||\n task.execution.sessionId !== ctx.session.id\n )\n return false;\n return (await options.recoverResult?.(task)) ?? false;\n }\n\n async function recoverFailedAttempt(\n binding: TaskAttemptRef,\n failure: string,\n ctx: Pick<SessionContext, \"session\">,\n ): Promise<void> {\n const task = await stores.tasks.get(binding.taskId);\n if (\n !task ||\n task.attempt !== binding.attempt ||\n (options.recoverResult &&\n task.execution &&\n (task.execution.provider !== \"eve\" || task.execution.sessionId !== ctx.session.id))\n ) {\n return;\n }\n if (await recoverBoundResult(task, ctx)) {\n await settleIfTerminal(task.id);\n return;\n }\n if (task.state !== \"running\") return;\n const exhausted = task.attempt >= maxAttempts;\n await stores.tasks.transition(task.id, exhausted ? \"failed\" : \"queued\", {\n expectFrom: \"running\",\n expectAttempt: binding.attempt,\n phase: \"retry\",\n reason: exhausted\n ? `${failure} on attempt ${task.attempt} of ${maxAttempts}; attempts exhausted`\n : `${failure} on attempt ${task.attempt} of ${maxAttempts}; replacement queued`,\n });\n }\n\n async function cancelAttempt(binding: TaskAttemptRef, reason: string): Promise<void> {\n const task = await stores.tasks.get(binding.taskId);\n if (task?.state !== \"running\" || task.attempt !== binding.attempt) {\n return;\n }\n await stores.tasks.transition(task.id, \"cancelled\", {\n expectFrom: \"running\",\n expectAttempt: binding.attempt,\n phase: \"execution\",\n reason,\n });\n }\n\n async function isConversation(task: Task): Promise<boolean> {\n try {\n return (await options.isConversation?.(task)) ?? false;\n } catch {\n return false;\n }\n }\n\n async function settleIfTerminal(taskId: TaskId): Promise<void> {\n const task = await stores.tasks.get(taskId);\n if (\n task?.usage === undefined ||\n task.settledUsd !== undefined ||\n !terminalTaskStates.has(task.state)\n ) {\n return;\n }\n await stores.tasks.settle(task.id, {\n cost: { totalUsd: task.usage.costUsd },\n ...{\n reason: \"measured usage settled at turn end\",\n },\n });\n }\n\n async function escalateUnresolvedTurn(task: Task, completedSessionId?: string): Promise<void> {\n await createAndPresentQuestion(\n stores,\n task,\n UNRESOLVED_TURN_QUESTION,\n options.presentQuestion,\n completedSessionId,\n ).catch(() => undefined);\n }\n\n return {\n factory: defineHook({\n events: {\n async \"step.completed\"(event, ctx) {\n const taskId = boundTaskId(ctx);\n const usage = event.data.usage;\n if (taskId === undefined || usage === undefined) {\n return;\n }\n const binding = boundExecution(ctx);\n if (binding !== undefined) {\n const task = await stores.tasks.get(taskId).catch(() => null);\n if (task?.attempt !== binding.attempt) return;\n }\n await stores.tasks\n .recordUsage(taskId, {\n step: {\n turnId: JSON.stringify([ctx.session.id, event.data.turnId]),\n stepIndex: event.data.stepIndex,\n ...usage,\n },\n })\n .catch(() => undefined);\n },\n async \"turn.failed\"(event, ctx) {\n const binding = boundExecution(ctx);\n if (binding !== undefined) {\n await recoverFailedAttempt(\n binding,\n `provider turn failed (${event.data.code}): ${event.data.message}`,\n ctx,\n ).catch(() => undefined);\n return;\n }\n const taskId = boundTaskId(ctx);\n const task = taskId ? await stores.tasks.get(taskId).catch(() => null) : null;\n if (task?.state === \"running\" && (await isConversation(task))) {\n await stores.tasks\n .transition(task.id, \"failed\", {\n expectFrom: \"running\",\n reason: \"Conversation turn failed\",\n })\n .catch(() => undefined);\n await settleIfTerminal(task.id).catch(() => undefined);\n }\n },\n async \"session.failed\"(event, ctx) {\n const binding = boundExecution(ctx);\n if (binding !== undefined) {\n await recoverFailedAttempt(\n binding,\n `provider session failed (${event.data.code}): ${event.data.message}`,\n ctx,\n ).catch(() => undefined);\n }\n await options.materializeTranscript?.(materializationInput(ctx)).catch(() => undefined);\n },\n async \"session.waiting\"(_event, ctx) {\n await options.materializeTranscript?.(materializationInput(ctx)).catch(() => undefined);\n },\n async \"session.completed\"(_event, ctx) {\n await options.materializeTranscript?.(materializationInput(ctx)).catch(() => undefined);\n },\n async \"turn.cancelled\"(_event, ctx) {\n const binding = boundExecution(ctx);\n if (binding !== undefined) {\n await cancelAttempt(\n binding,\n `provider turn cancelled on attempt ${binding.attempt}`,\n ).catch(() => undefined);\n return;\n }\n const taskId = boundTaskId(ctx);\n const task = taskId ? await stores.tasks.get(taskId).catch(() => null) : null;\n if (task && (await isConversation(task))) {\n if (task.state === \"running\")\n await stores.tasks\n .transition(task.id, \"cancelled\", {\n expectFrom: \"running\",\n reason: \"Conversation turn interrupted\",\n })\n .catch(() => undefined);\n await settleIfTerminal(task.id).catch(() => undefined);\n }\n },\n async \"turn.completed\"(_event, ctx) {\n const taskId = boundTaskId(ctx);\n const binding = boundExecution(ctx);\n if (taskId !== undefined) {\n const task = await stores.tasks.get(taskId).catch(() => null);\n if (binding !== undefined && task?.attempt !== binding.attempt) return;\n if (\n binding &&\n options.recoverResult &&\n task?.execution &&\n (task.execution.provider !== \"eve\" || task.execution.sessionId !== ctx.session.id)\n )\n return;\n let recovered: boolean;\n try {\n recovered = task !== null && (await recoverBoundResult(task, ctx));\n } catch {\n // A saved outcome may exist; storage failure must not authorize escalation.\n return;\n }\n if (!recovered && task?.state === \"running\" && !task.waitingForMessages) {\n const completion = await options\n .completeConversation?.(task)\n .catch(() => false as const);\n if (completion) {\n await stores.tasks\n .transition(taskId, \"succeeded\", {\n expectFrom: \"running\",\n expectAttempt: task.attempt,\n reason: \"Conversation turn completed\",\n output: completion.output,\n })\n .catch(() => undefined);\n } else {\n // The turn genuinely finished (this is `turn.completed`, not a\n // crash or an interrupted turn.failed/turn.cancelled) with the\n // task still running: nothing was recorded, and an identical\n // retry cannot do better than this one did. Enforce the turn\n // contract now instead of leaving it to the 45-minute stall\n // window, which would otherwise burn up to `maxAttempts`\n // replacement attempts on a task no attempt can ever finish.\n await escalateUnresolvedTurn(\n task,\n binding &&\n task.execution?.provider === \"eve\" &&\n task.execution.sessionId === ctx.session.id\n ? ctx.session.id\n : undefined,\n );\n }\n }\n await settleIfTerminal(taskId).catch(() => undefined);\n }\n const address = ctx.channel.continuationToken;\n if (address === undefined) {\n return;\n }\n const tasks = await collectFactoryRecords(stores.tasks.list, {\n limit: 100,\n match: { state: \"running\", replyTo: { address } },\n }).catch(() => []);\n const abandoned = tasks.filter(\n (task) =>\n task.id !== taskId &&\n task.state === \"running\" &&\n !task.waitingForMessages &&\n task.replyTo.address === address,\n );\n for (const task of abandoned) {\n await escalateUnresolvedTurn(task);\n }\n },\n },\n }),\n };\n}\n\n/** The minimal command-running sandbox surface used by Eve tools. */\nexport {\n createDelegateAgentTool,\n createInvokeAgentTool,\n type DelegateAgentLaunchInput,\n type DelegateAgentTool,\n type DelegateAgentToolInput,\n type DelegateAgentToolOptions,\n type DelegateAgentToolStores,\n type DelegateWorkflowInput,\n type DelegateWorkflowPolicy,\n type DelegateWorkflowResult,\n type InvokeAgentTool,\n type InvokeAgentToolInput,\n type InvokeAgentToolOptions,\n type InvokeAgentToolResult,\n type InvokeAgentToolStores,\n} from \"./invoke\";\n"],"mappings":"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"}
@@ -67,6 +67,7 @@ declare function requireTaskExecution(stores: Pick<FactoryStores, "tasks">, ctx:
67
67
  };
68
68
  attempt: number;
69
69
  updatedAt: string;
70
+ createdAt: string;
70
71
  repositoryIds: readonly [`repo_${string}`, ...`repo_${string}`[]];
71
72
  kind: TaskKind;
72
73
  model?: string | undefined;
@@ -114,7 +115,6 @@ declare function requireTaskExecution(stores: Pick<FactoryStores, "tasks">, ctx:
114
115
  answerEventIds?: readonly `ans_${string}`[] | undefined;
115
116
  changeId?: `chg_${string}` | undefined;
116
117
  reportRef?: string | undefined;
117
- createdAt: string;
118
118
  } & {
119
119
  stalledAt?: never;
120
120
  stalledReason?: never;
@@ -143,6 +143,7 @@ declare function requireTaskExecution(stores: Pick<FactoryStores, "tasks">, ctx:
143
143
  };
144
144
  attempt: number;
145
145
  updatedAt: string;
146
+ createdAt: string;
146
147
  repositoryIds: readonly [`repo_${string}`, ...`repo_${string}`[]];
147
148
  kind: TaskKind;
148
149
  model?: string | undefined;
@@ -190,7 +191,6 @@ declare function requireTaskExecution(stores: Pick<FactoryStores, "tasks">, ctx:
190
191
  answerEventIds?: readonly `ans_${string}`[] | undefined;
191
192
  changeId?: `chg_${string}` | undefined;
192
193
  reportRef?: string | undefined;
193
- createdAt: string;
194
194
  } & {
195
195
  stalledAt?: never;
196
196
  stalledReason?: never;
@@ -225,6 +225,7 @@ declare function requireTaskExecution(stores: Pick<FactoryStores, "tasks">, ctx:
225
225
  };
226
226
  attempt: number;
227
227
  updatedAt: string;
228
+ createdAt: string;
228
229
  repositoryIds: readonly [`repo_${string}`, ...`repo_${string}`[]];
229
230
  kind: TaskKind;
230
231
  model?: string | undefined;
@@ -272,7 +273,6 @@ declare function requireTaskExecution(stores: Pick<FactoryStores, "tasks">, ctx:
272
273
  answerEventIds?: readonly `ans_${string}`[] | undefined;
273
274
  changeId?: `chg_${string}` | undefined;
274
275
  reportRef?: string | undefined;
275
- createdAt: string;
276
276
  } & {
277
277
  stalledAt?: never;
278
278
  stalledReason?: never;
@@ -307,6 +307,7 @@ declare function requireTaskExecution(stores: Pick<FactoryStores, "tasks">, ctx:
307
307
  };
308
308
  attempt: number;
309
309
  updatedAt: string;
310
+ createdAt: string;
310
311
  repositoryIds: readonly [`repo_${string}`, ...`repo_${string}`[]];
311
312
  kind: TaskKind;
312
313
  model?: string | undefined;
@@ -354,7 +355,6 @@ declare function requireTaskExecution(stores: Pick<FactoryStores, "tasks">, ctx:
354
355
  answerEventIds?: readonly `ans_${string}`[] | undefined;
355
356
  changeId?: `chg_${string}` | undefined;
356
357
  reportRef?: string | undefined;
357
- createdAt: string;
358
358
  } & {
359
359
  stalledAt?: never;
360
360
  stalledReason?: never;
@@ -22,7 +22,9 @@ interface EveTranscriptRequestScope {
22
22
  }
23
23
  /** Configures durable transcript storage and authenticated access to Eve session events. */
24
24
  interface EveTranscriptMaterializerOptions {
25
- stores: Pick<FactoryStores, "transcripts">;
25
+ stores: {
26
+ transcripts: Pick<FactoryStores["transcripts"], "get" | "put">;
27
+ };
26
28
  origin: string | (() => string);
27
29
  headers?(scope: EveTranscriptRequestScope): MaybePromise<HeadersInit>;
28
30
  fetch?: typeof globalThis.fetch;
@@ -1 +1 @@
1
- {"version":3,"file":"transcript.mjs","names":[],"sources":["../../src/eve/transcript.ts"],"sourcesContent":["import { readEvents } from \"../client-events\";\nimport { Transcript } from \"../client-transcript\";\nimport { replyToSchema, type MaybePromise, type ReplyTo } from \"../schema/common\";\nimport { taskAttemptRef, type TaskAttemptRef } from \"../schema/execution\";\nimport { repositoryIdSchema, taskIdSchema, type RepositoryId } from \"../schema/id\";\nimport type { FactoryStores } from \"../store/engine\";\nimport {\n factoryReplyAddressAttribute,\n factoryReplyChannelAttribute,\n factoryRepositoryIdsAttribute,\n factoryTaskAttemptAttribute,\n factoryTaskAttribute,\n} from \"./task-session\";\n\n/** Identifies the Eve execution and authenticated attributes to project into a transcript. */\nexport interface TranscriptMaterializationInput {\n agent: string;\n execution: { provider: \"eve\"; sessionId: string };\n attributes: Readonly<Record<string, string | readonly string[]>>;\n}\n\n/** The Task and conversation authorization context forwarded when reading an Eve transcript. */\nexport interface EveTranscriptRequestScope {\n task?: TaskAttemptRef;\n conversation?: { repositoryIds: readonly RepositoryId[]; replyTo: ReplyTo };\n}\n\n/** Configures durable transcript storage and authenticated access to Eve session events. */\nexport interface EveTranscriptMaterializerOptions {\n stores: Pick<FactoryStores, \"transcripts\">;\n origin: string | (() => string);\n headers?(scope: EveTranscriptRequestScope): MaybePromise<HeadersInit>;\n fetch?: typeof globalThis.fetch;\n timeoutMs?: number;\n}\n\n/** Materializes one Eve execution stream into Factory's browser-safe transcript store. */\nexport type EveTranscriptMaterializer = (input: TranscriptMaterializationInput) => Promise<void>;\n\nfunction attribute(\n attributes: TranscriptMaterializationInput[\"attributes\"],\n name: string,\n): string | undefined {\n const value = attributes[name];\n return typeof value === \"string\" ? value : undefined;\n}\n\nfunction scopeFor(input: TranscriptMaterializationInput): EveTranscriptRequestScope {\n const taskId = taskIdSchema.safeParse(attribute(input.attributes, factoryTaskAttribute));\n const attempt = Number(attribute(input.attributes, factoryTaskAttemptAttribute));\n const repositories = repositoryIdSchema\n .array()\n .min(1)\n .safeParse(\n attribute(input.attributes, factoryRepositoryIdsAttribute)?.split(\",\").filter(Boolean),\n );\n const replyTo = replyToSchema.safeParse({\n channel: attribute(input.attributes, factoryReplyChannelAttribute),\n address: attribute(input.attributes, factoryReplyAddressAttribute),\n });\n return {\n ...(taskId.success && Number.isSafeInteger(attempt) && attempt > 0\n ? { task: taskAttemptRef(taskId.data, attempt) }\n : {}),\n ...(repositories.success && replyTo.success\n ? { conversation: { repositoryIds: repositories.data, replyTo: replyTo.data } }\n : {}),\n };\n}\n\n/** Materialize a browser-safe transcript projection from one durable Eve session stream. */\nexport function createEveTranscriptMaterializer(\n options: EveTranscriptMaterializerOptions,\n): EveTranscriptMaterializer {\n const fetchImplementation = options.fetch ?? globalThis.fetch;\n const timeoutMs = options.timeoutMs ?? 30_000;\n return async (input: TranscriptMaterializationInput): Promise<void> => {\n const previous = await options.stores.transcripts.get(input.execution);\n const transcript = new Transcript();\n if (previous !== null) transcript.hydrate(previous);\n const query = new URLSearchParams({\n startIndex: String(transcript.cursor),\n includeTailIndex: \"1\",\n });\n const origin = typeof options.origin === \"string\" ? options.origin : options.origin();\n const response = await fetchImplementation(\n `${origin.replace(/\\/+$/u, \"\")}/eve/agents/${encodeURIComponent(input.agent)}/eve/v1/session/${encodeURIComponent(input.execution.sessionId)}/stream?${query}`,\n {\n cache: \"no-store\",\n headers: await options.headers?.(scopeFor(input)),\n signal: AbortSignal.timeout(timeoutMs),\n redirect: \"error\",\n },\n );\n if (!response.ok || !response.body) {\n await response.body?.cancel();\n throw new Error(`Eve refused transcript materialization: ${response.status}`);\n }\n const tailValue = response.headers.get(\"x-eve-stream-tail-index\");\n const tail = tailValue === null ? Number.NaN : Number(tailValue);\n if (!Number.isSafeInteger(tail) || tail < -1) {\n await response.body.cancel();\n throw new Error(\"Eve transcript materialization did not include a valid durable cursor\");\n }\n if (transcript.cursor <= tail) {\n for await (const event of readEvents(response.body)) {\n transcript.accept(event);\n if (transcript.cursor === tail + 1) break;\n }\n } else {\n await response.body.cancel();\n }\n if (transcript.cursor !== tail + 1) {\n throw new Error(\"Eve transcript materialization closed before its durable cursor\");\n }\n await options.stores.transcripts.put(input.execution, transcript.snapshot());\n };\n}\n"],"mappings":";;;;;;;AAuCA,SAAS,UACP,YACA,MACoB;CACpB,MAAM,QAAQ,WAAW;CACzB,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAA;AAC7C;AAEA,SAAS,SAAS,OAAkE;CAClF,MAAM,SAAS,aAAa,UAAU,UAAU,MAAM,YAAY,oBAAoB,CAAC;CACvF,MAAM,UAAU,OAAO,UAAU,MAAM,YAAY,2BAA2B,CAAC;CAC/E,MAAM,eAAe,mBAClB,MAAM,CAAC,CACP,IAAI,CAAC,CAAC,CACN,UACC,UAAU,MAAM,YAAY,6BAA6B,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO,CACvF;CACF,MAAM,UAAU,cAAc,UAAU;EACtC,SAAS,UAAU,MAAM,YAAY,4BAA4B;EACjE,SAAS,UAAU,MAAM,YAAY,4BAA4B;CACnE,CAAC;CACD,OAAO;EACL,GAAI,OAAO,WAAW,OAAO,cAAc,OAAO,KAAK,UAAU,IAC7D,EAAE,MAAM,eAAe,OAAO,MAAM,OAAO,EAAE,IAC7C,CAAC;EACL,GAAI,aAAa,WAAW,QAAQ,UAChC,EAAE,cAAc;GAAE,eAAe,aAAa;GAAM,SAAS,QAAQ;EAAK,EAAE,IAC5E,CAAC;CACP;AACF;;AAGA,SAAgB,gCACd,SAC2B;CAC3B,MAAM,sBAAsB,QAAQ,SAAS,WAAW;CACxD,MAAM,YAAY,QAAQ,aAAa;CACvC,OAAO,OAAO,UAAyD;EACrE,MAAM,WAAW,MAAM,QAAQ,OAAO,YAAY,IAAI,MAAM,SAAS;EACrE,MAAM,aAAa,IAAI,WAAW;EAClC,IAAI,aAAa,MAAM,WAAW,QAAQ,QAAQ;EAClD,MAAM,QAAQ,IAAI,gBAAgB;GAChC,YAAY,OAAO,WAAW,MAAM;GACpC,kBAAkB;EACpB,CAAC;EACD,MAAM,SAAS,OAAO,QAAQ,WAAW,WAAW,QAAQ,SAAS,QAAQ,OAAO;EACpF,MAAM,WAAW,MAAM,oBACrB,GAAG,OAAO,QAAQ,SAAS,EAAE,EAAE,cAAc,mBAAmB,MAAM,KAAK,EAAE,kBAAkB,mBAAmB,MAAM,UAAU,SAAS,EAAE,UAAU,SACvJ;GACE,OAAO;GACP,SAAS,MAAM,QAAQ,UAAU,SAAS,KAAK,CAAC;GAChD,QAAQ,YAAY,QAAQ,SAAS;GACrC,UAAU;EACZ,CACF;EACA,IAAI,CAAC,SAAS,MAAM,CAAC,SAAS,MAAM;GAClC,MAAM,SAAS,MAAM,OAAO;GAC5B,MAAM,IAAI,MAAM,2CAA2C,SAAS,QAAQ;EAC9E;EACA,MAAM,YAAY,SAAS,QAAQ,IAAI,yBAAyB;EAChE,MAAM,OAAO,cAAc,OAAO,MAAa,OAAO,SAAS;EAC/D,IAAI,CAAC,OAAO,cAAc,IAAI,KAAK,OAAO,IAAI;GAC5C,MAAM,SAAS,KAAK,OAAO;GAC3B,MAAM,IAAI,MAAM,uEAAuE;EACzF;EACA,IAAI,WAAW,UAAU,MACvB,WAAW,MAAM,SAAS,WAAW,SAAS,IAAI,GAAG;GACnD,WAAW,OAAO,KAAK;GACvB,IAAI,WAAW,WAAW,OAAO,GAAG;EACtC;OAEA,MAAM,SAAS,KAAK,OAAO;EAE7B,IAAI,WAAW,WAAW,OAAO,GAC/B,MAAM,IAAI,MAAM,iEAAiE;EAEnF,MAAM,QAAQ,OAAO,YAAY,IAAI,MAAM,WAAW,WAAW,SAAS,CAAC;CAC7E;AACF"}
1
+ {"version":3,"file":"transcript.mjs","names":[],"sources":["../../src/eve/transcript.ts"],"sourcesContent":["import { readEvents } from \"../client-events\";\nimport { Transcript } from \"../client-transcript\";\nimport { replyToSchema, type MaybePromise, type ReplyTo } from \"../schema/common\";\nimport { taskAttemptRef, type TaskAttemptRef } from \"../schema/execution\";\nimport { repositoryIdSchema, taskIdSchema, type RepositoryId } from \"../schema/id\";\nimport type { FactoryStores } from \"../store/engine\";\nimport {\n factoryReplyAddressAttribute,\n factoryReplyChannelAttribute,\n factoryRepositoryIdsAttribute,\n factoryTaskAttemptAttribute,\n factoryTaskAttribute,\n} from \"./task-session\";\n\n/** Identifies the Eve execution and authenticated attributes to project into a transcript. */\nexport interface TranscriptMaterializationInput {\n agent: string;\n execution: { provider: \"eve\"; sessionId: string };\n attributes: Readonly<Record<string, string | readonly string[]>>;\n}\n\n/** The Task and conversation authorization context forwarded when reading an Eve transcript. */\nexport interface EveTranscriptRequestScope {\n task?: TaskAttemptRef;\n conversation?: { repositoryIds: readonly RepositoryId[]; replyTo: ReplyTo };\n}\n\n/** Configures durable transcript storage and authenticated access to Eve session events. */\nexport interface EveTranscriptMaterializerOptions {\n stores: { transcripts: Pick<FactoryStores[\"transcripts\"], \"get\" | \"put\"> };\n origin: string | (() => string);\n headers?(scope: EveTranscriptRequestScope): MaybePromise<HeadersInit>;\n fetch?: typeof globalThis.fetch;\n timeoutMs?: number;\n}\n\n/** Materializes one Eve execution stream into Factory's browser-safe transcript store. */\nexport type EveTranscriptMaterializer = (input: TranscriptMaterializationInput) => Promise<void>;\n\nfunction attribute(\n attributes: TranscriptMaterializationInput[\"attributes\"],\n name: string,\n): string | undefined {\n const value = attributes[name];\n return typeof value === \"string\" ? value : undefined;\n}\n\nfunction scopeFor(input: TranscriptMaterializationInput): EveTranscriptRequestScope {\n const taskId = taskIdSchema.safeParse(attribute(input.attributes, factoryTaskAttribute));\n const attempt = Number(attribute(input.attributes, factoryTaskAttemptAttribute));\n const repositories = repositoryIdSchema\n .array()\n .min(1)\n .safeParse(\n attribute(input.attributes, factoryRepositoryIdsAttribute)?.split(\",\").filter(Boolean),\n );\n const replyTo = replyToSchema.safeParse({\n channel: attribute(input.attributes, factoryReplyChannelAttribute),\n address: attribute(input.attributes, factoryReplyAddressAttribute),\n });\n return {\n ...(taskId.success && Number.isSafeInteger(attempt) && attempt > 0\n ? { task: taskAttemptRef(taskId.data, attempt) }\n : {}),\n ...(repositories.success && replyTo.success\n ? { conversation: { repositoryIds: repositories.data, replyTo: replyTo.data } }\n : {}),\n };\n}\n\n/** Materialize a browser-safe transcript projection from one durable Eve session stream. */\nexport function createEveTranscriptMaterializer(\n options: EveTranscriptMaterializerOptions,\n): EveTranscriptMaterializer {\n const fetchImplementation = options.fetch ?? globalThis.fetch;\n const timeoutMs = options.timeoutMs ?? 30_000;\n return async (input: TranscriptMaterializationInput): Promise<void> => {\n const previous = await options.stores.transcripts.get(input.execution);\n const transcript = new Transcript();\n if (previous !== null) transcript.hydrate(previous);\n const query = new URLSearchParams({\n startIndex: String(transcript.cursor),\n includeTailIndex: \"1\",\n });\n const origin = typeof options.origin === \"string\" ? options.origin : options.origin();\n const response = await fetchImplementation(\n `${origin.replace(/\\/+$/u, \"\")}/eve/agents/${encodeURIComponent(input.agent)}/eve/v1/session/${encodeURIComponent(input.execution.sessionId)}/stream?${query}`,\n {\n cache: \"no-store\",\n headers: await options.headers?.(scopeFor(input)),\n signal: AbortSignal.timeout(timeoutMs),\n redirect: \"error\",\n },\n );\n if (!response.ok || !response.body) {\n await response.body?.cancel();\n throw new Error(`Eve refused transcript materialization: ${response.status}`);\n }\n const tailValue = response.headers.get(\"x-eve-stream-tail-index\");\n const tail = tailValue === null ? Number.NaN : Number(tailValue);\n if (!Number.isSafeInteger(tail) || tail < -1) {\n await response.body.cancel();\n throw new Error(\"Eve transcript materialization did not include a valid durable cursor\");\n }\n if (transcript.cursor <= tail) {\n for await (const event of readEvents(response.body)) {\n transcript.accept(event);\n if (transcript.cursor === tail + 1) break;\n }\n } else {\n await response.body.cancel();\n }\n if (transcript.cursor !== tail + 1) {\n throw new Error(\"Eve transcript materialization closed before its durable cursor\");\n }\n await options.stores.transcripts.put(input.execution, transcript.snapshot());\n };\n}\n"],"mappings":";;;;;;;AAuCA,SAAS,UACP,YACA,MACoB;CACpB,MAAM,QAAQ,WAAW;CACzB,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAA;AAC7C;AAEA,SAAS,SAAS,OAAkE;CAClF,MAAM,SAAS,aAAa,UAAU,UAAU,MAAM,YAAY,oBAAoB,CAAC;CACvF,MAAM,UAAU,OAAO,UAAU,MAAM,YAAY,2BAA2B,CAAC;CAC/E,MAAM,eAAe,mBAClB,MAAM,CAAC,CACP,IAAI,CAAC,CAAC,CACN,UACC,UAAU,MAAM,YAAY,6BAA6B,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,OAAO,OAAO,CACvF;CACF,MAAM,UAAU,cAAc,UAAU;EACtC,SAAS,UAAU,MAAM,YAAY,4BAA4B;EACjE,SAAS,UAAU,MAAM,YAAY,4BAA4B;CACnE,CAAC;CACD,OAAO;EACL,GAAI,OAAO,WAAW,OAAO,cAAc,OAAO,KAAK,UAAU,IAC7D,EAAE,MAAM,eAAe,OAAO,MAAM,OAAO,EAAE,IAC7C,CAAC;EACL,GAAI,aAAa,WAAW,QAAQ,UAChC,EAAE,cAAc;GAAE,eAAe,aAAa;GAAM,SAAS,QAAQ;EAAK,EAAE,IAC5E,CAAC;CACP;AACF;;AAGA,SAAgB,gCACd,SAC2B;CAC3B,MAAM,sBAAsB,QAAQ,SAAS,WAAW;CACxD,MAAM,YAAY,QAAQ,aAAa;CACvC,OAAO,OAAO,UAAyD;EACrE,MAAM,WAAW,MAAM,QAAQ,OAAO,YAAY,IAAI,MAAM,SAAS;EACrE,MAAM,aAAa,IAAI,WAAW;EAClC,IAAI,aAAa,MAAM,WAAW,QAAQ,QAAQ;EAClD,MAAM,QAAQ,IAAI,gBAAgB;GAChC,YAAY,OAAO,WAAW,MAAM;GACpC,kBAAkB;EACpB,CAAC;EACD,MAAM,SAAS,OAAO,QAAQ,WAAW,WAAW,QAAQ,SAAS,QAAQ,OAAO;EACpF,MAAM,WAAW,MAAM,oBACrB,GAAG,OAAO,QAAQ,SAAS,EAAE,EAAE,cAAc,mBAAmB,MAAM,KAAK,EAAE,kBAAkB,mBAAmB,MAAM,UAAU,SAAS,EAAE,UAAU,SACvJ;GACE,OAAO;GACP,SAAS,MAAM,QAAQ,UAAU,SAAS,KAAK,CAAC;GAChD,QAAQ,YAAY,QAAQ,SAAS;GACrC,UAAU;EACZ,CACF;EACA,IAAI,CAAC,SAAS,MAAM,CAAC,SAAS,MAAM;GAClC,MAAM,SAAS,MAAM,OAAO;GAC5B,MAAM,IAAI,MAAM,2CAA2C,SAAS,QAAQ;EAC9E;EACA,MAAM,YAAY,SAAS,QAAQ,IAAI,yBAAyB;EAChE,MAAM,OAAO,cAAc,OAAO,MAAa,OAAO,SAAS;EAC/D,IAAI,CAAC,OAAO,cAAc,IAAI,KAAK,OAAO,IAAI;GAC5C,MAAM,SAAS,KAAK,OAAO;GAC3B,MAAM,IAAI,MAAM,uEAAuE;EACzF;EACA,IAAI,WAAW,UAAU,MACvB,WAAW,MAAM,SAAS,WAAW,SAAS,IAAI,GAAG;GACnD,WAAW,OAAO,KAAK;GACvB,IAAI,WAAW,WAAW,OAAO,GAAG;EACtC;OAEA,MAAM,SAAS,KAAK,OAAO;EAE7B,IAAI,WAAW,WAAW,OAAO,GAC/B,MAAM,IAAI,MAAM,iEAAiE;EAEnF,MAAM,QAAQ,OAAO,YAAY,IAAI,MAAM,WAAW,WAAW,SAAS,CAAC;CAC7E;AACF"}
@@ -1,8 +1,8 @@
1
- import { ApprovalPolicy } from "../approval.mjs";
2
1
  import { RepositoryId, SignalId, TaskId } from "../schema/id.mjs";
3
2
  import { AgentRoute } from "../agent-routes.mjs";
4
3
  import { ReplyTo } from "../schema/common.mjs";
5
4
  import { FactoryStores } from "../store/engine.mjs";
5
+ import { ApprovalPolicy } from "../approval.mjs";
6
6
  import { Finding } from "../schema/finding.mjs";
7
7
  //#region src/finding-remediation/admission.d.ts
8
8
  /** Store capabilities required to admit a finding and its remediation Task. */
@@ -1,5 +1,5 @@
1
- import { Repository } from "../schema/repository.mjs";
2
1
  import { Task } from "../schema/task.mjs";
2
+ import { Repository } from "../schema/repository.mjs";
3
3
  import { FactoryStores } from "../store/engine.mjs";
4
4
  import { Finding } from "../schema/finding.mjs";
5
5
  //#region src/finding-remediation/brief.d.ts
@@ -1,5 +1,5 @@
1
- import { RepositorySlug } from "./schema/repository.mjs";
2
1
  import { MillisecondClock } from "./schema/common.mjs";
2
+ import { RepositorySlug } from "./schema/repository.mjs";
3
3
  //#region src/github-app.d.ts
4
4
  /** Exact repository permission map requested for a short-lived GitHub App token. */
5
5
  type GitHubAppPermissions = Readonly<Record<string, "read" | "write">>;
@@ -1,6 +1,6 @@
1
1
  import { TaskId } from "./schema/id.mjs";
2
- import { RepositorySlug } from "./schema/repository.mjs";
3
2
  import { AgentSessionId } from "./schema/session.mjs";
3
+ import { RepositorySlug } from "./schema/repository.mjs";
4
4
  import { GitHubGitTransport, GitTransferSandbox } from "./github-transfer.mjs";
5
5
  //#region src/github-publication.d.ts
6
6
  /** Minimal sandbox command boundary required by Git checkout and publication helpers. */
package/dist/github.d.mts CHANGED
@@ -123,6 +123,14 @@ interface GitHubPullRequest extends GitHubPullRequestReference {
123
123
  /** GitHub account type of the author, e.g. "User" or "Bot", when reported. */
124
124
  authorType?: string;
125
125
  state: "open" | "closed";
126
+ /** Whether GitHub currently treats the open pull request as a draft. */
127
+ draft: boolean;
128
+ /** Whether a closed pull request was merged. */
129
+ merged: boolean;
130
+ /** GitHub actor that merged the pull request, when reported. */
131
+ mergedBy?: string;
132
+ /** Merge commit recorded by GitHub, when reported. */
133
+ mergeCommitSha?: string;
126
134
  }
127
135
  /** GitHub review action submitted with a pull-request review. */
128
136
  type PullRequestReviewEvent = "APPROVE" | "REQUEST_CHANGES" | "COMMENT";
@@ -344,7 +352,7 @@ declare function readPullRequest(input: GitHubPullRequestTarget): Promise<GitHub
344
352
  declare function closePullRequest(input: GitHubPullRequestTarget): Promise<GitHubPullRequest>;
345
353
  /** List open, same-repository pull requests by one author that GitHub reports as conflicting. */
346
354
  declare function listConflictedPullRequests(input: ListConflictedPullRequestsInput): Promise<readonly ConflictedGitHubPullRequest[]>;
347
- /** List open pull requests in a repository, newest first, to check for existing work before triage. */
355
+ /** List all open PRs newest first; fail rather than return a partial inventory at the ten-page bound. */
348
356
  declare function listOpenPullRequests(input: ListOpenPullRequestsInput): Promise<readonly OpenPullRequest[]>;
349
357
  /** Ensure a repository label exists, including when another publisher creates it concurrently. */
350
358
  declare function ensureGitHubLabel(input: EnsureGitHubLabelInput): Promise<void>;
package/dist/github.mjs CHANGED
@@ -110,6 +110,10 @@ const pullRequestSchema = z.object({
110
110
  });
111
111
  const readablePullRequestSchema = pullRequestSchema.extend({
112
112
  state: z.enum(["open", "closed"]),
113
+ draft: z.boolean().default(false),
114
+ merged: z.boolean().default(false),
115
+ merged_by: z.object({ login: z.string().min(1) }).nullable().optional(),
116
+ merge_commit_sha: z.string().regex(/^[0-9a-f]{40}$/).nullable().optional(),
113
117
  user: z.object({
114
118
  login: z.string().min(1),
115
119
  type: z.string().optional()
@@ -460,7 +464,11 @@ async function readPullRequest(input) {
460
464
  pullRequestUrl: pull.html_url,
461
465
  author: pull.user.login,
462
466
  authorType: pull.user.type,
463
- state: pull.state
467
+ state: pull.state,
468
+ draft: pull.draft,
469
+ merged: pull.merged,
470
+ ...pull.merged_by ? { mergedBy: pull.merged_by.login } : {},
471
+ ...pull.merge_commit_sha ? { mergeCommitSha: pull.merge_commit_sha } : {}
464
472
  };
465
473
  }
466
474
  /** Close an open pull request without merging it. */
@@ -474,7 +482,11 @@ async function closePullRequest(input) {
474
482
  pullRequestUrl: pull.html_url,
475
483
  author: pull.user.login,
476
484
  authorType: pull.user.type,
477
- state: pull.state
485
+ state: pull.state,
486
+ draft: pull.draft,
487
+ merged: pull.merged,
488
+ ...pull.merged_by ? { mergedBy: pull.merged_by.login } : {},
489
+ ...pull.merge_commit_sha ? { mergeCommitSha: pull.merge_commit_sha } : {}
478
490
  };
479
491
  }
480
492
  /** List open, same-repository pull requests by one author that GitHub reports as conflicting. */
@@ -494,16 +506,22 @@ async function listConflictedPullRequests(input) {
494
506
  }
495
507
  return conflicted;
496
508
  }
497
- /** List open pull requests in a repository, newest first, to check for existing work before triage. */
509
+ /** List all open PRs newest first; fail rather than return a partial inventory at the ten-page bound. */
498
510
  async function listOpenPullRequests(input) {
499
- return z.array(openPullRequestSummarySchema).parse(await gitHubRequest(input, `/pulls?state=open&per_page=100&sort=created&direction=desc`)).map((pull) => ({
500
- prNumber: pull.number,
501
- prUrl: pull.html_url,
502
- title: pull.title,
503
- author: pull.user.login,
504
- headBranch: pull.head.ref,
505
- draft: pull.draft
506
- }));
511
+ const pulls = /* @__PURE__ */ new Map();
512
+ for (let page = 1; page <= 10; page++) {
513
+ const batch = z.array(openPullRequestSummarySchema).parse(await gitHubRequest(input, `/pulls?state=open&per_page=100&sort=created&direction=desc&page=${page}`));
514
+ for (const pull of batch) pulls.set(pull.number, {
515
+ prNumber: pull.number,
516
+ prUrl: pull.html_url,
517
+ title: pull.title,
518
+ author: pull.user.login,
519
+ headBranch: pull.head.ref,
520
+ draft: pull.draft
521
+ });
522
+ if (batch.length < 100) return [...pulls.values()];
523
+ }
524
+ throw new Error("Open pull request lookup is incomplete after ten pages; do not conclude that no duplicate exists. Inspect targeted repository work before admitting a new change.");
507
525
  }
508
526
  /** Ensure a repository label exists, including when another publisher creates it concurrently. */
509
527
  async function ensureGitHubLabel(input) {