@desplega.ai/agent-swarm 1.55.0 → 1.56.3

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.55.0",
5
+ "version": "1.56.3",
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": [
@@ -509,6 +509,10 @@
509
509
  "type": "string",
510
510
  "maxLength": 65536
511
511
  },
512
+ "heartbeatMd": {
513
+ "type": "string",
514
+ "maxLength": 65536
515
+ },
512
516
  "changeSource": {
513
517
  "type": "string"
514
518
  },
@@ -1911,6 +1915,27 @@
1911
1915
  }
1912
1916
  }
1913
1917
  },
1918
+ "/api/heartbeat/checklist": {
1919
+ "post": {
1920
+ "summary": "Trigger an immediate heartbeat checklist check",
1921
+ "tags": [
1922
+ "Heartbeat"
1923
+ ],
1924
+ "security": [
1925
+ {
1926
+ "bearerAuth": []
1927
+ }
1928
+ ],
1929
+ "responses": {
1930
+ "200": {
1931
+ "description": "Checklist check completed successfully"
1932
+ },
1933
+ "401": {
1934
+ "description": "Unauthorized"
1935
+ }
1936
+ }
1937
+ }
1938
+ },
1914
1939
  "/api/memory/index": {
1915
1940
  "post": {
1916
1941
  "summary": "Ingest content into memory system (async embedding)",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@desplega.ai/agent-swarm",
3
- "version": "1.55.0",
3
+ "version": "1.56.3",
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/be/db.ts CHANGED
@@ -229,6 +229,7 @@ const VERSIONABLE_FIELDS: VersionableField[] = [
229
229
  "toolsMd",
230
230
  "claudeMd",
231
231
  "setupScript",
232
+ "heartbeatMd",
232
233
  ];
233
234
 
234
235
  function ensureAgentProfileColumns(database: Database): void {
@@ -399,9 +400,10 @@ function seedContextVersions(): void {
399
400
  toolsMd: string | null;
400
401
  claudeMd: string | null;
401
402
  setupScript: string | null;
403
+ heartbeatMd: string | null;
402
404
  },
403
405
  []
404
- >(`SELECT id, soulMd, identityMd, toolsMd, claudeMd, setupScript FROM agents`)
406
+ >(`SELECT id, soulMd, identityMd, toolsMd, claudeMd, setupScript, heartbeatMd FROM agents`)
405
407
  .all();
406
408
 
407
409
  for (const agent of agents) {
@@ -450,6 +452,7 @@ type AgentRow = {
450
452
  identityMd: string | null;
451
453
  setupScript: string | null;
452
454
  toolsMd: string | null;
455
+ heartbeatMd: string | null;
453
456
  lastActivityAt: string | null;
454
457
  createdAt: string;
455
458
  lastUpdatedAt: string;
@@ -471,6 +474,7 @@ function rowToAgent(row: AgentRow): Agent {
471
474
  identityMd: row.identityMd ?? undefined,
472
475
  setupScript: row.setupScript ?? undefined,
473
476
  toolsMd: row.toolsMd ?? undefined,
477
+ heartbeatMd: row.heartbeatMd ?? undefined,
474
478
  lastActivityAt: row.lastActivityAt ?? undefined,
475
479
  createdAt: row.createdAt,
476
480
  lastUpdatedAt: row.lastUpdatedAt,
@@ -2259,6 +2263,7 @@ export function updateAgentProfile(
2259
2263
  identityMd?: string;
2260
2264
  setupScript?: string;
2261
2265
  toolsMd?: string;
2266
+ heartbeatMd?: string;
2262
2267
  },
2263
2268
  meta?: VersionMeta,
2264
2269
  ): Agent | null {
@@ -2312,6 +2317,7 @@ export function updateAgentProfile(
2312
2317
  string | null,
2313
2318
  string | null,
2314
2319
  string | null,
2320
+ string | null,
2315
2321
  string,
2316
2322
  string,
2317
2323
  ]
@@ -2325,6 +2331,7 @@ export function updateAgentProfile(
2325
2331
  identityMd = COALESCE(?, identityMd),
2326
2332
  setupScript = COALESCE(?, setupScript),
2327
2333
  toolsMd = COALESCE(?, toolsMd),
2334
+ heartbeatMd = COALESCE(?, heartbeatMd),
2328
2335
  lastUpdatedAt = ?
2329
2336
  WHERE id = ? RETURNING *`,
2330
2337
  )
@@ -2337,6 +2344,7 @@ export function updateAgentProfile(
2337
2344
  updates.identityMd ?? null,
2338
2345
  updates.setupScript ?? null,
2339
2346
  updates.toolsMd ?? null,
2347
+ updates.heartbeatMd ?? null,
2340
2348
  now,
2341
2349
  id,
2342
2350
  );
@@ -1,5 +1,3 @@
1
- PRAGMA defer_foreign_keys = ON;
2
-
3
1
  -- Add 'cancelled' as a valid status for workflow runs.
4
2
  -- SQLite does not support ALTER CHECK constraints, so we recreate the table.
5
3
 
@@ -0,0 +1 @@
1
+ ALTER TABLE agents ADD COLUMN heartbeatMd TEXT DEFAULT NULL;
@@ -152,6 +152,12 @@ export function runMigrations(db: Database): void {
152
152
  }
153
153
 
154
154
  // 5. Run pending migrations
155
+ // Disable FK checks for the entire migration pass. Individual migrations
156
+ // (008, 025, 026) need to DROP + recreate tables, which can violate FKs
157
+ // mid-transaction. PRAGMA foreign_keys cannot be changed inside a transaction,
158
+ // so we disable it here, outside any transaction, and re-enable after.
159
+ db.run("PRAGMA foreign_keys = OFF");
160
+
155
161
  for (const migration of migrations) {
156
162
  const existing = applied.get(migration.version);
157
163
 
@@ -184,4 +190,6 @@ export function runMigrations(db: Database): void {
184
190
  const elapsed = (performance.now() - start).toFixed(1);
185
191
  console.debug(`[migrations] Applied: ${migration.name} (${elapsed}ms)`);
186
192
  }
193
+
194
+ db.run("PRAGMA foreign_keys = ON");
187
195
  }
@@ -1991,6 +1991,7 @@ export async function runAgent(config: RunnerConfig, opts: RunnerOptions) {
1991
1991
  let agentSetupScript: string | undefined;
1992
1992
  let agentToolsMd: string | undefined;
1993
1993
  let agentClaudeMd: string | undefined;
1994
+ let agentHeartbeatMd: string | undefined;
1994
1995
  let agentProfileName: string | undefined;
1995
1996
  let agentDescription: string | undefined;
1996
1997
  let agentSkillsSummary: { name: string; description: string }[] | undefined;
@@ -2170,6 +2171,7 @@ export async function runAgent(config: RunnerConfig, opts: RunnerOptions) {
2170
2171
  claudeMd?: string;
2171
2172
  setupScript?: string;
2172
2173
  toolsMd?: string;
2174
+ heartbeatMd?: string;
2173
2175
  name?: string;
2174
2176
  description?: string;
2175
2177
  };
@@ -2178,12 +2180,19 @@ export async function runAgent(config: RunnerConfig, opts: RunnerOptions) {
2178
2180
  agentSetupScript = profile.setupScript;
2179
2181
  agentToolsMd = profile.toolsMd;
2180
2182
  agentClaudeMd = profile.claudeMd;
2183
+ agentHeartbeatMd = profile.heartbeatMd;
2181
2184
  agentProfileName = profile.name;
2182
2185
  agentDescription = profile.description;
2183
2186
 
2184
2187
  // Generate default templates if missing (runner registers via POST /api/agents
2185
2188
  // which doesn't generate templates like join-swarm does)
2186
- if (!agentSoulMd || !agentIdentityMd || !agentToolsMd || !agentClaudeMd) {
2189
+ if (
2190
+ !agentSoulMd ||
2191
+ !agentIdentityMd ||
2192
+ !agentToolsMd ||
2193
+ !agentClaudeMd ||
2194
+ !agentHeartbeatMd
2195
+ ) {
2187
2196
  // Use already-fetched template (from pre-registration step)
2188
2197
  if (cachedTemplate) {
2189
2198
  const ctx = {
@@ -2202,6 +2211,8 @@ export async function runAgent(config: RunnerConfig, opts: RunnerOptions) {
2202
2211
  agentClaudeMd = interpolate(cachedTemplate.files.claudeMd, ctx).result;
2203
2212
  if (!agentSetupScript)
2204
2213
  agentSetupScript = interpolate(cachedTemplate.files.setupScript, ctx).result;
2214
+ if (!agentHeartbeatMd)
2215
+ agentHeartbeatMd = interpolate(cachedTemplate.files.heartbeatMd, ctx).result;
2205
2216
  console.log(`[${role}] Applied template: ${templateId}`);
2206
2217
  }
2207
2218
 
@@ -2226,6 +2237,8 @@ export async function runAgent(config: RunnerConfig, opts: RunnerOptions) {
2226
2237
  if (!profile.claudeMd && agentClaudeMd) profileUpdate.claudeMd = agentClaudeMd;
2227
2238
  if (!profile.setupScript && agentSetupScript)
2228
2239
  profileUpdate.setupScript = agentSetupScript;
2240
+ if (!profile.heartbeatMd && agentHeartbeatMd)
2241
+ profileUpdate.heartbeatMd = agentHeartbeatMd;
2229
2242
 
2230
2243
  await fetch(`${apiUrl}/api/agents/${agentId}/profile`, {
2231
2244
  method: "PUT",
@@ -2364,6 +2377,16 @@ export async function runAgent(config: RunnerConfig, opts: RunnerOptions) {
2364
2377
  }
2365
2378
  }
2366
2379
 
2380
+ // Write HEARTBEAT.md to workspace (lead's periodic checklist)
2381
+ if (agentHeartbeatMd) {
2382
+ try {
2383
+ await Bun.write("/workspace/HEARTBEAT.md", agentHeartbeatMd);
2384
+ console.log(`[${role}] Wrote HEARTBEAT.md to workspace`);
2385
+ } catch (err) {
2386
+ console.warn(`[${role}] Could not write HEARTBEAT.md: ${(err as Error).message}`);
2387
+ }
2388
+ }
2389
+
2367
2390
  // Write CLAUDE.md to workspace (agent-level instructions)
2368
2391
  if (agentClaudeMd) {
2369
2392
  try {