agent-relay 3.1.18 → 3.1.20
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 +13 -1
- 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 +446 -192
- package/dist/src/cli/bootstrap.js +0 -15
- package/dist/src/cli/bootstrap.js.map +1 -1
- package/dist/src/cli/commands/agent-management.d.ts +1 -0
- package/dist/src/cli/commands/agent-management.d.ts.map +1 -1
- package/dist/src/cli/commands/agent-management.js +235 -16
- package/dist/src/cli/commands/agent-management.js.map +1 -1
- package/dist/src/cli/commands/core.js +1 -1
- package/dist/src/cli/commands/core.js.map +1 -1
- package/dist/src/cli/index.d.ts.map +1 -1
- package/dist/src/cli/index.js +13 -1
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/cli/lib/broker-lifecycle.d.ts.map +1 -1
- package/dist/src/cli/lib/broker-lifecycle.js +3 -5
- package/dist/src/cli/lib/broker-lifecycle.js.map +1 -1
- package/dist/src/cli/lib/connect-daytona.js +2 -2
- package/dist/src/cli/lib/connect-daytona.js.map +1 -1
- package/dist/src/cli/lib/core-maintenance.d.ts.map +1 -1
- package/dist/src/cli/lib/core-maintenance.js +52 -0
- package/dist/src/cli/lib/core-maintenance.js.map +1 -1
- package/install.sh +32 -0
- package/package.json +13 -13
- package/packages/acp-bridge/package.json +2 -2
- package/packages/config/package.json +1 -1
- package/packages/hooks/package.json +4 -4
- package/packages/memory/package.json +2 -2
- package/packages/openclaw/dist/cli.js +79 -2
- package/packages/openclaw/dist/cli.js.map +1 -1
- package/packages/openclaw/dist/config.d.ts +28 -1
- package/packages/openclaw/dist/config.d.ts.map +1 -1
- package/packages/openclaw/dist/config.js +145 -0
- package/packages/openclaw/dist/config.js.map +1 -1
- package/packages/openclaw/dist/index.d.ts +2 -2
- package/packages/openclaw/dist/index.d.ts.map +1 -1
- package/packages/openclaw/dist/index.js +1 -1
- package/packages/openclaw/dist/index.js.map +1 -1
- package/packages/openclaw/dist/setup.d.ts.map +1 -1
- package/packages/openclaw/dist/setup.js +24 -1
- package/packages/openclaw/dist/setup.js.map +1 -1
- package/packages/openclaw/dist/types.d.ts +23 -0
- package/packages/openclaw/dist/types.d.ts.map +1 -1
- package/packages/openclaw/package.json +2 -2
- package/packages/openclaw/skill/SKILL.md +46 -0
- package/packages/openclaw/src/cli.ts +90 -2
- package/packages/openclaw/src/config.ts +165 -1
- package/packages/openclaw/src/index.ts +7 -1
- package/packages/openclaw/src/setup.ts +26 -1
- package/packages/openclaw/src/types.ts +25 -0
- package/packages/policy/package.json +2 -2
- package/packages/sdk/dist/__tests__/integration.test.js +35 -0
- package/packages/sdk/dist/__tests__/integration.test.js.map +1 -1
- package/packages/sdk/dist/client.d.ts +9 -0
- package/packages/sdk/dist/client.d.ts.map +1 -1
- package/packages/sdk/dist/client.js +48 -25
- package/packages/sdk/dist/client.js.map +1 -1
- package/packages/sdk/dist/protocol.d.ts +1 -0
- package/packages/sdk/dist/protocol.d.ts.map +1 -1
- package/packages/sdk/dist/relay.d.ts +8 -0
- package/packages/sdk/dist/relay.d.ts.map +1 -1
- package/packages/sdk/dist/relay.js +50 -5
- package/packages/sdk/dist/relay.js.map +1 -1
- package/packages/sdk/dist/workflows/cli.js +2 -0
- package/packages/sdk/dist/workflows/cli.js.map +1 -1
- package/packages/sdk/dist/workflows/runner.d.ts +11 -0
- package/packages/sdk/dist/workflows/runner.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/runner.js +350 -167
- package/packages/sdk/dist/workflows/runner.js.map +1 -1
- package/packages/sdk/dist/workflows/trajectory.d.ts +6 -1
- package/packages/sdk/dist/workflows/trajectory.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/trajectory.js +16 -2
- package/packages/sdk/dist/workflows/trajectory.js.map +1 -1
- package/packages/sdk/package.json +2 -2
- package/packages/sdk/src/__tests__/integration.test.ts +49 -0
- package/packages/sdk/src/__tests__/orchestration-upgrades.test.ts +50 -1
- package/packages/sdk/src/client.ts +57 -24
- package/packages/sdk/src/protocol.ts +1 -1
- package/packages/sdk/src/relay.ts +70 -5
- package/packages/sdk/src/workflows/cli.ts +2 -0
- package/packages/sdk/src/workflows/runner.ts +414 -185
- package/packages/sdk/src/workflows/trajectory.ts +22 -2
- package/packages/sdk-py/pyproject.toml +1 -1
- package/packages/sdk-py/src/agent_relay/client.py +18 -1
- package/packages/sdk-py/src/agent_relay/relay.py +4 -0
- package/packages/sdk-py/src/agent_relay/types.py +4 -0
- package/packages/telemetry/package.json +1 -1
- package/packages/trajectory/package.json +2 -2
- package/packages/user-directory/package.json +2 -2
- package/packages/utils/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -9102,7 +9102,8 @@ var AgentRelayClient = class _AgentRelayClient {
|
|
|
9102
9102
|
agent,
|
|
9103
9103
|
...input.task != null ? { initial_task: input.task } : {},
|
|
9104
9104
|
...input.idleThresholdSecs != null ? { idle_threshold_secs: input.idleThresholdSecs } : {},
|
|
9105
|
-
...input.continueFrom != null ? { continue_from: input.continueFrom } : {}
|
|
9105
|
+
...input.continueFrom != null ? { continue_from: input.continueFrom } : {},
|
|
9106
|
+
...input.skipRelayPrompt != null ? { skip_relay_prompt: input.skipRelayPrompt } : {}
|
|
9106
9107
|
});
|
|
9107
9108
|
return result;
|
|
9108
9109
|
}
|
|
@@ -9117,7 +9118,8 @@ var AgentRelayClient = class _AgentRelayClient {
|
|
|
9117
9118
|
};
|
|
9118
9119
|
const result = await this.requestOk("spawn_agent", {
|
|
9119
9120
|
agent,
|
|
9120
|
-
...input.task != null ? { initial_task: input.task } : {}
|
|
9121
|
+
...input.task != null ? { initial_task: input.task } : {},
|
|
9122
|
+
...input.skipRelayPrompt != null ? { skip_relay_prompt: input.skipRelayPrompt } : {}
|
|
9121
9123
|
});
|
|
9122
9124
|
return result;
|
|
9123
9125
|
}
|
|
@@ -9132,7 +9134,8 @@ var AgentRelayClient = class _AgentRelayClient {
|
|
|
9132
9134
|
provider: input.provider,
|
|
9133
9135
|
args: input.args,
|
|
9134
9136
|
channels: input.channels,
|
|
9135
|
-
task: input.task
|
|
9137
|
+
task: input.task,
|
|
9138
|
+
skipRelayPrompt: input.skipRelayPrompt
|
|
9136
9139
|
});
|
|
9137
9140
|
}
|
|
9138
9141
|
return this.spawnPty({
|
|
@@ -9148,7 +9151,8 @@ var AgentRelayClient = class _AgentRelayClient {
|
|
|
9148
9151
|
shadowMode: input.shadowMode,
|
|
9149
9152
|
idleThresholdSecs: input.idleThresholdSecs,
|
|
9150
9153
|
restartPolicy: input.restartPolicy,
|
|
9151
|
-
continueFrom: input.continueFrom
|
|
9154
|
+
continueFrom: input.continueFrom,
|
|
9155
|
+
skipRelayPrompt: input.skipRelayPrompt
|
|
9152
9156
|
});
|
|
9153
9157
|
}
|
|
9154
9158
|
async spawnClaude(input) {
|
|
@@ -9214,25 +9218,35 @@ var AgentRelayClient = class _AgentRelayClient {
|
|
|
9214
9218
|
if (!this.child) {
|
|
9215
9219
|
return;
|
|
9216
9220
|
}
|
|
9217
|
-
|
|
9218
|
-
|
|
9219
|
-
} catch {
|
|
9220
|
-
}
|
|
9221
|
+
void this.requestOk("shutdown", {}).catch(() => {
|
|
9222
|
+
});
|
|
9221
9223
|
const child = this.child;
|
|
9222
9224
|
const wait = this.exitPromise ?? Promise.resolve();
|
|
9223
|
-
const
|
|
9224
|
-
|
|
9225
|
-
|
|
9226
|
-
|
|
9227
|
-
|
|
9228
|
-
|
|
9229
|
-
|
|
9230
|
-
|
|
9231
|
-
|
|
9232
|
-
|
|
9233
|
-
|
|
9234
|
-
|
|
9225
|
+
const waitForExit = async (timeoutMs) => {
|
|
9226
|
+
let timer;
|
|
9227
|
+
const result = await Promise.race([
|
|
9228
|
+
wait.then(() => true),
|
|
9229
|
+
new Promise((resolve3) => {
|
|
9230
|
+
timer = setTimeout(() => resolve3(false), timeoutMs);
|
|
9231
|
+
})
|
|
9232
|
+
]);
|
|
9233
|
+
if (timer !== void 0)
|
|
9234
|
+
clearTimeout(timer);
|
|
9235
|
+
return result;
|
|
9236
|
+
};
|
|
9237
|
+
if (await waitForExit(this.options.shutdownTimeoutMs)) {
|
|
9238
|
+
return;
|
|
9239
|
+
}
|
|
9240
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
9241
|
+
child.kill("SIGTERM");
|
|
9235
9242
|
}
|
|
9243
|
+
if (await waitForExit(1e3)) {
|
|
9244
|
+
return;
|
|
9245
|
+
}
|
|
9246
|
+
if (child.exitCode === null && child.signalCode === null) {
|
|
9247
|
+
child.kill("SIGKILL");
|
|
9248
|
+
}
|
|
9249
|
+
await waitForExit(1e3);
|
|
9236
9250
|
}
|
|
9237
9251
|
async waitForExit() {
|
|
9238
9252
|
if (!this.child) {
|
|
@@ -9250,8 +9264,7 @@ var AgentRelayClient = class _AgentRelayClient {
|
|
|
9250
9264
|
"init",
|
|
9251
9265
|
"--name",
|
|
9252
9266
|
this.options.brokerName,
|
|
9253
|
-
"--channels",
|
|
9254
|
-
this.options.channels.join(","),
|
|
9267
|
+
...this.options.channels.length > 0 ? ["--channels", this.options.channels.join(",")] : [],
|
|
9255
9268
|
...this.options.binaryArgs
|
|
9256
9269
|
];
|
|
9257
9270
|
const env = { ...this.options.env };
|
|
@@ -9262,7 +9275,7 @@ var AgentRelayClient = class _AgentRelayClient {
|
|
|
9262
9275
|
env.PATH = `${binDir}${import_node_path.default.delimiter}${currentPath}`;
|
|
9263
9276
|
}
|
|
9264
9277
|
}
|
|
9265
|
-
console.
|
|
9278
|
+
console.error(`[broker] Starting: ${resolvedBinary} ${args.join(" ")}`);
|
|
9266
9279
|
const child = (0, import_node_child_process.spawn)(resolvedBinary, args, {
|
|
9267
9280
|
cwd: this.options.cwd,
|
|
9268
9281
|
env,
|
|
@@ -9298,7 +9311,7 @@ var AgentRelayClient = class _AgentRelayClient {
|
|
|
9298
9311
|
});
|
|
9299
9312
|
});
|
|
9300
9313
|
const helloAck = await this.requestHello();
|
|
9301
|
-
console.
|
|
9314
|
+
console.error("[broker] Broker ready (hello handshake complete)");
|
|
9302
9315
|
if (helloAck.workspace_key) {
|
|
9303
9316
|
this.workspaceKey = helloAck.workspace_key;
|
|
9304
9317
|
}
|
|
@@ -9471,8 +9484,10 @@ function getLatestVersionSync() {
|
|
|
9471
9484
|
timeout: 15e3,
|
|
9472
9485
|
stdio: ["pipe", "pipe", "pipe"]
|
|
9473
9486
|
}).toString();
|
|
9474
|
-
const match = result.match(/"tag_name"\s*:\s*"
|
|
9475
|
-
|
|
9487
|
+
const match = result.match(/"tag_name"\s*:\s*"([^"]+)"/);
|
|
9488
|
+
if (!match?.[1])
|
|
9489
|
+
return null;
|
|
9490
|
+
return match[1].replace(/^openclaw-/, "").replace(/^v/, "");
|
|
9476
9491
|
} catch {
|
|
9477
9492
|
return null;
|
|
9478
9493
|
}
|
|
@@ -9502,6 +9517,13 @@ function installBrokerBinary() {
|
|
|
9502
9517
|
});
|
|
9503
9518
|
import_node_fs.default.chmodSync(targetPath, 493);
|
|
9504
9519
|
if (process.platform === "darwin") {
|
|
9520
|
+
try {
|
|
9521
|
+
(0, import_node_child_process.execSync)(`xattr -d com.apple.quarantine "${targetPath}" 2>/dev/null || true`, {
|
|
9522
|
+
timeout: 1e4,
|
|
9523
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
9524
|
+
});
|
|
9525
|
+
} catch {
|
|
9526
|
+
}
|
|
9505
9527
|
try {
|
|
9506
9528
|
(0, import_node_child_process.execSync)(`codesign --force --sign - "${targetPath}"`, {
|
|
9507
9529
|
timeout: 1e4,
|
|
@@ -45187,7 +45209,7 @@ var AgentRelay = class {
|
|
|
45187
45209
|
this.clientOptions = {
|
|
45188
45210
|
binaryPath: options.binaryPath,
|
|
45189
45211
|
binaryArgs: options.binaryArgs,
|
|
45190
|
-
brokerName: options.brokerName,
|
|
45212
|
+
brokerName: options.brokerName ?? options.workspaceName,
|
|
45191
45213
|
channels: this.defaultChannels,
|
|
45192
45214
|
cwd: options.cwd,
|
|
45193
45215
|
env: options.env,
|
|
@@ -45246,7 +45268,8 @@ var AgentRelay = class {
|
|
|
45246
45268
|
shadowOf: input.shadowOf,
|
|
45247
45269
|
shadowMode: input.shadowMode,
|
|
45248
45270
|
idleThresholdSecs: input.idleThresholdSecs,
|
|
45249
|
-
restartPolicy: input.restartPolicy
|
|
45271
|
+
restartPolicy: input.restartPolicy,
|
|
45272
|
+
skipRelayPrompt: input.skipRelayPrompt
|
|
45250
45273
|
});
|
|
45251
45274
|
} catch (error95) {
|
|
45252
45275
|
await this.invokeLifecycleHook(input.onError, {
|
|
@@ -45279,6 +45302,7 @@ var AgentRelay = class {
|
|
|
45279
45302
|
shadowMode: options?.shadowMode,
|
|
45280
45303
|
idleThresholdSecs: options?.idleThresholdSecs,
|
|
45281
45304
|
restartPolicy: options?.restartPolicy,
|
|
45305
|
+
skipRelayPrompt: options?.skipRelayPrompt,
|
|
45282
45306
|
onStart: options?.onStart,
|
|
45283
45307
|
onSuccess: options?.onSuccess,
|
|
45284
45308
|
onError: options?.onError
|
|
@@ -45588,10 +45612,21 @@ var AgentRelay = class {
|
|
|
45588
45612
|
this.unsubEvent();
|
|
45589
45613
|
this.unsubEvent = void 0;
|
|
45590
45614
|
}
|
|
45591
|
-
|
|
45592
|
-
|
|
45593
|
-
|
|
45615
|
+
let client = this.client;
|
|
45616
|
+
if (!client && this.startPromise) {
|
|
45617
|
+
try {
|
|
45618
|
+
client = await this.startPromise;
|
|
45619
|
+
} catch {
|
|
45620
|
+
client = void 0;
|
|
45621
|
+
}
|
|
45622
|
+
}
|
|
45623
|
+
if (client) {
|
|
45624
|
+
await client.shutdown();
|
|
45625
|
+
if (this.client === client) {
|
|
45626
|
+
this.client = void 0;
|
|
45627
|
+
}
|
|
45594
45628
|
}
|
|
45629
|
+
this.startPromise = void 0;
|
|
45595
45630
|
this.knownAgents.clear();
|
|
45596
45631
|
this.readyAgents.clear();
|
|
45597
45632
|
this.messageReadyAgents.clear();
|
|
@@ -45663,8 +45698,10 @@ var AgentRelay = class {
|
|
|
45663
45698
|
* 4. Auto-create a fresh workspace via the Relaycast REST API
|
|
45664
45699
|
*/
|
|
45665
45700
|
async ensureRelaycastApiKey() {
|
|
45666
|
-
if (this.relayApiKey)
|
|
45701
|
+
if (this.relayApiKey) {
|
|
45702
|
+
this.wireRelaycastBaseUrl();
|
|
45667
45703
|
return;
|
|
45704
|
+
}
|
|
45668
45705
|
const envKey = this.clientOptions.env?.RELAY_API_KEY ?? process.env.RELAY_API_KEY;
|
|
45669
45706
|
if (envKey) {
|
|
45670
45707
|
this.relayApiKey = envKey;
|
|
@@ -45673,11 +45710,19 @@ var AgentRelay = class {
|
|
|
45673
45710
|
} else if (!this.clientOptions.env.RELAY_API_KEY) {
|
|
45674
45711
|
this.clientOptions.env.RELAY_API_KEY = envKey;
|
|
45675
45712
|
}
|
|
45713
|
+
this.wireRelaycastBaseUrl();
|
|
45676
45714
|
return;
|
|
45677
45715
|
}
|
|
45678
45716
|
if (!this.clientOptions.env) {
|
|
45679
45717
|
this.clientOptions.env = { ...process.env };
|
|
45680
45718
|
}
|
|
45719
|
+
this.wireRelaycastBaseUrl();
|
|
45720
|
+
}
|
|
45721
|
+
/** Inject relaycastBaseUrl into broker env. Explicit option wins over inherited env. */
|
|
45722
|
+
wireRelaycastBaseUrl() {
|
|
45723
|
+
if (this.relaycastBaseUrl && this.clientOptions.env) {
|
|
45724
|
+
this.clientOptions.env.RELAYCAST_BASE_URL = this.relaycastBaseUrl;
|
|
45725
|
+
}
|
|
45681
45726
|
}
|
|
45682
45727
|
async ensureStarted() {
|
|
45683
45728
|
if (this.client)
|
|
@@ -45840,12 +45885,31 @@ var AgentRelay = class {
|
|
|
45840
45885
|
name,
|
|
45841
45886
|
reason: releaseOptions.reason
|
|
45842
45887
|
};
|
|
45888
|
+
if (!relay.knownAgents.has(name)) {
|
|
45889
|
+
await relay.invokeLifecycleHook(releaseOptions.onStart, releaseContext, `release("${name}") onStart`);
|
|
45890
|
+
await relay.invokeLifecycleHook(releaseOptions.onSuccess, releaseContext, `release("${name}") onSuccess`);
|
|
45891
|
+
return;
|
|
45892
|
+
}
|
|
45843
45893
|
const client = await relay.ensureStarted();
|
|
45844
45894
|
await relay.invokeLifecycleHook(releaseOptions.onStart, releaseContext, `release("${name}") onStart`);
|
|
45845
45895
|
try {
|
|
45846
45896
|
await client.release(name, releaseOptions.reason);
|
|
45847
45897
|
await relay.invokeLifecycleHook(releaseOptions.onSuccess, releaseContext, `release("${name}") onSuccess`);
|
|
45848
45898
|
} catch (error95) {
|
|
45899
|
+
if (error95 instanceof AgentRelayProtocolError && error95.code === "agent_not_found") {
|
|
45900
|
+
relay.exitedAgents.add(name);
|
|
45901
|
+
relay.readyAgents.delete(name);
|
|
45902
|
+
relay.messageReadyAgents.delete(name);
|
|
45903
|
+
relay.idleAgents.delete(name);
|
|
45904
|
+
relay.knownAgents.delete(name);
|
|
45905
|
+
relay.outputListeners.delete(name);
|
|
45906
|
+
relay.exitResolvers.get(name)?.resolve("released");
|
|
45907
|
+
relay.exitResolvers.delete(name);
|
|
45908
|
+
relay.idleResolvers.get(name)?.resolve("exited");
|
|
45909
|
+
relay.idleResolvers.delete(name);
|
|
45910
|
+
await relay.invokeLifecycleHook(releaseOptions.onSuccess, releaseContext, `release("${name}") onSuccess`);
|
|
45911
|
+
return;
|
|
45912
|
+
}
|
|
45849
45913
|
await relay.invokeLifecycleHook(releaseOptions.onError, {
|
|
45850
45914
|
...releaseContext,
|
|
45851
45915
|
error: error95
|
|
@@ -45987,6 +46051,7 @@ var AgentRelay = class {
|
|
|
45987
46051
|
task,
|
|
45988
46052
|
model: options?.model,
|
|
45989
46053
|
cwd: options?.cwd,
|
|
46054
|
+
skipRelayPrompt: options?.skipRelayPrompt,
|
|
45990
46055
|
onStart: options?.onStart,
|
|
45991
46056
|
onSuccess: options?.onSuccess,
|
|
45992
46057
|
onError: options?.onError
|
|
@@ -46008,7 +46073,8 @@ var AgentRelay = class {
|
|
|
46008
46073
|
transport: "headless",
|
|
46009
46074
|
args,
|
|
46010
46075
|
channels,
|
|
46011
|
-
task
|
|
46076
|
+
task,
|
|
46077
|
+
skipRelayPrompt: options?.skipRelayPrompt
|
|
46012
46078
|
});
|
|
46013
46079
|
} catch (error95) {
|
|
46014
46080
|
await this.invokeLifecycleHook(options?.onError, {
|
|
@@ -47233,7 +47299,7 @@ var WorkflowTrajectory = class {
|
|
|
47233
47299
|
id,
|
|
47234
47300
|
version: 1,
|
|
47235
47301
|
task: {
|
|
47236
|
-
title:
|
|
47302
|
+
title: workflowName,
|
|
47237
47303
|
source: { system: "workflow-runner", id: this.runId }
|
|
47238
47304
|
},
|
|
47239
47305
|
status: "active",
|
|
@@ -47287,6 +47353,8 @@ var WorkflowTrajectory = class {
|
|
|
47287
47353
|
if (participants?.reviewer) {
|
|
47288
47354
|
await this.registerAgent(participants.reviewer, "reviewer");
|
|
47289
47355
|
}
|
|
47356
|
+
this.closeCurrentChapter();
|
|
47357
|
+
this.openChapter(`Execution: ${step.name}`, agent);
|
|
47290
47358
|
const intent = step.task ? step.task.trim().split(/\n|\.(?=\s)/)[0].trim().slice(0, 120) : `${step.type ?? "deterministic"} step`;
|
|
47291
47359
|
this.addEvent("note", `"${step.name}": ${intent}`, void 0, { agent });
|
|
47292
47360
|
await this.flush();
|
|
@@ -47440,12 +47508,24 @@ var WorkflowTrajectory = class {
|
|
|
47440
47508
|
await this.moveToCompleted();
|
|
47441
47509
|
}
|
|
47442
47510
|
/** Abandon the trajectory. */
|
|
47443
|
-
async abandon(reason) {
|
|
47511
|
+
async abandon(reason, meta5) {
|
|
47444
47512
|
if (!this.enabled || !this.trajectory)
|
|
47445
47513
|
return;
|
|
47514
|
+
const elapsed = Date.now() - this.startTime;
|
|
47515
|
+
const elapsedStr = elapsed > 6e4 ? `${Math.round(elapsed / 6e4)} minutes` : `${Math.round(elapsed / 1e3)} seconds`;
|
|
47516
|
+
const summary = meta5?.summary ?? `Workflow abandoned: ${reason}`;
|
|
47517
|
+
this.openRetrospective();
|
|
47518
|
+
this.addEvent("reflection", `${summary} (abandoned after ${elapsedStr})`, "high");
|
|
47446
47519
|
this.addEvent("error", `Workflow abandoned: ${reason}`, "high");
|
|
47447
47520
|
this.trajectory.status = "abandoned";
|
|
47448
47521
|
this.trajectory.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
47522
|
+
this.trajectory.retrospective = {
|
|
47523
|
+
summary,
|
|
47524
|
+
approach: `${this.swarmPattern} workflow (${this.trajectory.agents.filter((a) => a.role !== "workflow-runner").length} agents)`,
|
|
47525
|
+
confidence: meta5?.confidence ?? 0,
|
|
47526
|
+
learnings: meta5?.learnings,
|
|
47527
|
+
challenges: meta5?.challenges
|
|
47528
|
+
};
|
|
47449
47529
|
this.closeCurrentChapter();
|
|
47450
47530
|
await this.flush();
|
|
47451
47531
|
await this.moveToCompleted();
|
|
@@ -47604,6 +47684,16 @@ var WorkflowTrajectory = class {
|
|
|
47604
47684
|
};
|
|
47605
47685
|
|
|
47606
47686
|
// packages/sdk/dist/workflows/runner.js
|
|
47687
|
+
var SpawnExitError = class extends Error {
|
|
47688
|
+
exitCode;
|
|
47689
|
+
exitSignal;
|
|
47690
|
+
constructor(message, exitCode, exitSignal) {
|
|
47691
|
+
super(message);
|
|
47692
|
+
this.name = "SpawnExitError";
|
|
47693
|
+
this.exitCode = exitCode;
|
|
47694
|
+
this.exitSignal = exitSignal ?? void 0;
|
|
47695
|
+
}
|
|
47696
|
+
};
|
|
47607
47697
|
var _resolvedCursorCli;
|
|
47608
47698
|
function resolveCursorCli() {
|
|
47609
47699
|
if (_resolvedCursorCli !== void 0)
|
|
@@ -47647,6 +47737,8 @@ var WorkflowRunner = class _WorkflowRunner {
|
|
|
47647
47737
|
activeAgentHandles = /* @__PURE__ */ new Map();
|
|
47648
47738
|
// PTY-based output capture: accumulate terminal output per-agent
|
|
47649
47739
|
ptyOutputBuffers = /* @__PURE__ */ new Map();
|
|
47740
|
+
/** Snapshot of PTY output from the most recent failed attempt, keyed by step name. */
|
|
47741
|
+
lastFailedStepOutput = /* @__PURE__ */ new Map();
|
|
47650
47742
|
ptyListeners = /* @__PURE__ */ new Map();
|
|
47651
47743
|
ptyLogStreams = /* @__PURE__ */ new Map();
|
|
47652
47744
|
/** Path to workers.json so `agents:kill` can find workflow-spawned agents */
|
|
@@ -48374,7 +48466,10 @@ ${err.suggestion}`);
|
|
|
48374
48466
|
// ── Execution ───────────────────────────────────────────────────────────
|
|
48375
48467
|
/** Execute a named workflow from a validated config. */
|
|
48376
48468
|
async execute(config3, workflowName, vars) {
|
|
48469
|
+
this.abortController = new AbortController();
|
|
48470
|
+
this.paused = false;
|
|
48377
48471
|
const resolved = vars ? this.resolveVariables(config3, vars) : config3;
|
|
48472
|
+
this.validateConfig(resolved);
|
|
48378
48473
|
const pathResult = this.resolvePathDefinitions(resolved.paths, this.cwd);
|
|
48379
48474
|
if (pathResult.errors.length > 0) {
|
|
48380
48475
|
throw new Error(`Path validation failed:
|
|
@@ -48437,6 +48532,8 @@ ${err.suggestion}`);
|
|
|
48437
48532
|
}
|
|
48438
48533
|
/** Resume a previously paused or partially completed run. */
|
|
48439
48534
|
async resume(runId, vars) {
|
|
48535
|
+
this.abortController = new AbortController();
|
|
48536
|
+
this.paused = false;
|
|
48440
48537
|
const run = await this.db.getRun(runId);
|
|
48441
48538
|
if (!run) {
|
|
48442
48539
|
throw new Error(`Run "${runId}" not found`);
|
|
@@ -48483,8 +48580,6 @@ ${err.suggestion}`);
|
|
|
48483
48580
|
async runWorkflowCore(input) {
|
|
48484
48581
|
const { run, workflow: workflow2, config: config3, stepStates, isResume } = input;
|
|
48485
48582
|
const runId = run.id;
|
|
48486
|
-
this.abortController = new AbortController();
|
|
48487
|
-
this.paused = false;
|
|
48488
48583
|
this.currentConfig = config3;
|
|
48489
48584
|
this.currentRunId = runId;
|
|
48490
48585
|
this.runStartTime = Date.now();
|
|
@@ -48508,14 +48603,18 @@ ${err.suggestion}`);
|
|
|
48508
48603
|
config3.swarm.channel = channel;
|
|
48509
48604
|
await this.db.updateRun(runId, { config: config3 });
|
|
48510
48605
|
}
|
|
48511
|
-
|
|
48512
|
-
|
|
48513
|
-
|
|
48514
|
-
|
|
48515
|
-
|
|
48516
|
-
this.
|
|
48517
|
-
this.log(
|
|
48518
|
-
this.
|
|
48606
|
+
const relaycastDisabled = this.relayOptions.env?.AGENT_RELAY_WORKFLOW_DISABLE_RELAYCAST === "1";
|
|
48607
|
+
const requiresBroker = !this.executor && workflow2.steps.some((step) => step.type !== "deterministic" && step.type !== "worktree");
|
|
48608
|
+
if (requiresBroker) {
|
|
48609
|
+
if (!relaycastDisabled) {
|
|
48610
|
+
this.log("Resolving Relaycast API key...");
|
|
48611
|
+
await this.ensureRelaycastApiKey(channel);
|
|
48612
|
+
this.log("API key resolved");
|
|
48613
|
+
if (this.relayApiKeyAutoCreated && this.relayApiKey) {
|
|
48614
|
+
this.log(`Workspace created \u2014 follow this run in Relaycast:`);
|
|
48615
|
+
this.log(` Observer: https://agentrelay.dev/observer?key=${this.relayApiKey}`);
|
|
48616
|
+
this.log(` Channel: ${channel}`);
|
|
48617
|
+
}
|
|
48519
48618
|
}
|
|
48520
48619
|
this.log("Starting broker...");
|
|
48521
48620
|
const brokerBaseName = import_node_path8.default.basename(this.cwd) || "workflow";
|
|
@@ -48523,7 +48622,7 @@ ${err.suggestion}`);
|
|
|
48523
48622
|
this.relay = new AgentRelay({
|
|
48524
48623
|
...this.relayOptions,
|
|
48525
48624
|
brokerName,
|
|
48526
|
-
channels: [channel],
|
|
48625
|
+
channels: relaycastDisabled ? [] : [channel],
|
|
48527
48626
|
env: this.getRelayEnv(),
|
|
48528
48627
|
// Workflows spawn agents across multiple waves; each spawn requires a PTY +
|
|
48529
48628
|
// Relaycast registration. 60s is too tight when the broker is saturated with
|
|
@@ -48571,6 +48670,18 @@ ${err.suggestion}`);
|
|
|
48571
48670
|
}
|
|
48572
48671
|
};
|
|
48573
48672
|
this.relay.onMessageReceived = (msg) => {
|
|
48673
|
+
this.emit({
|
|
48674
|
+
type: "broker:event",
|
|
48675
|
+
runId,
|
|
48676
|
+
event: {
|
|
48677
|
+
kind: "relay_inbound",
|
|
48678
|
+
event_id: msg.eventId,
|
|
48679
|
+
from: msg.from,
|
|
48680
|
+
target: msg.to,
|
|
48681
|
+
body: msg.text,
|
|
48682
|
+
thread_id: msg.threadId
|
|
48683
|
+
}
|
|
48684
|
+
});
|
|
48574
48685
|
const body = msg.text.length > 120 ? msg.text.slice(0, 117) + "..." : msg.text;
|
|
48575
48686
|
const fromShort = msg.from.replace(/-[a-f0-9]{6,}$/, "");
|
|
48576
48687
|
const toShort = msg.to.replace(/-[a-f0-9]{6,}$/, "");
|
|
@@ -48581,18 +48692,59 @@ ${err.suggestion}`);
|
|
|
48581
48692
|
}
|
|
48582
48693
|
};
|
|
48583
48694
|
this.relay.onAgentSpawned = (agent) => {
|
|
48695
|
+
this.emit({
|
|
48696
|
+
type: "broker:event",
|
|
48697
|
+
runId,
|
|
48698
|
+
event: {
|
|
48699
|
+
kind: "agent_spawned",
|
|
48700
|
+
name: agent.name,
|
|
48701
|
+
runtime: agent.runtime
|
|
48702
|
+
}
|
|
48703
|
+
});
|
|
48584
48704
|
if (!this.activeAgentHandles.has(agent.name)) {
|
|
48585
48705
|
this.log(`[spawned] ${agent.name} (${agent.runtime})`);
|
|
48586
48706
|
}
|
|
48587
48707
|
};
|
|
48708
|
+
this.relay.onAgentReleased = (agent) => {
|
|
48709
|
+
this.emit({
|
|
48710
|
+
type: "broker:event",
|
|
48711
|
+
runId,
|
|
48712
|
+
event: {
|
|
48713
|
+
kind: "agent_released",
|
|
48714
|
+
name: agent.name
|
|
48715
|
+
}
|
|
48716
|
+
});
|
|
48717
|
+
};
|
|
48588
48718
|
this.relay.onAgentExited = (agent) => {
|
|
48719
|
+
this.emit({
|
|
48720
|
+
type: "broker:event",
|
|
48721
|
+
runId,
|
|
48722
|
+
event: {
|
|
48723
|
+
kind: "agent_exited",
|
|
48724
|
+
name: agent.name,
|
|
48725
|
+
code: agent.exitCode,
|
|
48726
|
+
signal: agent.exitSignal
|
|
48727
|
+
}
|
|
48728
|
+
});
|
|
48589
48729
|
this.lastActivity.delete(agent.name);
|
|
48590
48730
|
this.lastIdleLog.delete(agent.name);
|
|
48591
48731
|
if (!this.activeAgentHandles.has(agent.name)) {
|
|
48592
48732
|
this.log(`[exited] ${agent.name} (code: ${agent.exitCode ?? "?"})`);
|
|
48593
48733
|
}
|
|
48594
48734
|
};
|
|
48735
|
+
this.relay.onDeliveryUpdate = (event) => {
|
|
48736
|
+
this.emit({ type: "broker:event", runId, event });
|
|
48737
|
+
};
|
|
48595
48738
|
this.relay.onAgentIdle = ({ name, idleSecs }) => {
|
|
48739
|
+
this.emit({
|
|
48740
|
+
type: "broker:event",
|
|
48741
|
+
runId,
|
|
48742
|
+
event: {
|
|
48743
|
+
kind: "agent_idle",
|
|
48744
|
+
name,
|
|
48745
|
+
idle_secs: idleSecs
|
|
48746
|
+
}
|
|
48747
|
+
});
|
|
48596
48748
|
const bucket = Math.floor(idleSecs / 30) * 30;
|
|
48597
48749
|
if (bucket >= 30 && this.lastIdleLog.get(name) !== bucket) {
|
|
48598
48750
|
this.lastIdleLog.set(name, bucket);
|
|
@@ -48605,17 +48757,19 @@ ${err.suggestion}`);
|
|
|
48605
48757
|
this.unsubBrokerStderr = this.relay.onBrokerStderr((line) => {
|
|
48606
48758
|
console.log(`[broker] ${line}`);
|
|
48607
48759
|
});
|
|
48608
|
-
|
|
48609
|
-
|
|
48610
|
-
|
|
48611
|
-
|
|
48612
|
-
|
|
48613
|
-
|
|
48614
|
-
|
|
48615
|
-
|
|
48616
|
-
|
|
48617
|
-
|
|
48618
|
-
|
|
48760
|
+
if (!relaycastDisabled) {
|
|
48761
|
+
this.log(`Creating channel: ${channel}...`);
|
|
48762
|
+
if (isResume) {
|
|
48763
|
+
await this.createAndJoinRelaycastChannel(channel);
|
|
48764
|
+
} else {
|
|
48765
|
+
await this.createAndJoinRelaycastChannel(channel, workflow2.description);
|
|
48766
|
+
}
|
|
48767
|
+
this.log("Channel ready");
|
|
48768
|
+
if (isResume) {
|
|
48769
|
+
this.postToChannel(`Workflow **${workflow2.name}** resumed \u2014 ${pendingCount} pending steps`);
|
|
48770
|
+
} else {
|
|
48771
|
+
this.postToChannel(`Workflow **${workflow2.name}** started \u2014 ${workflow2.steps.length} steps, pattern: ${config3.swarm.pattern}`);
|
|
48772
|
+
}
|
|
48619
48773
|
}
|
|
48620
48774
|
}
|
|
48621
48775
|
const agentMap = /* @__PURE__ */ new Map();
|
|
@@ -48627,7 +48781,9 @@ ${err.suggestion}`);
|
|
|
48627
48781
|
}
|
|
48628
48782
|
this.log(`Executing ${workflow2.steps.length} steps (pattern: ${config3.swarm.pattern})`);
|
|
48629
48783
|
await this.executeSteps(workflow2, stepStates, agentMap, config3.errorHandling, runId);
|
|
48630
|
-
const
|
|
48784
|
+
const errorStrategy = config3.errorHandling?.strategy ?? workflow2.onError ?? "fail-fast";
|
|
48785
|
+
const continueOnError = errorStrategy === "continue" || errorStrategy === "skip";
|
|
48786
|
+
const allCompleted = [...stepStates.values()].every((s) => s.row.status === "completed" || s.row.status === "skipped" || continueOnError && s.row.status === "failed");
|
|
48631
48787
|
if (allCompleted) {
|
|
48632
48788
|
this.log("Workflow completed successfully");
|
|
48633
48789
|
await this.updateRunStatus(runId, "completed");
|
|
@@ -48647,24 +48803,52 @@ ${err.suggestion}`);
|
|
|
48647
48803
|
await this.updateRunStatus(runId, "failed", errorMsg);
|
|
48648
48804
|
this.emit({ type: "run:failed", runId, error: errorMsg });
|
|
48649
48805
|
const outcomes = this.collectOutcomes(stepStates, workflow2.steps);
|
|
48806
|
+
const summary = this.trajectory.buildRunSummary(outcomes);
|
|
48807
|
+
const confidence = this.trajectory.computeConfidence(outcomes);
|
|
48808
|
+
const learnings = this.trajectory.extractLearnings(outcomes);
|
|
48809
|
+
const challenges = this.trajectory.extractChallenges(outcomes);
|
|
48650
48810
|
this.postFailureReport(workflow2.name, outcomes, errorMsg);
|
|
48651
48811
|
this.logRunSummary(workflow2.name, outcomes, runId);
|
|
48652
|
-
await this.trajectory.abandon(errorMsg
|
|
48812
|
+
await this.trajectory.abandon(errorMsg, {
|
|
48813
|
+
summary,
|
|
48814
|
+
confidence,
|
|
48815
|
+
learnings,
|
|
48816
|
+
challenges
|
|
48817
|
+
});
|
|
48653
48818
|
}
|
|
48654
48819
|
} catch (err) {
|
|
48655
48820
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
48656
48821
|
const status = !isResume && this.abortController?.signal.aborted ? "cancelled" : "failed";
|
|
48657
48822
|
await this.updateRunStatus(runId, status, errorMsg);
|
|
48658
48823
|
if (status === "cancelled") {
|
|
48824
|
+
for (const [stepName, state] of stepStates) {
|
|
48825
|
+
if (state.row.status === "pending" || state.row.status === "running") {
|
|
48826
|
+
state.row.status = "failed";
|
|
48827
|
+
state.row.error = "Cancelled";
|
|
48828
|
+
await this.db.updateStep(state.row.id, {
|
|
48829
|
+
status: "failed",
|
|
48830
|
+
error: "Cancelled",
|
|
48831
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
48832
|
+
});
|
|
48833
|
+
this.emit({ type: "step:failed", runId, stepName, error: "Cancelled" });
|
|
48834
|
+
}
|
|
48835
|
+
}
|
|
48659
48836
|
this.emit({ type: "run:cancelled", runId });
|
|
48660
48837
|
this.postToChannel(`Workflow **${workflow2.name}** cancelled`);
|
|
48661
48838
|
await this.trajectory.abandon("Cancelled by user");
|
|
48662
48839
|
} else {
|
|
48663
48840
|
this.emit({ type: "run:failed", runId, error: errorMsg });
|
|
48664
48841
|
this.postToChannel(`Workflow failed: ${errorMsg}`);
|
|
48665
|
-
|
|
48842
|
+
const outcomes = this.collectOutcomes(stepStates, workflow2.steps);
|
|
48843
|
+
await this.trajectory.abandon(errorMsg, {
|
|
48844
|
+
summary: this.trajectory.buildRunSummary(outcomes),
|
|
48845
|
+
confidence: this.trajectory.computeConfidence(outcomes),
|
|
48846
|
+
learnings: this.trajectory.extractLearnings(outcomes),
|
|
48847
|
+
challenges: this.trajectory.extractChallenges(outcomes)
|
|
48848
|
+
});
|
|
48666
48849
|
}
|
|
48667
48850
|
} finally {
|
|
48851
|
+
this.lastFailedStepOutput.clear();
|
|
48668
48852
|
for (const stream of this.ptyLogStreams.values())
|
|
48669
48853
|
stream.end();
|
|
48670
48854
|
this.ptyLogStreams.clear();
|
|
@@ -48675,9 +48859,11 @@ ${err.suggestion}`);
|
|
|
48675
48859
|
if (this.relay) {
|
|
48676
48860
|
this.relay.onMessageReceived = null;
|
|
48677
48861
|
this.relay.onAgentSpawned = null;
|
|
48862
|
+
this.relay.onAgentReleased = null;
|
|
48678
48863
|
this.relay.onAgentExited = null;
|
|
48679
48864
|
this.relay.onAgentIdle = null;
|
|
48680
48865
|
this.relay.onWorkerOutput = null;
|
|
48866
|
+
this.relay.onDeliveryUpdate = null;
|
|
48681
48867
|
}
|
|
48682
48868
|
this.lastIdleLog.clear();
|
|
48683
48869
|
this.lastActivity.clear();
|
|
@@ -48898,7 +49084,7 @@ ${trimmedOutput.slice(0, 200)}`);
|
|
|
48898
49084
|
}
|
|
48899
49085
|
async executeStep(step, stepStates, agentMap, errorHandling, runId) {
|
|
48900
49086
|
if (this.isDeterministicStep(step)) {
|
|
48901
|
-
return this.executeDeterministicStep(step, stepStates, runId);
|
|
49087
|
+
return this.executeDeterministicStep(step, stepStates, runId, errorHandling);
|
|
48902
49088
|
}
|
|
48903
49089
|
if (this.isWorktreeStep(step)) {
|
|
48904
49090
|
return this.executeWorktreeStep(step, stepStates, runId);
|
|
@@ -48909,131 +49095,154 @@ ${trimmedOutput.slice(0, 200)}`);
|
|
|
48909
49095
|
* Execute a deterministic step (shell command).
|
|
48910
49096
|
* Fast, reliable, $0 LLM cost.
|
|
48911
49097
|
*/
|
|
48912
|
-
async executeDeterministicStep(step, stepStates, runId) {
|
|
49098
|
+
async executeDeterministicStep(step, stepStates, runId, errorHandling) {
|
|
48913
49099
|
const state = stepStates.get(step.name);
|
|
48914
49100
|
if (!state)
|
|
48915
49101
|
throw new Error(`Step state not found: ${step.name}`);
|
|
48916
|
-
|
|
48917
|
-
|
|
48918
|
-
|
|
48919
|
-
|
|
48920
|
-
|
|
48921
|
-
|
|
48922
|
-
|
|
48923
|
-
|
|
48924
|
-
|
|
48925
|
-
this.postToChannel(`**[${step.name}]** Started (deterministic)`);
|
|
48926
|
-
const stepOutputContext = this.buildStepOutputContext(stepStates, runId);
|
|
48927
|
-
let resolvedCommand = this.interpolateStepTask(step.command ?? "", stepOutputContext);
|
|
48928
|
-
resolvedCommand = resolvedCommand.replace(/\{\{([\w][\w.\-]*)\}\}/g, (_match, key) => {
|
|
48929
|
-
if (key.startsWith("steps."))
|
|
48930
|
-
return _match;
|
|
48931
|
-
const value = this.resolveDotPath(key, stepOutputContext);
|
|
48932
|
-
return value !== void 0 ? String(value) : _match;
|
|
48933
|
-
});
|
|
48934
|
-
const stepCwd = this.resolveStepWorkdir(step) ?? this.cwd;
|
|
48935
|
-
try {
|
|
48936
|
-
if (this.executor?.executeDeterministicStep) {
|
|
48937
|
-
const result = await this.executor.executeDeterministicStep(step, resolvedCommand, stepCwd);
|
|
48938
|
-
const failOnError = step.failOnError !== false;
|
|
48939
|
-
if (failOnError && result.exitCode !== 0) {
|
|
48940
|
-
throw new Error(`Command failed with exit code ${result.exitCode}: ${result.output.slice(0, 500)}`);
|
|
48941
|
-
}
|
|
48942
|
-
const output2 = step.captureOutput !== false ? result.output : `Command completed (exit code ${result.exitCode})`;
|
|
48943
|
-
state.row.status = "completed";
|
|
48944
|
-
state.row.output = output2;
|
|
48945
|
-
state.row.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
49102
|
+
const maxRetries = step.retries ?? errorHandling?.maxRetries ?? 0;
|
|
49103
|
+
const retryDelay = errorHandling?.retryDelayMs ?? 1e3;
|
|
49104
|
+
let lastError;
|
|
49105
|
+
for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
|
|
49106
|
+
this.checkAborted();
|
|
49107
|
+
if (attempt > 0) {
|
|
49108
|
+
this.emit({ type: "step:retrying", runId, stepName: step.name, attempt });
|
|
49109
|
+
this.postToChannel(`**[${step.name}]** Retrying (attempt ${attempt + 1}/${maxRetries + 1})`);
|
|
49110
|
+
state.row.retryCount = attempt;
|
|
48946
49111
|
await this.db.updateStep(state.row.id, {
|
|
48947
|
-
|
|
48948
|
-
output: output2,
|
|
48949
|
-
completedAt: state.row.completedAt,
|
|
49112
|
+
retryCount: attempt,
|
|
48950
49113
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
48951
49114
|
});
|
|
48952
|
-
await this.
|
|
48953
|
-
this.emit({ type: "step:completed", runId, stepName: step.name, output: output2 });
|
|
48954
|
-
return;
|
|
49115
|
+
await this.delay(retryDelay);
|
|
48955
49116
|
}
|
|
48956
|
-
|
|
48957
|
-
|
|
48958
|
-
|
|
48959
|
-
|
|
48960
|
-
|
|
48961
|
-
|
|
48962
|
-
|
|
48963
|
-
|
|
48964
|
-
|
|
48965
|
-
|
|
48966
|
-
|
|
48967
|
-
|
|
48968
|
-
|
|
48969
|
-
|
|
48970
|
-
|
|
48971
|
-
|
|
48972
|
-
|
|
48973
|
-
|
|
48974
|
-
|
|
48975
|
-
if (
|
|
48976
|
-
|
|
48977
|
-
|
|
48978
|
-
|
|
48979
|
-
|
|
48980
|
-
}, step.timeoutMs);
|
|
48981
|
-
}
|
|
48982
|
-
child.stdout?.on("data", (chunk) => {
|
|
48983
|
-
stdoutChunks.push(chunk.toString());
|
|
48984
|
-
});
|
|
48985
|
-
child.stderr?.on("data", (chunk) => {
|
|
48986
|
-
stderrChunks.push(chunk.toString());
|
|
48987
|
-
});
|
|
48988
|
-
child.on("close", (code) => {
|
|
48989
|
-
if (timer)
|
|
48990
|
-
clearTimeout(timer);
|
|
48991
|
-
if (abortHandler && abortSignal) {
|
|
48992
|
-
abortSignal.removeEventListener("abort", abortHandler);
|
|
49117
|
+
state.row.status = "running";
|
|
49118
|
+
state.row.startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
49119
|
+
await this.db.updateStep(state.row.id, {
|
|
49120
|
+
status: "running",
|
|
49121
|
+
startedAt: state.row.startedAt,
|
|
49122
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
49123
|
+
});
|
|
49124
|
+
this.emit({ type: "step:started", runId, stepName: step.name });
|
|
49125
|
+
this.postToChannel(`**[${step.name}]** Started (deterministic)`);
|
|
49126
|
+
const stepOutputContext = this.buildStepOutputContext(stepStates, runId);
|
|
49127
|
+
let resolvedCommand = this.interpolateStepTask(step.command ?? "", stepOutputContext);
|
|
49128
|
+
resolvedCommand = resolvedCommand.replace(/\{\{([\w][\w.\-]*)\}\}/g, (_match, key) => {
|
|
49129
|
+
if (key.startsWith("steps."))
|
|
49130
|
+
return _match;
|
|
49131
|
+
const value = this.resolveDotPath(key, stepOutputContext);
|
|
49132
|
+
return value !== void 0 ? String(value) : _match;
|
|
49133
|
+
});
|
|
49134
|
+
const stepCwd = this.resolveStepWorkdir(step) ?? this.cwd;
|
|
49135
|
+
try {
|
|
49136
|
+
if (this.executor?.executeDeterministicStep) {
|
|
49137
|
+
const result = await this.executor.executeDeterministicStep(step, resolvedCommand, stepCwd);
|
|
49138
|
+
const failOnError = step.failOnError !== false;
|
|
49139
|
+
if (failOnError && result.exitCode !== 0) {
|
|
49140
|
+
throw new Error(`Command failed with exit code ${result.exitCode}: ${result.output.slice(0, 500)}`);
|
|
48993
49141
|
}
|
|
48994
|
-
|
|
48995
|
-
|
|
48996
|
-
|
|
49142
|
+
const output2 = step.captureOutput !== false ? result.output : `Command completed (exit code ${result.exitCode})`;
|
|
49143
|
+
if (step.verification) {
|
|
49144
|
+
this.runVerification(step.verification, output2, step.name);
|
|
48997
49145
|
}
|
|
48998
|
-
|
|
48999
|
-
|
|
49000
|
-
|
|
49146
|
+
state.row.status = "completed";
|
|
49147
|
+
state.row.output = output2;
|
|
49148
|
+
state.row.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
49149
|
+
await this.db.updateStep(state.row.id, {
|
|
49150
|
+
status: "completed",
|
|
49151
|
+
output: output2,
|
|
49152
|
+
completedAt: state.row.completedAt,
|
|
49153
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
49154
|
+
});
|
|
49155
|
+
await this.persistStepOutput(runId, step.name, output2);
|
|
49156
|
+
this.emit({ type: "step:completed", runId, stepName: step.name, output: output2 });
|
|
49157
|
+
return;
|
|
49158
|
+
}
|
|
49159
|
+
const output = await new Promise((resolve3, reject) => {
|
|
49160
|
+
const child = (0, import_node_child_process3.spawn)("sh", ["-c", resolvedCommand], {
|
|
49161
|
+
stdio: "pipe",
|
|
49162
|
+
cwd: stepCwd,
|
|
49163
|
+
env: { ...process.env }
|
|
49164
|
+
});
|
|
49165
|
+
const stdoutChunks = [];
|
|
49166
|
+
const stderrChunks = [];
|
|
49167
|
+
const abortSignal = this.abortController?.signal;
|
|
49168
|
+
let abortHandler;
|
|
49169
|
+
if (abortSignal && !abortSignal.aborted) {
|
|
49170
|
+
abortHandler = () => {
|
|
49171
|
+
child.kill("SIGTERM");
|
|
49172
|
+
setTimeout(() => child.kill("SIGKILL"), 5e3);
|
|
49173
|
+
};
|
|
49174
|
+
abortSignal.addEventListener("abort", abortHandler, { once: true });
|
|
49001
49175
|
}
|
|
49002
|
-
|
|
49003
|
-
|
|
49004
|
-
|
|
49005
|
-
|
|
49006
|
-
|
|
49007
|
-
|
|
49176
|
+
let timedOut = false;
|
|
49177
|
+
let timer;
|
|
49178
|
+
if (step.timeoutMs) {
|
|
49179
|
+
timer = setTimeout(() => {
|
|
49180
|
+
timedOut = true;
|
|
49181
|
+
child.kill("SIGTERM");
|
|
49182
|
+
setTimeout(() => child.kill("SIGKILL"), 5e3);
|
|
49183
|
+
}, step.timeoutMs);
|
|
49008
49184
|
}
|
|
49009
|
-
|
|
49185
|
+
child.stdout?.on("data", (chunk) => {
|
|
49186
|
+
stdoutChunks.push(chunk.toString());
|
|
49187
|
+
});
|
|
49188
|
+
child.stderr?.on("data", (chunk) => {
|
|
49189
|
+
stderrChunks.push(chunk.toString());
|
|
49190
|
+
});
|
|
49191
|
+
child.on("close", (code) => {
|
|
49192
|
+
if (timer)
|
|
49193
|
+
clearTimeout(timer);
|
|
49194
|
+
if (abortHandler && abortSignal) {
|
|
49195
|
+
abortSignal.removeEventListener("abort", abortHandler);
|
|
49196
|
+
}
|
|
49197
|
+
if (abortSignal?.aborted) {
|
|
49198
|
+
reject(new Error(`Step "${step.name}" aborted`));
|
|
49199
|
+
return;
|
|
49200
|
+
}
|
|
49201
|
+
if (timedOut) {
|
|
49202
|
+
reject(new Error(`Step "${step.name}" timed out (no step timeout set, check global swarm.timeoutMs)`));
|
|
49203
|
+
return;
|
|
49204
|
+
}
|
|
49205
|
+
const stdout = stdoutChunks.join("");
|
|
49206
|
+
const stderr = stderrChunks.join("");
|
|
49207
|
+
const failOnError = step.failOnError !== false;
|
|
49208
|
+
if (failOnError && code !== 0 && code !== null) {
|
|
49209
|
+
reject(new Error(`Command failed with exit code ${code}${stderr ? `: ${stderr.slice(0, 500)}` : ""}`));
|
|
49210
|
+
return;
|
|
49211
|
+
}
|
|
49212
|
+
resolve3(step.captureOutput !== false ? stdout : `Command completed (exit code ${code ?? 0})`);
|
|
49213
|
+
});
|
|
49214
|
+
child.on("error", (err) => {
|
|
49215
|
+
if (timer)
|
|
49216
|
+
clearTimeout(timer);
|
|
49217
|
+
if (abortHandler && abortSignal) {
|
|
49218
|
+
abortSignal.removeEventListener("abort", abortHandler);
|
|
49219
|
+
}
|
|
49220
|
+
reject(new Error(`Failed to execute command: ${err.message}`));
|
|
49221
|
+
});
|
|
49010
49222
|
});
|
|
49011
|
-
|
|
49012
|
-
|
|
49013
|
-
|
|
49014
|
-
|
|
49015
|
-
|
|
49016
|
-
|
|
49017
|
-
|
|
49223
|
+
if (step.verification) {
|
|
49224
|
+
this.runVerification(step.verification, output, step.name);
|
|
49225
|
+
}
|
|
49226
|
+
state.row.status = "completed";
|
|
49227
|
+
state.row.output = output;
|
|
49228
|
+
state.row.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
49229
|
+
await this.db.updateStep(state.row.id, {
|
|
49230
|
+
status: "completed",
|
|
49231
|
+
output,
|
|
49232
|
+
completedAt: state.row.completedAt,
|
|
49233
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
49018
49234
|
});
|
|
49019
|
-
|
|
49020
|
-
|
|
49021
|
-
|
|
49022
|
-
|
|
49023
|
-
|
|
49024
|
-
|
|
49025
|
-
output,
|
|
49026
|
-
completedAt: state.row.completedAt,
|
|
49027
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
49028
|
-
});
|
|
49029
|
-
await this.persistStepOutput(runId, step.name, output);
|
|
49030
|
-
this.emit({ type: "step:completed", runId, stepName: step.name, output });
|
|
49031
|
-
} catch (err) {
|
|
49032
|
-
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
49033
|
-
this.postToChannel(`**[${step.name}]** Failed: ${errorMsg}`);
|
|
49034
|
-
await this.markStepFailed(state, errorMsg, runId);
|
|
49035
|
-
throw new Error(`Step "${step.name}" failed: ${errorMsg}`);
|
|
49235
|
+
await this.persistStepOutput(runId, step.name, output);
|
|
49236
|
+
this.emit({ type: "step:completed", runId, stepName: step.name, output });
|
|
49237
|
+
return;
|
|
49238
|
+
} catch (err) {
|
|
49239
|
+
lastError = err instanceof Error ? err.message : String(err);
|
|
49240
|
+
}
|
|
49036
49241
|
}
|
|
49242
|
+
const errorMsg = lastError ?? "Unknown error";
|
|
49243
|
+
this.postToChannel(`**[${step.name}]** Failed: ${errorMsg}`);
|
|
49244
|
+
await this.markStepFailed(state, errorMsg, runId);
|
|
49245
|
+
throw new Error(`Step "${step.name}" failed: ${errorMsg}`);
|
|
49037
49246
|
}
|
|
49038
49247
|
/**
|
|
49039
49248
|
* Execute a worktree step (git worktree setup).
|
|
@@ -49202,8 +49411,12 @@ ${trimmedOutput.slice(0, 200)}`);
|
|
|
49202
49411
|
const retryDelay = errorHandling?.retryDelayMs ?? 1e3;
|
|
49203
49412
|
const timeoutMs = step.timeoutMs ?? ownerDef.constraints?.timeoutMs ?? specialistDef.constraints?.timeoutMs ?? this.currentConfig?.swarm?.timeoutMs;
|
|
49204
49413
|
let lastError;
|
|
49414
|
+
let lastExitCode;
|
|
49415
|
+
let lastExitSignal;
|
|
49205
49416
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
49206
49417
|
this.checkAborted();
|
|
49418
|
+
lastExitCode = void 0;
|
|
49419
|
+
lastExitSignal = void 0;
|
|
49207
49420
|
if (attempt > 0) {
|
|
49208
49421
|
this.emit({ type: "step:retrying", runId, stepName: step.name, attempt });
|
|
49209
49422
|
this.postToChannel(`**[${step.name}]** Retrying (attempt ${attempt + 1}/${maxRetries + 1})`);
|
|
@@ -49243,6 +49456,15 @@ ${trimmedOutput.slice(0, 200)}`);
|
|
|
49243
49456
|
});
|
|
49244
49457
|
const stepOutputContext = this.buildStepOutputContext(stepStates, runId);
|
|
49245
49458
|
let resolvedTask = this.interpolateStepTask(step.task ?? "", stepOutputContext);
|
|
49459
|
+
if (attempt > 0 && lastError) {
|
|
49460
|
+
const priorOutput = (this.lastFailedStepOutput.get(step.name) ?? "").slice(-2e3);
|
|
49461
|
+
resolvedTask = `[RETRY \u2014 Attempt ${attempt + 1}/${maxRetries + 1}]
|
|
49462
|
+
Previous attempt failed: ${lastError}
|
|
49463
|
+
` + (priorOutput ? `Previous output (last 2000 chars):
|
|
49464
|
+
${priorOutput}
|
|
49465
|
+
` : "") + `---
|
|
49466
|
+
${resolvedTask}`;
|
|
49467
|
+
}
|
|
49246
49468
|
if (specialistDef.interactive !== false || ownerDef.interactive !== false) {
|
|
49247
49469
|
const nonInteractiveInfo = this.buildNonInteractiveAwareness(agentMap, stepStates);
|
|
49248
49470
|
if (nonInteractiveInfo) {
|
|
@@ -49273,7 +49495,10 @@ ${trimmedOutput.slice(0, 200)}`);
|
|
|
49273
49495
|
this.log(`[${step.name}] Spawning owner "${effectiveOwner.name}" (cli: ${effectiveOwner.cli})${step.workdir ? ` [workdir: ${step.workdir}]` : ""}`);
|
|
49274
49496
|
const resolvedStep = { ...step, task: ownerTask };
|
|
49275
49497
|
const ownerStartTime = Date.now();
|
|
49276
|
-
const
|
|
49498
|
+
const spawnResult = this.executor ? await this.executor.executeAgentStep(resolvedStep, effectiveOwner, ownerTask, timeoutMs) : await this.spawnAndWait(effectiveOwner, resolvedStep, timeoutMs);
|
|
49499
|
+
const output = typeof spawnResult === "string" ? spawnResult : spawnResult.output;
|
|
49500
|
+
lastExitCode = typeof spawnResult === "string" ? void 0 : spawnResult.exitCode;
|
|
49501
|
+
lastExitSignal = typeof spawnResult === "string" ? void 0 : spawnResult.exitSignal;
|
|
49277
49502
|
ownerElapsed = Date.now() - ownerStartTime;
|
|
49278
49503
|
this.log(`[${step.name}] Owner "${effectiveOwner.name}" exited`);
|
|
49279
49504
|
if (usesOwnerFlow) {
|
|
@@ -49283,7 +49508,7 @@ ${trimmedOutput.slice(0, 200)}`);
|
|
|
49283
49508
|
ownerOutput = output;
|
|
49284
49509
|
}
|
|
49285
49510
|
if (step.verification) {
|
|
49286
|
-
this.runVerification(step.verification, specialistOutput, step.name, resolvedTask);
|
|
49511
|
+
this.runVerification(step.verification, specialistOutput, step.name, effectiveOwner.interactive === false ? void 0 : resolvedTask);
|
|
49287
49512
|
}
|
|
49288
49513
|
let combinedOutput = specialistOutput;
|
|
49289
49514
|
if (usesOwnerFlow && reviewDef) {
|
|
@@ -49301,11 +49526,15 @@ ${trimmedOutput.slice(0, 200)}`);
|
|
|
49301
49526
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
49302
49527
|
});
|
|
49303
49528
|
await this.persistStepOutput(runId, step.name, combinedOutput);
|
|
49304
|
-
this.emit({ type: "step:completed", runId, stepName: step.name, output: combinedOutput });
|
|
49529
|
+
this.emit({ type: "step:completed", runId, stepName: step.name, output: combinedOutput, exitCode: lastExitCode, exitSignal: lastExitSignal });
|
|
49305
49530
|
await this.trajectory?.stepCompleted(step, combinedOutput, attempt + 1);
|
|
49306
49531
|
return;
|
|
49307
49532
|
} catch (err) {
|
|
49308
49533
|
lastError = err instanceof Error ? err.message : String(err);
|
|
49534
|
+
if (err instanceof SpawnExitError) {
|
|
49535
|
+
lastExitCode = err.exitCode;
|
|
49536
|
+
lastExitSignal = err.exitSignal;
|
|
49537
|
+
}
|
|
49309
49538
|
const ownerTimedOut = usesDedicatedOwner ? /\bowner timed out\b/i.test(lastError) : /\btimed out\b/i.test(lastError) && !lastError.includes(`${step.name}-review`);
|
|
49310
49539
|
if (ownerTimedOut) {
|
|
49311
49540
|
this.emit({ type: "step:owner-timeout", runId, stepName: step.name, ownerName: ownerDef.name });
|
|
@@ -49320,7 +49549,10 @@ ${trimmedOutput.slice(0, 200)}`);
|
|
|
49320
49549
|
verificationValue
|
|
49321
49550
|
});
|
|
49322
49551
|
this.postToChannel(`**[${step.name}]** Failed: ${lastError ?? "Unknown error"}`);
|
|
49323
|
-
await this.markStepFailed(state, lastError ?? "Unknown error", runId
|
|
49552
|
+
await this.markStepFailed(state, lastError ?? "Unknown error", runId, {
|
|
49553
|
+
exitCode: lastExitCode,
|
|
49554
|
+
exitSignal: lastExitSignal
|
|
49555
|
+
});
|
|
49324
49556
|
throw new Error(`Step "${step.name}" failed after ${maxRetries} retries: ${lastError ?? "Unknown error"}`);
|
|
49325
49557
|
}
|
|
49326
49558
|
injectStepOwnerContract(step, resolvedTask, ownerDef, specialistDef) {
|
|
@@ -49438,10 +49670,10 @@ Output exactly: STEP_COMPLETE:${step.name}`;
|
|
|
49438
49670
|
throw error95;
|
|
49439
49671
|
});
|
|
49440
49672
|
const workerSettled = workerPromise.catch(() => void 0);
|
|
49441
|
-
workerPromise.then((
|
|
49673
|
+
workerPromise.then((result) => {
|
|
49442
49674
|
workerReleased = true;
|
|
49443
49675
|
this.postToChannel(`**[${step.name}]** Worker \`${workerRuntimeName}\` exited`);
|
|
49444
|
-
if (step.verification?.type === "output_contains" && output.includes(step.verification.value)) {
|
|
49676
|
+
if (step.verification?.type === "output_contains" && result.output.includes(step.verification.value)) {
|
|
49445
49677
|
this.postToChannel(`**[${step.name}]** Verification gate observed: output contains ${JSON.stringify(step.verification.value)}`);
|
|
49446
49678
|
}
|
|
49447
49679
|
}).catch((error95) => {
|
|
@@ -49459,7 +49691,7 @@ Output exactly: STEP_COMPLETE:${step.name}`;
|
|
|
49459
49691
|
this.log(`[${step.name}] Spawning owner "${supervised.owner.name}" (cli: ${supervised.owner.cli})`);
|
|
49460
49692
|
const ownerStartTime = Date.now();
|
|
49461
49693
|
try {
|
|
49462
|
-
const
|
|
49694
|
+
const ownerResultObj = await this.spawnAndWait(supervised.owner, ownerStep, timeoutMs, {
|
|
49463
49695
|
agentNameSuffix: "owner",
|
|
49464
49696
|
onSpawned: ({ actualName }) => {
|
|
49465
49697
|
this.supervisedRuntimeAgents.set(actualName, {
|
|
@@ -49473,9 +49705,10 @@ Output exactly: STEP_COMPLETE:${step.name}`;
|
|
|
49473
49705
|
}
|
|
49474
49706
|
});
|
|
49475
49707
|
const ownerElapsed = Date.now() - ownerStartTime;
|
|
49708
|
+
const ownerOutput = ownerResultObj.output;
|
|
49476
49709
|
this.log(`[${step.name}] Owner "${supervised.owner.name}" exited`);
|
|
49477
49710
|
this.assertOwnerCompletionMarker(step, ownerOutput, supervisorTask);
|
|
49478
|
-
const specialistOutput = await workerPromise;
|
|
49711
|
+
const specialistOutput = (await workerPromise).output;
|
|
49479
49712
|
return { specialistOutput, ownerOutput, ownerElapsed };
|
|
49480
49713
|
} catch (error95) {
|
|
49481
49714
|
const message = error95 instanceof Error ? error95.message : String(error95);
|
|
@@ -49673,7 +49906,7 @@ Then output /exit.`;
|
|
|
49673
49906
|
})();
|
|
49674
49907
|
};
|
|
49675
49908
|
try {
|
|
49676
|
-
|
|
49909
|
+
await this.spawnAndWait(reviewerDef, reviewStep, safetyTimeoutMs, {
|
|
49677
49910
|
onSpawned: ({ agent }) => {
|
|
49678
49911
|
reviewerHandle = agent;
|
|
49679
49912
|
},
|
|
@@ -49853,7 +50086,7 @@ DO NOT:
|
|
|
49853
50086
|
const stdoutChunks = [];
|
|
49854
50087
|
const stderrChunks = [];
|
|
49855
50088
|
try {
|
|
49856
|
-
const output = await new Promise((resolve3, reject) => {
|
|
50089
|
+
const { stdout: output, exitCode, exitSignal } = await new Promise((resolve3, reject) => {
|
|
49857
50090
|
const child = (0, import_node_child_process3.spawn)(cmd, args, {
|
|
49858
50091
|
stdio: ["ignore", "pipe", "pipe"],
|
|
49859
50092
|
cwd: this.resolveAgentCwd(agentDef),
|
|
@@ -49899,7 +50132,7 @@ DO NOT:
|
|
|
49899
50132
|
setTimeout(() => child.kill("SIGKILL"), 5e3);
|
|
49900
50133
|
}, timeoutMs);
|
|
49901
50134
|
}
|
|
49902
|
-
child.on("close", (code) => {
|
|
50135
|
+
child.on("close", (code, signal) => {
|
|
49903
50136
|
clearInterval(heartbeat);
|
|
49904
50137
|
if (timer)
|
|
49905
50138
|
clearTimeout(timer);
|
|
@@ -49917,10 +50150,14 @@ DO NOT:
|
|
|
49917
50150
|
}
|
|
49918
50151
|
if (code !== 0 && code !== null) {
|
|
49919
50152
|
const stderr = stderrChunks.join("");
|
|
49920
|
-
reject(new
|
|
50153
|
+
reject(new SpawnExitError(`Step "${step.name}" exited with code ${code}${stderr ? `: ${stderr.slice(0, 500)}` : ""}`, code, signal));
|
|
49921
50154
|
return;
|
|
49922
50155
|
}
|
|
49923
|
-
resolve3(
|
|
50156
|
+
resolve3({
|
|
50157
|
+
stdout,
|
|
50158
|
+
exitCode: code ?? void 0,
|
|
50159
|
+
exitSignal: signal ?? void 0
|
|
50160
|
+
});
|
|
49924
50161
|
});
|
|
49925
50162
|
child.on("error", (err) => {
|
|
49926
50163
|
clearInterval(heartbeat);
|
|
@@ -49932,8 +50169,10 @@ DO NOT:
|
|
|
49932
50169
|
reject(new Error(`Failed to spawn ${cmd}: ${err.message}`));
|
|
49933
50170
|
});
|
|
49934
50171
|
});
|
|
49935
|
-
return output;
|
|
50172
|
+
return { output, exitCode, exitSignal };
|
|
49936
50173
|
} finally {
|
|
50174
|
+
const combinedOutput = stdoutChunks.join("") + stderrChunks.join("");
|
|
50175
|
+
this.lastFailedStepOutput.set(step.name, combinedOutput);
|
|
49937
50176
|
stopHeartbeat?.();
|
|
49938
50177
|
logStream.end();
|
|
49939
50178
|
this.unregisterWorker(agentName);
|
|
@@ -50053,8 +50292,12 @@ DO NOT:
|
|
|
50053
50292
|
throw new Error(`Step "${step.name}" timed out after ${timeoutMs ?? "unknown"}ms`);
|
|
50054
50293
|
}
|
|
50055
50294
|
}
|
|
50295
|
+
if (exitResult === "force-released") {
|
|
50296
|
+
throw new Error(`Step "${step.name}" failed \u2014 agent was force-released after exhausting idle nudges without completing`);
|
|
50297
|
+
}
|
|
50056
50298
|
} finally {
|
|
50057
50299
|
ptyChunks = this.ptyOutputBuffers.get(agentName) ?? [];
|
|
50300
|
+
this.lastFailedStepOutput.set(step.name, ptyChunks.join(""));
|
|
50058
50301
|
stopHeartbeat?.();
|
|
50059
50302
|
this.activeAgentHandles.delete(agentName);
|
|
50060
50303
|
this.ptyOutputBuffers.delete(agentName);
|
|
@@ -50072,9 +50315,13 @@ DO NOT:
|
|
|
50072
50315
|
output = ptyChunks.join("");
|
|
50073
50316
|
} else {
|
|
50074
50317
|
const summaryPath = import_node_path8.default.join(this.summaryDir, `${step.name}.md`);
|
|
50075
|
-
output = (0, import_node_fs4.existsSync)(summaryPath) ? await (0, import_promises3.readFile)(summaryPath, "utf-8") : exitResult === "timeout" ? "Agent completed (released after idle timeout)" : exitResult === "released" ? "Agent completed (
|
|
50318
|
+
output = (0, import_node_fs4.existsSync)(summaryPath) ? await (0, import_promises3.readFile)(summaryPath, "utf-8") : exitResult === "timeout" ? "Agent completed (released after idle timeout)" : exitResult === "released" ? "Agent completed (idle \u2014 treated as done)" : `Agent exited (${exitResult})`;
|
|
50076
50319
|
}
|
|
50077
|
-
return
|
|
50320
|
+
return {
|
|
50321
|
+
output,
|
|
50322
|
+
exitCode: agent?.exitCode,
|
|
50323
|
+
exitSignal: agent?.exitSignal
|
|
50324
|
+
};
|
|
50078
50325
|
}
|
|
50079
50326
|
// ── Idle nudging ────────────────────────────────────────────────────────
|
|
50080
50327
|
/** Patterns where a hub agent coordinates spoke agents. */
|
|
@@ -50133,7 +50380,7 @@ DO NOT:
|
|
|
50133
50380
|
if (exitResult !== "timeout") {
|
|
50134
50381
|
return exitResult;
|
|
50135
50382
|
}
|
|
50136
|
-
if (
|
|
50383
|
+
if (timeoutMs !== void 0 && Date.now() - startTime >= timeoutMs) {
|
|
50137
50384
|
return "timeout";
|
|
50138
50385
|
}
|
|
50139
50386
|
if (nudgeCount < maxNudges) {
|
|
@@ -50146,7 +50393,7 @@ DO NOT:
|
|
|
50146
50393
|
this.postToChannel(`**[${step.name}]** Agent \`${agent.name}\` still idle after ${nudgeCount} nudge(s) \u2014 force-releasing`);
|
|
50147
50394
|
this.emit({ type: "step:force-released", runId: this.currentRunId ?? "", stepName: step.name });
|
|
50148
50395
|
await agent.release();
|
|
50149
|
-
return "released";
|
|
50396
|
+
return "force-released";
|
|
50150
50397
|
}
|
|
50151
50398
|
}
|
|
50152
50399
|
/**
|
|
@@ -50241,7 +50488,7 @@ DO NOT:
|
|
|
50241
50488
|
}
|
|
50242
50489
|
await this.db.updateRun(runId, patch);
|
|
50243
50490
|
}
|
|
50244
|
-
async markStepFailed(state, error95, runId) {
|
|
50491
|
+
async markStepFailed(state, error95, runId, exitInfo) {
|
|
50245
50492
|
state.row.status = "failed";
|
|
50246
50493
|
state.row.error = error95;
|
|
50247
50494
|
state.row.completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -50251,7 +50498,14 @@ DO NOT:
|
|
|
50251
50498
|
completedAt: state.row.completedAt,
|
|
50252
50499
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
50253
50500
|
});
|
|
50254
|
-
this.emit({
|
|
50501
|
+
this.emit({
|
|
50502
|
+
type: "step:failed",
|
|
50503
|
+
runId,
|
|
50504
|
+
stepName: state.row.stepName,
|
|
50505
|
+
error: error95,
|
|
50506
|
+
exitCode: exitInfo?.exitCode,
|
|
50507
|
+
exitSignal: exitInfo?.exitSignal
|
|
50508
|
+
});
|
|
50255
50509
|
}
|
|
50256
50510
|
async markDownstreamSkipped(failedStepName, allSteps, stepStates, runId) {
|
|
50257
50511
|
const queue = [failedStepName];
|