agent-relay 3.2.22 → 4.0.1
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 +5 -5
- package/bin/agent-relay-broker-darwin-arm64 +0 -0
- package/bin/agent-relay-broker-darwin-x64 +0 -0
- package/bin/agent-relay-broker-linux-arm64 +0 -0
- package/bin/agent-relay-broker-linux-x64 +0 -0
- package/dist/index.cjs +6564 -2100
- package/dist/src/cli/bootstrap.d.ts.map +1 -1
- package/dist/src/cli/bootstrap.js +2 -0
- package/dist/src/cli/bootstrap.js.map +1 -1
- package/dist/src/cli/commands/agent-management.d.ts.map +1 -1
- package/dist/src/cli/commands/agent-management.js +14 -4
- package/dist/src/cli/commands/agent-management.js.map +1 -1
- package/dist/src/cli/commands/core.d.ts +2 -6
- package/dist/src/cli/commands/core.d.ts.map +1 -1
- package/dist/src/cli/commands/core.js +31 -12
- package/dist/src/cli/commands/core.js.map +1 -1
- package/dist/src/cli/commands/messaging.d.ts.map +1 -1
- package/dist/src/cli/commands/messaging.js +10 -3
- package/dist/src/cli/commands/messaging.js.map +1 -1
- package/dist/src/cli/commands/monitoring.d.ts +2 -2
- package/dist/src/cli/commands/monitoring.d.ts.map +1 -1
- package/dist/src/cli/commands/monitoring.js +15 -6
- package/dist/src/cli/commands/monitoring.js.map +1 -1
- package/dist/src/cli/commands/on/dotfiles.d.ts +35 -0
- package/dist/src/cli/commands/on/dotfiles.d.ts.map +1 -0
- package/dist/src/cli/commands/on/dotfiles.js +157 -0
- package/dist/src/cli/commands/on/dotfiles.js.map +1 -0
- package/dist/src/cli/commands/on/prereqs.d.ts +15 -0
- package/dist/src/cli/commands/on/prereqs.d.ts.map +1 -0
- package/dist/src/cli/commands/on/prereqs.js +103 -0
- package/dist/src/cli/commands/on/prereqs.js.map +1 -0
- package/dist/src/cli/commands/on/provision.d.ts +22 -0
- package/dist/src/cli/commands/on/provision.d.ts.map +1 -0
- package/dist/src/cli/commands/on/provision.js +157 -0
- package/dist/src/cli/commands/on/provision.js.map +1 -0
- package/dist/src/cli/commands/on/relayfile-binary.d.ts +2 -0
- package/dist/src/cli/commands/on/relayfile-binary.d.ts.map +1 -0
- package/dist/src/cli/commands/on/relayfile-binary.js +208 -0
- package/dist/src/cli/commands/on/relayfile-binary.js.map +1 -0
- package/dist/src/cli/commands/on/scan.d.ts +8 -0
- package/dist/src/cli/commands/on/scan.d.ts.map +1 -0
- package/dist/src/cli/commands/on/scan.js +59 -0
- package/dist/src/cli/commands/on/scan.js.map +1 -0
- package/dist/src/cli/commands/on/services.d.ts +17 -0
- package/dist/src/cli/commands/on/services.d.ts.map +1 -0
- package/dist/src/cli/commands/on/services.js +328 -0
- package/dist/src/cli/commands/on/services.js.map +1 -0
- package/dist/src/cli/commands/on/start.d.ts +61 -0
- package/dist/src/cli/commands/on/start.d.ts.map +1 -0
- package/dist/src/cli/commands/on/start.js +1107 -0
- package/dist/src/cli/commands/on/start.js.map +1 -0
- package/dist/src/cli/commands/on/stop.d.ts +4 -0
- package/dist/src/cli/commands/on/stop.d.ts.map +1 -0
- package/dist/src/cli/commands/on/stop.js +11 -0
- package/dist/src/cli/commands/on/stop.js.map +1 -0
- package/dist/src/cli/commands/on/token.d.ts +8 -0
- package/dist/src/cli/commands/on/token.d.ts.map +1 -0
- package/dist/src/cli/commands/on/token.js +26 -0
- package/dist/src/cli/commands/on/token.js.map +1 -0
- package/dist/src/cli/commands/on/workspace.d.ts +4 -0
- package/dist/src/cli/commands/on/workspace.d.ts.map +1 -0
- package/dist/src/cli/commands/on/workspace.js +245 -0
- package/dist/src/cli/commands/on/workspace.js.map +1 -0
- package/dist/src/cli/commands/on.d.ts +10 -0
- package/dist/src/cli/commands/on.d.ts.map +1 -0
- package/dist/src/cli/commands/on.js +52 -0
- package/dist/src/cli/commands/on.js.map +1 -0
- package/dist/src/cli/commands/setup.d.ts.map +1 -1
- package/dist/src/cli/commands/setup.js +10 -21
- package/dist/src/cli/commands/setup.js.map +1 -1
- package/dist/src/cli/lib/bridge.js +1 -1
- package/dist/src/cli/lib/bridge.js.map +1 -1
- package/dist/src/cli/lib/broker-lifecycle.d.ts +14 -4
- package/dist/src/cli/lib/broker-lifecycle.d.ts.map +1 -1
- package/dist/src/cli/lib/broker-lifecycle.js +82 -120
- package/dist/src/cli/lib/broker-lifecycle.js.map +1 -1
- package/dist/src/cli/lib/client-factory.d.ts +4 -4
- package/dist/src/cli/lib/client-factory.d.ts.map +1 -1
- package/dist/src/cli/lib/client-factory.js +14 -11
- package/dist/src/cli/lib/client-factory.js.map +1 -1
- package/dist/src/cli/lib/core-maintenance.d.ts.map +1 -1
- package/dist/src/cli/lib/core-maintenance.js +11 -22
- package/dist/src/cli/lib/core-maintenance.js.map +1 -1
- package/dist/src/cost/pricing.d.ts +18 -0
- package/dist/src/cost/pricing.d.ts.map +1 -0
- package/dist/src/cost/pricing.js +111 -0
- package/dist/src/cost/pricing.js.map +1 -0
- package/dist/src/cost/tracker.d.ts +13 -0
- package/dist/src/cost/tracker.d.ts.map +1 -0
- package/dist/src/cost/tracker.js +152 -0
- package/dist/src/cost/tracker.js.map +1 -0
- package/dist/src/cost/types.d.ts +23 -0
- package/dist/src/cost/types.d.ts.map +1 -0
- package/dist/src/cost/types.js +2 -0
- package/dist/src/cost/types.js.map +1 -0
- package/package.json +15 -12
- package/packages/acp-bridge/package.json +2 -2
- package/packages/brand/package.json +1 -1
- package/packages/cloud/package.json +3 -3
- package/packages/config/package.json +1 -1
- package/packages/hooks/package.json +4 -4
- package/packages/memory/package.json +2 -2
- package/packages/openclaw/package.json +2 -2
- package/packages/policy/package.json +2 -2
- package/packages/sdk/README.md +10 -3
- package/packages/sdk/dist/broker-path.d.ts +3 -2
- package/packages/sdk/dist/broker-path.d.ts.map +1 -1
- package/packages/sdk/dist/broker-path.js +119 -32
- package/packages/sdk/dist/broker-path.js.map +1 -1
- package/packages/sdk/dist/client.d.ts +119 -197
- package/packages/sdk/dist/client.d.ts.map +1 -1
- package/packages/sdk/dist/client.js +354 -823
- package/packages/sdk/dist/client.js.map +1 -1
- package/packages/sdk/dist/examples/example.js +2 -5
- package/packages/sdk/dist/examples/example.js.map +1 -1
- package/packages/sdk/dist/index.d.ts +3 -1
- package/packages/sdk/dist/index.d.ts.map +1 -1
- package/packages/sdk/dist/index.js +3 -1
- package/packages/sdk/dist/index.js.map +1 -1
- package/packages/sdk/dist/relay-adapter.d.ts +9 -26
- package/packages/sdk/dist/relay-adapter.d.ts.map +1 -1
- package/packages/sdk/dist/relay-adapter.js +75 -47
- package/packages/sdk/dist/relay-adapter.js.map +1 -1
- package/packages/sdk/dist/relay.d.ts +26 -6
- package/packages/sdk/dist/relay.d.ts.map +1 -1
- package/packages/sdk/dist/relay.js +213 -43
- package/packages/sdk/dist/relay.js.map +1 -1
- package/packages/sdk/dist/transport.d.ts +58 -0
- package/packages/sdk/dist/transport.d.ts.map +1 -0
- package/packages/sdk/dist/transport.js +184 -0
- package/packages/sdk/dist/transport.js.map +1 -0
- package/packages/sdk/dist/types.d.ts +69 -0
- package/packages/sdk/dist/types.d.ts.map +1 -0
- package/packages/sdk/dist/types.js +5 -0
- package/packages/sdk/dist/types.js.map +1 -0
- package/packages/sdk/dist/workflows/__tests__/channel-messenger.test.d.ts +2 -0
- package/packages/sdk/dist/workflows/__tests__/channel-messenger.test.d.ts.map +1 -0
- package/packages/sdk/dist/workflows/__tests__/channel-messenger.test.js +117 -0
- package/packages/sdk/dist/workflows/__tests__/channel-messenger.test.js.map +1 -0
- package/packages/sdk/dist/workflows/__tests__/run-summary-table.test.js +4 -3
- package/packages/sdk/dist/workflows/__tests__/run-summary-table.test.js.map +1 -1
- package/packages/sdk/dist/workflows/__tests__/step-executor.test.d.ts +2 -0
- package/packages/sdk/dist/workflows/__tests__/step-executor.test.d.ts.map +1 -0
- package/packages/sdk/dist/workflows/__tests__/step-executor.test.js +378 -0
- package/packages/sdk/dist/workflows/__tests__/step-executor.test.js.map +1 -0
- package/packages/sdk/dist/workflows/__tests__/template-resolver.test.d.ts +2 -0
- package/packages/sdk/dist/workflows/__tests__/template-resolver.test.d.ts.map +1 -0
- package/packages/sdk/dist/workflows/__tests__/template-resolver.test.js +145 -0
- package/packages/sdk/dist/workflows/__tests__/template-resolver.test.js.map +1 -0
- package/packages/sdk/dist/workflows/__tests__/verification.test.d.ts +2 -0
- package/packages/sdk/dist/workflows/__tests__/verification.test.d.ts.map +1 -0
- package/packages/sdk/dist/workflows/__tests__/verification.test.js +170 -0
- package/packages/sdk/dist/workflows/__tests__/verification.test.js.map +1 -0
- package/packages/sdk/dist/workflows/builder.d.ts +3 -2
- package/packages/sdk/dist/workflows/builder.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/builder.js +1 -3
- package/packages/sdk/dist/workflows/builder.js.map +1 -1
- package/packages/sdk/dist/workflows/channel-messenger.d.ts +28 -0
- package/packages/sdk/dist/workflows/channel-messenger.d.ts.map +1 -0
- package/packages/sdk/dist/workflows/channel-messenger.js +255 -0
- package/packages/sdk/dist/workflows/channel-messenger.js.map +1 -0
- package/packages/sdk/dist/workflows/index.d.ts +7 -0
- package/packages/sdk/dist/workflows/index.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/index.js +7 -0
- package/packages/sdk/dist/workflows/index.js.map +1 -1
- package/packages/sdk/dist/workflows/process-spawner.d.ts +35 -0
- package/packages/sdk/dist/workflows/process-spawner.d.ts.map +1 -0
- package/packages/sdk/dist/workflows/process-spawner.js +141 -0
- package/packages/sdk/dist/workflows/process-spawner.js.map +1 -0
- package/packages/sdk/dist/workflows/run.d.ts +2 -1
- package/packages/sdk/dist/workflows/run.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/run.js.map +1 -1
- package/packages/sdk/dist/workflows/runner.d.ts +6 -6
- package/packages/sdk/dist/workflows/runner.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/runner.js +443 -719
- package/packages/sdk/dist/workflows/runner.js.map +1 -1
- package/packages/sdk/dist/workflows/step-executor.d.ts +95 -0
- package/packages/sdk/dist/workflows/step-executor.d.ts.map +1 -0
- package/packages/sdk/dist/workflows/step-executor.js +393 -0
- package/packages/sdk/dist/workflows/step-executor.js.map +1 -0
- package/packages/sdk/dist/workflows/template-resolver.d.ts +33 -0
- package/packages/sdk/dist/workflows/template-resolver.d.ts.map +1 -0
- package/packages/sdk/dist/workflows/template-resolver.js +144 -0
- package/packages/sdk/dist/workflows/template-resolver.js.map +1 -0
- package/packages/sdk/dist/workflows/validator.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/validator.js +17 -2
- package/packages/sdk/dist/workflows/validator.js.map +1 -1
- package/packages/sdk/dist/workflows/verification.d.ts +33 -0
- package/packages/sdk/dist/workflows/verification.d.ts.map +1 -0
- package/packages/sdk/dist/workflows/verification.js +122 -0
- package/packages/sdk/dist/workflows/verification.js.map +1 -0
- package/packages/sdk/package.json +2 -2
- package/packages/sdk/src/__tests__/unit.test.ts +100 -1
- package/packages/sdk/src/broker-path.ts +136 -30
- package/packages/sdk/src/client.ts +453 -1069
- package/packages/sdk/src/examples/example.ts +2 -5
- package/packages/sdk/src/index.ts +9 -1
- package/packages/sdk/src/relay-adapter.ts +75 -55
- package/packages/sdk/src/relay.ts +262 -55
- package/packages/sdk/src/transport.ts +216 -0
- package/packages/sdk/src/types.ts +75 -0
- package/packages/sdk/src/workflows/__tests__/channel-messenger.test.ts +137 -0
- package/packages/sdk/src/workflows/__tests__/run-summary-table.test.ts +4 -3
- package/packages/sdk/src/workflows/__tests__/step-executor.test.ts +444 -0
- package/packages/sdk/src/workflows/__tests__/template-resolver.test.ts +162 -0
- package/packages/sdk/src/workflows/__tests__/verification.test.ts +229 -0
- package/packages/sdk/src/workflows/builder.ts +6 -6
- package/packages/sdk/src/workflows/channel-messenger.ts +314 -0
- package/packages/sdk/src/workflows/index.ts +12 -0
- package/packages/sdk/src/workflows/process-spawner.ts +201 -0
- package/packages/sdk/src/workflows/run.ts +2 -1
- package/packages/sdk/src/workflows/runner.ts +636 -951
- package/packages/sdk/src/workflows/step-executor.ts +579 -0
- package/packages/sdk/src/workflows/template-resolver.ts +180 -0
- package/packages/sdk/src/workflows/validator.ts +20 -2
- package/packages/sdk/src/workflows/verification.ts +184 -0
- package/packages/sdk-py/pyproject.toml +1 -1
- package/packages/sdk-py/src/agent_relay/__init__.py +0 -8
- package/packages/sdk-py/src/agent_relay/client.py +329 -522
- package/packages/sdk-py/src/agent_relay/protocol.py +2 -96
- package/packages/sdk-py/src/agent_relay/relay.py +1 -4
- package/packages/sdk-py/tests/test_wait_for_api_url.py +92 -0
- package/packages/sdk-py/uv.lock +5388 -0
- package/packages/telemetry/dist/client.d.ts.map +1 -1
- package/packages/telemetry/dist/client.js +1 -1
- package/packages/telemetry/dist/client.js.map +1 -1
- package/packages/telemetry/package.json +1 -1
- package/packages/telemetry/src/client.ts +3 -10
- package/packages/trajectory/package.json +2 -2
- package/packages/user-directory/package.json +2 -2
- package/packages/utils/package.json +2 -2
- package/scripts/postinstall.js +121 -1
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
import { ChannelMessenger } from './channel-messenger.js';
|
|
2
|
+
import type { ProcessSpawner } from './process-spawner.js';
|
|
3
|
+
import { TemplateResolver } from './template-resolver.js';
|
|
4
|
+
import type { StepOutcome } from './trajectory.js';
|
|
5
|
+
import type {
|
|
6
|
+
AgentDefinition,
|
|
7
|
+
ErrorHandlingConfig,
|
|
8
|
+
StepCompletionMode,
|
|
9
|
+
VerificationCheck,
|
|
10
|
+
WorkflowStep,
|
|
11
|
+
WorkflowStepCompletionReason,
|
|
12
|
+
WorkflowStepRow,
|
|
13
|
+
WorkflowStepStatus,
|
|
14
|
+
} from './types.js';
|
|
15
|
+
import {
|
|
16
|
+
runVerification,
|
|
17
|
+
type VerificationOptions,
|
|
18
|
+
type VerificationResult,
|
|
19
|
+
type VerificationSideEffects,
|
|
20
|
+
} from './verification.js';
|
|
21
|
+
|
|
22
|
+
type StateLike = {
|
|
23
|
+
row: WorkflowStepRow;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export interface StepResult {
|
|
27
|
+
status: WorkflowStepStatus;
|
|
28
|
+
output: string;
|
|
29
|
+
exitCode?: number;
|
|
30
|
+
exitSignal?: string;
|
|
31
|
+
duration: number;
|
|
32
|
+
retries: number;
|
|
33
|
+
completionReason?: WorkflowStepCompletionReason;
|
|
34
|
+
error?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface StepSchedule {
|
|
38
|
+
step: WorkflowStep;
|
|
39
|
+
readyAt: number;
|
|
40
|
+
staggerDelay: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface StepExecutorDeps<TState extends StateLike = StateLike> {
|
|
44
|
+
cwd: string;
|
|
45
|
+
runId?: string;
|
|
46
|
+
postToChannel?: (text: string) => void;
|
|
47
|
+
persistStepRow?: (stepId: string, patch: Partial<WorkflowStepRow>) => Promise<void>;
|
|
48
|
+
persistStepOutput?: (runId: string, stepName: string, output: string) => Promise<void>;
|
|
49
|
+
resolveTemplate?: (template: string, context: Record<string, unknown>) => string;
|
|
50
|
+
getStepOutput?: (stepName: string) => string | undefined;
|
|
51
|
+
loadStepOutput?: (runId: string, stepName: string) => string | undefined;
|
|
52
|
+
checkAborted?: () => void;
|
|
53
|
+
waitIfPaused?: () => Promise<void>;
|
|
54
|
+
log?: (message: string) => void;
|
|
55
|
+
processSpawner?: ProcessSpawner;
|
|
56
|
+
templateResolver?: TemplateResolver;
|
|
57
|
+
channelMessenger?: ChannelMessenger;
|
|
58
|
+
verificationRunner?: (
|
|
59
|
+
check: VerificationCheck,
|
|
60
|
+
output: string,
|
|
61
|
+
stepName: string,
|
|
62
|
+
injectedTaskText?: string,
|
|
63
|
+
options?: VerificationOptions,
|
|
64
|
+
sideEffects?: VerificationSideEffects
|
|
65
|
+
) => VerificationResult;
|
|
66
|
+
executeStep?: (
|
|
67
|
+
step: WorkflowStep,
|
|
68
|
+
state: TState,
|
|
69
|
+
agentMap: Map<string, AgentDefinition>,
|
|
70
|
+
errorHandling?: ErrorHandlingConfig
|
|
71
|
+
) => Promise<Partial<StepResult> | void>;
|
|
72
|
+
onStepStarted?: (step: WorkflowStep, state: TState) => Promise<void> | void;
|
|
73
|
+
onStepRetried?: (
|
|
74
|
+
step: WorkflowStep,
|
|
75
|
+
state: TState,
|
|
76
|
+
attempt: number,
|
|
77
|
+
maxRetries: number
|
|
78
|
+
) => Promise<void> | void;
|
|
79
|
+
onStepCompleted?: (step: WorkflowStep, state: TState, result: StepResult) => Promise<void> | void;
|
|
80
|
+
onStepFailed?: (step: WorkflowStep, state: TState, result: StepResult) => Promise<void> | void;
|
|
81
|
+
onBeginTrack?: (steps: WorkflowStep[]) => Promise<void> | void;
|
|
82
|
+
onConverge?: (steps: WorkflowStep[], outcomes: StepOutcome[]) => Promise<void> | void;
|
|
83
|
+
markDownstreamSkipped?: (failedStepName: string) => Promise<void>;
|
|
84
|
+
buildCompletionMode?: (
|
|
85
|
+
stepName: string,
|
|
86
|
+
completionReason?: WorkflowStepCompletionReason
|
|
87
|
+
) => StepCompletionMode | undefined;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface MonitorStepOptions<TState extends StateLike, TResult> {
|
|
91
|
+
maxRetries?: number;
|
|
92
|
+
retryDelayMs?: number;
|
|
93
|
+
startMessage?: string;
|
|
94
|
+
onStart?: (attempt: number, state: TState) => Promise<void> | void;
|
|
95
|
+
onRetry?: (attempt: number, maxRetries: number, state: TState) => Promise<void> | void;
|
|
96
|
+
execute: (attempt: number, state: TState) => Promise<TResult>;
|
|
97
|
+
toCompletionResult: (result: TResult, attempt: number, state: TState) => Partial<StepResult>;
|
|
98
|
+
onAttemptFailed?: (error: unknown, attempt: number, state: TState) => Promise<void> | void;
|
|
99
|
+
getFailureResult?: (error: unknown, attempt: number, state: TState) => Partial<StepResult>;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export class StepExecutor<TState extends StateLike = StateLike> {
|
|
103
|
+
private readonly templateResolver: TemplateResolver;
|
|
104
|
+
private readonly channelMessenger: ChannelMessenger;
|
|
105
|
+
private readonly verificationRunner: NonNullable<StepExecutorDeps<TState>['verificationRunner']>;
|
|
106
|
+
|
|
107
|
+
constructor(private readonly deps: StepExecutorDeps<TState>) {
|
|
108
|
+
this.templateResolver = deps.templateResolver ?? new TemplateResolver();
|
|
109
|
+
this.channelMessenger = deps.channelMessenger ?? new ChannelMessenger({ postFn: deps.postToChannel });
|
|
110
|
+
this.verificationRunner = deps.verificationRunner ?? runVerification;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
findReady(
|
|
114
|
+
steps: WorkflowStep[],
|
|
115
|
+
statuses: Map<string, WorkflowStepStatus> | Map<string, TState>
|
|
116
|
+
): WorkflowStep[] {
|
|
117
|
+
return steps.filter((step) => {
|
|
118
|
+
const state = statuses.get(step.name);
|
|
119
|
+
const status = this.getStatus(state);
|
|
120
|
+
if (status !== 'pending') return false;
|
|
121
|
+
|
|
122
|
+
return (step.dependsOn ?? []).every((dependency) => {
|
|
123
|
+
const depState = statuses.get(dependency);
|
|
124
|
+
const depStatus = this.getStatus(depState);
|
|
125
|
+
return depStatus === 'completed' || depStatus === 'skipped';
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** @deprecated Use {@link findReady} instead. This is an alias kept for backward compatibility. */
|
|
131
|
+
findReadySteps(
|
|
132
|
+
steps: WorkflowStep[],
|
|
133
|
+
statuses: Map<string, WorkflowStepStatus> | Map<string, TState>
|
|
134
|
+
): WorkflowStep[] {
|
|
135
|
+
return this.findReady(steps, statuses);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
scheduleStep(step: WorkflowStep, options: { readyAt?: number; staggerDelay?: number } = {}): StepSchedule {
|
|
139
|
+
return {
|
|
140
|
+
step,
|
|
141
|
+
readyAt: options.readyAt ?? Date.now(),
|
|
142
|
+
staggerDelay: options.staggerDelay ?? 0,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async startStep(step: WorkflowStep, state: TState, startMessage?: string): Promise<void> {
|
|
147
|
+
const startedAt = new Date().toISOString();
|
|
148
|
+
state.row.status = 'running';
|
|
149
|
+
state.row.error = undefined;
|
|
150
|
+
state.row.completionReason = undefined;
|
|
151
|
+
state.row.startedAt = startedAt;
|
|
152
|
+
|
|
153
|
+
await this.deps.persistStepRow?.(state.row.id, {
|
|
154
|
+
status: 'running',
|
|
155
|
+
error: undefined,
|
|
156
|
+
completionReason: undefined,
|
|
157
|
+
startedAt,
|
|
158
|
+
updatedAt: new Date().toISOString(),
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
if (startMessage) {
|
|
162
|
+
this.deps.postToChannel?.(startMessage);
|
|
163
|
+
}
|
|
164
|
+
await this.deps.onStepStarted?.(step, state);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async retryStep(step: WorkflowStep, state: TState, attempt: number, maxRetries: number): Promise<void> {
|
|
168
|
+
state.row.retryCount = attempt;
|
|
169
|
+
await this.deps.persistStepRow?.(state.row.id, {
|
|
170
|
+
retryCount: attempt,
|
|
171
|
+
updatedAt: new Date().toISOString(),
|
|
172
|
+
});
|
|
173
|
+
await this.deps.onStepRetried?.(step, state, attempt, maxRetries);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async completeStep(step: WorkflowStep, state: TState, result: Partial<StepResult>): Promise<StepResult> {
|
|
177
|
+
const completedAt = new Date().toISOString();
|
|
178
|
+
const finalResult: StepResult = {
|
|
179
|
+
status: result.status ?? 'completed',
|
|
180
|
+
output: result.output ?? '',
|
|
181
|
+
exitCode: result.exitCode,
|
|
182
|
+
exitSignal: result.exitSignal,
|
|
183
|
+
duration: result.duration ?? 0,
|
|
184
|
+
retries: result.retries ?? state.row.retryCount,
|
|
185
|
+
completionReason: result.completionReason,
|
|
186
|
+
error: result.error,
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
state.row.status = finalResult.status;
|
|
190
|
+
state.row.output = finalResult.output;
|
|
191
|
+
state.row.error = finalResult.error;
|
|
192
|
+
state.row.completionReason = finalResult.completionReason;
|
|
193
|
+
state.row.completedAt = completedAt;
|
|
194
|
+
|
|
195
|
+
await this.deps.persistStepRow?.(state.row.id, {
|
|
196
|
+
status: finalResult.status,
|
|
197
|
+
output: finalResult.output,
|
|
198
|
+
error: finalResult.error,
|
|
199
|
+
completionReason: finalResult.completionReason,
|
|
200
|
+
completedAt,
|
|
201
|
+
updatedAt: new Date().toISOString(),
|
|
202
|
+
});
|
|
203
|
+
if (finalResult.status === 'completed' && this.deps.runId && finalResult.output) {
|
|
204
|
+
await this.deps.persistStepOutput?.(this.deps.runId, step.name, finalResult.output);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (finalResult.status === 'failed') {
|
|
208
|
+
await this.deps.onStepFailed?.(step, state, finalResult);
|
|
209
|
+
} else {
|
|
210
|
+
await this.deps.onStepCompleted?.(step, state, finalResult);
|
|
211
|
+
}
|
|
212
|
+
return finalResult;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async monitorStep<TResult>(
|
|
216
|
+
step: WorkflowStep,
|
|
217
|
+
state: TState,
|
|
218
|
+
options: MonitorStepOptions<TState, TResult>
|
|
219
|
+
): Promise<StepResult> {
|
|
220
|
+
const maxRetries = options.maxRetries ?? 0;
|
|
221
|
+
const retryDelayMs = options.retryDelayMs ?? 1000;
|
|
222
|
+
let lastError: unknown;
|
|
223
|
+
|
|
224
|
+
for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
|
|
225
|
+
this.deps.checkAborted?.();
|
|
226
|
+
await this.deps.waitIfPaused?.();
|
|
227
|
+
|
|
228
|
+
if (attempt > 0) {
|
|
229
|
+
await this.retryStep(step, state, attempt, maxRetries);
|
|
230
|
+
await options.onRetry?.(attempt, maxRetries, state);
|
|
231
|
+
if (retryDelayMs > 0) {
|
|
232
|
+
await delay(retryDelayMs);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const attemptStartedAt = Date.now();
|
|
237
|
+
await this.startStep(step, state, options.startMessage);
|
|
238
|
+
await options.onStart?.(attempt, state);
|
|
239
|
+
|
|
240
|
+
try {
|
|
241
|
+
const rawResult = await options.execute(attempt, state);
|
|
242
|
+
const completion = options.toCompletionResult(rawResult, attempt, state);
|
|
243
|
+
return await this.completeStep(step, state, {
|
|
244
|
+
...completion,
|
|
245
|
+
duration: completion.duration ?? Date.now() - attemptStartedAt,
|
|
246
|
+
retries: completion.retries ?? attempt,
|
|
247
|
+
});
|
|
248
|
+
} catch (error) {
|
|
249
|
+
lastError = error;
|
|
250
|
+
await options.onAttemptFailed?.(error, attempt, state);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const failure = options.getFailureResult?.(lastError, maxRetries, state) ?? {
|
|
255
|
+
status: 'failed' as const,
|
|
256
|
+
output: '',
|
|
257
|
+
error: lastError instanceof Error ? lastError.message : String(lastError ?? 'Unknown error'),
|
|
258
|
+
retries: maxRetries,
|
|
259
|
+
};
|
|
260
|
+
return this.completeStep(step, state, {
|
|
261
|
+
...failure,
|
|
262
|
+
status: 'failed',
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
async executeAll(
|
|
267
|
+
steps: WorkflowStep[],
|
|
268
|
+
agentMap: Map<string, AgentDefinition>,
|
|
269
|
+
errorHandling?: ErrorHandlingConfig,
|
|
270
|
+
providedStates?: Map<string, TState>
|
|
271
|
+
): Promise<Map<string, StepResult>> {
|
|
272
|
+
const states = providedStates ?? this.createEphemeralStates(steps);
|
|
273
|
+
const strategy = normalizeStrategy(errorHandling?.strategy ?? 'fail-fast');
|
|
274
|
+
const results = new Map<string, StepResult>();
|
|
275
|
+
|
|
276
|
+
while (true) {
|
|
277
|
+
this.deps.checkAborted?.();
|
|
278
|
+
await this.deps.waitIfPaused?.();
|
|
279
|
+
|
|
280
|
+
const readySteps = this.findReady(steps, states);
|
|
281
|
+
if (readySteps.length === 0) break;
|
|
282
|
+
|
|
283
|
+
const schedules = readySteps.map((step, index) =>
|
|
284
|
+
this.scheduleStep(step, {
|
|
285
|
+
readyAt: Date.now(),
|
|
286
|
+
staggerDelay: readySteps.length > 3 ? index * 2_000 : 0,
|
|
287
|
+
})
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
if (schedules.length > 1) {
|
|
291
|
+
await this.deps.onBeginTrack?.(readySteps);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const settled = await Promise.allSettled(
|
|
295
|
+
schedules.map(async (schedule) => {
|
|
296
|
+
if (schedule.staggerDelay > 0) {
|
|
297
|
+
await delay(schedule.staggerDelay);
|
|
298
|
+
}
|
|
299
|
+
return this.executeScheduledStep(schedule.step, states, agentMap, errorHandling);
|
|
300
|
+
})
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
const batchOutcomes: StepOutcome[] = [];
|
|
304
|
+
|
|
305
|
+
for (let index = 0; index < settled.length; index += 1) {
|
|
306
|
+
const settledResult = settled[index];
|
|
307
|
+
const step = readySteps[index];
|
|
308
|
+
const state = states.get(step.name);
|
|
309
|
+
|
|
310
|
+
if (settledResult.status === 'fulfilled') {
|
|
311
|
+
const result = settledResult.value;
|
|
312
|
+
const outcomeStatus =
|
|
313
|
+
result.status === 'completed' || result.status === 'skipped' ? result.status : 'failed';
|
|
314
|
+
results.set(step.name, result);
|
|
315
|
+
batchOutcomes.push({
|
|
316
|
+
name: step.name,
|
|
317
|
+
agent: step.agent ?? 'deterministic',
|
|
318
|
+
status: outcomeStatus,
|
|
319
|
+
attempts: result.retries + 1,
|
|
320
|
+
output: result.output,
|
|
321
|
+
error: result.error,
|
|
322
|
+
verificationPassed: outcomeStatus === 'completed' && step.verification !== undefined,
|
|
323
|
+
completionMode:
|
|
324
|
+
result.completionReason !== undefined
|
|
325
|
+
? this.deps.buildCompletionMode?.(step.name, result.completionReason)
|
|
326
|
+
: undefined,
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
if (result.status === 'failed') {
|
|
330
|
+
await this.deps.markDownstreamSkipped?.(step.name);
|
|
331
|
+
if (strategy === 'fail-fast') {
|
|
332
|
+
throw new Error(`Step "${step.name}" failed: ${result.error ?? 'unknown error'}`);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const error =
|
|
339
|
+
settledResult.reason instanceof Error ? settledResult.reason.message : String(settledResult.reason);
|
|
340
|
+
if (state) {
|
|
341
|
+
const failed =
|
|
342
|
+
state.row.status === 'failed'
|
|
343
|
+
? {
|
|
344
|
+
status: 'failed' as const,
|
|
345
|
+
output: state.row.output ?? '',
|
|
346
|
+
duration: 0,
|
|
347
|
+
retries: state.row.retryCount,
|
|
348
|
+
completionReason: state.row.completionReason,
|
|
349
|
+
error: state.row.error ?? error,
|
|
350
|
+
}
|
|
351
|
+
: await this.completeStep(step, state, {
|
|
352
|
+
status: 'failed',
|
|
353
|
+
output: '',
|
|
354
|
+
error,
|
|
355
|
+
retries: state.row.retryCount,
|
|
356
|
+
});
|
|
357
|
+
results.set(step.name, failed);
|
|
358
|
+
}
|
|
359
|
+
batchOutcomes.push({
|
|
360
|
+
name: step.name,
|
|
361
|
+
agent: step.agent ?? 'deterministic',
|
|
362
|
+
status: 'failed',
|
|
363
|
+
attempts: (state?.row.retryCount ?? 0) + 1,
|
|
364
|
+
error,
|
|
365
|
+
});
|
|
366
|
+
await this.deps.markDownstreamSkipped?.(step.name);
|
|
367
|
+
if (strategy === 'fail-fast') {
|
|
368
|
+
throw new Error(`Step "${step.name}" failed: ${error}`);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (readySteps.length > 1 && batchOutcomes.length > 0) {
|
|
373
|
+
await this.deps.onConverge?.(readySteps, batchOutcomes);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return results;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
async executeOne(
|
|
381
|
+
step: WorkflowStep,
|
|
382
|
+
agentMap: Map<string, AgentDefinition>,
|
|
383
|
+
errorHandling?: ErrorHandlingConfig,
|
|
384
|
+
providedState?: TState
|
|
385
|
+
): Promise<StepResult> {
|
|
386
|
+
const state = providedState ?? this.createEphemeralState(step);
|
|
387
|
+
if (this.deps.executeStep) {
|
|
388
|
+
const result = await this.deps.executeStep(step, state, agentMap, errorHandling);
|
|
389
|
+
if (state.row.status !== 'pending' && state.row.status !== 'running') {
|
|
390
|
+
return {
|
|
391
|
+
status: state.row.status,
|
|
392
|
+
output: state.row.output ?? '',
|
|
393
|
+
duration: result?.duration ?? 0,
|
|
394
|
+
retries: result?.retries ?? state.row.retryCount,
|
|
395
|
+
exitCode: result?.exitCode,
|
|
396
|
+
exitSignal: result?.exitSignal,
|
|
397
|
+
completionReason: state.row.completionReason ?? result?.completionReason,
|
|
398
|
+
error: state.row.error ?? result?.error,
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
return this.completeStep(step, state, {
|
|
402
|
+
status: result?.status ?? 'completed',
|
|
403
|
+
output: result?.output ?? '',
|
|
404
|
+
exitCode: result?.exitCode,
|
|
405
|
+
exitSignal: result?.exitSignal,
|
|
406
|
+
completionReason: result?.completionReason,
|
|
407
|
+
retries: result?.retries ?? state.row.retryCount,
|
|
408
|
+
duration: result?.duration ?? 0,
|
|
409
|
+
error: result?.error,
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
return this.executeWithProcessSpawner(step, state, agentMap, errorHandling);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
async markFailed(stepName: string, error: string): Promise<void> {
|
|
417
|
+
this.deps.postToChannel?.(`**[${stepName}]** Failed: ${error}`);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
buildStepOutputContext(stepStates: Map<string, TState>): Record<string, { output: string }> {
|
|
421
|
+
const steps: Record<string, { output: string }> = {};
|
|
422
|
+
for (const [name, state] of stepStates) {
|
|
423
|
+
if (state.row.status === 'completed' && state.row.output !== undefined) {
|
|
424
|
+
steps[name] = { output: state.row.output };
|
|
425
|
+
continue;
|
|
426
|
+
}
|
|
427
|
+
if (state.row.status === 'completed' && this.deps.runId) {
|
|
428
|
+
const persisted = this.deps.loadStepOutput?.(this.deps.runId, name);
|
|
429
|
+
if (persisted !== undefined) {
|
|
430
|
+
state.row.output = persisted;
|
|
431
|
+
steps[name] = { output: persisted };
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return steps;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
resolveStepTemplate(template: string, context: Record<string, unknown>): string {
|
|
439
|
+
if (this.deps.resolveTemplate) {
|
|
440
|
+
return this.deps.resolveTemplate(template, context);
|
|
441
|
+
}
|
|
442
|
+
return this.templateResolver.interpolateStepTask(template, context);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
getChannelMessenger(): ChannelMessenger {
|
|
446
|
+
return this.channelMessenger;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
runVerification(
|
|
450
|
+
check: VerificationCheck,
|
|
451
|
+
output: string,
|
|
452
|
+
stepName: string,
|
|
453
|
+
injectedTaskText?: string,
|
|
454
|
+
options?: VerificationOptions
|
|
455
|
+
): VerificationResult {
|
|
456
|
+
return this.verificationRunner(check, output, stepName, injectedTaskText, {
|
|
457
|
+
...options,
|
|
458
|
+
cwd: options?.cwd ?? this.deps.cwd,
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
private async executeScheduledStep(
|
|
463
|
+
step: WorkflowStep,
|
|
464
|
+
states: Map<string, TState>,
|
|
465
|
+
agentMap: Map<string, AgentDefinition>,
|
|
466
|
+
errorHandling?: ErrorHandlingConfig
|
|
467
|
+
): Promise<StepResult> {
|
|
468
|
+
const state = states.get(step.name) ?? this.createEphemeralState(step);
|
|
469
|
+
if (!states.has(step.name)) {
|
|
470
|
+
states.set(step.name, state);
|
|
471
|
+
}
|
|
472
|
+
return this.executeOne(step, agentMap, errorHandling, state);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
private async executeWithProcessSpawner(
|
|
476
|
+
step: WorkflowStep,
|
|
477
|
+
state: TState,
|
|
478
|
+
agentMap: Map<string, AgentDefinition>,
|
|
479
|
+
errorHandling?: ErrorHandlingConfig
|
|
480
|
+
): Promise<StepResult> {
|
|
481
|
+
const spawner = this.deps.processSpawner;
|
|
482
|
+
if (!spawner) {
|
|
483
|
+
throw new Error(`No step execution callback or process spawner configured for step "${step.name}"`);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
const maxRetries = step.retries ?? errorHandling?.maxRetries ?? 0;
|
|
487
|
+
return this.monitorStep(step, state, {
|
|
488
|
+
maxRetries,
|
|
489
|
+
retryDelayMs: errorHandling?.retryDelayMs ?? 1000,
|
|
490
|
+
startMessage: `**[${step.name}]** Started`,
|
|
491
|
+
onRetry: (attempt, total) => {
|
|
492
|
+
this.deps.postToChannel?.(`**[${step.name}]** Retrying (attempt ${attempt + 1}/${total + 1})`);
|
|
493
|
+
},
|
|
494
|
+
execute: async () => {
|
|
495
|
+
if (step.type === 'deterministic') {
|
|
496
|
+
const command = step.command ?? '';
|
|
497
|
+
return spawner.spawnShell(command, { cwd: this.deps.cwd, timeoutMs: step.timeoutMs });
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
const agent = step.agent ? agentMap.get(step.agent) : undefined;
|
|
501
|
+
if (!agent) {
|
|
502
|
+
throw new Error(`Agent "${step.agent ?? '(missing)'}" not found in config`);
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
const task = step.task ?? '';
|
|
506
|
+
if (agent.interactive === false) {
|
|
507
|
+
return spawner.spawnAgent(agent, task, { cwd: this.deps.cwd, timeoutMs: step.timeoutMs });
|
|
508
|
+
}
|
|
509
|
+
return spawner.spawnInteractive(agent, task, { cwd: this.deps.cwd, timeoutMs: step.timeoutMs });
|
|
510
|
+
},
|
|
511
|
+
toCompletionResult: (spawnResult, attempt) => {
|
|
512
|
+
const failOnError = step.failOnError !== false;
|
|
513
|
+
const failed =
|
|
514
|
+
failOnError &&
|
|
515
|
+
((spawnResult.exitCode ?? 0) !== 0 ||
|
|
516
|
+
(spawnResult.exitCode === undefined && spawnResult.exitSignal !== undefined));
|
|
517
|
+
const output =
|
|
518
|
+
step.captureOutput === false
|
|
519
|
+
? `Command completed (exit code ${spawnResult.exitCode ?? 0})`
|
|
520
|
+
: spawnResult.output;
|
|
521
|
+
|
|
522
|
+
if (failed) {
|
|
523
|
+
return {
|
|
524
|
+
status: 'failed' as const,
|
|
525
|
+
output,
|
|
526
|
+
exitCode: spawnResult.exitCode,
|
|
527
|
+
exitSignal: spawnResult.exitSignal,
|
|
528
|
+
retries: attempt,
|
|
529
|
+
error: spawnResult.output || `Command failed with exit code ${spawnResult.exitCode ?? 'unknown'}`,
|
|
530
|
+
};
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
return {
|
|
534
|
+
status: 'completed' as const,
|
|
535
|
+
output,
|
|
536
|
+
exitCode: spawnResult.exitCode,
|
|
537
|
+
exitSignal: spawnResult.exitSignal,
|
|
538
|
+
retries: attempt,
|
|
539
|
+
};
|
|
540
|
+
},
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
private createEphemeralStates(steps: WorkflowStep[]): Map<string, TState> {
|
|
545
|
+
return new Map(steps.map((step) => [step.name, this.createEphemeralState(step)]));
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
private createEphemeralState(step: WorkflowStep): TState {
|
|
549
|
+
return {
|
|
550
|
+
row: {
|
|
551
|
+
id: `step-${step.name}`,
|
|
552
|
+
runId: this.deps.runId ?? 'run',
|
|
553
|
+
stepName: step.name,
|
|
554
|
+
agentName: step.agent ?? null,
|
|
555
|
+
stepType: step.type ?? 'agent',
|
|
556
|
+
status: 'pending',
|
|
557
|
+
task: step.task ?? step.command ?? step.branch ?? '',
|
|
558
|
+
dependsOn: step.dependsOn ?? [],
|
|
559
|
+
retryCount: 0,
|
|
560
|
+
createdAt: new Date().toISOString(),
|
|
561
|
+
updatedAt: new Date().toISOString(),
|
|
562
|
+
},
|
|
563
|
+
} as TState;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
private getStatus(state: WorkflowStepStatus | TState | undefined): WorkflowStepStatus | undefined {
|
|
567
|
+
if (typeof state === 'string') return state;
|
|
568
|
+
return state?.row.status;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
function normalizeStrategy(strategy: ErrorHandlingConfig['strategy']): 'fail-fast' | 'continue' {
|
|
573
|
+
if (strategy === 'continue') return 'continue';
|
|
574
|
+
return 'fail-fast';
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
function delay(ms: number): Promise<void> {
|
|
578
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
579
|
+
}
|