@agentproto/workflow-runtime 0.16.0 → 0.17.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/dist/index.d.ts +79 -1
- package/dist/index.mjs +175 -42
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -110,6 +110,19 @@ interface TolerantFanOutResult<T = unknown> {
|
|
|
110
110
|
/** Default for {@link MapStep.maxConsecutiveSpawnFailures} /
|
|
111
111
|
* {@link PipelineStep.maxConsecutiveSpawnFailures}. */
|
|
112
112
|
declare const DEFAULT_MAX_CONSECUTIVE_SPAWN_FAILURES = 3;
|
|
113
|
+
/** An {@link AgentStep}'s transport-failure retry policy — the same shape as
|
|
114
|
+
* {@link GateStep.retry}. `initialMs` is the wait before the first retry;
|
|
115
|
+
* `"exponential"` doubles it on each further one. */
|
|
116
|
+
interface AgentStepRetry {
|
|
117
|
+
maxAttempts: number;
|
|
118
|
+
backoff: "fixed" | "exponential";
|
|
119
|
+
initialMs?: number;
|
|
120
|
+
}
|
|
121
|
+
/** What an {@link AgentStep} without its own `retry` gets: two attempts —
|
|
122
|
+
* one retry after a 1 s pause — so a single dropped connection doesn't fail
|
|
123
|
+
* a long run. Applies to a session lost mid-turn only, never to a spawn
|
|
124
|
+
* failure or a completed turn (see {@link AgentStep.retry}). */
|
|
125
|
+
declare const DEFAULT_AGENT_TRANSPORT_RETRY: Readonly<AgentStepRetry>;
|
|
113
126
|
/** Default for {@link ToolStep.timeoutMs} / {@link GateStep.timeoutMs} when
|
|
114
127
|
* the step declares none — 10 minutes. Bounds an otherwise-unbounded `tool`
|
|
115
128
|
* or `gate` step dispatch (F45). */
|
|
@@ -404,6 +417,22 @@ interface AgentStep {
|
|
|
404
417
|
outputSchema?: OutputSchemaLike;
|
|
405
418
|
/** Re-prompt-and-retry attempts on schema mismatch before failing. Default 2. */
|
|
406
419
|
maxRetries?: number;
|
|
420
|
+
/** Re-spawn on a TRANSPORT failure, up to `maxAttempts` total attempts —
|
|
421
|
+
* the same shape as {@link GateStep.retry}. Only a failure that is the
|
|
422
|
+
* session's plumbing, never its content, is retried: the spawn rejected
|
|
423
|
+
* ({@link AgentSpawnError}), or the session died before its FIRST turn
|
|
424
|
+
* ended (the host threw an error marked `transport: true`, e.g.
|
|
425
|
+
* {@link AgentSessionLostError} for a killed session or a closed ACP
|
|
426
|
+
* connection). A step whose turn completed — including one that then
|
|
427
|
+
* failed its `outputSchema` re-prompts, or died during one — is never
|
|
428
|
+
* retried, nor is a cancelled run or a `sessionRef` reuse (there is no
|
|
429
|
+
* fresh session to spawn). Each retry spawns a NEW session with the same
|
|
430
|
+
* resolved prompt, cwd and harness, in the same run workspace.
|
|
431
|
+
* Unset ⇒ {@link DEFAULT_AGENT_TRANSPORT_RETRY} (one retry), and then
|
|
432
|
+
* for a session lost mid-turn ONLY — a spawn failure is usually
|
|
433
|
+
* deterministic (bad cwd, missing adapter), so it is re-spawned past only
|
|
434
|
+
* when the step declares `retry` itself. `maxAttempts: 1` disables it. */
|
|
435
|
+
retry?: AgentStepRetry;
|
|
407
436
|
/** Cache this step's output under the run's cacheKey; the resolved prompt +
|
|
408
437
|
* adapter + model + sessionRef are hashed. Default false — most agent steps have
|
|
409
438
|
* side effects. */
|
|
@@ -555,6 +584,20 @@ interface GateReportEvent {
|
|
|
555
584
|
report: unknown;
|
|
556
585
|
attempt: number;
|
|
557
586
|
}
|
|
587
|
+
/** One transport-failure retry of a `kind: "agent"` step, reported right
|
|
588
|
+
* before the fresh session is spawned — the `step.retrying` event. */
|
|
589
|
+
interface AgentRetryEvent {
|
|
590
|
+
stepId: string;
|
|
591
|
+
/** The attempt that is about to start (2 for the first retry). */
|
|
592
|
+
attempt: number;
|
|
593
|
+
maxAttempts: number;
|
|
594
|
+
/** The failed attempt's error message. */
|
|
595
|
+
error: string;
|
|
596
|
+
/** The failed attempt's session, when it got far enough to have one. */
|
|
597
|
+
sessionId?: string;
|
|
598
|
+
/** How long the runtime waits before spawning the next attempt. */
|
|
599
|
+
delayMs: number;
|
|
600
|
+
}
|
|
558
601
|
/** What a declarative agent-step's `agent.ref` resolves to — the adapter to
|
|
559
602
|
* spawn under plus any adapter options that pick which concrete agent runs
|
|
560
603
|
* (e.g. mastra-agent's `agent` option, an emitted AGENT.md path). Built by
|
|
@@ -616,6 +659,14 @@ interface RuntimeWorkflow {
|
|
|
616
659
|
* every top-level step finishes successfully. See {@link OutputsFileContract}
|
|
617
660
|
* and `run-workflow.ts`'s `checkOutputsFiles`. */
|
|
618
661
|
outputsFiles?: Readonly<Record<string, OutputsFileContract>>;
|
|
662
|
+
/**
|
|
663
|
+
* `false` ⇒ no step anywhere in this workflow reuses another step's session
|
|
664
|
+
* (`sessionRef`), so each top-level step's sessions are released as soon as
|
|
665
|
+
* that step finishes instead of being held until the run ends (a run parked
|
|
666
|
+
* on an approval would otherwise keep every finished step's session live).
|
|
667
|
+
* Omitted/`true` ⇒ the conservative default: release at run end.
|
|
668
|
+
*/
|
|
669
|
+
reusesSessions?: boolean;
|
|
619
670
|
}
|
|
620
671
|
/** A human/host decision on one approval request. `who` records WHO decided
|
|
621
672
|
* ("human", "timeout", "cancelled", …); `note` is optional free text. */
|
|
@@ -847,6 +898,9 @@ interface RunWorkflowArgs {
|
|
|
847
898
|
/** Called once per `kind: "gate"` command attempt (every attempt, not just
|
|
848
899
|
* the last) — the `gate-report` lifecycle event. */
|
|
849
900
|
onGateReport?: (ev: GateReportEvent) => void;
|
|
901
|
+
/** Called before each transport-failure retry of a `kind: "agent"` step
|
|
902
|
+
* (see {@link AgentStep.retry}) — never for the first attempt. */
|
|
903
|
+
onAgentRetry?: (ev: AgentRetryEvent) => void;
|
|
850
904
|
}
|
|
851
905
|
interface WorkflowRunResult {
|
|
852
906
|
output: unknown;
|
|
@@ -921,6 +975,28 @@ declare class AgentSpawnError extends Error {
|
|
|
921
975
|
readonly cause: unknown;
|
|
922
976
|
constructor(stepId: string, cause: unknown);
|
|
923
977
|
}
|
|
978
|
+
/**
|
|
979
|
+
* Thrown by an {@link AgentSessionHost} when a spawned session died under a
|
|
980
|
+
* step before its turn ended — killed or crashed mid-turn, its ACP
|
|
981
|
+
* connection closed, its prompt never delivered — as opposed to a turn that
|
|
982
|
+
* ended and produced the wrong thing. The `transport: true` marker (not the
|
|
983
|
+
* class identity) is what {@link isAgentTransportFailure} reads, so a host
|
|
984
|
+
* bundling its own copy of this package still classifies correctly.
|
|
985
|
+
*/
|
|
986
|
+
declare class AgentSessionLostError extends Error {
|
|
987
|
+
readonly sessionId: string;
|
|
988
|
+
readonly cause?: unknown | undefined;
|
|
989
|
+
readonly transport: true;
|
|
990
|
+
constructor(sessionId: string, message: string, cause?: unknown | undefined);
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
993
|
+
* True when `err` is a transport failure an {@link AgentStep}'s `retry` may
|
|
994
|
+
* re-spawn past: an {@link AgentSpawnError}, or any error carrying
|
|
995
|
+
* `transport: true` (see {@link AgentSessionLostError}). Everything else —
|
|
996
|
+
* an empty or errored turn, a schema mismatch, a budget cap — is the step's
|
|
997
|
+
* own outcome and is never retried.
|
|
998
|
+
*/
|
|
999
|
+
declare function isAgentTransportFailure(err: unknown): boolean;
|
|
924
1000
|
/**
|
|
925
1001
|
* Thrown instead of dispatching a step whose run was cancelled
|
|
926
1002
|
* (`ctx.signal.aborted`) — checked BEFORE any work for the step begins (see
|
|
@@ -1155,6 +1231,8 @@ interface AgentStepFields {
|
|
|
1155
1231
|
policy?: AgentStep["policy"];
|
|
1156
1232
|
outputSchema?: AgentStep["outputSchema"];
|
|
1157
1233
|
maxRetries?: number;
|
|
1234
|
+
/** See {@link AgentStep.retry}. */
|
|
1235
|
+
retry?: AgentStep["retry"];
|
|
1158
1236
|
options?: Record<string, boolean | number | string>;
|
|
1159
1237
|
harness?: AgentHarness;
|
|
1160
1238
|
agentTools?: readonly string[];
|
|
@@ -1215,4 +1293,4 @@ declare class NodeFsPort implements FsPort {
|
|
|
1215
1293
|
lock(): Promise<FsLockHandle>;
|
|
1216
1294
|
}
|
|
1217
1295
|
|
|
1218
|
-
export { type AgentHarness, AgentInputRequiredError, type AgentRefResolution, type AgentSandboxRef, type AgentSessionHost, AgentSpawnError, type AgentStep, type AgentStepFields, type ApprovalDecision, type ApprovalRequest, type ApprovalStep, type ArtifactEntry, type ArtifactStep, type Bindings, type BranchStep, type CompileWorkflowOptions, DEFAULT_MAX_CONSECUTIVE_SPAWN_FAILURES, DEFAULT_STEP_TIMEOUT_MS, type FanOutOutcome, type GateCommandResult, type GateCommandRunner, type GateReportEvent, type GateStep, type GroupStep, type HarnessKnowledgeSelector, type InputRequiredRequest, type KnowledgeAppliedRecord, type LoopStep, type MapStep, type MaterializedKnowledge, MissingArtifactError, NodeFsPort, type OutputSchemaLike, type OutputsFileContract, type ParallelStep, type PipelineStep, type ResumeRequest, type RunStep, type RunWorkflowArgs, type RuntimeWorkflow, type SchemaValidationIssue, type Selector, type StepCache, type StepCacheEntry, type StepFailedInfo, type StepHookInfo, StepOutcomeError, type StepSkippedInfo, type SubworkflowStep, type SuspendStep, type TolerantFanOutResult, type ToolStep, type TransformStep, WorkflowCancelledError, WorkflowCompileError, type WorkflowInputValidation, type WorkflowRunResult, WorkflowSuspendedError, buildAgentStep, compileWorkflow, compileWorkflowManifest, evalPredicate, interpolateTemplate, isCompilableJsonSchema, materializeKnowledge, normalizeWorkflowInputsSchema, resolveKnowledgeSelectors, resolveRef, resolveRefPrefixed, resolveValue, runWorkflow, validateAgainstJsonSchema, validateWorkflowInput };
|
|
1296
|
+
export { type AgentHarness, AgentInputRequiredError, type AgentRefResolution, type AgentRetryEvent, type AgentSandboxRef, type AgentSessionHost, AgentSessionLostError, AgentSpawnError, type AgentStep, type AgentStepFields, type AgentStepRetry, type ApprovalDecision, type ApprovalRequest, type ApprovalStep, type ArtifactEntry, type ArtifactStep, type Bindings, type BranchStep, type CompileWorkflowOptions, DEFAULT_AGENT_TRANSPORT_RETRY, DEFAULT_MAX_CONSECUTIVE_SPAWN_FAILURES, DEFAULT_STEP_TIMEOUT_MS, type FanOutOutcome, type GateCommandResult, type GateCommandRunner, type GateReportEvent, type GateStep, type GroupStep, type HarnessKnowledgeSelector, type InputRequiredRequest, type KnowledgeAppliedRecord, type LoopStep, type MapStep, type MaterializedKnowledge, MissingArtifactError, NodeFsPort, type OutputSchemaLike, type OutputsFileContract, type ParallelStep, type PipelineStep, type ResumeRequest, type RunStep, type RunWorkflowArgs, type RuntimeWorkflow, type SchemaValidationIssue, type Selector, type StepCache, type StepCacheEntry, type StepFailedInfo, type StepHookInfo, StepOutcomeError, type StepSkippedInfo, type SubworkflowStep, type SuspendStep, type TolerantFanOutResult, type ToolStep, type TransformStep, WorkflowCancelledError, WorkflowCompileError, type WorkflowInputValidation, type WorkflowRunResult, WorkflowSuspendedError, buildAgentStep, compileWorkflow, compileWorkflowManifest, evalPredicate, interpolateTemplate, isAgentTransportFailure, isCompilableJsonSchema, materializeKnowledge, normalizeWorkflowInputsSchema, resolveKnowledgeSelectors, resolveRef, resolveRefPrefixed, resolveValue, runWorkflow, validateAgainstJsonSchema, validateWorkflowInput };
|
package/dist/index.mjs
CHANGED
|
@@ -33,6 +33,7 @@ function buildAgentStep(id, fields) {
|
|
|
33
33
|
policy: fields.policy ?? { awaiting: "fail" },
|
|
34
34
|
...fields.outputSchema !== void 0 ? { outputSchema: fields.outputSchema } : {},
|
|
35
35
|
...fields.maxRetries !== void 0 ? { maxRetries: fields.maxRetries } : {},
|
|
36
|
+
...fields.retry !== void 0 ? { retry: fields.retry } : {},
|
|
36
37
|
...fields.harness !== void 0 ? { harness: fields.harness } : {},
|
|
37
38
|
...fields.agentTools !== void 0 ? { agentTools: fields.agentTools } : {}
|
|
38
39
|
};
|
|
@@ -355,6 +356,16 @@ function collectStepIds(steps, ids = /* @__PURE__ */ new Set()) {
|
|
|
355
356
|
}
|
|
356
357
|
return ids;
|
|
357
358
|
}
|
|
359
|
+
function usesSessionRef(steps) {
|
|
360
|
+
for (const s of steps) {
|
|
361
|
+
if (s?.sessionRef !== void 0 || s?.kind === "subworkflow") return true;
|
|
362
|
+
if (Array.isArray(s?.steps) && usesSessionRef(s.steps)) return true;
|
|
363
|
+
if (Array.isArray(s?.branches)) {
|
|
364
|
+
for (const br of s.branches) if (Array.isArray(br?.steps) && usesSessionRef(br.steps)) return true;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return false;
|
|
368
|
+
}
|
|
358
369
|
function compileWorkflow(handle, opts) {
|
|
359
370
|
const steps = handle.steps;
|
|
360
371
|
if (handle.start && handle.start !== steps[0]?.id) {
|
|
@@ -374,7 +385,8 @@ function compileWorkflow(handle, opts) {
|
|
|
374
385
|
steps: compileSiblingsToSteps(steps, ctx),
|
|
375
386
|
...finallySteps?.length ? { finally: compileSiblingsToSteps(finallySteps, finallyCtx) } : {},
|
|
376
387
|
...output ? { output } : {},
|
|
377
|
-
...outputsFiles ? { outputsFiles } : {}
|
|
388
|
+
...outputsFiles ? { outputsFiles } : {},
|
|
389
|
+
...usesSessionRef([...steps, ...finallySteps ?? []]) ? {} : { reusesSessions: false }
|
|
378
390
|
};
|
|
379
391
|
}
|
|
380
392
|
function compileSiblingsToSteps(steps, ctx) {
|
|
@@ -548,6 +560,7 @@ function compileAgentStep(step, id, ctx) {
|
|
|
548
560
|
}
|
|
549
561
|
const rawCwd = step.cwd;
|
|
550
562
|
const cwd = typeof rawCwd === "function" ? rawCwd : typeof rawCwd === "string" ? (b) => resolveRefString(id, "cwd", rawCwd, b, "error") : void 0;
|
|
563
|
+
const retry = compileAgentRetry(step.retry, id);
|
|
551
564
|
return buildAgentStep(id, {
|
|
552
565
|
prompt: (b) => renderPrompt(prompt, b),
|
|
553
566
|
...adapter !== void 0 ? { adapter } : {},
|
|
@@ -560,10 +573,31 @@ function compileAgentStep(step, id, ctx) {
|
|
|
560
573
|
policy: step.policy,
|
|
561
574
|
...outputSchema !== void 0 ? { outputSchema } : {},
|
|
562
575
|
...step.maxRetries !== void 0 ? { maxRetries: step.maxRetries } : {},
|
|
576
|
+
...retry !== void 0 ? { retry } : {},
|
|
563
577
|
...step.harness !== void 0 ? { harness: step.harness } : {},
|
|
564
578
|
...agentTools !== void 0 ? { agentTools } : {}
|
|
565
579
|
});
|
|
566
580
|
}
|
|
581
|
+
function compileAgentRetry(raw, id) {
|
|
582
|
+
if (raw === void 0) return void 0;
|
|
583
|
+
if (typeof raw !== "object" || raw === null) {
|
|
584
|
+
throw new WorkflowCompileError(`agent step '${id}': 'retry' must be an object { max_attempts, backoff?, initial_ms? }`);
|
|
585
|
+
}
|
|
586
|
+
const r = raw;
|
|
587
|
+
const maxAttempts = r.max_attempts ?? r.maxAttempts;
|
|
588
|
+
if (typeof maxAttempts !== "number" || !Number.isInteger(maxAttempts) || maxAttempts < 1 || maxAttempts > 20) {
|
|
589
|
+
throw new WorkflowCompileError(`agent step '${id}': 'retry.max_attempts' must be an integer between 1 and 20`);
|
|
590
|
+
}
|
|
591
|
+
const backoff = r.backoff ?? "fixed";
|
|
592
|
+
if (backoff !== "fixed" && backoff !== "exponential") {
|
|
593
|
+
throw new WorkflowCompileError(`agent step '${id}': 'retry.backoff' must be "fixed" or "exponential"`);
|
|
594
|
+
}
|
|
595
|
+
const initialMs = r.initial_ms ?? r.initialMs;
|
|
596
|
+
if (initialMs !== void 0 && (typeof initialMs !== "number" || !Number.isInteger(initialMs) || initialMs < 0)) {
|
|
597
|
+
throw new WorkflowCompileError(`agent step '${id}': 'retry.initial_ms' must be a non-negative integer`);
|
|
598
|
+
}
|
|
599
|
+
return { maxAttempts, backoff, ...initialMs !== void 0 ? { initialMs } : {} };
|
|
600
|
+
}
|
|
567
601
|
function compileGateStep(step, id) {
|
|
568
602
|
const command = f(step, "command");
|
|
569
603
|
if (typeof command !== "string" || command.trim().length === 0) {
|
|
@@ -784,6 +818,11 @@ function resolveRefString(stepId, field, value, b, onUnmatched = "passthrough")
|
|
|
784
818
|
|
|
785
819
|
// src/types.ts
|
|
786
820
|
var DEFAULT_MAX_CONSECUTIVE_SPAWN_FAILURES = 3;
|
|
821
|
+
var DEFAULT_AGENT_TRANSPORT_RETRY = Object.freeze({
|
|
822
|
+
maxAttempts: 2,
|
|
823
|
+
backoff: "fixed",
|
|
824
|
+
initialMs: 1e3
|
|
825
|
+
});
|
|
787
826
|
var DEFAULT_STEP_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
788
827
|
var NodeFsPort = class {
|
|
789
828
|
root;
|
|
@@ -1026,6 +1065,23 @@ var AgentSpawnError = class extends Error {
|
|
|
1026
1065
|
stepId;
|
|
1027
1066
|
cause;
|
|
1028
1067
|
};
|
|
1068
|
+
var AgentSessionLostError = class extends Error {
|
|
1069
|
+
constructor(sessionId, message, cause) {
|
|
1070
|
+
super(message);
|
|
1071
|
+
this.sessionId = sessionId;
|
|
1072
|
+
this.cause = cause;
|
|
1073
|
+
this.name = "AgentSessionLostError";
|
|
1074
|
+
}
|
|
1075
|
+
sessionId;
|
|
1076
|
+
cause;
|
|
1077
|
+
transport = true;
|
|
1078
|
+
};
|
|
1079
|
+
function isAgentTransportFailure(err) {
|
|
1080
|
+
return err instanceof AgentSpawnError || isAgentSessionLost(err);
|
|
1081
|
+
}
|
|
1082
|
+
function isAgentSessionLost(err) {
|
|
1083
|
+
return typeof err === "object" && err !== null && err.transport === true;
|
|
1084
|
+
}
|
|
1029
1085
|
var WorkflowCancelledError = class extends Error {
|
|
1030
1086
|
constructor(stepId) {
|
|
1031
1087
|
super(`step '${stepId}': run cancelled \u2014 not started`);
|
|
@@ -1055,7 +1111,7 @@ function view(ctx, item, index) {
|
|
|
1055
1111
|
}
|
|
1056
1112
|
function withIndexedHooks(ctx, index) {
|
|
1057
1113
|
const cacheKeySuffix = `${ctx.cacheKeySuffix ?? ""}[${index}]`;
|
|
1058
|
-
if (!ctx.onStepStart && !ctx.onStepComplete && !ctx.onStepSkipped && !ctx.onStepFailed) {
|
|
1114
|
+
if (!ctx.onStepStart && !ctx.onStepComplete && !ctx.onStepSkipped && !ctx.onStepFailed && !ctx.onAgentRetry) {
|
|
1059
1115
|
return { ...ctx, cacheKeySuffix };
|
|
1060
1116
|
}
|
|
1061
1117
|
return {
|
|
@@ -1064,7 +1120,8 @@ function withIndexedHooks(ctx, index) {
|
|
|
1064
1120
|
onStepStart: ctx.onStepStart ? (id, info) => ctx.onStepStart(`${id}[${index}]`, info) : void 0,
|
|
1065
1121
|
onStepComplete: ctx.onStepComplete ? (id, out, info) => ctx.onStepComplete(`${id}[${index}]`, out, info) : void 0,
|
|
1066
1122
|
onStepSkipped: ctx.onStepSkipped ? (id, info) => ctx.onStepSkipped(`${id}[${index}]`, info) : void 0,
|
|
1067
|
-
onStepFailed: ctx.onStepFailed ? (id, info) => ctx.onStepFailed(`${id}[${index}]`, info) : void 0
|
|
1123
|
+
onStepFailed: ctx.onStepFailed ? (id, info) => ctx.onStepFailed(`${id}[${index}]`, info) : void 0,
|
|
1124
|
+
onAgentRetry: ctx.onAgentRetry ? (ev) => ctx.onAgentRetry({ ...ev, stepId: `${ev.stepId}[${index}]` }) : void 0
|
|
1068
1125
|
};
|
|
1069
1126
|
}
|
|
1070
1127
|
var failureOrigin = /* @__PURE__ */ new WeakMap();
|
|
@@ -1299,25 +1356,90 @@ async function runSequence(steps, ctx, item, index) {
|
|
|
1299
1356
|
return last;
|
|
1300
1357
|
}
|
|
1301
1358
|
async function sendPromptAndAwaitOutcome(ctx, step, sessionId, prompt) {
|
|
1302
|
-
|
|
1359
|
+
await ctx.agents.sendPromptAndWait(sessionId, prompt);
|
|
1360
|
+
await settleInputRequests(ctx, step, sessionId);
|
|
1361
|
+
}
|
|
1362
|
+
async function settleInputRequests(ctx, step, sessionId) {
|
|
1303
1363
|
for (; ; ) {
|
|
1304
|
-
await ctx.agents.sendPromptAndWait(sessionId, next);
|
|
1305
1364
|
const req = ctx.agents.takeInputRequest?.(sessionId);
|
|
1306
1365
|
if (!req) return;
|
|
1307
1366
|
if (!ctx.onInputRequired) {
|
|
1308
1367
|
throw new AgentInputRequiredError(step.id, req.prompt, req.schema);
|
|
1309
1368
|
}
|
|
1310
1369
|
const payload = await ctx.onInputRequired({ stepId: step.id, prompt: req.prompt, schema: req.schema });
|
|
1311
|
-
|
|
1370
|
+
await ctx.agents.sendPromptAndWait(sessionId, JSON.stringify(payload));
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
function agentRetryDelayMs(retry, attempt) {
|
|
1374
|
+
if (!retry.initialMs) return 0;
|
|
1375
|
+
return retry.backoff === "exponential" ? retry.initialMs * 2 ** (attempt - 2) : retry.initialMs;
|
|
1376
|
+
}
|
|
1377
|
+
function sleepUnlessAborted(ms, signal) {
|
|
1378
|
+
if (ms <= 0 || signal?.aborted) return Promise.resolve();
|
|
1379
|
+
return new Promise((resolve2) => {
|
|
1380
|
+
const onAbort = () => {
|
|
1381
|
+
clearTimeout(timer);
|
|
1382
|
+
resolve2();
|
|
1383
|
+
};
|
|
1384
|
+
const timer = setTimeout(() => {
|
|
1385
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1386
|
+
resolve2();
|
|
1387
|
+
}, ms);
|
|
1388
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1389
|
+
});
|
|
1390
|
+
}
|
|
1391
|
+
async function spawnOrResolveSession(step, ctx, b, spawnOpts) {
|
|
1392
|
+
let sessionId;
|
|
1393
|
+
if (step.adapter) {
|
|
1394
|
+
if (ctx.signal?.aborted) throw new WorkflowCancelledError(step.id);
|
|
1395
|
+
const { cwd, sandbox, harness } = spawnOpts;
|
|
1396
|
+
try {
|
|
1397
|
+
sessionId = await ctx.agents.spawn(resolveSel(step.adapter, b), {
|
|
1398
|
+
cwd,
|
|
1399
|
+
workspaceSlug: ctx.workspaceSlug,
|
|
1400
|
+
stepId: step.id,
|
|
1401
|
+
...sandbox !== void 0 ? { sandbox } : {},
|
|
1402
|
+
...step.options !== void 0 ? { options: step.options } : {},
|
|
1403
|
+
...harness !== void 0 ? { harness } : {},
|
|
1404
|
+
...step.agentTools !== void 0 ? { agentTools: step.agentTools } : {},
|
|
1405
|
+
...b.index !== void 0 ? { stepKey: `${step.id}[${b.index}]` } : {}
|
|
1406
|
+
});
|
|
1407
|
+
} catch (err) {
|
|
1408
|
+
throw new AgentSpawnError(step.id, err);
|
|
1409
|
+
}
|
|
1410
|
+
} else {
|
|
1411
|
+
sessionId = ctx.agents.resolveByLabel(resolveSessionRef(step.sessionRef, b));
|
|
1412
|
+
}
|
|
1413
|
+
if (!sessionId) throw new Error(`step '${step.id}': no session (adapter and sessionRef both unresolved)`);
|
|
1414
|
+
return sessionId;
|
|
1415
|
+
}
|
|
1416
|
+
async function retireLostSession(ctx, sessionId) {
|
|
1417
|
+
const agents = ctx.agents;
|
|
1418
|
+
if (agents.readCostUsd) {
|
|
1419
|
+
try {
|
|
1420
|
+
ctx.state.costBySession.set(sessionId, await agents.readCostUsd(sessionId));
|
|
1421
|
+
} catch {
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
const idx = ctx.spawned?.indexOf(sessionId) ?? -1;
|
|
1425
|
+
if (idx >= 0) ctx.spawned.splice(idx, 1);
|
|
1426
|
+
if (agents.releaseSession) {
|
|
1427
|
+
try {
|
|
1428
|
+
await agents.releaseSession(sessionId);
|
|
1429
|
+
} catch {
|
|
1430
|
+
}
|
|
1312
1431
|
}
|
|
1313
1432
|
}
|
|
1314
1433
|
async function execAgentStep(step, ctx, b) {
|
|
1315
1434
|
ctx.onStepStart?.(step.id);
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1435
|
+
const assertBudget = () => {
|
|
1436
|
+
if (step.adapter && ctx.state.maxTotalCostUsd !== void 0 && spentUsd(ctx.state) >= ctx.state.maxTotalCostUsd) {
|
|
1437
|
+
throw new Error(
|
|
1438
|
+
`step '${step.id}': budget_exceeded \u2014 run spend $${spentUsd(ctx.state).toFixed(4)} >= cap $${ctx.state.maxTotalCostUsd}`
|
|
1439
|
+
);
|
|
1440
|
+
}
|
|
1441
|
+
};
|
|
1442
|
+
assertBudget();
|
|
1321
1443
|
const stepCwd = step.cwd ? resolveSel(step.cwd, b) : void 0;
|
|
1322
1444
|
const cwd = step.harness?.cwd ?? (stepCwd !== void 0 ? ctx.cwd !== void 0 ? resolve(ctx.cwd, stepCwd) : stepCwd : ctx.cwd);
|
|
1323
1445
|
let knowledgeOut;
|
|
@@ -1351,45 +1473,53 @@ ${inner(b2)}` };
|
|
|
1351
1473
|
...step.harness ?? {},
|
|
1352
1474
|
...model !== void 0 && step.harness?.model === void 0 ? { model } : {}
|
|
1353
1475
|
} : void 0;
|
|
1476
|
+
const inputRequestAffordance = ctx.agents.takeInputRequest ? "\n\nIf you need information you don't have, call the run_request_input tool instead of asking in your reply." : "";
|
|
1477
|
+
const outputSchemaNote = step.outputSchema ? describeOutputSchemaForPrompt(step.outputSchema) : void 0;
|
|
1478
|
+
const outputSchemaAffordance = outputSchemaNote ? `
|
|
1479
|
+
|
|
1480
|
+
${outputSchemaNote}` : "";
|
|
1481
|
+
const firstPrompt = step.prompt(b) + inputRequestAffordance + outputSchemaAffordance;
|
|
1482
|
+
const retry = step.adapter ? step.retry ?? DEFAULT_AGENT_TRANSPORT_RETRY : void 0;
|
|
1483
|
+
const maxAttempts = Math.max(1, Math.floor(retry?.maxAttempts ?? 1));
|
|
1354
1484
|
let sessionId;
|
|
1355
|
-
|
|
1356
|
-
|
|
1485
|
+
for (let attempt = 1; ; attempt++) {
|
|
1486
|
+
let attemptSession;
|
|
1357
1487
|
try {
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1488
|
+
attemptSession = await spawnOrResolveSession(step, ctx, b, { cwd, sandbox, harness });
|
|
1489
|
+
if (step.adapter) ctx.spawned?.push(attemptSession);
|
|
1490
|
+
if (knowledgeWarnings.length > 0 && ctx.agents.emitHarnessWarning) {
|
|
1491
|
+
ctx.agents.emitHarnessWarning({
|
|
1492
|
+
sessionId: attemptSession,
|
|
1493
|
+
warnings: knowledgeWarnings,
|
|
1494
|
+
label: step.id
|
|
1495
|
+
});
|
|
1496
|
+
}
|
|
1497
|
+
await ctx.agents.sendPromptAndWait(attemptSession, firstPrompt);
|
|
1498
|
+
sessionId = attemptSession;
|
|
1499
|
+
break;
|
|
1500
|
+
} catch (err) {
|
|
1501
|
+
const retryable = step.retry !== void 0 ? isAgentTransportFailure(err) : isAgentSessionLost(err);
|
|
1502
|
+
if (attempt >= maxAttempts || !retryable || ctx.signal?.aborted) throw err;
|
|
1503
|
+
if (attemptSession !== void 0) await retireLostSession(ctx, attemptSession);
|
|
1504
|
+
const delayMs = agentRetryDelayMs(retry, attempt + 1);
|
|
1505
|
+
ctx.onAgentRetry?.({
|
|
1361
1506
|
stepId: step.id,
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
...
|
|
1366
|
-
|
|
1507
|
+
attempt: attempt + 1,
|
|
1508
|
+
maxAttempts,
|
|
1509
|
+
error: errorMessage(err),
|
|
1510
|
+
...attemptSession !== void 0 ? { sessionId: attemptSession } : {},
|
|
1511
|
+
delayMs
|
|
1367
1512
|
});
|
|
1368
|
-
|
|
1369
|
-
throw new
|
|
1513
|
+
await sleepUnlessAborted(delayMs, ctx.signal);
|
|
1514
|
+
if (ctx.signal?.aborted) throw new WorkflowCancelledError(step.id);
|
|
1515
|
+
assertBudget();
|
|
1370
1516
|
}
|
|
1371
|
-
} else {
|
|
1372
|
-
sessionId = ctx.agents.resolveByLabel(resolveSessionRef(step.sessionRef, b));
|
|
1373
|
-
}
|
|
1374
|
-
if (!sessionId) throw new Error(`step '${step.id}': no session (adapter and sessionRef both unresolved)`);
|
|
1375
|
-
if (step.adapter) ctx.spawned?.push(sessionId);
|
|
1376
|
-
if (knowledgeWarnings.length > 0 && ctx.agents.emitHarnessWarning) {
|
|
1377
|
-
ctx.agents.emitHarnessWarning({
|
|
1378
|
-
sessionId,
|
|
1379
|
-
warnings: knowledgeWarnings,
|
|
1380
|
-
label: step.id
|
|
1381
|
-
});
|
|
1382
1517
|
}
|
|
1518
|
+
await settleInputRequests(ctx, step, sessionId);
|
|
1383
1519
|
const harnessOut = harness !== void 0 ? {
|
|
1384
1520
|
...harness,
|
|
1385
1521
|
...harness.tools && harness.tools.length > 0 ? { toolsApplied: false } : {}
|
|
1386
1522
|
} : void 0;
|
|
1387
|
-
const inputRequestAffordance = ctx.agents.takeInputRequest ? "\n\nIf you need information you don't have, call the run_request_input tool instead of asking in your reply." : "";
|
|
1388
|
-
const outputSchemaNote = step.outputSchema ? describeOutputSchemaForPrompt(step.outputSchema) : void 0;
|
|
1389
|
-
const outputSchemaAffordance = outputSchemaNote ? `
|
|
1390
|
-
|
|
1391
|
-
${outputSchemaNote}` : "";
|
|
1392
|
-
await sendPromptAndAwaitOutcome(ctx, step, sessionId, step.prompt(b) + inputRequestAffordance + outputSchemaAffordance);
|
|
1393
1523
|
if (step.policy && ctx.agents.onAwaitingInput) {
|
|
1394
1524
|
await ctx.agents.onAwaitingInput(sessionId, step.policy);
|
|
1395
1525
|
}
|
|
@@ -1877,6 +2007,7 @@ async function execStepBody(step, ctx, item, index) {
|
|
|
1877
2007
|
cacheKey: ctx.cacheKey,
|
|
1878
2008
|
runGateCommand: ctx.runGateCommand,
|
|
1879
2009
|
onGateReport: ctx.onGateReport,
|
|
2010
|
+
onAgentRetry: ctx.onAgentRetry,
|
|
1880
2011
|
spawned: ctx.spawned,
|
|
1881
2012
|
usedArtifactNames: ctx.usedArtifactNames
|
|
1882
2013
|
});
|
|
@@ -2034,6 +2165,7 @@ async function runWorkflowInner(workflow, input, hooks, maxTotalCostUsd) {
|
|
|
2034
2165
|
const out = await execStep(step, ctx, void 0, void 0);
|
|
2035
2166
|
state.steps[step.id] = out;
|
|
2036
2167
|
completeStep(ctx, step.id, out);
|
|
2168
|
+
if (ownsScope && workflow.reusesSessions === false) await releaseScope(ctx);
|
|
2037
2169
|
lastId = step.id;
|
|
2038
2170
|
}
|
|
2039
2171
|
await checkOutputsFiles(workflow.outputsFiles, ctx, workflow.id, lastId);
|
|
@@ -2071,7 +2203,8 @@ async function runWorkflow(args) {
|
|
|
2071
2203
|
onStepSkipped: args.onStepSkipped,
|
|
2072
2204
|
onStepFailed: args.onStepFailed,
|
|
2073
2205
|
runGateCommand: args.runGateCommand,
|
|
2074
|
-
onGateReport: args.onGateReport
|
|
2206
|
+
onGateReport: args.onGateReport,
|
|
2207
|
+
onAgentRetry: args.onAgentRetry
|
|
2075
2208
|
}, args.maxTotalCostUsd);
|
|
2076
2209
|
}
|
|
2077
2210
|
function compileWorkflowManifest(source, opts) {
|
|
@@ -2080,6 +2213,6 @@ function compileWorkflowManifest(source, opts) {
|
|
|
2080
2213
|
return compileWorkflow(handle, opts);
|
|
2081
2214
|
}
|
|
2082
2215
|
|
|
2083
|
-
export { AgentInputRequiredError, AgentSpawnError, DEFAULT_MAX_CONSECUTIVE_SPAWN_FAILURES, DEFAULT_STEP_TIMEOUT_MS, MissingArtifactError, NodeFsPort, StepOutcomeError, WorkflowCancelledError, WorkflowCompileError, WorkflowSuspendedError, buildAgentStep, compileWorkflow, compileWorkflowManifest, evalPredicate, interpolateTemplate, isCompilableJsonSchema, materializeKnowledge, normalizeWorkflowInputsSchema, resolveKnowledgeSelectors, resolveRef, resolveRefPrefixed, resolveValue, runWorkflow, validateAgainstJsonSchema, validateWorkflowInput };
|
|
2216
|
+
export { AgentInputRequiredError, AgentSessionLostError, AgentSpawnError, DEFAULT_AGENT_TRANSPORT_RETRY, DEFAULT_MAX_CONSECUTIVE_SPAWN_FAILURES, DEFAULT_STEP_TIMEOUT_MS, MissingArtifactError, NodeFsPort, StepOutcomeError, WorkflowCancelledError, WorkflowCompileError, WorkflowSuspendedError, buildAgentStep, compileWorkflow, compileWorkflowManifest, evalPredicate, interpolateTemplate, isAgentTransportFailure, isCompilableJsonSchema, materializeKnowledge, normalizeWorkflowInputsSchema, resolveKnowledgeSelectors, resolveRef, resolveRefPrefixed, resolveValue, runWorkflow, validateAgainstJsonSchema, validateWorkflowInput };
|
|
2084
2217
|
//# sourceMappingURL=index.mjs.map
|
|
2085
2218
|
//# sourceMappingURL=index.mjs.map
|