@vercel/factory 0.0.15 → 0.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +356 -0
- package/README.md +49 -261
- package/dist/agent-routes.d.mts +47 -3
- package/dist/agent-routes.mjs +28 -1
- package/dist/agent-routes.mjs.map +1 -1
- package/dist/api-contracts.d.mts +20 -1
- package/dist/api-contracts.mjs +2 -1
- package/dist/api-contracts.mjs.map +1 -1
- package/dist/api.d.mts +45 -2
- package/dist/api.mjs +199 -10
- package/dist/api.mjs.map +1 -1
- package/dist/approval-contracts.d.mts +6 -0
- package/dist/blob/index.d.mts +51 -14
- package/dist/blob/index.mjs +26 -10
- package/dist/blob/index.mjs.map +1 -1
- package/dist/budget.d.mts +7 -0
- package/dist/budget.mjs +6 -0
- package/dist/budget.mjs.map +1 -1
- package/dist/build-factory.d.mts +1 -0
- package/dist/change-verification/dispatch.d.mts +16 -3
- package/dist/change-verification/dispatch.mjs +45 -7
- package/dist/change-verification/dispatch.mjs.map +1 -1
- package/dist/change-verification/eve-tool.d.mts +2 -1
- package/dist/change-verification/eve-tool.mjs +35 -47
- package/dist/change-verification/eve-tool.mjs.map +1 -1
- package/dist/change-verification/result.mjs +130 -0
- package/dist/change-verification/result.mjs.map +1 -0
- package/dist/changes/eve-record-change.d.mts +2 -2
- package/dist/changes/eve-record-change.mjs +43 -9
- package/dist/changes/eve-record-change.mjs.map +1 -1
- package/dist/changes.d.mts +4 -3
- package/dist/changes.mjs +2 -2
- package/dist/changes.mjs.map +1 -1
- package/dist/client-events.d.mts +10 -3
- package/dist/client-events.mjs +6 -2
- package/dist/client-events.mjs.map +1 -1
- package/dist/client-stream.mjs +8 -2
- package/dist/client-stream.mjs.map +1 -1
- package/dist/client-transcript.mjs +5 -1
- package/dist/client-transcript.mjs.map +1 -1
- package/dist/client.d.mts +121 -12
- package/dist/client.mjs +117 -9
- package/dist/client.mjs.map +1 -1
- package/dist/code-review/contracts.d.mts +1 -0
- package/dist/code-review/eve-post-review.d.mts +4 -4
- package/dist/code-review/eve-post-review.mjs +29 -17
- package/dist/code-review/eve-post-review.mjs.map +1 -1
- package/dist/code-review/eve-review-comments.d.mts +13 -2
- package/dist/code-review/eve-review-comments.mjs +45 -11
- package/dist/code-review/eve-review-comments.mjs.map +1 -1
- package/dist/code-review/github-reporter.d.mts +2 -0
- package/dist/code-review/github-reporter.mjs +7 -5
- package/dist/code-review/github-reporter.mjs.map +1 -1
- package/dist/code-review.d.mts +3 -2
- package/dist/deepsec/eve-tool.mjs +3 -1
- package/dist/deepsec/eve-tool.mjs.map +1 -1
- package/dist/dispatch.d.mts +79 -8
- package/dist/dispatch.mjs +68 -9
- package/dist/dispatch.mjs.map +1 -1
- package/dist/eve/index.d.mts +70 -10
- package/dist/eve/index.mjs +93 -22
- package/dist/eve/index.mjs.map +1 -1
- package/dist/eve/invoke.mjs +24 -11
- package/dist/eve/invoke.mjs.map +1 -1
- package/dist/eve/session-client.d.mts +122 -4
- package/dist/eve/session-client.mjs +127 -13
- package/dist/eve/session-client.mjs.map +1 -1
- package/dist/eve/task-execution.d.mts +380 -0
- package/dist/eve/task-execution.mjs +57 -2
- package/dist/eve/task-execution.mjs.map +1 -1
- package/dist/eve/task-session.d.mts +44 -2
- package/dist/eve/task-session.mjs +44 -2
- package/dist/eve/task-session.mjs.map +1 -1
- package/dist/eve/transcript.mjs +5 -1
- package/dist/eve/transcript.mjs.map +1 -1
- package/dist/execution.d.mts +117 -9
- package/dist/execution.mjs +76 -6
- package/dist/execution.mjs.map +1 -1
- package/dist/finding-remediation/admission.d.mts +2 -0
- package/dist/finding-remediation/admission.mjs +4 -1
- package/dist/finding-remediation/admission.mjs.map +1 -1
- package/dist/findings.d.mts +1 -0
- package/dist/github-publication.d.mts +1 -0
- package/dist/github-publication.mjs +97 -84
- package/dist/github-publication.mjs.map +1 -1
- package/dist/github-transfer.d.mts +15 -6
- package/dist/github-transfer.mjs +214 -66
- package/dist/github-transfer.mjs.map +1 -1
- package/dist/github.d.mts +51 -11
- package/dist/github.mjs +126 -24
- package/dist/github.mjs.map +1 -1
- package/dist/inbox-activity.d.mts +53 -0
- package/dist/inbox-activity.mjs +41 -0
- package/dist/inbox-activity.mjs.map +1 -0
- package/dist/index.d.mts +3 -1
- package/dist/index.mjs +3 -2
- package/dist/intake-contracts.d.mts +0 -1
- package/dist/integrations/deepsec.d.mts +1 -0
- package/dist/integrations/github.d.mts +2 -2
- package/dist/integrations/github.mjs +2 -2
- package/dist/integrations/slack.d.mts +3 -1
- package/dist/integrations/slack.mjs +3 -1
- package/dist/integrations/vercel.d.mts +4 -2
- package/dist/integrations/vercel.mjs +3 -2
- package/dist/merge-resolution/eve-tools.d.mts +1 -0
- package/dist/merge-resolution/eve-tools.mjs +7 -2
- package/dist/merge-resolution/eve-tools.mjs.map +1 -1
- package/dist/model-settings.d.mts +41 -0
- package/dist/model-settings.mjs +35 -0
- package/dist/model-settings.mjs.map +1 -0
- package/dist/planning/reconcile.mjs +6 -0
- package/dist/planning/reconcile.mjs.map +1 -1
- package/dist/postgres/index.d.mts +43 -2
- package/dist/postgres/index.mjs +40 -2
- package/dist/postgres/index.mjs.map +1 -1
- package/dist/presets/software-development/dispatch.d.mts +4 -1
- package/dist/presets/software-development/dispatch.mjs +2 -1
- package/dist/presets/software-development/dispatch.mjs.map +1 -1
- package/dist/presets/software-development/task-communication.d.mts +1 -0
- package/dist/presets/software-development/task-communication.mjs +48 -11
- package/dist/presets/software-development/task-communication.mjs.map +1 -1
- package/dist/presets/software-development.d.mts +1 -0
- package/dist/pull-requests/github-publisher.d.mts +15 -1
- package/dist/pull-requests/github-publisher.mjs +61 -1
- package/dist/pull-requests/github-publisher.mjs.map +1 -1
- package/dist/pull-requests.d.mts +1 -0
- package/dist/sandbox/index.d.mts +1 -0
- package/dist/schema/agent-route.d.mts +19 -1
- package/dist/schema/agent-route.mjs +19 -1
- package/dist/schema/agent-route.mjs.map +1 -1
- package/dist/schema/factory-config.d.mts +27 -0
- package/dist/schema/factory-config.mjs +33 -3
- package/dist/schema/factory-config.mjs.map +1 -1
- package/dist/schema/repository.d.mts +4 -0
- package/dist/schema/repository.mjs +5 -1
- package/dist/schema/repository.mjs.map +1 -1
- package/dist/schema/session.d.mts +1 -0
- package/dist/schema/session.mjs +1 -0
- package/dist/schema/session.mjs.map +1 -1
- package/dist/schema/slack-pr-notifications.d.mts +12 -0
- package/dist/schema/slack-pr-notifications.mjs +11 -0
- package/dist/schema/slack-pr-notifications.mjs.map +1 -0
- package/dist/schema/task-graph.d.mts +39 -0
- package/dist/schema/task.d.mts +1 -0
- package/dist/schema/task.mjs +2 -1
- package/dist/schema/task.mjs.map +1 -1
- package/dist/schema/transcript.d.mts +6 -0
- package/dist/schema/transcript.mjs +2 -1
- package/dist/schema/transcript.mjs.map +1 -1
- package/dist/schema/work.d.mts +52 -3
- package/dist/schema/work.mjs.map +1 -1
- package/dist/session-previews.d.mts +76 -0
- package/dist/session-previews.mjs +55 -0
- package/dist/session-previews.mjs.map +1 -0
- package/dist/session-review.d.mts +120 -0
- package/dist/session-review.mjs +79 -0
- package/dist/session-review.mjs.map +1 -0
- package/dist/signal-triage.mjs +1 -1
- package/dist/signals.d.mts +1 -0
- package/dist/stall.d.mts +4 -1
- package/dist/stall.mjs +6 -2
- package/dist/stall.mjs.map +1 -1
- package/dist/store/driver.d.mts +1 -1
- package/dist/store/driver.mjs.map +1 -1
- package/dist/store/engine.d.mts +206 -8
- package/dist/store/engine.mjs +147 -13
- package/dist/store/engine.mjs.map +1 -1
- package/dist/store/memory.d.mts +18 -1
- package/dist/store/memory.mjs +18 -1
- package/dist/store/memory.mjs.map +1 -1
- package/dist/store/slack-pr-notifications.d.mts +44 -0
- package/dist/store/slack-pr-notifications.mjs +121 -0
- package/dist/store/slack-pr-notifications.mjs.map +1 -0
- package/dist/store/task-work.d.mts +121 -6
- package/dist/store/task-work.mjs +7 -4
- package/dist/store/task-work.mjs.map +1 -1
- package/dist/sweep.d.mts +28 -6
- package/dist/sweep.mjs +34 -6
- package/dist/sweep.mjs.map +1 -1
- package/dist/task-graph-view.d.mts +3 -0
- package/dist/tasks.d.mts +3 -3
- package/dist/tasks.mjs +3 -3
- package/dist/vercel-git.d.mts +35 -3
- package/dist/vercel-git.mjs +265 -33
- package/dist/vercel-git.mjs.map +1 -1
- package/dist/vercel-github-api.d.mts +103 -0
- package/dist/vercel-github-api.mjs +363 -0
- package/dist/vercel-github-api.mjs.map +1 -0
- package/dist/vercel.d.mts +3 -2
- package/dist/vercel.mjs +3 -2
- package/dist/vercel.mjs.map +1 -1
- package/dist/work-triage.d.mts +1 -0
- package/dist/workflows.d.mts +102 -4
- package/dist/workflows.mjs +55 -2
- package/dist/workflows.mjs.map +1 -1
- package/dist/workspace-files-git.d.mts +15 -0
- package/dist/workspace-files-git.mjs +61 -0
- package/dist/workspace-files-git.mjs.map +1 -0
- package/dist/workspace-files.d.mts +107 -0
- package/dist/workspace-files.mjs +74 -0
- package/dist/workspace-files.mjs.map +1 -0
- package/docs/getting-started.md +104 -0
- package/docs/index.md +100 -0
- package/docs/recipes/cancellation.md +215 -0
- package/docs/recipes/custom-workflow.md +153 -0
- package/docs/recipes/dependent-tasks.md +207 -0
- package/docs/recipes/eve-agent.md +277 -0
- package/docs/recipes/human-input.md +204 -0
- package/docs/recipes/persistence-recovery.md +268 -0
- package/docs/recipes/retry-recovery.md +241 -0
- package/docs/recipes/task-messaging.md +215 -0
- package/docs/recipes/typed-eve-result.md +161 -0
- package/docs/runtime-integration.md +137 -0
- package/package.json +17 -6
package/dist/eve/index.mjs.map
CHANGED
|
@@ -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 readonly get_task: GetTaskTool;\n readonly decide_signal: DecideSignalTool;\n readonly finish_task: FinishTaskTool;\n readonly request_human_input: RequestHumanInputTool;\n readonly answer_question: AnswerQuestionTool;\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): 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({ title: needsHumanAction.slice(0, 256), input: { question } }),\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 * The factory's provider-neutral store operations wrapped as Eve tools. Expose a tool to an\n * agent by re-exporting it from a file under agent/tools/, e.g.\n * `export default createTaskTools(stores).get_task;`. Every write goes through\n * the engine, so agents inherit transition legality and receipts.\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 /** 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 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 * Observe-only eve hooks that keep the ledger honest about session outcomes.\n * Mount by re-exporting from agent/hooks/, e.g.\n * `export default createFactoryHooks(stores, { presentQuestion }).factory;`.\n *\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 (turnId, stepIndex).\n *\n * turn.failed/session.failed: immediately 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: 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 *\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 */\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 recoverFailedAttempt(binding: TaskAttemptRef, failure: 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 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): Promise<void> {\n await createAndPresentQuestion(\n stores,\n task,\n UNRESOLVED_TURN_QUESTION,\n options.presentQuestion,\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: 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 ).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 ).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 (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(task);\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 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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"}
|
package/dist/eve/invoke.mjs
CHANGED
|
@@ -116,6 +116,10 @@ function createDelegateAgentTool(stores, options) {
|
|
|
116
116
|
};
|
|
117
117
|
const auth = ctx.session.auth.initiator;
|
|
118
118
|
const attributes = auth?.attributes;
|
|
119
|
+
const boundSignal = signalIdSchema.safeParse(attributes?.factorySignalId);
|
|
120
|
+
if (attributes?.factorySignalId !== void 0 && !boundSignal.success) throw new Error("Connector-authenticated Signal context is invalid.");
|
|
121
|
+
if (boundSignal.success && input.signalId !== void 0 && input.signalId !== boundSignal.data) throw new Error("Delegation must accept its authenticated Signal.");
|
|
122
|
+
const signalId = input.signalId ?? (boundSignal.success ? boundSignal.data : void 0);
|
|
119
123
|
const boundRepositoryIds = repositoryIdsForSession(auth);
|
|
120
124
|
if (boundRepositoryIds !== null && input.repositoryIds.some((id) => !boundRepositoryIds.includes(id))) throw new Error("Delegation includes a repository outside the connector-authenticated context.");
|
|
121
125
|
const boundReplyTo = replyToForSession(auth);
|
|
@@ -133,16 +137,21 @@ function createDelegateAgentTool(stores, options) {
|
|
|
133
137
|
const change = await stores.changes.get(changeId);
|
|
134
138
|
if (change === null || change.repositoryId !== input.repositoryIds[0] || change.state !== "verifying" && change.state !== "ready") throw new Error(`Change ${changeId} is not an open pull request in the selected repository.`);
|
|
135
139
|
}
|
|
136
|
-
if (
|
|
137
|
-
const signal = await stores.signals.get(
|
|
138
|
-
if (signal === null) throw new Error(`Signal ${
|
|
140
|
+
if (signalId !== void 0) {
|
|
141
|
+
const signal = await stores.signals.get(signalId);
|
|
142
|
+
if (signal === null) throw new Error(`Signal ${signalId} does not exist.`);
|
|
143
|
+
if (boundSignal.success && (signal.triage?.attempt !== Number(attributes?.factorySignalAttempt) || signal.triage?.state !== "running" || signal.triage.sessionId !== void 0 && signal.triage.sessionId !== ctx.session.id)) throw new Error("Delegation requires the current running Signal triage attempt.");
|
|
139
144
|
if (!input.repositoryIds.includes(signal.repositoryId) || signal.replyTo.channel !== replyTo.channel || signal.replyTo.address !== replyTo.address) throw new Error("Delegation does not match the signal's authenticated repository and reply address.");
|
|
140
|
-
if (signal.outcome
|
|
145
|
+
if (signal.outcome !== "pending" && signal.outcome !== "accepted") throw new Error(`Signal ${signal.id} is already ${signal.outcome}.`);
|
|
146
|
+
}
|
|
147
|
+
const acceptSignal = async () => {
|
|
148
|
+
if (signalId === void 0) return;
|
|
149
|
+
if ((await stores.signals.get(signalId))?.outcome === "accepted") return;
|
|
150
|
+
await stores.signals.decide(signalId, {
|
|
141
151
|
outcome: "accepted",
|
|
142
152
|
reason: `Foreman selected ${route.id}.`
|
|
143
153
|
});
|
|
144
|
-
|
|
145
|
-
}
|
|
154
|
+
};
|
|
146
155
|
let parentTaskId = input.parentTaskId;
|
|
147
156
|
const boundTaskId = optionalTaskIdForSession(auth);
|
|
148
157
|
if (boundTaskId !== null && boundReplyTo?.channel === "api") {
|
|
@@ -157,7 +166,7 @@ function createDelegateAgentTool(stores, options) {
|
|
|
157
166
|
stores,
|
|
158
167
|
route,
|
|
159
168
|
repositoryIds: input.repositoryIds,
|
|
160
|
-
signalId
|
|
169
|
+
signalId,
|
|
161
170
|
parentTaskId,
|
|
162
171
|
changeId,
|
|
163
172
|
replyTo,
|
|
@@ -165,7 +174,10 @@ function createDelegateAgentTool(stores, options) {
|
|
|
165
174
|
operator,
|
|
166
175
|
dispatch: (task) => dispatchRouteTask(stores, options, doFetch, route, task, changeId, input.brief)
|
|
167
176
|
});
|
|
168
|
-
if (workflowResult !== void 0 && workflowResult !== null)
|
|
177
|
+
if (workflowResult !== void 0 && workflowResult !== null) {
|
|
178
|
+
await acceptSignal();
|
|
179
|
+
return workflowResult;
|
|
180
|
+
}
|
|
169
181
|
const taskInput = {
|
|
170
182
|
repositoryIds: input.repositoryIds,
|
|
171
183
|
kind: route.taskKind,
|
|
@@ -176,19 +188,20 @@ function createDelegateAgentTool(stores, options) {
|
|
|
176
188
|
}),
|
|
177
189
|
replyTo,
|
|
178
190
|
changeId,
|
|
179
|
-
...
|
|
191
|
+
...signalId === void 0 ? {} : { dedupeKey: `route:${signalId}:${route.id}:${route.version}` }
|
|
180
192
|
};
|
|
181
193
|
const task = await stores.tasks.create(parentTaskId !== void 0 ? {
|
|
182
194
|
...taskInput,
|
|
183
195
|
parentTaskId,
|
|
184
196
|
origin: {}
|
|
185
|
-
} :
|
|
197
|
+
} : signalId !== void 0 ? {
|
|
186
198
|
...taskInput,
|
|
187
|
-
origin: { signalId
|
|
199
|
+
origin: { signalId }
|
|
188
200
|
} : {
|
|
189
201
|
...taskInput,
|
|
190
202
|
origin: { operator }
|
|
191
203
|
});
|
|
204
|
+
await acceptSignal();
|
|
192
205
|
const running = task.state === "queued" ? await dispatchRouteTask(stores, options, doFetch, route, task, changeId, input.brief) : task;
|
|
193
206
|
return {
|
|
194
207
|
taskId: running.id,
|
package/dist/eve/invoke.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"invoke.mjs","names":[],"sources":["../../src/eve/invoke.ts"],"sourcesContent":["import { defineTool, type ToolContext, type ToolDefinition } from \"eve/tools\";\nimport { z } from \"zod\";\nimport {\n agentRouteBindingSchema,\n parseAgentRouteBinding,\n routeTaskWork,\n type AgentRoute,\n type AgentRouteBinding,\n type AgentRouteRegistry,\n} from \"../agent-routes\";\nimport { dispatchTask, type DispatchBudget } from \"../dispatch\";\nimport type { MaybePromise, ReplyTo } from \"../schema/common\";\nimport {\n changeIdSchema,\n repositoryIdsSchema,\n signalIdSchema,\n taskIdSchema,\n type ChangeId,\n type RepositoryId,\n type RepositoryIds,\n type SignalId,\n type TaskId,\n} from \"../schema/id\";\nimport type { ExecutionReference, Task, TaskKind, TaskState } from \"../schema/task\";\nimport type { FactoryStores } from \"../store/engine\";\nimport {\n optionalTaskIdForSession,\n repositoryIdsForSession,\n replyToForSession,\n} from \"./task-session\";\nimport { startEveSession } from \"./session-client\";\n\n/** Graph, receipt, and Task capabilities required by the fixed-route invocation tool. */\nexport type InvokeAgentToolStores = Pick<FactoryStores, \"graphs\" | \"receipts\" | \"tasks\">;\n\n/** Durable capabilities required by route-based agent delegation. */\nexport type DelegateAgentToolStores = Pick<\n FactoryStores,\n \"changes\" | \"graphs\" | \"receipts\" | \"repositories\" | \"signals\" | \"tasks\" | \"work\"\n>;\n\n/** Configures one fixed agent handoff route and its authenticated Eve session boundary. */\nexport interface InvokeAgentToolOptions {\n /** Application-selected exact route for this handoff. */\n route: AgentRoute;\n /** Model-facing description of what the target agent does and when to invoke it. */\n description: string;\n /**\n * Base URL of the target agent's eve transport, e.g.\n * `https://factory.example.com/eve/agents/verifier`. Resolved at call time so\n * a deployment can derive it from its own origin.\n */\n targetUrl: () => string;\n /** Bearer token for the target agent's eve channel, when it requires auth. */\n token?: () => MaybePromise<string | undefined>;\n /** Extra headers for the session request, e.g. a deployment protection bypass. */\n headers?: (task: Task) => MaybePromise<HeadersInit>;\n /**\n * Reserve-before-execution limits for the handoff dispatch, resolved at\n * call time. Without it, handoff tasks run outside the budget.\n */\n budget?: () => DispatchBudget | undefined;\n fetch?: typeof fetch;\n}\n\n/** Scope, lineage, reply destination, and brief supplied to a fixed-route handoff. */\nexport interface InvokeAgentToolInput {\n repositoryIds: RepositoryIds;\n parentTaskId?: TaskId;\n changeId?: ChangeId;\n channel: string;\n address: string;\n brief: string;\n}\n\n/** Durable Task identity and lifecycle returned after a fixed-route handoff launches. */\nexport interface InvokeAgentToolResult {\n readonly taskId: TaskId;\n readonly kind: TaskKind;\n readonly state: TaskState;\n}\n\n/** Eve tool that launches work through one configured exact agent route. */\nexport interface InvokeAgentTool extends ToolDefinition<\n InvokeAgentToolInput,\n InvokeAgentToolResult\n> {\n execute(input: InvokeAgentToolInput, ctx: ToolContext): Promise<InvokeAgentToolResult>;\n}\n\ninterface DelegateAgentToolCommonOptions {\n routes: AgentRouteRegistry;\n /** Retry bound for application-owned routes with a reconciler; default is one launch. */\n maxLaunchAttempts?: (route: AgentRoute) => number;\n budget?: (route: AgentRoute) => DispatchBudget | undefined;\n /** Application workflow policy that may replace single-task delegation. */\n workflow?: DelegateWorkflowPolicy;\n}\n\n/** Identifies one validated delegated Task launch on its exact registered route. */\nexport interface DelegateAgentLaunchInput {\n readonly task: Task;\n readonly route: AgentRoute;\n readonly brief: string;\n}\n\n/** Delegation mode whose application owns the provider launch boundary. */\ninterface DelegateAgentCustomLaunchOptions extends DelegateAgentToolCommonOptions {\n /** Application execution boundary, after route, scope, approval, and budget validation. */\n launch(input: DelegateAgentLaunchInput): Promise<ExecutionReference>;\n targetUrl?: never;\n token?: never;\n headers?: never;\n fetch?: never;\n}\n\n/** Delegation mode that starts a fresh session through an Eve HTTP endpoint. */\ninterface DelegateAgentEveLaunchOptions extends DelegateAgentToolCommonOptions {\n launch?: never;\n targetUrl(route: AgentRoute): string;\n token?: (route: AgentRoute) => MaybePromise<string | undefined>;\n headers?: (task: Task, route: AgentRoute) => MaybePromise<HeadersInit>;\n fetch?: typeof fetch;\n}\n\n/** Configures route-based delegation with one explicit launch mode and workflow policy. */\nexport type DelegateAgentToolOptions =\n | DelegateAgentCustomLaunchOptions\n | DelegateAgentEveLaunchOptions;\n\n/** Exact route, scope, provenance, and brief supplied to bounded agent delegation. */\nexport interface DelegateAgentToolInput {\n route: AgentRouteBinding;\n repositoryIds: RepositoryIds;\n signalId?: SignalId;\n parentTaskId?: TaskId;\n changeId?: ChangeId;\n channel: string;\n address: string;\n brief: string;\n}\n\n/** Trusted context supplied to an application-owned delegation workflow policy. */\nexport interface DelegateWorkflowInput {\n stores: DelegateAgentToolStores;\n route: AgentRoute;\n repositoryIds: readonly RepositoryId[];\n signalId?: SignalId;\n parentTaskId?: TaskId;\n changeId?: ChangeId;\n replyTo: ReplyTo;\n brief: string;\n operator: string;\n dispatch(task: Task): Promise<Task>;\n}\n\n/** Identifies the durable work and exact agent route selected by a delegation policy. */\nexport interface DelegateWorkflowResult {\n readonly taskId: TaskId;\n readonly childTaskIds?: readonly TaskId[];\n readonly route: AgentRouteBinding;\n readonly agent: string;\n readonly state: Task[\"state\"];\n}\n\n/** Eve tool that delegates durable work through a registered exact agent route. */\nexport interface DelegateAgentTool extends ToolDefinition<\n DelegateAgentToolInput,\n DelegateWorkflowResult\n> {\n execute(input: DelegateAgentToolInput, ctx: ToolContext): Promise<DelegateWorkflowResult>;\n}\n\n/** Application policy that may replace ordinary single-Task delegation with a workflow. */\nexport type DelegateWorkflowPolicy = (\n input: DelegateWorkflowInput,\n) => Promise<DelegateWorkflowResult | null>;\n\n/**\n * A tool that lets one agent hand work to another, NMA-style: the caller\n * decides to invoke; trusted code owns the how. The handoff is recorded as a\n * factory task (single-winner dispatch, receipts), then a fresh session is\n * started on the target agent carrying only the task id — never the caller's\n * context. The session start is create-once on the task attempt, so a retried\n * launch cannot duplicate that attempt while a later attempt starts fresh.\n */\nexport function createInvokeAgentTool(\n stores: InvokeAgentToolStores,\n options: InvokeAgentToolOptions,\n): InvokeAgentTool {\n const doFetch = options.fetch ?? fetch;\n return defineTool({\n description: `${options.description} Hands the work to a separate agent in a fresh session; you will not see its reasoning. Returns the handoff task id.`,\n inputSchema: z.object({\n repositoryIds: repositoryIdsSchema,\n parentTaskId: taskIdSchema.optional().describe(\"Your own task, when this work continues it.\"),\n changeId: changeIdSchema.optional().describe(\"The change the target agent should work on.\"),\n channel: z.string().min(1).describe(\"Reply connector, e.g. github.\"),\n address: z.string().min(1).describe(\"Reply address, e.g. acme/web#41.\"),\n brief: z\n .string()\n .min(1)\n .max(4000)\n .describe(\n \"What the target agent needs to know. Facts and references only, not your reasoning.\",\n ),\n }),\n async execute(input) {\n const taskInput = {\n repositoryIds: input.repositoryIds,\n kind: options.route.taskKind,\n work: routeTaskWork({\n route: options.route,\n title: input.brief.trim().slice(0, 256),\n input: {\n brief: input.brief,\n },\n }),\n changeId: input.changeId,\n replyTo: { channel: input.channel, address: input.address },\n };\n const task = await stores.tasks.create(\n input.parentTaskId === undefined\n ? { ...taskInput, origin: { operator: \"factory\" } }\n : { ...taskInput, origin: {}, parentTaskId: input.parentTaskId },\n );\n\n const running = await dispatchTask(stores, {\n taskId: task.id,\n route: options.route,\n // Handoffs are launched by the caller, not the sweep, so a refused\n // session must fail now — no sweep would pick a re-queued handoff up.\n maxLaunchAttempts: 1,\n budget: options.budget?.(),\n launch: async (dispatched) => {\n const headers = new Headers(await options.headers?.(dispatched));\n headers.set(\"content-type\", \"application/json\");\n const token = await options.token?.();\n if (token !== undefined) {\n headers.set(\"authorization\", `Bearer ${token}`);\n }\n const message = [\n `You are assigned factory task ${dispatched.id}.`,\n input.changeId === undefined ? null : `Change under review: ${input.changeId}.`,\n `Reply address: ${input.channel} ${input.address}.`,\n \"\",\n input.brief,\n \"\",\n \"Read the task with get_task first, do the work, and record the outcome with your factory tools.\",\n ]\n .filter((line) => line !== null)\n .join(\"\\n\");\n return startEveSession({\n agentUrl: options.targetUrl(),\n agentName: \"target agent\",\n headers,\n message,\n operationId: `factory-task:${dispatched.id}:${dispatched.attempt}`,\n fetch: doFetch,\n });\n },\n });\n\n return { taskId: running.id, kind: running.kind, state: running.state };\n },\n });\n}\n\n/**\n * Give a conversational agent one bounded delegation surface backed by the\n * application's route registry. The caller selects a declared route; trusted\n * code owns task creation, budget enforcement, and the fresh target session.\n */\nexport function createDelegateAgentTool(\n stores: DelegateAgentToolStores,\n options: DelegateAgentToolOptions,\n): DelegateAgentTool {\n const doFetch = options.fetch ?? fetch;\n const routeSummary = options.routes.routes\n .map((route) => `${route.id}@${route.version}: ${route.description}`)\n .join(\"\\n\");\n return defineTool({\n description:\n \"Delegate durable work to one registered specialist. Select only a listed route; \" +\n \"the specialist runs independently and reports its result through Factory.\\n\" +\n routeSummary,\n inputSchema: z.object({\n route: agentRouteBindingSchema.describe(\n \"Exact route id and version listed in this tool's description.\",\n ),\n repositoryIds: repositoryIdsSchema.refine(\n (repositoryIds) => new Set(repositoryIds).size === repositoryIds.length,\n \"Repository ids must be unique.\",\n ),\n signalId: signalIdSchema\n .optional()\n .describe(\"Pending external signal this delegation accepts.\"),\n parentTaskId: taskIdSchema.optional().describe(\"Existing task this work continues.\"),\n changeId: changeIdSchema\n .optional()\n .describe(\"Existing change the specialist should inspect.\"),\n channel: z.string().min(1).describe(\"Originating conversation channel.\"),\n address: z.string().min(1).describe(\"Originating conversation reply address.\"),\n brief: z\n .string()\n .min(1)\n .max(4000)\n .describe(\"Facts and desired outcome, without hidden reasoning.\"),\n }),\n async execute(input, ctx) {\n const route = options.routes.get(input.route);\n if (route === undefined) {\n throw new Error(`Unknown agent route ${input.route.id}@${input.route.version}.`);\n }\n const replyTo = { channel: input.channel, address: input.address };\n const auth = ctx.session.auth.initiator;\n const attributes = auth?.attributes;\n const boundRepositoryIds = repositoryIdsForSession(auth);\n if (\n boundRepositoryIds !== null &&\n input.repositoryIds.some((id) => !boundRepositoryIds.includes(id))\n ) {\n throw new Error(\n \"Delegation includes a repository outside the connector-authenticated context.\",\n );\n }\n const boundReplyTo = replyToForSession(auth);\n if (\n boundReplyTo !== null &&\n (boundReplyTo.channel !== replyTo.channel || boundReplyTo.address !== replyTo.address)\n ) {\n throw new Error(\"Delegation reply address does not match the authenticated conversation.\");\n }\n for (const repositoryId of input.repositoryIds) {\n const repository = await stores.repositories.get(repositoryId);\n if (repository === null || !repository.enabled) {\n throw new Error(`Repository ${repositoryId} is not enabled in this factory.`);\n }\n }\n const boundChange = changeIdSchema.safeParse(attributes?.factoryChangeId);\n if (attributes?.factoryChangeId !== undefined && !boundChange.success) {\n throw new Error(\"Connector-authenticated change context is invalid.\");\n }\n if (\n input.changeId !== undefined &&\n boundChange.success &&\n input.changeId !== boundChange.data\n ) {\n throw new Error(\"Delegated change does not match the connector-authenticated context.\");\n }\n const changeId = input.changeId ?? (boundChange.success ? boundChange.data : undefined);\n if (changeId !== undefined) {\n if (input.repositoryIds.length !== 1) {\n throw new Error(\"Delegating work on an existing Change requires exactly one repository.\");\n }\n const change = await stores.changes.get(changeId);\n if (\n change === null ||\n change.repositoryId !== input.repositoryIds[0] ||\n (change.state !== \"verifying\" && change.state !== \"ready\")\n ) {\n throw new Error(\n `Change ${changeId} is not an open pull request in the selected repository.`,\n );\n }\n }\n if (input.signalId !== undefined) {\n const signal = await stores.signals.get(input.signalId);\n if (signal === null) throw new Error(`Signal ${input.signalId} does not exist.`);\n if (\n !input.repositoryIds.includes(signal.repositoryId) ||\n signal.replyTo.channel !== replyTo.channel ||\n signal.replyTo.address !== replyTo.address\n ) {\n throw new Error(\n \"Delegation does not match the signal's authenticated repository and reply address.\",\n );\n }\n if (signal.outcome === \"pending\") {\n await stores.signals.decide(signal.id, {\n outcome: \"accepted\",\n ...{\n reason: `Foreman selected ${route.id}.`,\n },\n });\n } else if (signal.outcome !== \"accepted\") {\n throw new Error(`Signal ${signal.id} is already ${signal.outcome}.`);\n }\n }\n let parentTaskId = input.parentTaskId;\n const boundTaskId = optionalTaskIdForSession(auth);\n if (boundTaskId !== null && boundReplyTo?.channel === \"api\") {\n const boundTask = await stores.tasks.get(boundTaskId);\n if (boundTask?.area === \"conversation\") {\n if (parentTaskId !== undefined && parentTaskId !== boundTask.id)\n throw new Error(\n \"Follow-up delegation must name its authenticated conversation task as parent.\",\n );\n parentTaskId = boundTask.id;\n }\n }\n const initiator = ctx.session.auth.initiator;\n const operator = initiator?.principalId ?? \"foreman\";\n const workflowResult = await options.workflow?.({\n stores,\n route,\n repositoryIds: input.repositoryIds,\n signalId: input.signalId,\n parentTaskId,\n changeId,\n replyTo,\n brief: input.brief,\n operator,\n dispatch: (task) =>\n dispatchRouteTask(stores, options, doFetch, route, task, changeId, input.brief),\n });\n if (workflowResult !== undefined && workflowResult !== null) return workflowResult;\n const taskInput = {\n repositoryIds: input.repositoryIds,\n kind: route.taskKind,\n work: routeTaskWork({\n route: route,\n title: input.brief.trim().slice(0, 256),\n input: { brief: input.brief },\n }),\n replyTo,\n changeId,\n ...(input.signalId === undefined\n ? {}\n : { dedupeKey: `route:${input.signalId}:${route.id}:${route.version}` }),\n };\n const task = await stores.tasks.create(\n parentTaskId !== undefined\n ? { ...taskInput, parentTaskId, origin: {} }\n : input.signalId !== undefined\n ? { ...taskInput, origin: { signalId: input.signalId } }\n : { ...taskInput, origin: { operator } },\n );\n const running =\n task.state === \"queued\"\n ? await dispatchRouteTask(stores, options, doFetch, route, task, changeId, input.brief)\n : task;\n return {\n taskId: running.id,\n route: parseAgentRouteBinding({ id: route.id, version: route.version }),\n agent: route.agent,\n state: running.state,\n };\n },\n });\n}\n\nasync function dispatchRouteTask(\n stores: DelegateAgentToolStores,\n options: DelegateAgentToolOptions,\n doFetch: typeof fetch,\n route: AgentRoute,\n task: Task,\n changeId: ChangeId | undefined,\n brief: string,\n): Promise<Task> {\n return dispatchTask(stores, {\n taskId: task.id,\n route,\n maxLaunchAttempts: options.maxLaunchAttempts?.(route) ?? 1,\n budget: options.budget?.(route),\n launch: async (dispatched) => {\n if (options.launch) return options.launch({ task: dispatched, route, brief });\n const headers = new Headers(await options.headers?.(dispatched, route));\n headers.set(\"content-type\", \"application/json\");\n const token = await options.token?.(route);\n if (token !== undefined) headers.set(\"authorization\", `Bearer ${token}`);\n const message = [\n `You are assigned factory task ${dispatched.id} through route ${route.id}.`,\n changeId === undefined ? null : `Change under review: ${changeId}.`,\n brief,\n \"Read the task first and return exactly one bounded Factory outcome.\",\n ]\n .filter((line) => line !== null)\n .join(\"\\n\\n\");\n return startEveSession({\n agentUrl: options.targetUrl(route),\n agentName: route.agent,\n headers,\n message,\n operationId: `factory-task:${dispatched.id}:${dispatched.attempt}`,\n fetch: doFetch,\n });\n },\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"invoke.mjs","names":[],"sources":["../../src/eve/invoke.ts"],"sourcesContent":["import { defineTool, type ToolContext, type ToolDefinition } from \"eve/tools\";\nimport { z } from \"zod\";\nimport {\n agentRouteBindingSchema,\n parseAgentRouteBinding,\n routeTaskWork,\n type AgentRoute,\n type AgentRouteBinding,\n type AgentRouteRegistry,\n} from \"../agent-routes\";\nimport { dispatchTask, type DispatchBudget } from \"../dispatch\";\nimport type { MaybePromise, ReplyTo } from \"../schema/common\";\nimport {\n changeIdSchema,\n repositoryIdsSchema,\n signalIdSchema,\n taskIdSchema,\n type ChangeId,\n type RepositoryId,\n type RepositoryIds,\n type SignalId,\n type TaskId,\n} from \"../schema/id\";\nimport type { ExecutionReference, Task, TaskKind, TaskState } from \"../schema/task\";\nimport type { FactoryStores } from \"../store/engine\";\nimport {\n optionalTaskIdForSession,\n repositoryIdsForSession,\n replyToForSession,\n} from \"./task-session\";\nimport { startEveSession } from \"./session-client\";\n\n/** Graph, receipt, and Task capabilities required by the fixed-route invocation tool. */\nexport type InvokeAgentToolStores = Pick<FactoryStores, \"graphs\" | \"receipts\" | \"tasks\">;\n\n/** Durable capabilities required by route-based agent delegation. */\nexport type DelegateAgentToolStores = Pick<\n FactoryStores,\n \"changes\" | \"graphs\" | \"receipts\" | \"repositories\" | \"signals\" | \"tasks\" | \"work\"\n>;\n\n/** Configures one fixed agent handoff route and its authenticated Eve session boundary. */\nexport interface InvokeAgentToolOptions {\n /** Application-selected exact route for this handoff. */\n route: AgentRoute;\n /** Model-facing description of what the target agent does and when to invoke it. */\n description: string;\n /**\n * Base URL of the target agent's eve transport, e.g.\n * `https://factory.example.com/eve/agents/verifier`. Resolved at call time so\n * a deployment can derive it from its own origin.\n */\n targetUrl: () => string;\n /** Bearer token for the target agent's eve channel, when it requires auth. */\n token?: () => MaybePromise<string | undefined>;\n /** Extra headers for the session request, e.g. a deployment protection bypass. */\n headers?: (task: Task) => MaybePromise<HeadersInit>;\n /**\n * Reserve-before-execution limits for the handoff dispatch, resolved at\n * call time. Without it, handoff tasks run outside the budget.\n */\n budget?: () => DispatchBudget | undefined;\n fetch?: typeof fetch;\n}\n\n/** Scope, lineage, reply destination, and brief supplied to a fixed-route handoff. */\nexport interface InvokeAgentToolInput {\n repositoryIds: RepositoryIds;\n parentTaskId?: TaskId;\n changeId?: ChangeId;\n channel: string;\n address: string;\n brief: string;\n}\n\n/** Durable Task identity and lifecycle returned after a fixed-route handoff launches. */\nexport interface InvokeAgentToolResult {\n readonly taskId: TaskId;\n readonly kind: TaskKind;\n readonly state: TaskState;\n}\n\n/** Eve tool that launches work through one configured exact agent route. */\nexport interface InvokeAgentTool extends ToolDefinition<\n InvokeAgentToolInput,\n InvokeAgentToolResult\n> {\n execute(input: InvokeAgentToolInput, ctx: ToolContext): Promise<InvokeAgentToolResult>;\n}\n\ninterface DelegateAgentToolCommonOptions {\n routes: AgentRouteRegistry;\n /** Retry bound for application-owned routes with a reconciler; default is one launch. */\n maxLaunchAttempts?: (route: AgentRoute) => number;\n budget?: (route: AgentRoute) => DispatchBudget | undefined;\n /** Application workflow policy that may replace single-task delegation. */\n workflow?: DelegateWorkflowPolicy;\n}\n\n/** Identifies one validated delegated Task launch on its exact registered route. */\nexport interface DelegateAgentLaunchInput {\n readonly task: Task;\n readonly route: AgentRoute;\n readonly brief: string;\n}\n\n/** Delegation mode whose application owns the provider launch boundary. */\ninterface DelegateAgentCustomLaunchOptions extends DelegateAgentToolCommonOptions {\n /** Application execution boundary, after route, scope, approval, and budget validation. */\n launch(input: DelegateAgentLaunchInput): Promise<ExecutionReference>;\n targetUrl?: never;\n token?: never;\n headers?: never;\n fetch?: never;\n}\n\n/** Delegation mode that starts a fresh session through an Eve HTTP endpoint. */\ninterface DelegateAgentEveLaunchOptions extends DelegateAgentToolCommonOptions {\n launch?: never;\n targetUrl(route: AgentRoute): string;\n token?: (route: AgentRoute) => MaybePromise<string | undefined>;\n headers?: (task: Task, route: AgentRoute) => MaybePromise<HeadersInit>;\n fetch?: typeof fetch;\n}\n\n/** Configures route-based delegation with one explicit launch mode and workflow policy. */\nexport type DelegateAgentToolOptions =\n | DelegateAgentCustomLaunchOptions\n | DelegateAgentEveLaunchOptions;\n\n/** Exact route, scope, provenance, and brief supplied to bounded agent delegation. */\nexport interface DelegateAgentToolInput {\n route: AgentRouteBinding;\n repositoryIds: RepositoryIds;\n signalId?: SignalId;\n parentTaskId?: TaskId;\n changeId?: ChangeId;\n channel: string;\n address: string;\n brief: string;\n}\n\n/** Trusted context supplied to an application-owned delegation workflow policy. */\nexport interface DelegateWorkflowInput {\n stores: DelegateAgentToolStores;\n route: AgentRoute;\n repositoryIds: readonly RepositoryId[];\n signalId?: SignalId;\n parentTaskId?: TaskId;\n changeId?: ChangeId;\n replyTo: ReplyTo;\n brief: string;\n operator: string;\n dispatch(task: Task): Promise<Task>;\n}\n\n/** Identifies the durable work and exact agent route selected by a delegation policy. */\nexport interface DelegateWorkflowResult {\n readonly taskId: TaskId;\n readonly childTaskIds?: readonly TaskId[];\n readonly route: AgentRouteBinding;\n readonly agent: string;\n readonly state: Task[\"state\"];\n}\n\n/** Eve tool that delegates durable work through a registered exact agent route. */\nexport interface DelegateAgentTool extends ToolDefinition<\n DelegateAgentToolInput,\n DelegateWorkflowResult\n> {\n execute(input: DelegateAgentToolInput, ctx: ToolContext): Promise<DelegateWorkflowResult>;\n}\n\n/** Application policy that may replace ordinary single-Task delegation with a workflow. */\nexport type DelegateWorkflowPolicy = (\n input: DelegateWorkflowInput,\n) => Promise<DelegateWorkflowResult | null>;\n\n/**\n * A tool that lets one agent hand work to another, NMA-style: the caller\n * decides to invoke; trusted code owns the how. The handoff is recorded as a\n * factory task (single-winner dispatch, receipts), then a fresh session is\n * started on the target agent carrying only the task id — never the caller's\n * context. The session start is create-once on the task attempt, so a retried\n * launch cannot duplicate that attempt while a later attempt starts fresh.\n */\nexport function createInvokeAgentTool(\n stores: InvokeAgentToolStores,\n options: InvokeAgentToolOptions,\n): InvokeAgentTool {\n const doFetch = options.fetch ?? fetch;\n return defineTool({\n description: `${options.description} Hands the work to a separate agent in a fresh session; you will not see its reasoning. Returns the handoff task id.`,\n inputSchema: z.object({\n repositoryIds: repositoryIdsSchema,\n parentTaskId: taskIdSchema.optional().describe(\"Your own task, when this work continues it.\"),\n changeId: changeIdSchema.optional().describe(\"The change the target agent should work on.\"),\n channel: z.string().min(1).describe(\"Reply connector, e.g. github.\"),\n address: z.string().min(1).describe(\"Reply address, e.g. acme/web#41.\"),\n brief: z\n .string()\n .min(1)\n .max(4000)\n .describe(\n \"What the target agent needs to know. Facts and references only, not your reasoning.\",\n ),\n }),\n async execute(input) {\n const taskInput = {\n repositoryIds: input.repositoryIds,\n kind: options.route.taskKind,\n work: routeTaskWork({\n route: options.route,\n title: input.brief.trim().slice(0, 256),\n input: {\n brief: input.brief,\n },\n }),\n changeId: input.changeId,\n replyTo: { channel: input.channel, address: input.address },\n };\n const task = await stores.tasks.create(\n input.parentTaskId === undefined\n ? { ...taskInput, origin: { operator: \"factory\" } }\n : { ...taskInput, origin: {}, parentTaskId: input.parentTaskId },\n );\n\n const running = await dispatchTask(stores, {\n taskId: task.id,\n route: options.route,\n // Handoffs are launched by the caller, not the sweep, so a refused\n // session must fail now — no sweep would pick a re-queued handoff up.\n maxLaunchAttempts: 1,\n budget: options.budget?.(),\n launch: async (dispatched) => {\n const headers = new Headers(await options.headers?.(dispatched));\n headers.set(\"content-type\", \"application/json\");\n const token = await options.token?.();\n if (token !== undefined) {\n headers.set(\"authorization\", `Bearer ${token}`);\n }\n const message = [\n `You are assigned factory task ${dispatched.id}.`,\n input.changeId === undefined ? null : `Change under review: ${input.changeId}.`,\n `Reply address: ${input.channel} ${input.address}.`,\n \"\",\n input.brief,\n \"\",\n \"Read the task with get_task first, do the work, and record the outcome with your factory tools.\",\n ]\n .filter((line) => line !== null)\n .join(\"\\n\");\n return startEveSession({\n agentUrl: options.targetUrl(),\n agentName: \"target agent\",\n headers,\n message,\n operationId: `factory-task:${dispatched.id}:${dispatched.attempt}`,\n fetch: doFetch,\n });\n },\n });\n\n return { taskId: running.id, kind: running.kind, state: running.state };\n },\n });\n}\n\n/**\n * Give a conversational agent one bounded delegation surface backed by the\n * application's route registry. The caller selects a declared route; trusted\n * code owns task creation, budget enforcement, and the fresh target session.\n */\nexport function createDelegateAgentTool(\n stores: DelegateAgentToolStores,\n options: DelegateAgentToolOptions,\n): DelegateAgentTool {\n const doFetch = options.fetch ?? fetch;\n const routeSummary = options.routes.routes\n .map((route) => `${route.id}@${route.version}: ${route.description}`)\n .join(\"\\n\");\n return defineTool({\n description:\n \"Delegate durable work to one registered specialist. Select only a listed route; \" +\n \"the specialist runs independently and reports its result through Factory.\\n\" +\n routeSummary,\n inputSchema: z.object({\n route: agentRouteBindingSchema.describe(\n \"Exact route id and version listed in this tool's description.\",\n ),\n repositoryIds: repositoryIdsSchema.refine(\n (repositoryIds) => new Set(repositoryIds).size === repositoryIds.length,\n \"Repository ids must be unique.\",\n ),\n signalId: signalIdSchema\n .optional()\n .describe(\"Pending external signal this delegation accepts.\"),\n parentTaskId: taskIdSchema.optional().describe(\"Existing task this work continues.\"),\n changeId: changeIdSchema\n .optional()\n .describe(\"Existing change the specialist should inspect.\"),\n channel: z.string().min(1).describe(\"Originating conversation channel.\"),\n address: z.string().min(1).describe(\"Originating conversation reply address.\"),\n brief: z\n .string()\n .min(1)\n .max(4000)\n .describe(\"Facts and desired outcome, without hidden reasoning.\"),\n }),\n async execute(input, ctx) {\n const route = options.routes.get(input.route);\n if (route === undefined) {\n throw new Error(`Unknown agent route ${input.route.id}@${input.route.version}.`);\n }\n const replyTo = { channel: input.channel, address: input.address };\n const auth = ctx.session.auth.initiator;\n const attributes = auth?.attributes;\n const boundSignal = signalIdSchema.safeParse(attributes?.factorySignalId);\n if (attributes?.factorySignalId !== undefined && !boundSignal.success)\n throw new Error(\"Connector-authenticated Signal context is invalid.\");\n if (\n boundSignal.success &&\n input.signalId !== undefined &&\n input.signalId !== boundSignal.data\n )\n throw new Error(\"Delegation must accept its authenticated Signal.\");\n const signalId = input.signalId ?? (boundSignal.success ? boundSignal.data : undefined);\n const boundRepositoryIds = repositoryIdsForSession(auth);\n if (\n boundRepositoryIds !== null &&\n input.repositoryIds.some((id) => !boundRepositoryIds.includes(id))\n ) {\n throw new Error(\n \"Delegation includes a repository outside the connector-authenticated context.\",\n );\n }\n const boundReplyTo = replyToForSession(auth);\n if (\n boundReplyTo !== null &&\n (boundReplyTo.channel !== replyTo.channel || boundReplyTo.address !== replyTo.address)\n ) {\n throw new Error(\"Delegation reply address does not match the authenticated conversation.\");\n }\n for (const repositoryId of input.repositoryIds) {\n const repository = await stores.repositories.get(repositoryId);\n if (repository === null || !repository.enabled) {\n throw new Error(`Repository ${repositoryId} is not enabled in this factory.`);\n }\n }\n const boundChange = changeIdSchema.safeParse(attributes?.factoryChangeId);\n if (attributes?.factoryChangeId !== undefined && !boundChange.success) {\n throw new Error(\"Connector-authenticated change context is invalid.\");\n }\n if (\n input.changeId !== undefined &&\n boundChange.success &&\n input.changeId !== boundChange.data\n ) {\n throw new Error(\"Delegated change does not match the connector-authenticated context.\");\n }\n const changeId = input.changeId ?? (boundChange.success ? boundChange.data : undefined);\n if (changeId !== undefined) {\n if (input.repositoryIds.length !== 1) {\n throw new Error(\"Delegating work on an existing Change requires exactly one repository.\");\n }\n const change = await stores.changes.get(changeId);\n if (\n change === null ||\n change.repositoryId !== input.repositoryIds[0] ||\n (change.state !== \"verifying\" && change.state !== \"ready\")\n ) {\n throw new Error(\n `Change ${changeId} is not an open pull request in the selected repository.`,\n );\n }\n }\n if (signalId !== undefined) {\n const signal = await stores.signals.get(signalId);\n if (signal === null) throw new Error(`Signal ${signalId} does not exist.`);\n if (\n boundSignal.success &&\n (signal.triage?.attempt !== Number(attributes?.factorySignalAttempt) ||\n signal.triage?.state !== \"running\" ||\n (signal.triage.sessionId !== undefined && signal.triage.sessionId !== ctx.session.id))\n )\n throw new Error(\"Delegation requires the current running Signal triage attempt.\");\n if (\n !input.repositoryIds.includes(signal.repositoryId) ||\n signal.replyTo.channel !== replyTo.channel ||\n signal.replyTo.address !== replyTo.address\n ) {\n throw new Error(\n \"Delegation does not match the signal's authenticated repository and reply address.\",\n );\n }\n if (signal.outcome !== \"pending\" && signal.outcome !== \"accepted\") {\n throw new Error(`Signal ${signal.id} is already ${signal.outcome}.`);\n }\n }\n const acceptSignal = async () => {\n if (signalId === undefined) return;\n const signal = await stores.signals.get(signalId);\n if (signal?.outcome === \"accepted\") return;\n await stores.signals.decide(signalId, {\n outcome: \"accepted\",\n reason: `Foreman selected ${route.id}.`,\n });\n };\n let parentTaskId = input.parentTaskId;\n const boundTaskId = optionalTaskIdForSession(auth);\n if (boundTaskId !== null && boundReplyTo?.channel === \"api\") {\n const boundTask = await stores.tasks.get(boundTaskId);\n if (boundTask?.area === \"conversation\") {\n if (parentTaskId !== undefined && parentTaskId !== boundTask.id)\n throw new Error(\n \"Follow-up delegation must name its authenticated conversation task as parent.\",\n );\n parentTaskId = boundTask.id;\n }\n }\n const initiator = ctx.session.auth.initiator;\n const operator = initiator?.principalId ?? \"foreman\";\n const workflowResult = await options.workflow?.({\n stores,\n route,\n repositoryIds: input.repositoryIds,\n signalId,\n parentTaskId,\n changeId,\n replyTo,\n brief: input.brief,\n operator,\n dispatch: (task) =>\n dispatchRouteTask(stores, options, doFetch, route, task, changeId, input.brief),\n });\n if (workflowResult !== undefined && workflowResult !== null) {\n await acceptSignal();\n return workflowResult;\n }\n const taskInput = {\n repositoryIds: input.repositoryIds,\n kind: route.taskKind,\n work: routeTaskWork({\n route: route,\n title: input.brief.trim().slice(0, 256),\n input: { brief: input.brief },\n }),\n replyTo,\n changeId,\n ...(signalId === undefined\n ? {}\n : { dedupeKey: `route:${signalId}:${route.id}:${route.version}` }),\n };\n const task = await stores.tasks.create(\n parentTaskId !== undefined\n ? { ...taskInput, parentTaskId, origin: {} }\n : signalId !== undefined\n ? { ...taskInput, origin: { signalId } }\n : { ...taskInput, origin: { operator } },\n );\n await acceptSignal();\n const running =\n task.state === \"queued\"\n ? await dispatchRouteTask(stores, options, doFetch, route, task, changeId, input.brief)\n : task;\n return {\n taskId: running.id,\n route: parseAgentRouteBinding({ id: route.id, version: route.version }),\n agent: route.agent,\n state: running.state,\n };\n },\n });\n}\n\nasync function dispatchRouteTask(\n stores: DelegateAgentToolStores,\n options: DelegateAgentToolOptions,\n doFetch: typeof fetch,\n route: AgentRoute,\n task: Task,\n changeId: ChangeId | undefined,\n brief: string,\n): Promise<Task> {\n return dispatchTask(stores, {\n taskId: task.id,\n route,\n maxLaunchAttempts: options.maxLaunchAttempts?.(route) ?? 1,\n budget: options.budget?.(route),\n launch: async (dispatched) => {\n if (options.launch) return options.launch({ task: dispatched, route, brief });\n const headers = new Headers(await options.headers?.(dispatched, route));\n headers.set(\"content-type\", \"application/json\");\n const token = await options.token?.(route);\n if (token !== undefined) headers.set(\"authorization\", `Bearer ${token}`);\n const message = [\n `You are assigned factory task ${dispatched.id} through route ${route.id}.`,\n changeId === undefined ? null : `Change under review: ${changeId}.`,\n brief,\n \"Read the task first and return exactly one bounded Factory outcome.\",\n ]\n .filter((line) => line !== null)\n .join(\"\\n\\n\");\n return startEveSession({\n agentUrl: options.targetUrl(route),\n agentName: route.agent,\n headers,\n message,\n operationId: `factory-task:${dispatched.id}:${dispatched.attempt}`,\n fetch: doFetch,\n });\n },\n });\n}\n"],"mappings":"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"}
|
|
@@ -3,65 +3,183 @@ import { MaybePromise } from "../schema/common.mjs";
|
|
|
3
3
|
//#region src/eve/session-client.d.ts
|
|
4
4
|
/** The minimal event shape consumed from an Eve session stream. */
|
|
5
5
|
interface EveSessionEvent {
|
|
6
|
+
/** Eve protocol event discriminator. */
|
|
6
7
|
type: string;
|
|
8
|
+
/** Provider payload retained as unknown until the consuming operation validates required fields. */
|
|
7
9
|
data?: unknown;
|
|
8
10
|
}
|
|
9
11
|
/** Identifies and bounds a direct read of one Eve session event stream. */
|
|
10
12
|
interface EveSessionStreamInput {
|
|
13
|
+
/** Base URL of the selected Eve agent. */
|
|
11
14
|
agentUrl: string;
|
|
15
|
+
/** Exact provider session whose ordered event log is read. */
|
|
12
16
|
sessionId: string;
|
|
17
|
+
/** Authentication and Factory binding headers for the stream request. */
|
|
13
18
|
headers: HeadersInit;
|
|
19
|
+
/** Cancellation or deadline signal that bounds the stream read. */
|
|
14
20
|
signal: AbortSignal;
|
|
21
|
+
/** Inclusive provider event index, or `-1` for the latest event. */
|
|
15
22
|
startIndex: number;
|
|
23
|
+
/** Whether the stream waits for events appended after the current end. */
|
|
16
24
|
follow: boolean;
|
|
17
25
|
}
|
|
18
26
|
/** Injectable reader for an Eve session's ordered event stream. */
|
|
19
27
|
type EveSessionStream = (input: EveSessionStreamInput) => AsyncIterable<EveSessionEvent>;
|
|
20
28
|
/** Configures an idempotent request to start or reconnect to an Eve session. */
|
|
21
29
|
interface StartEveSessionOptions {
|
|
30
|
+
/** Base URL of the selected Eve agent; trailing slashes are ignored. */
|
|
22
31
|
agentUrl: string;
|
|
32
|
+
/** Initial user message serialized into Eve's session-create request. */
|
|
23
33
|
message: unknown;
|
|
34
|
+
/** Stable application idempotency key reused when reconnecting the same logical launch. */
|
|
24
35
|
operationId: string;
|
|
36
|
+
/** Authentication and Factory binding headers, resolved immediately before the request. */
|
|
25
37
|
headers?: MaybePromise<HeadersInit>;
|
|
38
|
+
/**
|
|
39
|
+
* HTTP implementation. Defaults to `globalThis.fetch`. Transport rejections propagate; a launch
|
|
40
|
+
* adapter that owns durable recovery must translate ambiguous acceptance at its boundary.
|
|
41
|
+
*/
|
|
26
42
|
fetch?: typeof globalThis.fetch;
|
|
43
|
+
/** Request deadline in milliseconds. Defaults to 20,000. */
|
|
27
44
|
timeoutMs?: number;
|
|
45
|
+
/** Human-readable agent name included in refusal diagnostics. Defaults to `Eve agent`. */
|
|
28
46
|
agentName?: string;
|
|
29
47
|
}
|
|
30
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* Starts or reconnects to one Eve session through its idempotent HTTP boundary.
|
|
50
|
+
*
|
|
51
|
+
* @remarks
|
|
52
|
+
* This call sends one POST to the selected agent's `/eve/v1/session` endpoint. Reusing
|
|
53
|
+
* `operationId` asks Eve to return the same logical session rather than create duplicate work.
|
|
54
|
+
* The returned execution reference identifies provider acceptance only; Factory dispatch must
|
|
55
|
+
* still persist it, and the agent must later complete or otherwise advance its Task.
|
|
56
|
+
*
|
|
57
|
+
* An HTTP refusal throws a regular error with the status. A successful response without a usable
|
|
58
|
+
* session ID throws `ExecutionLaunchPendingError`, because the provider may have accepted work and
|
|
59
|
+
* recovery must use the same operation ID. Request timeouts and transport failures propagate
|
|
60
|
+
* unchanged because this transport helper does not own a durable retry loop. A launcher that can
|
|
61
|
+
* recover the same running attempt must translate an ambiguous transport failure to
|
|
62
|
+
* `ExecutionLaunchPendingError` at that boundary and retry with the same operation ID.
|
|
63
|
+
*
|
|
64
|
+
* @param options - Agent URL, initial message, stable operation ID, and optional request controls.
|
|
65
|
+
* @returns An Eve execution reference containing the accepted session ID.
|
|
66
|
+
* @throws `ExecutionLaunchPendingError` after an accepted response without a session ID, and a
|
|
67
|
+
* regular error on timeout, transport failure, HTTP refusal, or invalid input.
|
|
68
|
+
* @see The shipped `docs/recipes/eve-agent.md` recipe for the complete dispatch boundary.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```ts
|
|
72
|
+
* import { startEveSession, taskHeaders } from "@vercel/factory";
|
|
73
|
+
* import type { TaskAttemptRef } from "@vercel/factory/execution";
|
|
74
|
+
*
|
|
75
|
+
* declare const task: TaskAttemptRef;
|
|
76
|
+
* const execution = await startEveSession({
|
|
77
|
+
* agentUrl: "https://factory.example/eve/agents/worker",
|
|
78
|
+
* message: `Work Task ${task.taskId}`,
|
|
79
|
+
* operationId: `factory-task:${task.taskId}:${task.attempt}`,
|
|
80
|
+
* headers: taskHeaders(task),
|
|
81
|
+
* });
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
31
84
|
declare function startEveSession(options: StartEveSessionOptions): Promise<ExecutionReference>;
|
|
32
85
|
/** Configures forwarding an authorized HTTP request to one exact Eve session. */
|
|
33
86
|
interface ForwardEveSessionRequestOptions {
|
|
87
|
+
/** Base URL of the selected Eve agent; trailing slashes are ignored. */
|
|
34
88
|
agentUrl: string;
|
|
89
|
+
/** Exact provider session that owns the forwarded Factory API request. */
|
|
35
90
|
sessionId: string;
|
|
91
|
+
/** Empty for the session endpoint, otherwise an absolute path/query suffix within it; fragments are refused. */
|
|
36
92
|
path: string;
|
|
93
|
+
/** Incoming request supplying method, abort signal, and POST body. */
|
|
37
94
|
source: Request;
|
|
95
|
+
/** Authentication and Factory binding headers, resolved immediately before forwarding. */
|
|
38
96
|
headers?: MaybePromise<HeadersInit>;
|
|
97
|
+
/** HTTP implementation. Defaults to `globalThis.fetch`. */
|
|
39
98
|
fetch?: typeof globalThis.fetch;
|
|
40
99
|
}
|
|
41
|
-
/**
|
|
100
|
+
/**
|
|
101
|
+
* Forwards one authorized Factory API request to its exact Eve session.
|
|
102
|
+
*
|
|
103
|
+
* @remarks
|
|
104
|
+
* The caller must authenticate and authorize the source request before invoking this helper. It
|
|
105
|
+
* forwards the source method, abort signal, and POST body using the supplied binding headers, then
|
|
106
|
+
* returns Eve's response unchanged. It does not interpret non-success status codes, retry the
|
|
107
|
+
* request, or update Factory Task state, so callers retain operation-specific response and replay
|
|
108
|
+
* handling.
|
|
109
|
+
*
|
|
110
|
+
* @param options - Exact Eve session, path, authorized source request, and transport controls.
|
|
111
|
+
* @returns The unconsumed response from the Eve session endpoint.
|
|
112
|
+
* @throws When a nonempty path is not absolute, resolves outside the session, contains a fragment,
|
|
113
|
+
* or the request transport fails.
|
|
114
|
+
*/
|
|
42
115
|
declare function forwardEveSessionRequest(options: ForwardEveSessionRequestOptions): Promise<Response>;
|
|
43
116
|
/** Configures cancellation and provider-stream confirmation for one exact Eve session. */
|
|
44
117
|
interface CancelEveSessionOptions {
|
|
118
|
+
/** Base URL of the selected Eve agent; trailing slashes are ignored. */
|
|
45
119
|
agentUrl: string;
|
|
120
|
+
/** Exact provider session to cancel and observe. */
|
|
46
121
|
sessionId: string;
|
|
122
|
+
/** Authentication and Factory binding headers used for cancellation and confirmation. */
|
|
47
123
|
headers?: MaybePromise<HeadersInit>;
|
|
124
|
+
/** HTTP implementation. Defaults to `globalThis.fetch`. */
|
|
48
125
|
fetch?: typeof globalThis.fetch;
|
|
126
|
+
/** Cancellation request deadline in milliseconds. Defaults to 10,000. */
|
|
49
127
|
requestTimeoutMs?: number;
|
|
128
|
+
/** Provider-stream confirmation deadline in milliseconds. Defaults to 5,000. */
|
|
50
129
|
confirmationTimeoutMs?: number;
|
|
130
|
+
/** Injectable ordered event reader. Defaults to the Eve client stream. */
|
|
51
131
|
stream?: EveSessionStream;
|
|
52
132
|
}
|
|
53
|
-
/**
|
|
133
|
+
/**
|
|
134
|
+
* Requests cancellation and confirms that the exact Eve session has stopped running.
|
|
135
|
+
*
|
|
136
|
+
* @remarks
|
|
137
|
+
* A successful cancellation response is not sufficient by itself. Unless Eve reports
|
|
138
|
+
* `no_active_turn`, this helper follows from the latest provider event and returns only after
|
|
139
|
+
* observing `session.waiting`, `session.completed`, or `session.failed`. A refusal, timeout, stream
|
|
140
|
+
* failure, or non-confirming event throws; the caller should keep canonical Task cancellation and
|
|
141
|
+
* retry or inspect this same session rather than assume it stopped.
|
|
142
|
+
*
|
|
143
|
+
* This helper does not transition or settle a Factory Task. Use the higher-level execution
|
|
144
|
+
* cancellation flow when Task lifecycle and usage must be persisted together.
|
|
145
|
+
*
|
|
146
|
+
* @param options - Exact Eve session plus request and confirmation bounds.
|
|
147
|
+
* @returns After the provider confirms that the session has no active turn.
|
|
148
|
+
* @throws On HTTP refusal, request or confirmation timeout, stream failure, or missing confirmation.
|
|
149
|
+
*/
|
|
54
150
|
declare function cancelEveSession(options: CancelEveSessionOptions): Promise<void>;
|
|
55
151
|
/** Configures a bounded scan for the response from the latest completed Eve turn. */
|
|
56
152
|
interface ReadCompletedEveTurnOptions {
|
|
153
|
+
/** Base URL of the selected Eve agent; trailing slashes are ignored. */
|
|
57
154
|
agentUrl: string;
|
|
155
|
+
/** Exact provider session whose finite event history is scanned. */
|
|
58
156
|
sessionId: string;
|
|
157
|
+
/** Authentication and Factory binding headers for the stream request. */
|
|
59
158
|
headers?: MaybePromise<HeadersInit>;
|
|
159
|
+
/** Event-stream read deadline in milliseconds. Defaults to 10,000. */
|
|
60
160
|
timeoutMs?: number;
|
|
161
|
+
/** Maximum events inspected before returning `null`. Defaults to 10,000. */
|
|
61
162
|
maxEvents?: number;
|
|
163
|
+
/** HTTP implementation used by the default bounded stream reader. Defaults to `globalThis.fetch`. */
|
|
164
|
+
fetch?: typeof globalThis.fetch;
|
|
165
|
+
/** Injectable ordered event reader; it must propagate transport failures to preserve certainty. */
|
|
62
166
|
stream?: EveSessionStream;
|
|
63
167
|
}
|
|
64
|
-
/**
|
|
168
|
+
/**
|
|
169
|
+
* Reads the response from the latest fully completed Eve turn, if one exists.
|
|
170
|
+
*
|
|
171
|
+
* @remarks
|
|
172
|
+
* This performs a bounded, non-following scan from the start of the session. It returns a nonempty
|
|
173
|
+
* completed message only when the matching latest turn reaches `turn.completed`; failed,
|
|
174
|
+
* cancelled, incomplete, over-limit, or message-less turns return `null`. The operation is
|
|
175
|
+
* read-only and safe to repeat. The default reader verifies that it reached Eve's advertised
|
|
176
|
+
* durable tail before accepting output. Transport and timeout failures are propagated so callers
|
|
177
|
+
* can distinguish unavailable or truncated history from a confirmed lack of completed output.
|
|
178
|
+
*
|
|
179
|
+
* @param options - Exact Eve session plus stream deadline and event bound.
|
|
180
|
+
* @returns The latest completed turn response, or `null` when no bounded complete response exists.
|
|
181
|
+
* @throws When the event stream cannot be read or its deadline expires.
|
|
182
|
+
*/
|
|
65
183
|
declare function readCompletedEveTurn(options: ReadCompletedEveTurnOptions): Promise<string | null>;
|
|
66
184
|
//#endregion
|
|
67
185
|
export { CancelEveSessionOptions, EveSessionEvent, EveSessionStream, EveSessionStreamInput, ForwardEveSessionRequestOptions, ReadCompletedEveTurnOptions, StartEveSessionOptions, cancelEveSession, forwardEveSessionRequest, readCompletedEveTurn, startEveSession };
|