@desplega.ai/agent-swarm 1.76.3 → 1.77.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.76.3",
5
+ "version": "1.77.0",
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.76.3",
3
+ "version": "1.77.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/cli.tsx CHANGED
@@ -34,7 +34,6 @@ interface ParsedArgs {
34
34
  systemPrompt: string;
35
35
  systemPromptFile: string;
36
36
  additionalArgs: string[];
37
- aiLoop: boolean;
38
37
  preset: string;
39
38
  open: boolean;
40
39
  showHelp: boolean;
@@ -54,7 +53,6 @@ function parseArgs(args: string[]): ParsedArgs {
54
53
  let systemPrompt = "";
55
54
  let systemPromptFile = "";
56
55
  let additionalArgs: string[] = [];
57
- let aiLoop = false;
58
56
  let preset = "";
59
57
  let open = false;
60
58
  let showHelp = false;
@@ -92,8 +90,6 @@ function parseArgs(args: string[]): ParsedArgs {
92
90
  } else if (arg === "--system-prompt-file") {
93
91
  systemPromptFile = mainArgs[i + 1] || systemPromptFile;
94
92
  i++;
95
- } else if (arg === "--ai-loop") {
96
- aiLoop = true;
97
93
  } else if (arg === "--preset") {
98
94
  preset = mainArgs[i + 1] || preset;
99
95
  i++;
@@ -122,7 +118,6 @@ function parseArgs(args: string[]): ParsedArgs {
122
118
  systemPrompt,
123
119
  systemPromptFile,
124
120
  additionalArgs,
125
- aiLoop,
126
121
  preset,
127
122
  open,
128
123
  showHelp,
@@ -207,7 +202,6 @@ const COMMAND_HELP: Record<
207
202
  " --yolo Continue on errors instead of stopping",
208
203
  " --system-prompt <text> Custom system prompt (appended to Claude)",
209
204
  " --system-prompt-file <path> Read system prompt from file",
210
- " --ai-loop Use AI-based polling (legacy mode)",
211
205
  " -- <args...> Additional arguments to pass to Claude CLI",
212
206
  " -h, --help Show this help",
213
207
  ].join("\n"),
@@ -226,7 +220,6 @@ const COMMAND_HELP: Record<
226
220
  " --yolo Continue on errors instead of stopping",
227
221
  " --system-prompt <text> Custom system prompt",
228
222
  " --system-prompt-file <path> Read system prompt from file",
229
- " --ai-loop Use AI-based polling (legacy mode)",
230
223
  " -- <args...> Additional arguments to pass to Claude CLI",
231
224
  " -h, --help Show this help",
232
225
  ].join("\n"),
@@ -406,7 +399,6 @@ interface RunnerProps {
406
399
  systemPrompt: string;
407
400
  systemPromptFile: string;
408
401
  additionalArgs: string[];
409
- aiLoop: boolean;
410
402
  }
411
403
 
412
404
  function WorkerRunner({
@@ -415,7 +407,6 @@ function WorkerRunner({
415
407
  systemPrompt,
416
408
  systemPromptFile,
417
409
  additionalArgs,
418
- aiLoop,
419
410
  }: RunnerProps) {
420
411
  const { exit } = useApp();
421
412
 
@@ -427,25 +418,17 @@ function WorkerRunner({
427
418
  systemPromptFile: systemPromptFile || undefined,
428
419
  additionalArgs,
429
420
  logsDir: "./logs",
430
- aiLoop,
431
421
  }).catch((err) => {
432
422
  console.error("[error] Worker encountered an error:", err);
433
423
  exit(err);
434
424
  });
435
425
  // Note: runWorker runs indefinitely, so we don't call exit() on success
436
- }, [prompt, yolo, systemPrompt, systemPromptFile, additionalArgs, aiLoop, exit]);
426
+ }, [prompt, yolo, systemPrompt, systemPromptFile, additionalArgs, exit]);
437
427
 
438
428
  return null;
439
429
  }
440
430
 
441
- function LeadRunner({
442
- prompt,
443
- yolo,
444
- systemPrompt,
445
- systemPromptFile,
446
- additionalArgs,
447
- aiLoop,
448
- }: RunnerProps) {
431
+ function LeadRunner({ prompt, yolo, systemPrompt, systemPromptFile, additionalArgs }: RunnerProps) {
449
432
  const { exit } = useApp();
450
433
 
451
434
  useEffect(() => {
@@ -456,13 +439,12 @@ function LeadRunner({
456
439
  systemPromptFile: systemPromptFile || undefined,
457
440
  additionalArgs,
458
441
  logsDir: "./logs",
459
- aiLoop,
460
442
  }).catch((err) => {
461
443
  console.error("[error] Lead encountered an error:", err);
462
444
  exit(err);
463
445
  });
464
446
  // Note: runLead runs indefinitely, so we don't call exit() on success
465
- }, [prompt, yolo, systemPrompt, systemPromptFile, additionalArgs, aiLoop, exit]);
447
+ }, [prompt, yolo, systemPrompt, systemPromptFile, additionalArgs, exit]);
466
448
 
467
449
  return null;
468
450
  }
@@ -495,7 +477,6 @@ function App({ args }: { args: ParsedArgs }) {
495
477
  systemPrompt,
496
478
  systemPromptFile,
497
479
  additionalArgs,
498
- aiLoop,
499
480
  preset,
500
481
  } = args;
501
482
 
@@ -516,7 +497,6 @@ function App({ args }: { args: ParsedArgs }) {
516
497
  systemPrompt={systemPrompt}
517
498
  systemPromptFile={systemPromptFile}
518
499
  additionalArgs={additionalArgs}
519
- aiLoop={aiLoop}
520
500
  />
521
501
  );
522
502
  case "lead":
@@ -527,7 +507,6 @@ function App({ args }: { args: ParsedArgs }) {
527
507
  systemPrompt={systemPrompt}
528
508
  systemPromptFile={systemPromptFile}
529
509
  additionalArgs={additionalArgs}
530
- aiLoop={aiLoop}
531
510
  />
532
511
  );
533
512
  // version, help, docs handled before render()
@@ -46,8 +46,17 @@ export class BootMaxWaitExceededError extends Error {
46
46
  }
47
47
 
48
48
  export interface AwaitCredentialsOptions {
49
- /** Harness provider name — picks the predicate to run. */
49
+ /** Initial harness provider name — picks the predicate to run. */
50
50
  provider: string;
51
+ /**
52
+ * Optional getter that re-reads the harness provider on each tick. Lets the
53
+ * caller swap the harness mid-wait when an operator flips `HARNESS_PROVIDER`
54
+ * in `swarm_config` to escape a missing-credentials wedge. When unset, the
55
+ * static `provider` is used for the whole wait. The caller is responsible
56
+ * for any side effects of the swap (adapter, prompt rebuild, etc.) inside
57
+ * `refreshEnv` or out-of-band.
58
+ */
59
+ getProvider?: () => string;
51
60
  /** Pull latest swarm_config values into env. Resolves to the merged env. */
52
61
  refreshEnv: () => Promise<Record<string, string | undefined>>;
53
62
  /** Callback invoked on every tick — Phase 3 wires this to the status-report API. */
@@ -118,10 +127,17 @@ export async function awaitCredentials(opts: AwaitCredentialsOptions): Promise<C
118
127
  const initialEnv = opts.initialEnv ?? process.env;
119
128
  const backoff = resolveBackoff(opts.backoff, initialEnv);
120
129
 
130
+ // Re-read on every iteration so an operator flipping HARNESS_PROVIDER in
131
+ // swarm_config during the wait actually flips the predicate.
132
+ const readProvider = () => opts.getProvider?.() ?? opts.provider;
133
+
121
134
  // Fast path: already satisfied at boot.
122
- let status = checkProviderCredentials(opts.provider, initialEnv, opts.credCheckOptions);
135
+ let currentProvider = readProvider();
136
+ let status = checkProviderCredentials(currentProvider, initialEnv, opts.credCheckOptions);
123
137
  if (status.ready) {
124
- log(`[boot] credentials ready (provider=${opts.provider}, satisfiedBy=${status.satisfiedBy})`);
138
+ log(
139
+ `[boot] credentials ready (provider=${currentProvider}, satisfiedBy=${status.satisfiedBy})`,
140
+ );
125
141
  return status;
126
142
  }
127
143
 
@@ -143,7 +159,7 @@ export async function awaitCredentials(opts: AwaitCredentialsOptions): Promise<C
143
159
 
144
160
  log(
145
161
  `[boot] waiting for ${status.missing.join(", ") || "credentials"} ` +
146
- `(attempt ${attempt}, retry in ${delayMs}ms)${status.hint ? ` — ${status.hint}` : ""}`,
162
+ `(attempt ${attempt}, retry in ${delayMs}ms, provider=${currentProvider})${status.hint ? ` — ${status.hint}` : ""}`,
147
163
  );
148
164
 
149
165
  await sleep(delayMs);
@@ -158,7 +174,16 @@ export async function awaitCredentials(opts: AwaitCredentialsOptions): Promise<C
158
174
  log(`[boot] env refresh failed (non-fatal): ${err}`);
159
175
  }
160
176
 
161
- status = checkProviderCredentials(opts.provider, process.env, opts.credCheckOptions);
177
+ // Re-read provider in case the operator flipped HARNESS_PROVIDER during
178
+ // the wait. The caller is expected to have already swapped adapter/prompt
179
+ // (typically inside `refreshEnv`); we just pivot the predicate here.
180
+ const nextProvider = readProvider();
181
+ if (nextProvider !== currentProvider) {
182
+ log(`[boot] provider changed mid-wait: ${currentProvider} → ${nextProvider}`);
183
+ currentProvider = nextProvider;
184
+ }
185
+
186
+ status = checkProviderCredentials(currentProvider, process.env, opts.credCheckOptions);
162
187
 
163
188
  if (!status.ready) {
164
189
  // Exponential backoff with cap.
@@ -174,7 +199,7 @@ export async function awaitCredentials(opts: AwaitCredentialsOptions): Promise<C
174
199
  }
175
200
 
176
201
  log(
177
- `[boot] credentials ready (provider=${opts.provider}, satisfiedBy=${status.satisfiedBy}, attempts=${attempt})`,
202
+ `[boot] credentials ready (provider=${currentProvider}, satisfiedBy=${status.satisfiedBy}, attempts=${attempt})`,
178
203
  );
179
204
  // Final tick so callers can clear the waiting state.
180
205
  try {