stageflow 0.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/LICENSE +21 -0
- package/README.md +56 -0
- package/dist/agent/activity.d.ts +76 -0
- package/dist/agent/activity.js +89 -0
- package/dist/agent/activityObserver.d.ts +17 -0
- package/dist/agent/activityObserver.js +66 -0
- package/dist/agent/cursorExtension.d.ts +12 -0
- package/dist/agent/cursorExtension.js +88 -0
- package/dist/agent/cursorProvider.d.ts +14 -0
- package/dist/agent/cursorProvider.js +139 -0
- package/dist/agent/fakeAgent.d.ts +29 -0
- package/dist/agent/fakeAgent.js +295 -0
- package/dist/agent/oauthSessionManager.d.ts +33 -0
- package/dist/agent/oauthSessionManager.js +214 -0
- package/dist/agent/piAdapter.d.ts +128 -0
- package/dist/agent/piAdapter.js +967 -0
- package/dist/agent/port.d.ts +63 -0
- package/dist/agent/port.js +46 -0
- package/dist/agent/providerAuth.d.ts +101 -0
- package/dist/agent/providerAuth.js +282 -0
- package/dist/agent/providerSupport.d.ts +28 -0
- package/dist/agent/providerSupport.js +19 -0
- package/dist/cli/providersCommand.d.ts +11 -0
- package/dist/cli/providersCommand.js +275 -0
- package/dist/cli/terminalAuthInteraction.d.ts +8 -0
- package/dist/cli/terminalAuthInteraction.js +65 -0
- package/dist/cli/terminalSecret.d.ts +2 -0
- package/dist/cli/terminalSecret.js +67 -0
- package/dist/cli/validateCommand.d.ts +11 -0
- package/dist/cli/validateCommand.js +81 -0
- package/dist/cli/validateOutput.d.ts +8 -0
- package/dist/cli/validateOutput.js +75 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +273 -0
- package/dist/config/createPipeline.d.ts +31 -0
- package/dist/config/createPipeline.js +255 -0
- package/dist/config/createStage.d.ts +25 -0
- package/dist/config/createStage.js +181 -0
- package/dist/config/listConfig.d.ts +33 -0
- package/dist/config/listConfig.js +195 -0
- package/dist/config/listExtensions.d.ts +26 -0
- package/dist/config/listExtensions.js +50 -0
- package/dist/config/listSkills.d.ts +29 -0
- package/dist/config/listSkills.js +55 -0
- package/dist/config/loadOutcome.d.ts +27 -0
- package/dist/config/loadOutcome.js +6 -0
- package/dist/config/loadPipeline.d.ts +25 -0
- package/dist/config/loadPipeline.js +196 -0
- package/dist/config/loadStage.d.ts +4 -0
- package/dist/config/loadStage.js +127 -0
- package/dist/config/loadTask.d.ts +7 -0
- package/dist/config/loadTask.js +69 -0
- package/dist/config/readYamlObject.d.ts +1 -0
- package/dist/config/readYamlObject.js +5 -0
- package/dist/config/resolvePipelineDag.d.ts +12 -0
- package/dist/config/resolvePipelineDag.js +282 -0
- package/dist/config/validateCatalog.d.ts +36 -0
- package/dist/config/validateCatalog.js +322 -0
- package/dist/envelope/check.d.ts +3 -0
- package/dist/envelope/check.js +52 -0
- package/dist/envelope/payloadSchema.d.ts +10 -0
- package/dist/envelope/payloadSchema.js +91 -0
- package/dist/hitl/qaTrail.d.ts +31 -0
- package/dist/hitl/qaTrail.js +65 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +7 -0
- package/dist/mcp/projectRun.d.ts +37 -0
- package/dist/mcp/projectRun.js +57 -0
- package/dist/mcp/readArtifact.d.ts +2 -0
- package/dist/mcp/readArtifact.js +46 -0
- package/dist/mcp/server.d.ts +7 -0
- package/dist/mcp/server.js +26 -0
- package/dist/mcp/tools.d.ts +9 -0
- package/dist/mcp/tools.js +96 -0
- package/dist/package-meta.d.ts +1 -0
- package/dist/package-meta.js +1 -0
- package/dist/prompt/priorEnvelope.d.ts +2 -0
- package/dist/prompt/priorEnvelope.js +5 -0
- package/dist/runstore/catalog.d.ts +8 -0
- package/dist/runstore/catalog.js +16 -0
- package/dist/runstore/createStore.d.ts +15 -0
- package/dist/runstore/createStore.js +23 -0
- package/dist/runstore/disk/DiskRunStore.d.ts +30 -0
- package/dist/runstore/disk/DiskRunStore.js +238 -0
- package/dist/runstore/disk/catalogHelpers.d.ts +11 -0
- package/dist/runstore/disk/catalogHelpers.js +102 -0
- package/dist/runstore/layout.d.ts +22 -0
- package/dist/runstore/layout.js +58 -0
- package/dist/runstore/paths.d.ts +7 -0
- package/dist/runstore/paths.js +60 -0
- package/dist/runstore/pipelineDagSnapshot.d.ts +5 -0
- package/dist/runstore/pipelineDagSnapshot.js +43 -0
- package/dist/runstore/port.d.ts +146 -0
- package/dist/runstore/port.js +28 -0
- package/dist/runstore/runProjection.d.ts +6 -0
- package/dist/runstore/runProjection.js +102 -0
- package/dist/runstore/sqlite/SqliteRunStore.d.ts +48 -0
- package/dist/runstore/sqlite/SqliteRunStore.js +460 -0
- package/dist/runstore/sqlite/migrateFromDisk.d.ts +5 -0
- package/dist/runstore/sqlite/migrateFromDisk.js +108 -0
- package/dist/runstore/sqlite/schema.d.ts +1 -0
- package/dist/runstore/sqlite/schema.js +59 -0
- package/dist/runstore/stageExecution.d.ts +13 -0
- package/dist/runstore/stageExecution.js +22 -0
- package/dist/runstore/stageSnapshot.d.ts +2 -0
- package/dist/runstore/stageSnapshot.js +46 -0
- package/dist/runstore/syntheticStageSnapshot.d.ts +2 -0
- package/dist/runstore/syntheticStageSnapshot.js +10 -0
- package/dist/runstore/trackProjection.d.ts +7 -0
- package/dist/runstore/trackProjection.js +94 -0
- package/dist/runstore/workspaceLayout.d.ts +31 -0
- package/dist/runstore/workspaceLayout.js +172 -0
- package/dist/runtime/answerResume.d.ts +16 -0
- package/dist/runtime/answerResume.js +22 -0
- package/dist/runtime/credentialBinding.d.ts +16 -0
- package/dist/runtime/credentialBinding.js +80 -0
- package/dist/runtime/dagTraversal.d.ts +3 -0
- package/dist/runtime/dagTraversal.js +26 -0
- package/dist/runtime/envelopeRouting.d.ts +20 -0
- package/dist/runtime/envelopeRouting.js +59 -0
- package/dist/runtime/hitlSeams.d.ts +38 -0
- package/dist/runtime/hitlSeams.js +2 -0
- package/dist/runtime/pipelineRunner.d.ts +77 -0
- package/dist/runtime/pipelineRunner.js +141 -0
- package/dist/runtime/pipelineScheduler.d.ts +90 -0
- package/dist/runtime/pipelineScheduler.js +494 -0
- package/dist/runtime/pipelineValidationError.d.ts +6 -0
- package/dist/runtime/pipelineValidationError.js +8 -0
- package/dist/runtime/resumeReconstruct.d.ts +33 -0
- package/dist/runtime/resumeReconstruct.js +144 -0
- package/dist/runtime/runManager.d.ts +129 -0
- package/dist/runtime/runManager.js +818 -0
- package/dist/runtime/runRetryCoordinator.d.ts +85 -0
- package/dist/runtime/runRetryCoordinator.js +372 -0
- package/dist/runtime/settingsFile.d.ts +17 -0
- package/dist/runtime/settingsFile.js +88 -0
- package/dist/runtime/stageAttemptBootstrap.d.ts +40 -0
- package/dist/runtime/stageAttemptBootstrap.js +82 -0
- package/dist/runtime/stageAttemptContext.d.ts +11 -0
- package/dist/runtime/stageAttemptContext.js +26 -0
- package/dist/runtime/stageConcurrency.d.ts +10 -0
- package/dist/runtime/stageConcurrency.js +51 -0
- package/dist/runtime/stageHitl.d.ts +95 -0
- package/dist/runtime/stageHitl.js +198 -0
- package/dist/runtime/stageProcessLauncher.d.ts +45 -0
- package/dist/runtime/stageProcessLauncher.js +199 -0
- package/dist/runtime/stageRecovery.d.ts +10 -0
- package/dist/runtime/stageRecovery.js +43 -0
- package/dist/runtime/stageRoots.d.ts +22 -0
- package/dist/runtime/stageRoots.js +93 -0
- package/dist/runtime/stageRunner.d.ts +59 -0
- package/dist/runtime/stageRunner.js +221 -0
- package/dist/runtime/stageWorker.d.ts +6 -0
- package/dist/runtime/stageWorker.js +109 -0
- package/dist/runtime/stageWorkerProtocol.d.ts +27 -0
- package/dist/runtime/stageWorkerProtocol.js +16 -0
- package/dist/runtime/taskInput.d.ts +15 -0
- package/dist/runtime/taskInput.js +38 -0
- package/dist/server/http.d.ts +31 -0
- package/dist/server/http.js +561 -0
- package/dist/server/providerRoutes.d.ts +15 -0
- package/dist/server/providerRoutes.js +186 -0
- package/dist/tools/askOperator.d.ts +185 -0
- package/dist/tools/askOperator.js +409 -0
- package/dist/tools/emitStageEnvelope.d.ts +42 -0
- package/dist/tools/emitStageEnvelope.js +61 -0
- package/dist/tools/writeStageArtifact.d.ts +26 -0
- package/dist/tools/writeStageArtifact.js +103 -0
- package/dist/types/envelope.d.ts +12 -0
- package/dist/types/envelope.js +6 -0
- package/dist/types/pipeline.d.ts +25 -0
- package/dist/types/pipeline.js +1 -0
- package/dist/types/stage.d.ts +12 -0
- package/dist/types/stage.js +6 -0
- package/dist/types/task.d.ts +7 -0
- package/dist/types/task.js +1 -0
- package/dist/ui/assets/index-Cry0Tpfx.js +118 -0
- package/dist/ui/assets/index-DCsDopak.css +1 -0
- package/dist/ui/index.html +21 -0
- package/package.json +72 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { AgentPort } from "../agent/port.js";
|
|
2
|
+
import type { RunDetail, RunStatus, RunStore } from "../runstore/port.js";
|
|
3
|
+
import type { PipelineRunResult } from "./pipelineRunner.js";
|
|
4
|
+
import { type SchedulerPreparedPipeline } from "./pipelineScheduler.js";
|
|
5
|
+
import type { StageExecutionMode } from "./stageConcurrency.js";
|
|
6
|
+
import type { StageProcessLauncher } from "./stageProcessLauncher.js";
|
|
7
|
+
import type { StageHitlController } from "./stageHitl.js";
|
|
8
|
+
import type { OperatorCatalog } from "./stageAttemptBootstrap.js";
|
|
9
|
+
export declare class DuplicateRetryRootError extends Error {
|
|
10
|
+
constructor(runId: string, stageId: string);
|
|
11
|
+
}
|
|
12
|
+
export declare class RetryCoordinatorInactiveError extends Error {
|
|
13
|
+
constructor(runId: string);
|
|
14
|
+
}
|
|
15
|
+
export declare class RetryRootWaitTimeoutError extends Error {
|
|
16
|
+
constructor(runId: string, stageId: string, attempt: number);
|
|
17
|
+
}
|
|
18
|
+
export type RunRetryCoordinatorStartOptions = {
|
|
19
|
+
runId: string;
|
|
20
|
+
roots: Map<string, number>;
|
|
21
|
+
prepared: SchedulerPreparedPipeline;
|
|
22
|
+
maxActiveStagesPerRun: number;
|
|
23
|
+
executionMode?: StageExecutionMode;
|
|
24
|
+
stageProcessLauncher?: StageProcessLauncher;
|
|
25
|
+
onLoopTick?: () => void | Promise<void>;
|
|
26
|
+
};
|
|
27
|
+
export type RetryStageResult = {
|
|
28
|
+
ok: true;
|
|
29
|
+
runId: string;
|
|
30
|
+
stageId: string;
|
|
31
|
+
attemptIndex: number;
|
|
32
|
+
} | {
|
|
33
|
+
ok: false;
|
|
34
|
+
reason: string;
|
|
35
|
+
status?: number;
|
|
36
|
+
};
|
|
37
|
+
export type RetryTrackingPort = {
|
|
38
|
+
ensureResumeTracked(runId: string): Promise<{
|
|
39
|
+
ok: true;
|
|
40
|
+
insertedForResume?: boolean;
|
|
41
|
+
} | {
|
|
42
|
+
ok: false;
|
|
43
|
+
reason: string;
|
|
44
|
+
}>;
|
|
45
|
+
onOrchestrationStarted(runId: string, promise: Promise<PipelineRunResult>): void;
|
|
46
|
+
rollbackStartTracking(runId: string, state: {
|
|
47
|
+
insertedForResume: boolean;
|
|
48
|
+
bumpedRunning: boolean;
|
|
49
|
+
priorRunStatus: RunStatus;
|
|
50
|
+
}): Promise<void>;
|
|
51
|
+
};
|
|
52
|
+
export type RetryStageRequest = {
|
|
53
|
+
runId: string;
|
|
54
|
+
stageId: string;
|
|
55
|
+
store: RunStore;
|
|
56
|
+
agent: AgentPort;
|
|
57
|
+
cwd: string;
|
|
58
|
+
operatorCatalog?: OperatorCatalog;
|
|
59
|
+
maxActiveStagesPerRun: number;
|
|
60
|
+
executionMode: StageExecutionMode;
|
|
61
|
+
stageProcessLauncher?: StageProcessLauncher;
|
|
62
|
+
hitl: StageHitlController;
|
|
63
|
+
orchestrationConflict: boolean;
|
|
64
|
+
tracking: RetryTrackingPort;
|
|
65
|
+
};
|
|
66
|
+
export declare function assertStageRetryEligible(detail: RunDetail, stageId: string, opts?: {
|
|
67
|
+
recoveryActive?: boolean;
|
|
68
|
+
}): {
|
|
69
|
+
ok: true;
|
|
70
|
+
} | {
|
|
71
|
+
ok: false;
|
|
72
|
+
reason: string;
|
|
73
|
+
status: number;
|
|
74
|
+
};
|
|
75
|
+
export declare class RunRetryCoordinator {
|
|
76
|
+
private readonly sessions;
|
|
77
|
+
isActive(runId: string): boolean;
|
|
78
|
+
getOrchestrationPromise(runId: string): Promise<PipelineRunResult> | undefined;
|
|
79
|
+
getActiveRoots(runId: string): ReadonlyMap<string, number> | undefined;
|
|
80
|
+
signalExternalRetryRootTerminal(runId: string, stageId: string): void;
|
|
81
|
+
start(options: RunRetryCoordinatorStartOptions): void;
|
|
82
|
+
addRoot(runId: string, stageId: string, attempt: number): Promise<void>;
|
|
83
|
+
retryStage(req: RetryStageRequest): Promise<RetryStageResult>;
|
|
84
|
+
waitForRoot(runId: string, stageId: string, _store: RunStore): Promise<void>;
|
|
85
|
+
}
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import { retryRun, } from "./pipelineScheduler.js";
|
|
2
|
+
import { loadRunContext } from "./resumeReconstruct.js";
|
|
3
|
+
import { syncRunStatusFromStages } from "./stageRecovery.js";
|
|
4
|
+
export class DuplicateRetryRootError extends Error {
|
|
5
|
+
constructor(runId, stageId) {
|
|
6
|
+
super(`Retry already in progress for run ${runId} stage ${stageId}`);
|
|
7
|
+
this.name = "DuplicateRetryRootError";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export class RetryCoordinatorInactiveError extends Error {
|
|
11
|
+
constructor(runId) {
|
|
12
|
+
super(`Retry coordinator is not active for run ${runId}`);
|
|
13
|
+
this.name = "RetryCoordinatorInactiveError";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export class RetryRootWaitTimeoutError extends Error {
|
|
17
|
+
constructor(runId, stageId, attempt) {
|
|
18
|
+
super(`Retry root wait timed out for run ${runId} stage ${stageId} attempt ${attempt}`);
|
|
19
|
+
this.name = "RetryRootWaitTimeoutError";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function assertStageRetryEligible(detail, stageId, opts) {
|
|
23
|
+
const stageSnap = detail.stages.find((s) => s.stage_id === stageId);
|
|
24
|
+
if (!stageSnap) {
|
|
25
|
+
return {
|
|
26
|
+
ok: false,
|
|
27
|
+
reason: `Stage not found: ${stageId}`,
|
|
28
|
+
status: 404,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if (stageSnap.status === "waiting_for_input") {
|
|
32
|
+
return {
|
|
33
|
+
ok: false,
|
|
34
|
+
reason: `Stage is waiting for input and cannot be retried`,
|
|
35
|
+
status: 409,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const recoveryActive = opts?.recoveryActive === true;
|
|
39
|
+
if (detail.status !== "failed") {
|
|
40
|
+
if (!(detail.status === "running" && recoveryActive)) {
|
|
41
|
+
return {
|
|
42
|
+
ok: false,
|
|
43
|
+
reason: `Run is not failed (status=${detail.status})`,
|
|
44
|
+
status: 409,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (stageSnap.status !== "failed") {
|
|
49
|
+
return {
|
|
50
|
+
ok: false,
|
|
51
|
+
reason: `Stage is not failed (status=${stageSnap.status})`,
|
|
52
|
+
status: 409,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return { ok: true };
|
|
56
|
+
}
|
|
57
|
+
function mutationKey(stageId, attempt) {
|
|
58
|
+
return `${stageId}\0${attempt}`;
|
|
59
|
+
}
|
|
60
|
+
function rootWaitKey(runId, stageId, attempt) {
|
|
61
|
+
return `${runId}\0${stageId}\0${attempt}`;
|
|
62
|
+
}
|
|
63
|
+
function readRetryRootWaitTimeoutMs() {
|
|
64
|
+
const raw = process.env.STAGEFLOW_RETRY_ROOT_WAIT_TIMEOUT_MS;
|
|
65
|
+
if (raw !== undefined && raw !== "") {
|
|
66
|
+
const parsed = Number(raw);
|
|
67
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
68
|
+
return parsed;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return 30 * 60 * 1000;
|
|
72
|
+
}
|
|
73
|
+
function logRetryRoots(runId, size, action) {
|
|
74
|
+
console.info(JSON.stringify({ event: "retry_roots", runId, size, action }));
|
|
75
|
+
}
|
|
76
|
+
function createMutationQueue() {
|
|
77
|
+
const pendingDeltas = [];
|
|
78
|
+
const waiters = new Map();
|
|
79
|
+
const rejectPendingEnqueues = (reason) => {
|
|
80
|
+
pendingDeltas.length = 0;
|
|
81
|
+
for (const list of waiters.values()) {
|
|
82
|
+
for (const waiter of list) {
|
|
83
|
+
waiter.reject(reason);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
waiters.clear();
|
|
87
|
+
};
|
|
88
|
+
const mutationQueue = {
|
|
89
|
+
enqueue(stageId, attempt) {
|
|
90
|
+
return new Promise((resolve, reject) => {
|
|
91
|
+
pendingDeltas.push({ stageId, attempt });
|
|
92
|
+
const key = mutationKey(stageId, attempt);
|
|
93
|
+
const list = waiters.get(key) ?? [];
|
|
94
|
+
list.push({ resolve, reject });
|
|
95
|
+
waiters.set(key, list);
|
|
96
|
+
});
|
|
97
|
+
},
|
|
98
|
+
drainPending(apply) {
|
|
99
|
+
for (const delta of pendingDeltas.splice(0)) {
|
|
100
|
+
apply(delta.stageId, delta.attempt);
|
|
101
|
+
const key = mutationKey(delta.stageId, delta.attempt);
|
|
102
|
+
const list = waiters.get(key);
|
|
103
|
+
if (list !== undefined) {
|
|
104
|
+
for (const waiter of list) {
|
|
105
|
+
waiter.resolve();
|
|
106
|
+
}
|
|
107
|
+
waiters.delete(key);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
pendingStageIds() {
|
|
112
|
+
return new Set(pendingDeltas.map((delta) => delta.stageId));
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
return { mutationQueue, rejectPendingEnqueues };
|
|
116
|
+
}
|
|
117
|
+
export class RunRetryCoordinator {
|
|
118
|
+
sessions = new Map();
|
|
119
|
+
isActive(runId) {
|
|
120
|
+
return this.sessions.has(runId);
|
|
121
|
+
}
|
|
122
|
+
getOrchestrationPromise(runId) {
|
|
123
|
+
return this.sessions.get(runId)?.orchestrationPromise;
|
|
124
|
+
}
|
|
125
|
+
getActiveRoots(runId) {
|
|
126
|
+
return this.sessions.get(runId)?.activeRoots;
|
|
127
|
+
}
|
|
128
|
+
signalExternalRetryRootTerminal(runId, stageId) {
|
|
129
|
+
const session = this.sessions.get(runId);
|
|
130
|
+
if (session === undefined)
|
|
131
|
+
return;
|
|
132
|
+
const attempt = session.activeRoots.get(stageId);
|
|
133
|
+
if (attempt === undefined)
|
|
134
|
+
return;
|
|
135
|
+
const key = rootWaitKey(runId, stageId, attempt);
|
|
136
|
+
if (session.terminalRoots.has(key))
|
|
137
|
+
return;
|
|
138
|
+
session.terminalRoots.add(key);
|
|
139
|
+
const list = session.rootWaiters.get(key);
|
|
140
|
+
if (list === undefined)
|
|
141
|
+
return;
|
|
142
|
+
for (const waiter of list) {
|
|
143
|
+
clearTimeout(waiter.timer);
|
|
144
|
+
waiter.resolve();
|
|
145
|
+
}
|
|
146
|
+
session.rootWaiters.delete(key);
|
|
147
|
+
}
|
|
148
|
+
start(options) {
|
|
149
|
+
const { runId, roots } = options;
|
|
150
|
+
if (this.sessions.has(runId)) {
|
|
151
|
+
throw new Error(`Retry coordinator already active for run ${runId}`);
|
|
152
|
+
}
|
|
153
|
+
const activeRoots = new Map(roots);
|
|
154
|
+
const { mutationQueue, rejectPendingEnqueues } = createMutationQueue();
|
|
155
|
+
const store = options.prepared.store;
|
|
156
|
+
const terminalRoots = new Set();
|
|
157
|
+
const rootWaiters = new Map();
|
|
158
|
+
const resolveRootWaiters = (key) => {
|
|
159
|
+
const list = rootWaiters.get(key);
|
|
160
|
+
if (list === undefined)
|
|
161
|
+
return;
|
|
162
|
+
for (const waiter of list) {
|
|
163
|
+
clearTimeout(waiter.timer);
|
|
164
|
+
waiter.resolve();
|
|
165
|
+
}
|
|
166
|
+
rootWaiters.delete(key);
|
|
167
|
+
};
|
|
168
|
+
const rejectPendingRootWaiters = (reason) => {
|
|
169
|
+
for (const list of rootWaiters.values()) {
|
|
170
|
+
for (const waiter of list) {
|
|
171
|
+
clearTimeout(waiter.timer);
|
|
172
|
+
waiter.reject(reason);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
rootWaiters.clear();
|
|
176
|
+
};
|
|
177
|
+
const orchestrationPromise = retryRun({
|
|
178
|
+
prepared: options.prepared,
|
|
179
|
+
retryRoots: activeRoots,
|
|
180
|
+
maxActiveStagesPerRun: options.maxActiveStagesPerRun,
|
|
181
|
+
executionMode: options.executionMode,
|
|
182
|
+
stageProcessLauncher: options.stageProcessLauncher,
|
|
183
|
+
mutationQueue,
|
|
184
|
+
onLoopTick: options.onLoopTick,
|
|
185
|
+
onRetryRootTerminal: (stageId, attempt) => {
|
|
186
|
+
const key = rootWaitKey(runId, stageId, attempt);
|
|
187
|
+
terminalRoots.add(key);
|
|
188
|
+
resolveRootWaiters(key);
|
|
189
|
+
},
|
|
190
|
+
}).finally(async () => {
|
|
191
|
+
try {
|
|
192
|
+
await syncRunStatusFromStages(store, runId);
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
// ignore secondary failures
|
|
196
|
+
}
|
|
197
|
+
logRetryRoots(runId, activeRoots.size, "complete");
|
|
198
|
+
rejectPendingRootWaiters(new RetryCoordinatorInactiveError(runId));
|
|
199
|
+
rejectPendingEnqueues(new RetryCoordinatorInactiveError(runId));
|
|
200
|
+
this.sessions.delete(runId);
|
|
201
|
+
});
|
|
202
|
+
this.sessions.set(runId, {
|
|
203
|
+
activeRoots,
|
|
204
|
+
orchestrationPromise,
|
|
205
|
+
mutationQueue,
|
|
206
|
+
rejectPendingEnqueues,
|
|
207
|
+
terminalRoots,
|
|
208
|
+
rootWaiters,
|
|
209
|
+
rejectPendingRootWaiters,
|
|
210
|
+
});
|
|
211
|
+
logRetryRoots(runId, activeRoots.size, "start");
|
|
212
|
+
}
|
|
213
|
+
async addRoot(runId, stageId, attempt) {
|
|
214
|
+
const session = this.sessions.get(runId);
|
|
215
|
+
if (session === undefined) {
|
|
216
|
+
throw new RetryCoordinatorInactiveError(runId);
|
|
217
|
+
}
|
|
218
|
+
if (session.activeRoots.has(stageId)) {
|
|
219
|
+
throw new DuplicateRetryRootError(runId, stageId);
|
|
220
|
+
}
|
|
221
|
+
session.activeRoots.set(stageId, attempt);
|
|
222
|
+
await session.mutationQueue.enqueue(stageId, attempt);
|
|
223
|
+
logRetryRoots(runId, session.activeRoots.size, "add");
|
|
224
|
+
}
|
|
225
|
+
async retryStage(req) {
|
|
226
|
+
const { runId, stageId, store, tracking } = req;
|
|
227
|
+
let detail;
|
|
228
|
+
try {
|
|
229
|
+
detail = await store.readRun(runId);
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
return { ok: false, reason: `Run not found: ${runId}`, status: 404 };
|
|
233
|
+
}
|
|
234
|
+
const recoveryActive = this.isActive(runId);
|
|
235
|
+
const eligibility = assertStageRetryEligible(detail, stageId, {
|
|
236
|
+
recoveryActive,
|
|
237
|
+
});
|
|
238
|
+
if (!eligibility.ok) {
|
|
239
|
+
return {
|
|
240
|
+
ok: false,
|
|
241
|
+
reason: eligibility.reason,
|
|
242
|
+
status: eligibility.status,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
if (recoveryActive) {
|
|
246
|
+
if (this.getActiveRoots(runId)?.has(stageId)) {
|
|
247
|
+
return {
|
|
248
|
+
ok: false,
|
|
249
|
+
reason: `Retry already in progress for run ${runId} stage ${stageId}`,
|
|
250
|
+
status: 409,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if (!recoveryActive && req.orchestrationConflict) {
|
|
255
|
+
return {
|
|
256
|
+
ok: false,
|
|
257
|
+
reason: `Run ${runId} already has active orchestration`,
|
|
258
|
+
status: 409,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
const priorRunStatus = detail.status;
|
|
262
|
+
let insertedForResume = false;
|
|
263
|
+
let bumpedRunning = false;
|
|
264
|
+
try {
|
|
265
|
+
if (!recoveryActive) {
|
|
266
|
+
const tracked = await tracking.ensureResumeTracked(runId);
|
|
267
|
+
if (!tracked.ok) {
|
|
268
|
+
return {
|
|
269
|
+
ok: false,
|
|
270
|
+
reason: tracked.reason,
|
|
271
|
+
status: 409,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
insertedForResume = tracked.insertedForResume === true;
|
|
275
|
+
if (priorRunStatus === "failed") {
|
|
276
|
+
await store.updateRunStatus(runId, "running");
|
|
277
|
+
bumpedRunning = true;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
const existingAttempts = await store.countStageAttempts(runId, stageId);
|
|
281
|
+
if (existingAttempts === 0) {
|
|
282
|
+
const snap = detail.stages.find((s) => s.stage_id === stageId);
|
|
283
|
+
if (snap &&
|
|
284
|
+
(snap.status === "failed" || snap.status === "succeeded")) {
|
|
285
|
+
await store.createStageExecution(runId, stageId);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
const execution = await store.createStageExecution(runId, stageId);
|
|
289
|
+
const { meta, task, loaded, workspaceDir } = await loadRunContext(store, runId, req.cwd);
|
|
290
|
+
const prepared = {
|
|
291
|
+
task,
|
|
292
|
+
loaded,
|
|
293
|
+
run: { runId, workspaceDir },
|
|
294
|
+
agent: req.agent,
|
|
295
|
+
store,
|
|
296
|
+
cwd: req.cwd,
|
|
297
|
+
checkoutRoot: meta.checkout_root,
|
|
298
|
+
hitl: req.hitl,
|
|
299
|
+
operatorCatalog: req.operatorCatalog,
|
|
300
|
+
};
|
|
301
|
+
if (recoveryActive) {
|
|
302
|
+
await this.addRoot(runId, stageId, execution.attempt);
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
this.start({
|
|
306
|
+
runId,
|
|
307
|
+
roots: new Map([[stageId, execution.attempt]]),
|
|
308
|
+
prepared,
|
|
309
|
+
maxActiveStagesPerRun: req.maxActiveStagesPerRun,
|
|
310
|
+
executionMode: req.executionMode,
|
|
311
|
+
stageProcessLauncher: req.stageProcessLauncher,
|
|
312
|
+
});
|
|
313
|
+
const orchestrationPromise = this.getOrchestrationPromise(runId);
|
|
314
|
+
if (orchestrationPromise !== undefined) {
|
|
315
|
+
tracking.onOrchestrationStarted(runId, orchestrationPromise);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
await this.waitForRoot(runId, stageId, store);
|
|
319
|
+
return {
|
|
320
|
+
ok: true,
|
|
321
|
+
runId,
|
|
322
|
+
stageId,
|
|
323
|
+
attemptIndex: execution.attempt,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
catch (err) {
|
|
327
|
+
await tracking.rollbackStartTracking(runId, {
|
|
328
|
+
insertedForResume,
|
|
329
|
+
bumpedRunning,
|
|
330
|
+
priorRunStatus,
|
|
331
|
+
});
|
|
332
|
+
if (err instanceof DuplicateRetryRootError) {
|
|
333
|
+
return { ok: false, reason: err.message, status: 409 };
|
|
334
|
+
}
|
|
335
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
336
|
+
return { ok: false, reason, status: 500 };
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
async waitForRoot(runId, stageId, _store) {
|
|
340
|
+
const session = this.sessions.get(runId);
|
|
341
|
+
if (session === undefined) {
|
|
342
|
+
throw new RetryCoordinatorInactiveError(runId);
|
|
343
|
+
}
|
|
344
|
+
const expectedAttempt = session.activeRoots.get(stageId);
|
|
345
|
+
if (expectedAttempt === undefined) {
|
|
346
|
+
throw new Error(`Retry root not registered for stage ${stageId}`);
|
|
347
|
+
}
|
|
348
|
+
const key = rootWaitKey(runId, stageId, expectedAttempt);
|
|
349
|
+
if (session.terminalRoots.has(key)) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
return new Promise((resolve, reject) => {
|
|
353
|
+
const timer = setTimeout(() => {
|
|
354
|
+
const list = session.rootWaiters.get(key);
|
|
355
|
+
if (list !== undefined) {
|
|
356
|
+
const remaining = list.filter((waiter) => waiter.timer !== timer);
|
|
357
|
+
if (remaining.length === 0) {
|
|
358
|
+
session.rootWaiters.delete(key);
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
session.rootWaiters.set(key, remaining);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
reject(new RetryRootWaitTimeoutError(runId, stageId, expectedAttempt));
|
|
365
|
+
}, readRetryRootWaitTimeoutMs());
|
|
366
|
+
const waiter = { resolve, reject, timer };
|
|
367
|
+
const list = session.rootWaiters.get(key) ?? [];
|
|
368
|
+
list.push(waiter);
|
|
369
|
+
session.rootWaiters.set(key, list);
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare const SETTINGS_FILE_NAME = "settings.json";
|
|
2
|
+
export declare const INVALID_SLOT_COUNT_MESSAGE = "maxConcurrent must be an integer >= 1";
|
|
3
|
+
export declare const INVALID_CREDENTIAL_SOURCE_MESSAGE = "credentialSource must be \"pi_home\" or \"sf_owned\"";
|
|
4
|
+
export type CredentialSource = "pi_home" | "sf_owned";
|
|
5
|
+
export type FactorySettings = {
|
|
6
|
+
maxConcurrent?: number;
|
|
7
|
+
credentialSource?: CredentialSource;
|
|
8
|
+
};
|
|
9
|
+
export declare function settingsFilePath(cwd: string): string;
|
|
10
|
+
export declare function parseSlotCount(value: unknown): number | undefined;
|
|
11
|
+
export declare function parseCredentialSource(value: unknown): CredentialSource | undefined;
|
|
12
|
+
export declare function readFactorySettings(cwd: string): FactorySettings;
|
|
13
|
+
export declare function writeFactorySettings(cwd: string, patch: FactorySettings): void;
|
|
14
|
+
export declare function readMaxConcurrentFromFile(cwd: string): number | undefined;
|
|
15
|
+
export declare function writeMaxConcurrentToFile(cwd: string, maxConcurrent: number): void;
|
|
16
|
+
export declare function readCredentialSourceFromFile(cwd: string): CredentialSource | undefined;
|
|
17
|
+
export declare function writeCredentialSourceToFile(cwd: string, credentialSource: CredentialSource): void;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { storeRootFor } from "../runstore/paths.js";
|
|
4
|
+
export const SETTINGS_FILE_NAME = "settings.json";
|
|
5
|
+
export const INVALID_SLOT_COUNT_MESSAGE = "maxConcurrent must be an integer >= 1";
|
|
6
|
+
export const INVALID_CREDENTIAL_SOURCE_MESSAGE = 'credentialSource must be "pi_home" or "sf_owned"';
|
|
7
|
+
export function settingsFilePath(cwd) {
|
|
8
|
+
return path.join(storeRootFor(cwd), SETTINGS_FILE_NAME);
|
|
9
|
+
}
|
|
10
|
+
export function parseSlotCount(value) {
|
|
11
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 1) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
export function parseCredentialSource(value) {
|
|
17
|
+
if (value === "pi_home" || value === "sf_owned") {
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
function readRawSettings(cwd) {
|
|
23
|
+
const filePath = settingsFilePath(cwd);
|
|
24
|
+
if (!existsSync(filePath))
|
|
25
|
+
return {};
|
|
26
|
+
try {
|
|
27
|
+
const raw = JSON.parse(readFileSync(filePath, "utf8"));
|
|
28
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
29
|
+
return {};
|
|
30
|
+
}
|
|
31
|
+
return raw;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return {};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export function readFactorySettings(cwd) {
|
|
38
|
+
const raw = readRawSettings(cwd);
|
|
39
|
+
const settings = {};
|
|
40
|
+
const maxConcurrent = parseSlotCount(raw.maxConcurrent);
|
|
41
|
+
if (maxConcurrent !== undefined) {
|
|
42
|
+
settings.maxConcurrent = maxConcurrent;
|
|
43
|
+
}
|
|
44
|
+
const credentialSource = parseCredentialSource(raw.credentialSource);
|
|
45
|
+
if (credentialSource !== undefined) {
|
|
46
|
+
settings.credentialSource = credentialSource;
|
|
47
|
+
}
|
|
48
|
+
return settings;
|
|
49
|
+
}
|
|
50
|
+
export function writeFactorySettings(cwd, patch) {
|
|
51
|
+
if (patch.credentialSource !== undefined &&
|
|
52
|
+
parseCredentialSource(patch.credentialSource) === undefined) {
|
|
53
|
+
throw new Error(INVALID_CREDENTIAL_SOURCE_MESSAGE);
|
|
54
|
+
}
|
|
55
|
+
if (patch.maxConcurrent !== undefined &&
|
|
56
|
+
parseSlotCount(patch.maxConcurrent) === undefined) {
|
|
57
|
+
throw new Error(INVALID_SLOT_COUNT_MESSAGE);
|
|
58
|
+
}
|
|
59
|
+
const current = readFactorySettings(cwd);
|
|
60
|
+
const next = { ...current };
|
|
61
|
+
if (patch.maxConcurrent !== undefined) {
|
|
62
|
+
next.maxConcurrent = patch.maxConcurrent;
|
|
63
|
+
}
|
|
64
|
+
if (patch.credentialSource !== undefined) {
|
|
65
|
+
next.credentialSource = patch.credentialSource;
|
|
66
|
+
}
|
|
67
|
+
const out = {};
|
|
68
|
+
if (next.maxConcurrent !== undefined) {
|
|
69
|
+
out.maxConcurrent = next.maxConcurrent;
|
|
70
|
+
}
|
|
71
|
+
if (next.credentialSource !== undefined) {
|
|
72
|
+
out.credentialSource = next.credentialSource;
|
|
73
|
+
}
|
|
74
|
+
mkdirSync(storeRootFor(cwd), { recursive: true });
|
|
75
|
+
writeFileSync(settingsFilePath(cwd), `${JSON.stringify(out, null, 2)}\n`, "utf8");
|
|
76
|
+
}
|
|
77
|
+
export function readMaxConcurrentFromFile(cwd) {
|
|
78
|
+
return readFactorySettings(cwd).maxConcurrent;
|
|
79
|
+
}
|
|
80
|
+
export function writeMaxConcurrentToFile(cwd, maxConcurrent) {
|
|
81
|
+
writeFactorySettings(cwd, { maxConcurrent });
|
|
82
|
+
}
|
|
83
|
+
export function readCredentialSourceFromFile(cwd) {
|
|
84
|
+
return readFactorySettings(cwd).credentialSource;
|
|
85
|
+
}
|
|
86
|
+
export function writeCredentialSourceToFile(cwd, credentialSource) {
|
|
87
|
+
writeFactorySettings(cwd, { credentialSource });
|
|
88
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { AgentPort, StageHandle, StageRunInput } from "../agent/port.js";
|
|
2
|
+
import type { RunStore } from "../runstore/port.js";
|
|
3
|
+
import type { StageEnvelope } from "../types/envelope.js";
|
|
4
|
+
import type { ResolvedPipelineDag } from "../types/pipeline.js";
|
|
5
|
+
import type { StageConfig } from "../types/stage.js";
|
|
6
|
+
import type { TaskFile } from "../types/task.js";
|
|
7
|
+
import { type StageAttemptContext } from "./stageAttemptContext.js";
|
|
8
|
+
import { type StageRoots } from "./stageRoots.js";
|
|
9
|
+
export type OperatorCatalog = {
|
|
10
|
+
cwd?: string;
|
|
11
|
+
agentDir?: string;
|
|
12
|
+
};
|
|
13
|
+
export type StageAttemptOpenInput = {
|
|
14
|
+
agent: Pick<AgentPort, "openStage">;
|
|
15
|
+
store: RunStore;
|
|
16
|
+
runId: string;
|
|
17
|
+
stage: StageConfig;
|
|
18
|
+
task: TaskFile;
|
|
19
|
+
dag: ResolvedPipelineDag;
|
|
20
|
+
checkoutRoot?: string;
|
|
21
|
+
workspaceDir: string;
|
|
22
|
+
factoryCwd?: string;
|
|
23
|
+
attemptCtx?: StageAttemptContext;
|
|
24
|
+
operatorCatalog?: OperatorCatalog;
|
|
25
|
+
onActivity?: StageRunInput["onActivity"];
|
|
26
|
+
roots?: StageRoots;
|
|
27
|
+
resumeToken?: string;
|
|
28
|
+
workerRoots?: boolean;
|
|
29
|
+
completedEnvelopes?: Map<string, StageEnvelope>;
|
|
30
|
+
};
|
|
31
|
+
export type StageAttemptOpenResult = {
|
|
32
|
+
ok: true;
|
|
33
|
+
handle: StageHandle;
|
|
34
|
+
roots: StageRoots;
|
|
35
|
+
prior: StageEnvelope | null;
|
|
36
|
+
} | {
|
|
37
|
+
ok: false;
|
|
38
|
+
reason: string;
|
|
39
|
+
};
|
|
40
|
+
export declare function openStageAttempt(input: StageAttemptOpenInput): Promise<StageAttemptOpenResult>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { resolveSkillByName } from "../config/listSkills.js";
|
|
2
|
+
import { buildCompletedEnvelopesFromRun, resolvePriorEnvelope, } from "./envelopeRouting.js";
|
|
3
|
+
import { resumeSessionFilePath } from "./stageAttemptContext.js";
|
|
4
|
+
import { buildStageRoots, rootsForStageWorker, withResolvedAuthPath, } from "./stageRoots.js";
|
|
5
|
+
async function resolveStageSkillForRun(stage, catalog) {
|
|
6
|
+
const name = stage.skill;
|
|
7
|
+
if (name === undefined)
|
|
8
|
+
return { ok: true };
|
|
9
|
+
if (catalog?.agentDir === undefined) {
|
|
10
|
+
return { ok: false, reason: `Skill "${name}" is not installed` };
|
|
11
|
+
}
|
|
12
|
+
const resolved = await resolveSkillByName(name, {
|
|
13
|
+
cwd: catalog.cwd ?? process.cwd(),
|
|
14
|
+
agentDir: catalog.agentDir,
|
|
15
|
+
});
|
|
16
|
+
if (!resolved) {
|
|
17
|
+
return { ok: false, reason: `Skill "${name}" is not installed` };
|
|
18
|
+
}
|
|
19
|
+
return { ok: true, skillFilePath: resolved.filePath };
|
|
20
|
+
}
|
|
21
|
+
async function openStageWithOperatorCatalog(agent, input, catalog) {
|
|
22
|
+
const skill = await resolveStageSkillForRun(input.stage, catalog);
|
|
23
|
+
if (!skill.ok)
|
|
24
|
+
return skill;
|
|
25
|
+
try {
|
|
26
|
+
const handle = agent.openStage({
|
|
27
|
+
...input,
|
|
28
|
+
...(skill.skillFilePath !== undefined
|
|
29
|
+
? { skillFilePath: skill.skillFilePath }
|
|
30
|
+
: {}),
|
|
31
|
+
});
|
|
32
|
+
return { ok: true, handle };
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
return {
|
|
36
|
+
ok: false,
|
|
37
|
+
reason: err instanceof Error ? err.message : String(err),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function resolveAttemptRoots(input) {
|
|
42
|
+
const baseRoots = input.roots ??
|
|
43
|
+
(input.workerRoots
|
|
44
|
+
? rootsForStageWorker(input.workspaceDir, input.stage.id, input.stage.model, input.checkoutRoot, input.attemptCtx)
|
|
45
|
+
: buildStageRoots(input.workspaceDir, input.stage.id, input.checkoutRoot, input.attemptCtx));
|
|
46
|
+
return input.factoryCwd !== undefined
|
|
47
|
+
? withResolvedAuthPath(baseRoots, input.factoryCwd)
|
|
48
|
+
: baseRoots;
|
|
49
|
+
}
|
|
50
|
+
export async function openStageAttempt(input) {
|
|
51
|
+
const completedEnvelopes = input.completedEnvelopes ??
|
|
52
|
+
(await buildCompletedEnvelopesFromRun(input.store, input.runId));
|
|
53
|
+
const priorResult = await resolvePriorEnvelope({
|
|
54
|
+
dag: input.dag,
|
|
55
|
+
stageId: input.stage.id,
|
|
56
|
+
completedEnvelopes,
|
|
57
|
+
store: input.store,
|
|
58
|
+
runId: input.runId,
|
|
59
|
+
});
|
|
60
|
+
if (!priorResult.ok)
|
|
61
|
+
return priorResult;
|
|
62
|
+
const roots = resolveAttemptRoots(input);
|
|
63
|
+
const attempt = input.attemptCtx?.attempt ?? 1;
|
|
64
|
+
const resumeToken = input.resumeToken ??
|
|
65
|
+
resumeSessionFilePath(input.workspaceDir, input.stage.id, attempt);
|
|
66
|
+
const opened = await openStageWithOperatorCatalog(input.agent, {
|
|
67
|
+
roots,
|
|
68
|
+
stage: input.stage,
|
|
69
|
+
task: input.task,
|
|
70
|
+
priorEnvelope: priorResult.prior,
|
|
71
|
+
resumeToken,
|
|
72
|
+
onActivity: input.onActivity,
|
|
73
|
+
}, input.operatorCatalog);
|
|
74
|
+
if (!opened.ok)
|
|
75
|
+
return opened;
|
|
76
|
+
return {
|
|
77
|
+
ok: true,
|
|
78
|
+
handle: opened.handle,
|
|
79
|
+
roots,
|
|
80
|
+
prior: priorResult.prior,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type StageAttemptContext = {
|
|
2
|
+
readonly attempt: number;
|
|
3
|
+
eventOptions(): {
|
|
4
|
+
attempt: number;
|
|
5
|
+
} | undefined;
|
|
6
|
+
sessionPath(workspaceDir: string, stageId: string): string;
|
|
7
|
+
agentDirPath(workspaceDir: string, stageId: string): string;
|
|
8
|
+
};
|
|
9
|
+
export declare function attemptContext(attempt: number): StageAttemptContext;
|
|
10
|
+
export declare function noAttemptContext(): StageAttemptContext;
|
|
11
|
+
export declare function resumeSessionFilePath(workspaceDir: string, stageId: string, attempt: number): string;
|