@mikode13/harness 11.0.0 → 11.1.0
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/README.md +138 -6
- package/dist/agent/domain/agent.d.ts +24 -3
- package/dist/agent/domain/agent.js.map +1 -1
- package/dist/agent/domain/approval.d.ts +20 -0
- package/dist/agent/domain/approval.js.map +1 -1
- package/dist/agent/domain/inbox.d.ts +42 -0
- package/dist/agent/domain/inbox.js +81 -0
- package/dist/agent/domain/inbox.js.map +1 -0
- package/dist/agent/domain/oneRunAtATime.d.ts +17 -0
- package/dist/agent/domain/oneRunAtATime.js +32 -0
- package/dist/agent/domain/oneRunAtATime.js.map +1 -0
- package/dist/agent/domain/runContext.d.ts +4 -0
- package/dist/agent/domain/runContext.js +5 -0
- package/dist/agent/domain/runContext.js.map +1 -1
- package/dist/agent/domain/session.d.ts +15 -0
- package/dist/agent/domain/session.js +80 -0
- package/dist/agent/domain/session.js.map +1 -0
- package/dist/engines/claude/infrastructure/model/claudeAgent.d.ts +6 -2
- package/dist/engines/claude/infrastructure/model/claudeAgent.js +20 -5
- package/dist/engines/claude/infrastructure/model/claudeAgent.js.map +1 -1
- package/dist/engines/codex/infrastructure/model/codexAgent.d.ts +5 -1
- package/dist/engines/codex/infrastructure/model/codexAgent.js +13 -2
- package/dist/engines/codex/infrastructure/model/codexAgent.js.map +1 -1
- package/dist/engines/domain/model/backgroundAgents.d.ts +5 -2
- package/dist/engines/domain/model/backgroundAgents.js +11 -3
- package/dist/engines/domain/model/backgroundAgents.js.map +1 -1
- package/dist/engines/domain/model/llmAgent.d.ts +5 -0
- package/dist/engines/domain/model/llmAgent.js +68 -10
- package/dist/engines/domain/model/llmAgent.js.map +1 -1
- package/dist/factory/infrastructure/agentLLMFactory.d.ts +18 -5
- package/dist/factory/infrastructure/agentLLMFactory.js +31 -8
- package/dist/factory/infrastructure/agentLLMFactory.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/orchestration/domain/model/orchestratorAgent.d.ts +9 -1
- package/dist/orchestration/domain/model/orchestratorAgent.js +20 -3
- package/dist/orchestration/domain/model/orchestratorAgent.js.map +1 -1
- package/dist/recovery/domain/history.d.ts +10 -3
- package/dist/recovery/domain/history.js +1 -0
- package/dist/recovery/domain/history.js.map +1 -1
- package/dist/recovery/domain/recoveryStore.d.ts +14 -2
- package/dist/recovery/domain/recoveryStore.js.map +1 -1
- package/dist/recovery/infrastructure/fileRecoveryStore.d.ts +21 -2
- package/dist/recovery/infrastructure/fileRecoveryStore.js +58 -6
- package/dist/recovery/infrastructure/fileRecoveryStore.js.map +1 -1
- package/dist/recovery/infrastructure/folderClaims.d.ts +27 -0
- package/dist/recovery/infrastructure/folderClaims.js +124 -0
- package/dist/recovery/infrastructure/folderClaims.js.map +1 -0
- package/dist/shared/domain/session.d.ts +12 -0
- package/dist/shared/domain/session.js +2 -0
- package/dist/shared/domain/session.js.map +1 -0
- package/dist/tools/domain/preparedCall.d.ts +3 -1
- package/dist/tools/domain/preparedCall.js.map +1 -1
- package/dist/tools/infrastructure/commandTool.js +2 -0
- package/dist/tools/infrastructure/commandTool.js.map +1 -1
- package/dist/tools/infrastructure/exploreTool.js +1 -1
- package/dist/tools/infrastructure/exploreTool.js.map +1 -1
- package/dist/tools/infrastructure/fileTools.d.ts +2 -0
- package/dist/tools/infrastructure/fileTools.js +9 -1
- package/dist/tools/infrastructure/fileTools.js.map +1 -1
- package/dist/tools/infrastructure/removeWorktreeTool.d.ts +23 -0
- package/dist/tools/infrastructure/removeWorktreeTool.js +172 -0
- package/dist/tools/infrastructure/removeWorktreeTool.js.map +1 -0
- package/dist/tools/infrastructure/workspaceWrites.d.ts +4 -0
- package/dist/tools/infrastructure/workspaceWrites.js +30 -16
- package/dist/tools/infrastructure/workspaceWrites.js.map +1 -1
- package/dist/tools/infrastructure/worktrees.d.ts +49 -0
- package/dist/tools/infrastructure/worktrees.js +154 -0
- package/dist/tools/infrastructure/worktrees.js.map +1 -0
- package/dist/tools/infrastructure/writeSession.d.ts +5 -1
- package/dist/tools/infrastructure/writeSession.js +7 -3
- package/dist/tools/infrastructure/writeSession.js.map +1 -1
- package/dist/workflow/domain/workflow.d.ts +3 -0
- package/dist/workflow/domain/workflow.js.map +1 -1
- package/dist/workflow/domain/workflowRunner.js +8 -2
- package/dist/workflow/domain/workflowRunner.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"writeSession.js","sourceRoot":"","sources":["../../../src/tools/infrastructure/writeSession.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"writeSession.js","sourceRoot":"","sources":["../../../src/tools/infrastructure/writeSession.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Session } from '#src/shared/domain/session';\nimport {\n\ttype RecoveryStore,\n\ttype RunJournal,\n\ttype RunStatus,\n\tWorkspaceBusyError,\n} from '#src/recovery/domain/recoveryStore';\nimport { EditRefusedError } from '../domain/fileEdits.ts';\n\n// Far more than a task needs; a run past it is more likely looping than working.\nconst defaultMaxChanges = 500;\n\n/**\n * Everything one top-level run writes, across all its roles and rounds. Its journal starts on\n * the first write, so a run that only reads never holds the workspace, and ends with the run.\n * Once a change could not be confirmed, the session refuses every further write: the record of\n * what the run did must stay complete.\n */\nexport class WriteSession {\n\tprivate readonly store: RecoveryStore;\n\tprivate readonly maxChanges: number;\n\tprivate readonly onStart: ((runId: string) => void) | undefined;\n\tprivate readonly onDiscard: (() => void) | undefined;\n\tprivate readonly session: Session | undefined;\n\tprivate journal: Promise<RunJournal> | undefined;\n\tprivate changes = 0;\n\tprivate stopped: string | undefined;\n\t// The end of the last write waiting or running, which the next one starts after.\n\tprivate writing: Promise<unknown> = Promise.resolve();\n\n\tconstructor({\n\t\tstore,\n\t\tmaxChanges = defaultMaxChanges,\n\t\tonStart,\n\t\tonDiscard,\n\t\tsession,\n\t}: {\n\t\tstore: RecoveryStore;\n\t\tmaxChanges?: number;\n\t\t/** Told the run's id once its journal has started, before its first change is recorded. */\n\t\tonStart?: (runId: string) => void;\n\t\t/** Told when the run ended having changed nothing, so the history no longer has it. */\n\t\tonDiscard?: () => void;\n\t\t/** The session the run belongs to, recorded with it. */\n\t\tsession?: Session;\n\t}) {\n\t\tthis.store = store;\n\t\tthis.maxChanges = maxChanges;\n\t\tthis.onStart = onStart;\n\t\tthis.onDiscard = onDiscard;\n\t\tthis.session = session;\n\t}\n\n\t/** The journal for one more change, counted against the run's limit. */\n\tasync journalForChange(): Promise<RunJournal> {\n\t\tif (this.stopped !== undefined) throw new EditRefusedError(this.stopped);\n\t\tif (this.changes >= this.maxChanges) {\n\t\t\tthrow new EditRefusedError(\n\t\t\t\t`This run has made ${String(this.maxChanges)} changes, the most one run may make`,\n\t\t\t);\n\t\t}\n\n\t\tthis.journal ??= this.store\n\t\t\t.startRun(this.session ? { session: this.session } : {})\n\t\t\t.then(started => {\n\t\t\t\tthis.onStart?.(started.runId);\n\t\t\t\treturn started;\n\t\t\t});\n\t\tlet journal: RunJournal;\n\t\ttry {\n\t\t\tjournal = await this.journal;\n\t\t} catch (error) {\n\t\t\t// A failed start is not kept, so the next change tries again.\n\t\t\tthis.journal = undefined;\n\t\t\tif (error instanceof WorkspaceBusyError) {\n\t\t\t\tthrow new EditRefusedError(\n\t\t\t\t\t'Another run, a move through its history, or the removal of its worktree holds this workspace, so nothing was changed. The harness lets one run at a time write to a workspace; tell the user, who can wait for it to end or give this chat a worktree of its own.',\n\t\t\t\t\t{\n\t\t\t\t\t\tcause: error,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow new EditRefusedError(\n\t\t\t\t'The harness could not start recording this run, so nothing was changed',\n\t\t\t\t{ cause: error },\n\t\t\t);\n\t\t}\n\t\tthis.changes++;\n\t\treturn journal;\n\t}\n\n\t/**\n\t * Runs `write` once every write before it has ended, so agents of one run that work at once\n\t * never change files together: each change is read, recorded and made alone, and a command\n\t * runs with no other write while the files are captured around it. A cancellation stops the\n\t * wait.\n\t */\n\tasync exclusive<Result>(write: () => Promise<Result>, signal: AbortSignal): Promise<Result> {\n\t\tconst before = this.writing;\n\t\tlet release!: () => void;\n\t\tthis.writing = new Promise<void>(resolve => {\n\t\t\trelease = resolve;\n\t\t});\n\t\ttry {\n\t\t\tawait untilAborted(before, signal);\n\t\t\tsignal.throwIfAborted();\n\t\t\treturn await write();\n\t\t} finally {\n\t\t\t// Released even when cancelled while waiting: the writes after it wait for those before.\n\t\t\tvoid before.then(release, release);\n\t\t}\n\t}\n\n\t/** Refuses every further write, for this reason. The first reason given is kept. */\n\tstop(reason: string): void {\n\t\tthis.stopped ??= reason;\n\t}\n\n\t/** The run's id once it has written, which is what an undo needs. */\n\tasync runId(): Promise<string | undefined> {\n\t\tconst journal = await this.journal?.catch(() => undefined);\n\t\treturn journal?.runId;\n\t}\n\n\t/**\n\t * Ends the run's record and releases the workspace. A run that never wrote has nothing to end,\n\t * and one whose every change was abandoned leaves the history.\n\t */\n\tasync finish(status: Exclude<RunStatus, 'running' | 'interrupted'>): Promise<void> {\n\t\tconst journal = await this.journal?.catch(() => undefined);\n\t\tthis.journal = undefined;\n\t\tif (!journal) return;\n\t\ttry {\n\t\t\tawait journal.finish(status);\n\t\t} finally {\n\t\t\t// Even when finishing failed: a run that changed nothing is out of the history either way.\n\t\t\tif (!journal.changed) this.onDiscard?.();\n\t\t}\n\t}\n}\n\n/** Waits for `promise`, settled either way, or until `signal` aborts, whichever comes first. */\nfunction untilAborted(promise: Promise<unknown>, signal: AbortSignal): Promise<void> {\n\tif (signal.aborted) return Promise.resolve();\n\treturn new Promise(resolve => {\n\t\tconst done = () => {\n\t\t\tsignal.removeEventListener('abort', done);\n\t\t\tresolve();\n\t\t};\n\t\tsignal.addEventListener('abort', done, { once: true });\n\t\tpromise.then(done, done);\n\t});\n}\n"]}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Agent, RunOptions } from '#src/agent/domain/agent';
|
|
2
|
+
import type { Session } from '#src/shared/domain/session';
|
|
2
3
|
import type { ILogger } from '#src/shared/domain/logger';
|
|
3
4
|
import type { Tokens } from '#src/shared/domain/tokens';
|
|
4
5
|
import type { Validator } from '#src/shared/domain/validator';
|
|
@@ -80,6 +81,8 @@ export interface WorkflowResult<Result> {
|
|
|
80
81
|
/** Seconds. */
|
|
81
82
|
duration: number;
|
|
82
83
|
runId?: string;
|
|
84
|
+
/** The session the run belonged to: the one `RunOptions.session` gave, or a new one. */
|
|
85
|
+
session?: Session;
|
|
83
86
|
/** One report per stage run, in order: what each stage received, answered and chose. */
|
|
84
87
|
stages: readonly WorkflowStageReport[];
|
|
85
88
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow.js","sourceRoot":"","sources":["../../../src/workflow/domain/workflow.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"workflow.js","sourceRoot":"","sources":["../../../src/workflow/domain/workflow.ts"],"names":[],"mappings":"AA2DA;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB;IAOnC,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;AACxD,CAAC","sourcesContent":["import type { Agent, RunOptions } from '#src/agent/domain/agent';\nimport type { Session } from '#src/shared/domain/session';\nimport type { ILogger } from '#src/shared/domain/logger';\nimport type { Tokens } from '#src/shared/domain/tokens';\nimport type { Validator } from '#src/shared/domain/validator';\n\n/** The names of a workflow's stages: the keys of its map of what each stage receives. */\nexport type WorkflowStageName<Inputs> = keyof Inputs & string;\n\n/**\n * What a stage chose once its answer was valid. `go` runs the stage it names, which receives\n * `input` and nothing else; `stop` ends the run early on purpose and `complete` ends it finished,\n * each with the run's result. `Inputs` maps each stage's name to what it receives, so a `go` that\n * names a stage that does not exist, or hands it the wrong input, does not compile.\n */\nexport type WorkflowTransition<Inputs, Result> =\n\t| {\n\t\t\t[Name in WorkflowStageName<Inputs>]: { type: 'go'; stage: Name; input: Inputs[Name] };\n\t }[WorkflowStageName<Inputs>]\n\t| { type: 'stop'; result: Result }\n\t| { type: 'complete'; result: Result };\n\n/**\n * One step of a workflow: how it creates its agent, the prompt that agent gets from the stage's\n * input, how its JSON answer is validated, and the transition the valid answer selects.\n */\nexport interface WorkflowStageDefinition<\n\tInputs,\n\tName extends WorkflowStageName<Inputs>,\n\tOutput,\n\tResult,\n> {\n\t/**\n\t * Creates the stage's agent, with a context of its own, the first time the stage runs in a\n\t * run. When the run comes back to the stage, the same agent goes on with its conversation;\n\t * the next run creates another. It may be async, as `createLLMOrchestrator` is.\n\t */\n\tagent(): Agent | PromiseLike<Agent>;\n\tprompt(input: Inputs[Name]): string | PromiseLike<string>;\n\t/** Receives the agent's answer parsed as JSON. */\n\treadonly validator: Validator<Output>;\n\t/** Receives the validated answer and the stage's own input, to carry forward what the next stage needs. */\n\ttransition(\n\t\toutput: Output,\n\t\tinput: Inputs[Name],\n\t): WorkflowTransition<Inputs, Result> | PromiseLike<WorkflowTransition<Inputs, Result>>;\n\t/** How many answers the stage may give before an invalid one fails the run. */\n\treadonly maxAttempts?: number;\n}\n\n/** A stage bound to its name, as `defineWorkflowStages` returns it. */\nexport interface WorkflowStage<\n\tInputs,\n\tResult,\n\tName extends WorkflowStageName<Inputs> = WorkflowStageName<Inputs>,\n> extends WorkflowStageDefinition<Inputs, Name, unknown, Result> {\n\treadonly name: Name;\n}\n\n/**\n * Binds stage definitions to one workflow's map of stage inputs and its result. The function it\n * returns infers each stage's output from its validator.\n *\n * @example\n * const stage = defineWorkflowStages<{ triage: string; draft: Brief }, string>();\n * const triage = stage('triage', { agent, prompt, validator, transition });\n */\nexport function defineWorkflowStages<Inputs, Result>(): <\n\tName extends WorkflowStageName<Inputs>,\n\tOutput,\n>(\n\tname: Name,\n\tdefinition: WorkflowStageDefinition<Inputs, Name, Output, Result>,\n) => WorkflowStage<Inputs, Result, Name> {\n\treturn (name, definition) => ({ ...definition, name });\n}\n\nexport interface WorkflowStageReport {\n\tstage: string;\n\t/** What the stage received. */\n\tinput: unknown;\n\t/** The stage's validated answer. */\n\toutput: unknown;\n\t/** How many answers the stage's agent gave, the last one valid. */\n\tattempts: number;\n\t/** Seconds. */\n\tduration: number;\n\t/** Absent when a provider did not report an answer's usage. */\n\ttokens?: Tokens;\n\ttransition: 'go' | 'stop' | 'complete';\n\t/** The stage that ran next, absent after `stop` or `complete`. */\n\tnextStage?: string;\n}\n\nexport interface WorkflowResult<Result> {\n\t/** `stopped` is a deliberate early end, not a failure: a failure rejects the run. */\n\tstatus: 'completed' | 'stopped';\n\tresult: Result;\n\t/** Every stage's usage. Absent when any answer's usage was not reported. */\n\ttokens?: Tokens;\n\t/** Seconds. */\n\tduration: number;\n\trunId?: string;\n\t/** The session the run belonged to: the one `RunOptions.session` gave, or a new one. */\n\tsession?: Session;\n\t/** One report per stage run, in order: what each stage received, answered and chose. */\n\tstages: readonly WorkflowStageReport[];\n}\n\nexport interface Workflow<Input, Result> {\n\trun(input: Input, options: RunOptions): Promise<WorkflowResult<Result>>;\n}\n\nexport interface CreateWorkflowOptions<\n\tInputs,\n\tResult,\n\tInitial extends WorkflowStageName<Inputs> = WorkflowStageName<Inputs>,\n> {\n\tinitialStage: Initial;\n\t/** Every stage, under its own name. */\n\tstages: {\n\t\treadonly [Name in WorkflowStageName<Inputs>]: WorkflowStage<Inputs, Result, Name>;\n\t} & Readonly<Record<string, WorkflowStage<Inputs, Result>>>;\n\t/** The default for a stage without its own. Defaults to 3. */\n\tmaxAttempts?: number;\n\t/** How many times a run may `go` to a stage, so a loop of stages ends. Defaults to 100. */\n\tmaxTransitions?: number;\n\tlogger?: ILogger;\n}\n"]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { newSession, sessionOf } from '#src/agent/domain/session';
|
|
1
2
|
import { RunContext, runContextOf, withRunContext, } from '#src/agent/domain/runContext';
|
|
2
3
|
import { RecoverableError, UnrecoverableError, withSpentTokens } from '#src/shared/domain/errors';
|
|
3
4
|
import { isAbortError } from '#src/shared/domain/isAbortError';
|
|
@@ -70,8 +71,10 @@ export class WorkflowRunner {
|
|
|
70
71
|
// Per invocation, not per instance: one workflow can serve several runs at once.
|
|
71
72
|
const totals = { tokens: undefined, unreported: false };
|
|
72
73
|
const stages = [];
|
|
74
|
+
// Per run, like the agents its stages create: each run is a conversation of its own.
|
|
75
|
+
const session = sessionOf(options) ?? newSession();
|
|
73
76
|
const inherited = runContextOf(options);
|
|
74
|
-
const context = inherited ?? new RunContext();
|
|
77
|
+
const context = inherited ?? new RunContext(session);
|
|
75
78
|
let end = 'failed';
|
|
76
79
|
let outcome;
|
|
77
80
|
try {
|
|
@@ -96,6 +99,7 @@ export class WorkflowRunner {
|
|
|
96
99
|
duration: (Date.now() - start) / 1000,
|
|
97
100
|
stages,
|
|
98
101
|
...(!inherited && runId ? { runId } : {}),
|
|
102
|
+
session: { ...session },
|
|
99
103
|
};
|
|
100
104
|
}
|
|
101
105
|
/** `agents` holds the agent each stage created in this run, so a stage the run comes back to goes on with it. */
|
|
@@ -205,7 +209,9 @@ export class WorkflowRunner {
|
|
|
205
209
|
}
|
|
206
210
|
async runAgent(stage, agent, prompt, options) {
|
|
207
211
|
try {
|
|
208
|
-
|
|
212
|
+
// No stage reads the inbox, as no role of an orchestrator does: what was sent stays for
|
|
213
|
+
// the next run.
|
|
214
|
+
return await agent.run(prompt, { ...options, inbox: undefined });
|
|
209
215
|
}
|
|
210
216
|
catch (error) {
|
|
211
217
|
// Whatever the agent threw once the run was cancelled, the run ends as cancelled.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflowRunner.js","sourceRoot":"","sources":["../../../src/workflow/domain/workflowRunner.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Agent, AgentResponse, ProgressEvent, RunOptions } from '#src/agent/domain/agent';\nimport {\n\tRunContext,\n\trunContextOf,\n\twithRunContext,\n\ttype RunEnd,\n} from '#src/agent/domain/runContext';\nimport { RecoverableError, UnrecoverableError, withSpentTokens } from '#src/shared/domain/errors';\nimport { isAbortError } from '#src/shared/domain/isAbortError';\nimport type { ILogger } from '#src/shared/domain/logger';\nimport {\n\tclassifyHostFailure,\n\tdescribeFailure,\n\ttreatErrors,\n} from '#src/shared/domain/providerFailure';\nimport { addTokens, type Tokens } from '#src/shared/domain/tokens';\nimport type { Validator } from '#src/shared/domain/validator';\nimport type { Workflow, WorkflowResult, WorkflowStageReport } from './workflow.ts';\n\n/** A stage with its types erased: the factory checked them, and the runner only passes values on. */\nexport interface RunnableStage {\n\treadonly name: string;\n\tagent(): Agent | PromiseLike<Agent>;\n\tprompt(input: unknown): string | PromiseLike<string>;\n\treadonly validator: Validator<unknown>;\n\ttransition(output: unknown, input: unknown): unknown;\n\treadonly maxAttempts?: number;\n}\n\ntype Transition<Result> =\n\t{ type: 'go'; stage: string; input: unknown } | { type: 'stop' | 'complete'; result: Result };\n\ninterface Totals {\n\ttokens: Tokens | undefined;\n\tunreported: boolean;\n}\n\nfunction addUsage(totals: Totals, response: AgentResponse): void {\n\tif (response.tokens === undefined) totals.unreported = true;\n\ttotals.tokens = addTokens(totals.tokens, response.tokens);\n}\n\nfunction knownTokens(totals: Totals): Tokens | undefined {\n\treturn totals.unreported ? undefined : totals.tokens;\n}\n\nfunction stripCodeFence(text: string): string {\n\tconst match = /^```(?:json)?\\s*([\\s\\S]*?)\\s*```$/i.exec(text.trim());\n\treturn match?.[1] ?? text;\n}\n\nfunction parseJSON(text: string): { value: unknown } | undefined {\n\ttry {\n\t\treturn { value: JSON.parse(stripCodeFence(text)) as unknown };\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction retryNotice(reason: string): string {\n\treturn `\\n\\nYour previous response could not be used: ${reason} Respond with JSON only, matching the schema exactly, with no surrounding text and no markdown code fences.`;\n}\n\nfunction has(record: object, field: string): boolean {\n\treturn Object.prototype.hasOwnProperty.call(record, field);\n}\n\n/** The types only bind TypeScript callers; a JavaScript callback can return anything. */\nfunction isTransition<Result>(value: unknown): value is Transition<Result> {\n\tif (typeof value !== 'object' || value === null || !('type' in value)) return false;\n\tswitch (value.type) {\n\t\tcase 'go':\n\t\t\treturn 'stage' in value && typeof value.stage === 'string' && has(value, 'input');\n\t\tcase 'stop':\n\t\tcase 'complete':\n\t\t\treturn has(value, 'result');\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\n/**\n * Runs the stages the factory already checked: one stage at a time, each answer validated before\n * its transition is taken, and only the transition's `input` reaching the next stage. Each stage\n * creates its agent once per run, so a run never sees another run's conversations.\n */\nexport class WorkflowRunner<Input, Result> implements Workflow<Input, Result> {\n\tprivate readonly stages: ReadonlyMap<string, RunnableStage>;\n\tprivate readonly initialStage: string;\n\tprivate readonly maxAttempts: number;\n\tprivate readonly maxTransitions: number;\n\tprivate readonly logger: ILogger;\n\n\tconstructor(options: {\n\t\tinitialStage: string;\n\t\tstages: ReadonlyMap<string, RunnableStage>;\n\t\tmaxAttempts: number;\n\t\tmaxTransitions: number;\n\t\tlogger: ILogger;\n\t}) {\n\t\tthis.initialStage = options.initialStage;\n\t\tthis.stages = options.stages;\n\t\tthis.maxAttempts = options.maxAttempts;\n\t\tthis.maxTransitions = options.maxTransitions;\n\t\tthis.logger = options.logger;\n\t}\n\n\t/**\n\t * Every stage of one run shares one run context, so what their agents write is one run of the\n\t * workspace's history. The workflow ends that context with the run, unless an outer agent owns it.\n\t */\n\tasync run(input: Input, options: RunOptions): Promise<WorkflowResult<Result>> {\n\t\tconst start = Date.now();\n\t\t// Per invocation, not per instance: one workflow can serve several runs at once.\n\t\tconst totals: Totals = { tokens: undefined, unreported: false };\n\t\tconst stages: WorkflowStageReport[] = [];\n\t\tconst inherited = runContextOf(options);\n\t\tconst context = inherited ?? new RunContext();\n\t\tlet end: RunEnd = 'failed';\n\t\tlet outcome: { status: 'completed' | 'stopped'; result: Result };\n\n\t\ttry {\n\t\t\toutcome = await this.runStages(\n\t\t\t\tinput,\n\t\t\t\tinherited ? options : withRunContext(options, context),\n\t\t\t\ttotals,\n\t\t\t\tstages,\n\t\t\t\tnew Map(),\n\t\t\t);\n\t\t\tend = 'completed';\n\t\t} catch (error) {\n\t\t\tif (isAbortError(error) || options.signal.aborted) end = 'cancelled';\n\t\t\t// A failing stage carries its own tokens; the stages before it are in the totals.\n\t\t\tthrow withSpentTokens(error, totals.tokens, totals.unreported);\n\t\t} finally {\n\t\t\tif (!inherited) await this.finishContext(context, end);\n\t\t}\n\n\t\t// Read once the run ended: a run that changed nothing left the history then.\n\t\tconst { runId } = context.historyRun;\n\t\treturn {\n\t\t\t...outcome,\n\t\t\ttokens: knownTokens(totals),\n\t\t\tduration: (Date.now() - start) / 1000,\n\t\t\tstages,\n\t\t\t...(!inherited && runId ? { runId } : {}),\n\t\t};\n\t}\n\n\t/** `agents` holds the agent each stage created in this run, so a stage the run comes back to goes on with it. */\n\tprivate async runStages(\n\t\tinput: Input,\n\t\toptions: RunOptions,\n\t\ttotals: Totals,\n\t\treports: WorkflowStageReport[],\n\t\tagents: Map<string, Agent>,\n\t): Promise<{ status: 'completed' | 'stopped'; result: Result }> {\n\t\tlet stage = this.stageNamed(this.initialStage);\n\t\tlet stageInput: unknown = input;\n\n\t\tfor (let transitions = 0; ; transitions++) {\n\t\t\tconst started = Date.now();\n\t\t\tconst stageTotals: Totals = { tokens: undefined, unreported: false };\n\t\t\tconst agent = await this.agentOf(stage, agents);\n\t\t\tconst { output, attempts } = await this.answer(\n\t\t\t\tstage,\n\t\t\t\tagent,\n\t\t\t\tstageInput,\n\t\t\t\toptions,\n\t\t\t\ttotals,\n\t\t\t\tstageTotals,\n\t\t\t);\n\t\t\tconst transition = await this.transition(stage, output, stageInput);\n\t\t\toptions.signal.throwIfAborted();\n\n\t\t\tconst tokens = knownTokens(stageTotals);\n\t\t\tconst nextStage = transition.type === 'go' ? transition.stage : undefined;\n\t\t\treports.push({\n\t\t\t\tstage: stage.name,\n\t\t\t\tinput: stageInput,\n\t\t\t\toutput,\n\t\t\t\tattempts,\n\t\t\t\tduration: (Date.now() - started) / 1000,\n\t\t\t\t...(tokens && { tokens }),\n\t\t\t\ttransition: transition.type,\n\t\t\t\t...(nextStage !== undefined && { nextStage }),\n\t\t\t});\n\t\t\tthis.emit(options, {\n\t\t\t\ttype: 'workflowTransition',\n\t\t\t\tstage: stage.name,\n\t\t\t\ttransition: transition.type,\n\t\t\t\t...(nextStage !== undefined && { nextStage }),\n\t\t\t});\n\n\t\t\tif (transition.type !== 'go') {\n\t\t\t\tconst status = transition.type === 'complete' ? 'completed' : 'stopped';\n\t\t\t\treturn { status, result: transition.result };\n\t\t\t}\n\t\t\tif (transitions + 1 > this.maxTransitions) {\n\t\t\t\tthrow new UnrecoverableError('Workflow transition limit exhausted', {\n\t\t\t\t\tcause: `The run went to another stage more than ${String(this.maxTransitions)} times.`,\n\t\t\t\t});\n\t\t\t}\n\t\t\tstage = this.stageNamed(transition.stage, stage.name);\n\t\t\tstageInput = transition.input;\n\t\t}\n\t}\n\n\t/** The stage's agent in this run: created the first time the stage runs, then reused. */\n\tprivate async agentOf(stage: RunnableStage, agents: Map<string, Agent>): Promise<Agent> {\n\t\tconst existing = agents.get(stage.name);\n\t\tif (existing) return existing;\n\t\tlet agent: Agent;\n\t\ttry {\n\t\t\tagent = await stage.agent();\n\t\t} catch (error) {\n\t\t\tthrow classifyHostFailure(error, `Workflow stage \"${stage.name}\" could not create its agent`);\n\t\t}\n\t\tagents.set(stage.name, agent);\n\t\treturn agent;\n\t}\n\n\t/**\n\t * Asks the stage's agent until an answer is valid JSON its validator accepts, or attempts run\n\t * out. Each answer's usage goes to the run's and the stage's totals; a failure carries the\n\t * run's when `run` rethrows it.\n\t */\n\tprivate async answer(\n\t\tstage: RunnableStage,\n\t\tagent: Agent,\n\t\tinput: unknown,\n\t\toptions: RunOptions,\n\t\ttotals: Totals,\n\t\tstageTotals: Totals,\n\t): Promise<{ output: unknown; attempts: number }> {\n\t\tconst maxAttempts = stage.maxAttempts ?? this.maxAttempts;\n\t\toptions.signal.throwIfAborted();\n\t\tlet prompt: string;\n\t\ttry {\n\t\t\tprompt = await stage.prompt(input);\n\t\t} catch (error) {\n\t\t\tthrow classifyHostFailure(error, `Workflow stage \"${stage.name}\" could not build its prompt`);\n\t\t}\n\n\t\tlet failureReason: string | undefined;\n\t\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\t\toptions.signal.throwIfAborted();\n\t\t\tthis.emit(options, { type: 'workflowStage', stage: stage.name, attempt });\n\t\t\tconst response = await this.runAgent(\n\t\t\t\tstage,\n\t\t\t\tagent,\n\t\t\t\tfailureReason ? prompt + retryNotice(failureReason) : prompt,\n\t\t\t\toptions,\n\t\t\t);\n\t\t\taddUsage(totals, response);\n\t\t\t// An answer that arrives after a cancellation is not used.\n\t\t\toptions.signal.throwIfAborted();\n\t\t\taddUsage(stageTotals, response);\n\n\t\t\tconst parsed = parseJSON(response.response);\n\t\t\tif (parsed) {\n\t\t\t\tlet output: unknown;\n\t\t\t\ttry {\n\t\t\t\t\toutput = stage.validator.validate(parsed.value);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthrow classifyHostFailure(\n\t\t\t\t\t\terror,\n\t\t\t\t\t\t`Workflow stage \"${stage.name}\" could not validate its answer`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (output !== undefined) return { output, attempts: attempt };\n\t\t\t\tfailureReason = 'it did not match the schema.';\n\t\t\t} else {\n\t\t\t\tfailureReason = 'it was not valid JSON.';\n\t\t\t}\n\n\t\t\tif (attempt < maxAttempts) {\n\t\t\t\tthis.warn(\n\t\t\t\t\t`Workflow stage \"${stage.name}\" answer ${String(attempt)}/${String(maxAttempts)} was unusable (${failureReason}); asking again`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tthrow new UnrecoverableError('Workflow stage attempts exhausted', {\n\t\t\tcause: `Stage \"${stage.name}\" gave no usable answer in ${String(maxAttempts)} attempts: the last one ${failureReason ?? ''}`,\n\t\t});\n\t}\n\n\tprivate async runAgent(\n\t\tstage: RunnableStage,\n\t\tagent: Agent,\n\t\tprompt: string,\n\t\toptions: RunOptions,\n\t): Promise<AgentResponse> {\n\t\ttry {\n\t\t\treturn await agent.run(prompt, options);\n\t\t} catch (error) {\n\t\t\t// Whatever the agent threw once the run was cancelled, the run ends as cancelled.\n\t\t\toptions.signal.throwIfAborted();\n\t\t\tif (isAbortError(error)) throw error;\n\t\t\tif (error instanceof RecoverableError || error instanceof UnrecoverableError) throw error;\n\t\t\tthrow new UnrecoverableError(`Workflow stage \"${stage.name}\" failed`, {\n\t\t\t\tcause: describeFailure(error),\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate async transition(\n\t\tstage: RunnableStage,\n\t\toutput: unknown,\n\t\tinput: unknown,\n\t): Promise<Transition<Result>> {\n\t\tlet transition: unknown;\n\t\ttry {\n\t\t\ttransition = await stage.transition(output, input);\n\t\t} catch (error) {\n\t\t\tthrow classifyHostFailure(\n\t\t\t\terror,\n\t\t\t\t`Workflow stage \"${stage.name}\" could not choose its transition`,\n\t\t\t);\n\t\t}\n\t\tif (!isTransition<Result>(transition)) {\n\t\t\tthrow new UnrecoverableError('Workflow transition is invalid', {\n\t\t\t\tcause: `Stage \"${stage.name}\" returned something that is not a transition.`,\n\t\t\t});\n\t\t}\n\t\treturn transition;\n\t}\n\n\tprivate stageNamed(name: string, from?: string): RunnableStage {\n\t\tconst stage = this.stages.get(name);\n\t\tif (!stage) {\n\t\t\tthrow new UnrecoverableError('Workflow stage does not exist', {\n\t\t\t\tcause: from\n\t\t\t\t\t? `Stage \"${from}\" selected unknown stage \"${name}\".`\n\t\t\t\t\t: `The workflow has no stage \"${name}\".`,\n\t\t\t});\n\t\t}\n\t\treturn stage;\n\t}\n\n\t/** The run's records end here, but its result stands: what it changed is already on disk. */\n\tprivate async finishContext(context: RunContext, end: RunEnd): Promise<void> {\n\t\ttry {\n\t\t\tawait context.finish(end);\n\t\t} catch (error) {\n\t\t\tthis.warn(`The run could not close its records: ${describeFailure(error)}`);\n\t\t}\n\t}\n\n\tprivate emit(options: RunOptions, event: ProgressEvent): void {\n\t\tconst { onProgress } = options;\n\t\tif (!onProgress) return;\n\t\ttreatErrors(\n\t\t\t() => {\n\t\t\t\tonProgress(event);\n\t\t\t},\n\t\t\tclassifyHostFailure,\n\t\t\t'Workflow progress callback failed',\n\t\t);\n\t}\n\n\tprivate warn(message: string): void {\n\t\ttreatErrors(\n\t\t\t() => {\n\t\t\t\tthis.logger.warn(message);\n\t\t\t},\n\t\t\tclassifyHostFailure,\n\t\t\t'Workflow logger failed while reporting a retry',\n\t\t);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"workflowRunner.js","sourceRoot":"","sources":["../../../src/workflow/domain/workflowRunner.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Agent, AgentResponse, ProgressEvent, RunOptions } from '#src/agent/domain/agent';\nimport { newSession, sessionOf } from '#src/agent/domain/session';\nimport {\n\tRunContext,\n\trunContextOf,\n\twithRunContext,\n\ttype RunEnd,\n} from '#src/agent/domain/runContext';\nimport { RecoverableError, UnrecoverableError, withSpentTokens } from '#src/shared/domain/errors';\nimport { isAbortError } from '#src/shared/domain/isAbortError';\nimport type { ILogger } from '#src/shared/domain/logger';\nimport {\n\tclassifyHostFailure,\n\tdescribeFailure,\n\ttreatErrors,\n} from '#src/shared/domain/providerFailure';\nimport { addTokens, type Tokens } from '#src/shared/domain/tokens';\nimport type { Validator } from '#src/shared/domain/validator';\nimport type { Workflow, WorkflowResult, WorkflowStageReport } from './workflow.ts';\n\n/** A stage with its types erased: the factory checked them, and the runner only passes values on. */\nexport interface RunnableStage {\n\treadonly name: string;\n\tagent(): Agent | PromiseLike<Agent>;\n\tprompt(input: unknown): string | PromiseLike<string>;\n\treadonly validator: Validator<unknown>;\n\ttransition(output: unknown, input: unknown): unknown;\n\treadonly maxAttempts?: number;\n}\n\ntype Transition<Result> =\n\t{ type: 'go'; stage: string; input: unknown } | { type: 'stop' | 'complete'; result: Result };\n\ninterface Totals {\n\ttokens: Tokens | undefined;\n\tunreported: boolean;\n}\n\nfunction addUsage(totals: Totals, response: AgentResponse): void {\n\tif (response.tokens === undefined) totals.unreported = true;\n\ttotals.tokens = addTokens(totals.tokens, response.tokens);\n}\n\nfunction knownTokens(totals: Totals): Tokens | undefined {\n\treturn totals.unreported ? undefined : totals.tokens;\n}\n\nfunction stripCodeFence(text: string): string {\n\tconst match = /^```(?:json)?\\s*([\\s\\S]*?)\\s*```$/i.exec(text.trim());\n\treturn match?.[1] ?? text;\n}\n\nfunction parseJSON(text: string): { value: unknown } | undefined {\n\ttry {\n\t\treturn { value: JSON.parse(stripCodeFence(text)) as unknown };\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction retryNotice(reason: string): string {\n\treturn `\\n\\nYour previous response could not be used: ${reason} Respond with JSON only, matching the schema exactly, with no surrounding text and no markdown code fences.`;\n}\n\nfunction has(record: object, field: string): boolean {\n\treturn Object.prototype.hasOwnProperty.call(record, field);\n}\n\n/** The types only bind TypeScript callers; a JavaScript callback can return anything. */\nfunction isTransition<Result>(value: unknown): value is Transition<Result> {\n\tif (typeof value !== 'object' || value === null || !('type' in value)) return false;\n\tswitch (value.type) {\n\t\tcase 'go':\n\t\t\treturn 'stage' in value && typeof value.stage === 'string' && has(value, 'input');\n\t\tcase 'stop':\n\t\tcase 'complete':\n\t\t\treturn has(value, 'result');\n\t\tdefault:\n\t\t\treturn false;\n\t}\n}\n\n/**\n * Runs the stages the factory already checked: one stage at a time, each answer validated before\n * its transition is taken, and only the transition's `input` reaching the next stage. Each stage\n * creates its agent once per run, so a run never sees another run's conversations.\n */\nexport class WorkflowRunner<Input, Result> implements Workflow<Input, Result> {\n\tprivate readonly stages: ReadonlyMap<string, RunnableStage>;\n\tprivate readonly initialStage: string;\n\tprivate readonly maxAttempts: number;\n\tprivate readonly maxTransitions: number;\n\tprivate readonly logger: ILogger;\n\n\tconstructor(options: {\n\t\tinitialStage: string;\n\t\tstages: ReadonlyMap<string, RunnableStage>;\n\t\tmaxAttempts: number;\n\t\tmaxTransitions: number;\n\t\tlogger: ILogger;\n\t}) {\n\t\tthis.initialStage = options.initialStage;\n\t\tthis.stages = options.stages;\n\t\tthis.maxAttempts = options.maxAttempts;\n\t\tthis.maxTransitions = options.maxTransitions;\n\t\tthis.logger = options.logger;\n\t}\n\n\t/**\n\t * Every stage of one run shares one run context, so what their agents write is one run of the\n\t * workspace's history. The workflow ends that context with the run, unless an outer agent owns it.\n\t */\n\tasync run(input: Input, options: RunOptions): Promise<WorkflowResult<Result>> {\n\t\tconst start = Date.now();\n\t\t// Per invocation, not per instance: one workflow can serve several runs at once.\n\t\tconst totals: Totals = { tokens: undefined, unreported: false };\n\t\tconst stages: WorkflowStageReport[] = [];\n\t\t// Per run, like the agents its stages create: each run is a conversation of its own.\n\t\tconst session = sessionOf(options) ?? newSession();\n\t\tconst inherited = runContextOf(options);\n\t\tconst context = inherited ?? new RunContext(session);\n\t\tlet end: RunEnd = 'failed';\n\t\tlet outcome: { status: 'completed' | 'stopped'; result: Result };\n\n\t\ttry {\n\t\t\toutcome = await this.runStages(\n\t\t\t\tinput,\n\t\t\t\tinherited ? options : withRunContext(options, context),\n\t\t\t\ttotals,\n\t\t\t\tstages,\n\t\t\t\tnew Map(),\n\t\t\t);\n\t\t\tend = 'completed';\n\t\t} catch (error) {\n\t\t\tif (isAbortError(error) || options.signal.aborted) end = 'cancelled';\n\t\t\t// A failing stage carries its own tokens; the stages before it are in the totals.\n\t\t\tthrow withSpentTokens(error, totals.tokens, totals.unreported);\n\t\t} finally {\n\t\t\tif (!inherited) await this.finishContext(context, end);\n\t\t}\n\n\t\t// Read once the run ended: a run that changed nothing left the history then.\n\t\tconst { runId } = context.historyRun;\n\t\treturn {\n\t\t\t...outcome,\n\t\t\ttokens: knownTokens(totals),\n\t\t\tduration: (Date.now() - start) / 1000,\n\t\t\tstages,\n\t\t\t...(!inherited && runId ? { runId } : {}),\n\t\t\tsession: { ...session },\n\t\t};\n\t}\n\n\t/** `agents` holds the agent each stage created in this run, so a stage the run comes back to goes on with it. */\n\tprivate async runStages(\n\t\tinput: Input,\n\t\toptions: RunOptions,\n\t\ttotals: Totals,\n\t\treports: WorkflowStageReport[],\n\t\tagents: Map<string, Agent>,\n\t): Promise<{ status: 'completed' | 'stopped'; result: Result }> {\n\t\tlet stage = this.stageNamed(this.initialStage);\n\t\tlet stageInput: unknown = input;\n\n\t\tfor (let transitions = 0; ; transitions++) {\n\t\t\tconst started = Date.now();\n\t\t\tconst stageTotals: Totals = { tokens: undefined, unreported: false };\n\t\t\tconst agent = await this.agentOf(stage, agents);\n\t\t\tconst { output, attempts } = await this.answer(\n\t\t\t\tstage,\n\t\t\t\tagent,\n\t\t\t\tstageInput,\n\t\t\t\toptions,\n\t\t\t\ttotals,\n\t\t\t\tstageTotals,\n\t\t\t);\n\t\t\tconst transition = await this.transition(stage, output, stageInput);\n\t\t\toptions.signal.throwIfAborted();\n\n\t\t\tconst tokens = knownTokens(stageTotals);\n\t\t\tconst nextStage = transition.type === 'go' ? transition.stage : undefined;\n\t\t\treports.push({\n\t\t\t\tstage: stage.name,\n\t\t\t\tinput: stageInput,\n\t\t\t\toutput,\n\t\t\t\tattempts,\n\t\t\t\tduration: (Date.now() - started) / 1000,\n\t\t\t\t...(tokens && { tokens }),\n\t\t\t\ttransition: transition.type,\n\t\t\t\t...(nextStage !== undefined && { nextStage }),\n\t\t\t});\n\t\t\tthis.emit(options, {\n\t\t\t\ttype: 'workflowTransition',\n\t\t\t\tstage: stage.name,\n\t\t\t\ttransition: transition.type,\n\t\t\t\t...(nextStage !== undefined && { nextStage }),\n\t\t\t});\n\n\t\t\tif (transition.type !== 'go') {\n\t\t\t\tconst status = transition.type === 'complete' ? 'completed' : 'stopped';\n\t\t\t\treturn { status, result: transition.result };\n\t\t\t}\n\t\t\tif (transitions + 1 > this.maxTransitions) {\n\t\t\t\tthrow new UnrecoverableError('Workflow transition limit exhausted', {\n\t\t\t\t\tcause: `The run went to another stage more than ${String(this.maxTransitions)} times.`,\n\t\t\t\t});\n\t\t\t}\n\t\t\tstage = this.stageNamed(transition.stage, stage.name);\n\t\t\tstageInput = transition.input;\n\t\t}\n\t}\n\n\t/** The stage's agent in this run: created the first time the stage runs, then reused. */\n\tprivate async agentOf(stage: RunnableStage, agents: Map<string, Agent>): Promise<Agent> {\n\t\tconst existing = agents.get(stage.name);\n\t\tif (existing) return existing;\n\t\tlet agent: Agent;\n\t\ttry {\n\t\t\tagent = await stage.agent();\n\t\t} catch (error) {\n\t\t\tthrow classifyHostFailure(error, `Workflow stage \"${stage.name}\" could not create its agent`);\n\t\t}\n\t\tagents.set(stage.name, agent);\n\t\treturn agent;\n\t}\n\n\t/**\n\t * Asks the stage's agent until an answer is valid JSON its validator accepts, or attempts run\n\t * out. Each answer's usage goes to the run's and the stage's totals; a failure carries the\n\t * run's when `run` rethrows it.\n\t */\n\tprivate async answer(\n\t\tstage: RunnableStage,\n\t\tagent: Agent,\n\t\tinput: unknown,\n\t\toptions: RunOptions,\n\t\ttotals: Totals,\n\t\tstageTotals: Totals,\n\t): Promise<{ output: unknown; attempts: number }> {\n\t\tconst maxAttempts = stage.maxAttempts ?? this.maxAttempts;\n\t\toptions.signal.throwIfAborted();\n\t\tlet prompt: string;\n\t\ttry {\n\t\t\tprompt = await stage.prompt(input);\n\t\t} catch (error) {\n\t\t\tthrow classifyHostFailure(error, `Workflow stage \"${stage.name}\" could not build its prompt`);\n\t\t}\n\n\t\tlet failureReason: string | undefined;\n\t\tfor (let attempt = 1; attempt <= maxAttempts; attempt++) {\n\t\t\toptions.signal.throwIfAborted();\n\t\t\tthis.emit(options, { type: 'workflowStage', stage: stage.name, attempt });\n\t\t\tconst response = await this.runAgent(\n\t\t\t\tstage,\n\t\t\t\tagent,\n\t\t\t\tfailureReason ? prompt + retryNotice(failureReason) : prompt,\n\t\t\t\toptions,\n\t\t\t);\n\t\t\taddUsage(totals, response);\n\t\t\t// An answer that arrives after a cancellation is not used.\n\t\t\toptions.signal.throwIfAborted();\n\t\t\taddUsage(stageTotals, response);\n\n\t\t\tconst parsed = parseJSON(response.response);\n\t\t\tif (parsed) {\n\t\t\t\tlet output: unknown;\n\t\t\t\ttry {\n\t\t\t\t\toutput = stage.validator.validate(parsed.value);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthrow classifyHostFailure(\n\t\t\t\t\t\terror,\n\t\t\t\t\t\t`Workflow stage \"${stage.name}\" could not validate its answer`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (output !== undefined) return { output, attempts: attempt };\n\t\t\t\tfailureReason = 'it did not match the schema.';\n\t\t\t} else {\n\t\t\t\tfailureReason = 'it was not valid JSON.';\n\t\t\t}\n\n\t\t\tif (attempt < maxAttempts) {\n\t\t\t\tthis.warn(\n\t\t\t\t\t`Workflow stage \"${stage.name}\" answer ${String(attempt)}/${String(maxAttempts)} was unusable (${failureReason}); asking again`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tthrow new UnrecoverableError('Workflow stage attempts exhausted', {\n\t\t\tcause: `Stage \"${stage.name}\" gave no usable answer in ${String(maxAttempts)} attempts: the last one ${failureReason ?? ''}`,\n\t\t});\n\t}\n\n\tprivate async runAgent(\n\t\tstage: RunnableStage,\n\t\tagent: Agent,\n\t\tprompt: string,\n\t\toptions: RunOptions,\n\t): Promise<AgentResponse> {\n\t\ttry {\n\t\t\t// No stage reads the inbox, as no role of an orchestrator does: what was sent stays for\n\t\t\t// the next run.\n\t\t\treturn await agent.run(prompt, { ...options, inbox: undefined });\n\t\t} catch (error) {\n\t\t\t// Whatever the agent threw once the run was cancelled, the run ends as cancelled.\n\t\t\toptions.signal.throwIfAborted();\n\t\t\tif (isAbortError(error)) throw error;\n\t\t\tif (error instanceof RecoverableError || error instanceof UnrecoverableError) throw error;\n\t\t\tthrow new UnrecoverableError(`Workflow stage \"${stage.name}\" failed`, {\n\t\t\t\tcause: describeFailure(error),\n\t\t\t});\n\t\t}\n\t}\n\n\tprivate async transition(\n\t\tstage: RunnableStage,\n\t\toutput: unknown,\n\t\tinput: unknown,\n\t): Promise<Transition<Result>> {\n\t\tlet transition: unknown;\n\t\ttry {\n\t\t\ttransition = await stage.transition(output, input);\n\t\t} catch (error) {\n\t\t\tthrow classifyHostFailure(\n\t\t\t\terror,\n\t\t\t\t`Workflow stage \"${stage.name}\" could not choose its transition`,\n\t\t\t);\n\t\t}\n\t\tif (!isTransition<Result>(transition)) {\n\t\t\tthrow new UnrecoverableError('Workflow transition is invalid', {\n\t\t\t\tcause: `Stage \"${stage.name}\" returned something that is not a transition.`,\n\t\t\t});\n\t\t}\n\t\treturn transition;\n\t}\n\n\tprivate stageNamed(name: string, from?: string): RunnableStage {\n\t\tconst stage = this.stages.get(name);\n\t\tif (!stage) {\n\t\t\tthrow new UnrecoverableError('Workflow stage does not exist', {\n\t\t\t\tcause: from\n\t\t\t\t\t? `Stage \"${from}\" selected unknown stage \"${name}\".`\n\t\t\t\t\t: `The workflow has no stage \"${name}\".`,\n\t\t\t});\n\t\t}\n\t\treturn stage;\n\t}\n\n\t/** The run's records end here, but its result stands: what it changed is already on disk. */\n\tprivate async finishContext(context: RunContext, end: RunEnd): Promise<void> {\n\t\ttry {\n\t\t\tawait context.finish(end);\n\t\t} catch (error) {\n\t\t\tthis.warn(`The run could not close its records: ${describeFailure(error)}`);\n\t\t}\n\t}\n\n\tprivate emit(options: RunOptions, event: ProgressEvent): void {\n\t\tconst { onProgress } = options;\n\t\tif (!onProgress) return;\n\t\ttreatErrors(\n\t\t\t() => {\n\t\t\t\tonProgress(event);\n\t\t\t},\n\t\t\tclassifyHostFailure,\n\t\t\t'Workflow progress callback failed',\n\t\t);\n\t}\n\n\tprivate warn(message: string): void {\n\t\ttreatErrors(\n\t\t\t() => {\n\t\t\t\tthis.logger.warn(message);\n\t\t\t},\n\t\t\tclassifyHostFailure,\n\t\t\t'Workflow logger failed while reporting a retry',\n\t\t);\n\t}\n}\n"]}
|