@desplega.ai/agent-swarm 1.76.2 → 1.77.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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.2",
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": [
@@ -7368,6 +7368,12 @@
7368
7368
  },
7369
7369
  "hide_cloud_promo": {
7370
7370
  "type": "boolean"
7371
+ },
7372
+ "org_id": {
7373
+ "type": [
7374
+ "string",
7375
+ "null"
7376
+ ]
7371
7377
  }
7372
7378
  },
7373
7379
  "required": [
@@ -7376,7 +7382,8 @@
7376
7382
  "brand_color",
7377
7383
  "is_cloud",
7378
7384
  "marketing_url",
7379
- "hide_cloud_promo"
7385
+ "hide_cloud_promo",
7386
+ "org_id"
7380
7387
  ]
7381
7388
  },
7382
7389
  "setup": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@desplega.ai/agent-swarm",
3
- "version": "1.76.2",
3
+ "version": "1.77.0",
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>",
@@ -57,6 +57,30 @@ export const SummaryWithRatingsSchema = z.object({
57
57
  export type LlmRating = z.infer<typeof RatingSchema>;
58
58
  export type SummaryWithRatings = z.infer<typeof SummaryWithRatingsSchema>;
59
59
 
60
+ /**
61
+ * Base prompt for session summarization. Extracted from `src/hooks/hook.ts`
62
+ * so both the claude Stop hook and the worker-side `summarizeSession` helper
63
+ * in `src/utils/internal-ai/summarize-session.ts` share one source of truth.
64
+ *
65
+ * Callers append a `Task: <prompt>` line (optional) and `Transcript:\n<text>`
66
+ * block, then wrap with `buildSummaryWithRatingsPrompt(basePrompt, retrievals)`.
67
+ */
68
+ export const BASE_SUMMARIZE_PROMPT = `You are summarizing an AI agent's work session. Extract ONLY high-value learnings.
69
+
70
+ DO NOT include:
71
+ - Generic descriptions of what was done ("worked on task X")
72
+ - Tool calls or file reads
73
+ - Routine progress updates
74
+
75
+ DO include (if present):
76
+ - **Mistakes made and corrections** — what went wrong and what fixed it
77
+ - **Discovered patterns** — reusable approaches, APIs, or codebase conventions
78
+ - **Codebase knowledge** — important file paths, architecture decisions, gotchas
79
+ - **Environment knowledge** — service URLs, config details, tool quirks
80
+ - **Failed approaches** — what was tried and didn't work (and why)
81
+
82
+ Format as a bulleted list of concrete, reusable facts. If the session was routine with no significant learnings, respond with exactly: "No significant learnings."`;
83
+
60
84
  /** Context augmentations LlmRater consumes when called directly (per-memory path). */
61
85
  export type LlmRatingContext = RatingContext & {
62
86
  /** What the agent asked the memory system. */
@@ -293,6 +317,7 @@ export function dedupeRetrievalsForRater<T extends { scheduleId?: string | null
293
317
  return out;
294
318
  }
295
319
 
320
+ // Worker-safe: uses fetch() only, no bun:sqlite import.
296
321
  /**
297
322
  * GET `/api/memory/retrievals?taskId=` — best-effort. Returns `[]` on any
298
323
  * failure so a transient API outage never blocks the summary-indexing path.
@@ -327,6 +352,7 @@ export async function fetchRetrievalsForTask(opts: {
327
352
  }
328
353
  }
329
354
 
355
+ // Worker-safe: uses fetch() only, no bun:sqlite import.
330
356
  /**
331
357
  * POST `/api/memory/rate` — best-effort. Logs on 4xx/5xx, never throws. The
332
358
  * worker hook wraps the whole rating block in its own try/catch as a final
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 {