@desplega.ai/agent-swarm 1.100.0 → 1.100.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/openapi.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"openapi": "3.1.0",
|
|
3
3
|
"info": {
|
|
4
4
|
"title": "Agent Swarm API",
|
|
5
|
-
"version": "1.100.
|
|
5
|
+
"version": "1.100.1",
|
|
6
6
|
"description": "Multi-agent orchestration API for Claude Code, Codex, and Gemini CLI. Enables task distribution, agent communication, and service discovery.\n\nMCP tools are documented separately in [MCP.md](./MCP.md)."
|
|
7
7
|
},
|
|
8
8
|
"servers": [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@desplega.ai/agent-swarm",
|
|
3
|
-
"version": "1.100.
|
|
3
|
+
"version": "1.100.1",
|
|
4
4
|
"description": "Multi-agent orchestration for Claude Code, Codex, Gemini CLI, and other AI coding assistants",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "desplega.sh <contact@desplega.sh>",
|
package/src/commands/runner.ts
CHANGED
|
@@ -1451,7 +1451,7 @@ function setupShutdownHandlers(
|
|
|
1451
1451
|
);
|
|
1452
1452
|
for (const [taskId, task] of state.activeTasks) {
|
|
1453
1453
|
console.log(`[${role}] Superseding task ${taskId.slice(0, 8)}`);
|
|
1454
|
-
task.session.abort().catch(() => {});
|
|
1454
|
+
task.session.abort("graceful_shutdown").catch(() => {});
|
|
1455
1455
|
if (apiConfig) {
|
|
1456
1456
|
const supersede = await supersedeTaskViaAPI(
|
|
1457
1457
|
apiConfig,
|
|
@@ -4706,7 +4706,7 @@ export async function runAgent(config: RunnerConfig, opts: RunnerOptions) {
|
|
|
4706
4706
|
console.log(
|
|
4707
4707
|
`[${role}] Task ${taskId.slice(0, 8)} was cancelled — sending SIGTERM to subprocess`,
|
|
4708
4708
|
);
|
|
4709
|
-
task.session.abort().catch(() => {});
|
|
4709
|
+
task.session.abort("cancelled").catch(() => {});
|
|
4710
4710
|
cancelledSignaled.add(taskId);
|
|
4711
4711
|
}
|
|
4712
4712
|
}
|
|
@@ -519,9 +519,9 @@ export class CodexSession implements ProviderSession {
|
|
|
519
519
|
return this.completionPromise;
|
|
520
520
|
}
|
|
521
521
|
|
|
522
|
-
async abort(): Promise<void> {
|
|
522
|
+
async abort(reason?: string): Promise<void> {
|
|
523
523
|
this.aborted = true;
|
|
524
|
-
this.abortController?.abort();
|
|
524
|
+
this.abortController?.abort(reason ?? "cancelled");
|
|
525
525
|
}
|
|
526
526
|
|
|
527
527
|
private emit(event: ProviderEvent): void {
|
|
@@ -992,6 +992,14 @@ export class CodexSession implements ProviderSession {
|
|
|
992
992
|
} catch (err) {
|
|
993
993
|
// AbortError from the SDK propagates here when signal.abort() fires.
|
|
994
994
|
if (this.aborted || (err instanceof Error && err.name === "AbortError")) {
|
|
995
|
+
// Prefer the abort reason from the signal (set by the caller of
|
|
996
|
+
// abort()) — this distinguishes tool-loop aborts from cancel-poll
|
|
997
|
+
// and graceful-shutdown aborts that all used to produce a bare
|
|
998
|
+
// "cancelled" failureReason.
|
|
999
|
+
const abortReason =
|
|
1000
|
+
typeof this.abortController?.signal.reason === "string"
|
|
1001
|
+
? this.abortController.signal.reason
|
|
1002
|
+
: "cancelled";
|
|
995
1003
|
const cost = this.buildCostData(this.lastUsage, true);
|
|
996
1004
|
this.emit({ type: "result", cost, isError: true, errorCategory: "cancelled" });
|
|
997
1005
|
this.settle({
|
|
@@ -999,7 +1007,7 @@ export class CodexSession implements ProviderSession {
|
|
|
999
1007
|
sessionId: this._sessionId,
|
|
1000
1008
|
cost,
|
|
1001
1009
|
isError: true,
|
|
1002
|
-
failureReason:
|
|
1010
|
+
failureReason: abortReason,
|
|
1003
1011
|
});
|
|
1004
1012
|
return;
|
|
1005
1013
|
}
|
|
@@ -126,7 +126,7 @@ export function createSwarmEventHandler(
|
|
|
126
126
|
console.log(
|
|
127
127
|
`[swarm-events] aborting task ${taskId}: cancelled via /cancelled-tasks poll`,
|
|
128
128
|
);
|
|
129
|
-
opts.abortRef.current?.abort();
|
|
129
|
+
opts.abortRef.current?.abort("cancelled");
|
|
130
130
|
if (opts.onCancel) {
|
|
131
131
|
try {
|
|
132
132
|
await opts.onCancel();
|
|
@@ -152,10 +152,9 @@ export function createSwarmEventHandler(
|
|
|
152
152
|
// was indistinguishable from a /cancelled-tasks abort or a runner
|
|
153
153
|
// SIGTERM. `result.reason` already carries the diagnostic detail
|
|
154
154
|
// ("Tool X called 15 times…", "ping-pong between A and B…").
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
);
|
|
158
|
-
opts.abortRef.current?.abort();
|
|
155
|
+
const loopReason = `tool-loop: ${result.reason ?? "unknown reason"}`;
|
|
156
|
+
console.log(`[swarm-events] aborting task ${taskId}: ${loopReason}`);
|
|
157
|
+
opts.abortRef.current?.abort(loopReason);
|
|
159
158
|
}
|
|
160
159
|
})
|
|
161
160
|
.catch(() => {});
|
package/src/providers/types.ts
CHANGED
|
@@ -118,7 +118,7 @@ export interface ProviderSession {
|
|
|
118
118
|
readonly sessionId: string | undefined;
|
|
119
119
|
onEvent(listener: (event: ProviderEvent) => void): void;
|
|
120
120
|
waitForCompletion(): Promise<ProviderResult>;
|
|
121
|
-
abort(): Promise<void>;
|
|
121
|
+
abort(reason?: string): Promise<void>;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
/** Result returned when a provider session completes. */
|
|
@@ -97,6 +97,30 @@ describe("createCodexSwarmEventHandler", () => {
|
|
|
97
97
|
expect(controller.signal.aborted).toBe(true);
|
|
98
98
|
});
|
|
99
99
|
|
|
100
|
+
test("sets abort signal reason to 'cancelled' on cancel-poll abort", async () => {
|
|
101
|
+
installFetchStub((url) => {
|
|
102
|
+
if (url.includes("/cancelled-tasks")) {
|
|
103
|
+
return new Response(
|
|
104
|
+
JSON.stringify({ cancelled: [{ id: "task-1", failureReason: "user request" }] }),
|
|
105
|
+
{ status: 200 },
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
return new Response("{}", { status: 200 });
|
|
109
|
+
});
|
|
110
|
+
const controller = new AbortController();
|
|
111
|
+
const opts = buildOpts({ abortRef: { current: controller } });
|
|
112
|
+
const handler = createCodexSwarmEventHandler(opts);
|
|
113
|
+
handler({
|
|
114
|
+
type: "tool_start",
|
|
115
|
+
toolCallId: "call-1",
|
|
116
|
+
toolName: "bash",
|
|
117
|
+
args: { command: "sleep 9999" },
|
|
118
|
+
});
|
|
119
|
+
await new Promise((resolve) => setTimeout(resolve, 30));
|
|
120
|
+
expect(controller.signal.aborted).toBe(true);
|
|
121
|
+
expect(controller.signal.reason).toBe("cancelled");
|
|
122
|
+
});
|
|
123
|
+
|
|
100
124
|
test("logs the abort reason when /cancelled-tasks reports the task", async () => {
|
|
101
125
|
installFetchStub((url) => {
|
|
102
126
|
if (url.includes("/cancelled-tasks")) {
|