@desplega.ai/agent-swarm 1.91.0 → 1.92.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.
Files changed (114) hide show
  1. package/README.md +3 -2
  2. package/openapi.json +1005 -152
  3. package/package.json +6 -6
  4. package/plugin/skills/pages/SKILL.md +5 -2
  5. package/src/be/db.ts +662 -19
  6. package/src/be/memory/constants.ts +2 -1
  7. package/src/be/memory/providers/openai-embedding.ts +2 -5
  8. package/src/be/memory/providers/sqlite-store.ts +293 -76
  9. package/src/be/memory/types.ts +35 -0
  10. package/src/be/migrations/083_script_workflows.sql +51 -0
  11. package/src/be/migrations/084_script_run_journal_duration.sql +5 -0
  12. package/src/be/migrations/085_script_runs_kind.sql +9 -0
  13. package/src/be/migrations/086_pages_default_authed.sql +64 -0
  14. package/src/be/migrations/087_skill_files.sql +19 -0
  15. package/src/be/modelsdev-cache.json +42310 -38617
  16. package/src/be/scripts/typecheck.ts +49 -0
  17. package/src/be/seed-scripts/catalog/boot-triage.ts +221 -0
  18. package/src/be/seed-scripts/catalog/catalog-report.ts +457 -0
  19. package/src/be/seed-scripts/catalog/compound-insights.ts +310 -6
  20. package/src/be/seed-scripts/catalog/gh-pr-snapshot.ts +1 -1
  21. package/src/be/seed-scripts/catalog/memory-eval.ts +1059 -0
  22. package/src/be/seed-scripts/catalog/ops-catalog-audit.ts +506 -0
  23. package/src/be/seed-scripts/catalog/schedule-health.ts +78 -2
  24. package/src/be/seed-scripts/catalog/task-context-gathering.ts +92 -0
  25. package/src/be/seed-scripts/catalog/task-failure-audit.ts +48 -1
  26. package/src/be/seed-scripts/catalog/tool-usage.ts +6 -3
  27. package/src/be/seed-scripts/index.ts +51 -5
  28. package/src/be/seed-skills/index.ts +3 -3
  29. package/src/be/skill-sync.ts +91 -7
  30. package/src/be/swarm-config-guard.ts +17 -0
  31. package/src/commands/runner.ts +49 -4
  32. package/src/heartbeat/templates.ts +20 -16
  33. package/src/http/db-query.ts +20 -5
  34. package/src/http/index.ts +51 -7
  35. package/src/http/mcp-user.ts +23 -0
  36. package/src/http/mcp.ts +58 -0
  37. package/src/http/memory.ts +58 -0
  38. package/src/http/pages.ts +1 -1
  39. package/src/http/script-runs.ts +557 -0
  40. package/src/http/scripts.ts +39 -2
  41. package/src/http/skills.ts +225 -0
  42. package/src/prompts/session-templates.ts +24 -4
  43. package/src/providers/claude-adapter.ts +107 -28
  44. package/src/script-workflows/executor.ts +110 -0
  45. package/src/script-workflows/harness.ts +73 -0
  46. package/src/script-workflows/label-lint.ts +51 -0
  47. package/src/script-workflows/limits.ts +22 -0
  48. package/src/script-workflows/supervisor.ts +139 -0
  49. package/src/script-workflows/workflow-ctx.ts +209 -0
  50. package/src/scripts-runtime/sdk-allowlist.ts +4 -0
  51. package/src/scripts-runtime/swarm-sdk.ts +13 -0
  52. package/src/scripts-runtime/types/stdlib.d.ts +61 -0
  53. package/src/scripts-runtime/types/swarm-sdk.d.ts +61 -0
  54. package/src/server.ts +4 -0
  55. package/src/slack/handlers.ts +11 -4
  56. package/src/slack/message-text.ts +98 -0
  57. package/src/slack/thread-buffer.ts +5 -3
  58. package/src/tests/claude-adapter-binary.test.ts +271 -74
  59. package/src/tests/create-page-tool.test.ts +19 -2
  60. package/src/tests/db-query.test.ts +28 -0
  61. package/src/tests/error-tracker.test.ts +121 -0
  62. package/src/tests/harness-provider-resolution.test.ts +33 -0
  63. package/src/tests/heartbeat-checklist.test.ts +36 -0
  64. package/src/tests/mcp-tools.test.ts +6 -0
  65. package/src/tests/mcp-transport-gc.test.ts +58 -0
  66. package/src/tests/memory-health-endpoint.test.ts +78 -0
  67. package/src/tests/memory-store.test.ts +221 -1
  68. package/src/tests/pages-http.test.ts +20 -2
  69. package/src/tests/pages-storage.test.ts +26 -0
  70. package/src/tests/prompt-template-session.test.ts +34 -5
  71. package/src/tests/script-runs-http.test.ts +278 -0
  72. package/src/tests/script-workflows-label-lint.test.ts +43 -0
  73. package/src/tests/script-workflows-runtime-e2e.test.ts +170 -0
  74. package/src/tests/scripts-mcp-e2e.test.ts +102 -2
  75. package/src/tests/seed-scripts.test.ts +468 -3
  76. package/src/tests/skill-files-http.test.ts +171 -0
  77. package/src/tests/skill-files.test.ts +162 -0
  78. package/src/tests/skill-get-file-tool.test.ts +110 -0
  79. package/src/tests/skill-sync.test.ts +125 -6
  80. package/src/tests/slack-message-text.test.ts +250 -0
  81. package/src/tests/system-default-skills.test.ts +40 -0
  82. package/src/tools/create-page.ts +2 -2
  83. package/src/tools/db-query.ts +16 -6
  84. package/src/tools/script-runs.ts +123 -0
  85. package/src/tools/skills/index.ts +1 -0
  86. package/src/tools/skills/skill-get-file.ts +80 -0
  87. package/src/tools/slack-read.ts +12 -3
  88. package/src/tools/tool-config.ts +6 -2
  89. package/src/types.ts +72 -0
  90. package/src/utils/error-tracker.ts +40 -1
  91. package/src/utils/internal-ai/complete-structured.ts +10 -4
  92. package/src/workflows/executors/raw-llm.ts +76 -59
  93. package/templates/schedules/daily-blocker-digest/content.md +68 -54
  94. package/templates/schedules/daily-compounding-reflection/content.md +4 -4
  95. package/templates/schedules/daily-hn-briefing/content.md +5 -5
  96. package/templates/schedules/daily-workflow-health-audit/content.md +6 -6
  97. package/templates/schedules/gtm-weekly-review/content.md +9 -9
  98. package/templates/schedules/weekly-dependabot-triage/content.md +24 -20
  99. package/templates/skills/agentmail-sending/content.md +6 -7
  100. package/templates/skills/desloppify/content.md +8 -9
  101. package/templates/skills/jira-interaction/content.md +25 -33
  102. package/templates/skills/kapso-whatsapp/content.md +29 -30
  103. package/templates/skills/linear-interaction/content.md +8 -9
  104. package/templates/skills/pages/content.md +205 -55
  105. package/templates/skills/profile-corruption-escalation/content.md +44 -85
  106. package/templates/skills/script-workflows/config.json +14 -0
  107. package/templates/skills/script-workflows/content.md +68 -0
  108. package/templates/skills/sprite-cli/content.md +4 -5
  109. package/templates/skills/swarm-scripts/content.md +2 -3
  110. package/templates/skills/turso-interaction/content.md +14 -17
  111. package/templates/skills/workflow-iterate/content.md +38 -391
  112. package/templates/skills/x-api-interactions/content.md +4 -6
  113. package/templates/skills/scheduled-task-resilience/config.json +0 -14
  114. package/templates/skills/scheduled-task-resilience/content.md +0 -95
@@ -1,6 +1,7 @@
1
1
  import { afterAll, beforeAll, describe, expect, test } from "bun:test";
2
2
  import { unlink } from "node:fs/promises";
3
- import { closeDb, createAgent, initDb } from "../be/db";
3
+ import { closeDb, createAgent, getDb, initDb } from "../be/db";
4
+ import { serializeEmbedding } from "../be/embedding";
4
5
  import { SqliteMemoryStore } from "../be/memory/providers/sqlite-store";
5
6
 
6
7
  const TEST_DB_PATH = "./test-memory-store.sqlite";
@@ -10,6 +11,14 @@ describe("SqliteMemoryStore", () => {
10
11
  const agentB = "bbbb0000-0000-4000-8000-000000000002";
11
12
  let store: SqliteMemoryStore;
12
13
 
14
+ function vector(values: Record<number, number>): Float32Array {
15
+ const embedding = new Float32Array(512);
16
+ for (const [index, value] of Object.entries(values)) {
17
+ embedding[Number(index)] = value;
18
+ }
19
+ return embedding;
20
+ }
21
+
13
22
  beforeAll(async () => {
14
23
  for (const suffix of ["", "-wal", "-shm"]) {
15
24
  try {
@@ -210,6 +219,95 @@ describe("SqliteMemoryStore", () => {
210
219
  const agents = new Set(results.map((r) => r.agentId));
211
220
  expect(agents.size).toBeGreaterThanOrEqual(1);
212
221
  });
222
+
223
+ test("uses sqlite-vec for 512d embeddings with scope-filter parity", () => {
224
+ for (let i = 0; i < 6; i++) {
225
+ const otherAgent = store.store({
226
+ agentId: agentB,
227
+ scope: "agent",
228
+ name: `vec-other-agent-exact-${i}`,
229
+ content: "exact but invisible to agentA",
230
+ source: "manual",
231
+ });
232
+ store.updateEmbedding(otherAgent.id, vector({ 0: 1 }), "test-model");
233
+ }
234
+
235
+ const visible = store.store({
236
+ agentId: agentA,
237
+ scope: "agent",
238
+ name: "vec-agent-visible",
239
+ content: "visible to agentA",
240
+ source: "manual",
241
+ });
242
+ store.updateEmbedding(visible.id, vector({ 0: 0.8, 1: 0.2 }), "test-model");
243
+
244
+ const query = vector({ 0: 1 });
245
+ const results = store.search(query, agentA, { scope: "agent", limit: 5 });
246
+
247
+ expect(results[0]!.id).toBe(visible.id);
248
+ expect(results.every((r) => r.agentId === agentA && r.scope === "agent")).toBe(true);
249
+
250
+ const health = store.getHealth();
251
+ expect(health.sqliteVec.schema).toContain("distance_metric=cosine");
252
+ expect(health.counts.memoryVec).toBeGreaterThanOrEqual(2);
253
+ expect(health.retrievalMode).toBe("vec");
254
+ });
255
+ });
256
+
257
+ describe("memory_vec population", () => {
258
+ test("populates existing embeddings on startup and reports health counts", () => {
259
+ const raw = store.store({
260
+ agentId: agentA,
261
+ scope: "agent",
262
+ name: "raw-existing-embedding",
263
+ content: "raw existing embedding",
264
+ source: "manual",
265
+ });
266
+ getDb()
267
+ .prepare("UPDATE agent_memory SET embedding = ?, embeddingModel = ? WHERE id = ?")
268
+ .run(serializeEmbedding(vector({ 2: 1 })), "test-model", raw.id);
269
+ getDb().prepare("DELETE FROM memory_vec WHERE memory_id = ?").run(raw.id);
270
+
271
+ const freshStore = new SqliteMemoryStore();
272
+ const health = freshStore.getHealth();
273
+
274
+ expect(health.counts.missingFromVec).toBe(0);
275
+ expect(health.sqliteVec.lastPopulate?.attempted).toBeGreaterThanOrEqual(1);
276
+ expect(health.sqliteVec.lastPopulate?.failed).toBe(0);
277
+
278
+ const resultIds = freshStore
279
+ .search(vector({ 2: 1 }), agentA, { scope: "agent", limit: 20 })
280
+ .map((r) => r.id);
281
+ expect(resultIds).toContain(raw.id);
282
+ });
283
+
284
+ test("rebuilds an old non-cosine memory_vec table from agent_memory", () => {
285
+ const raw = store.store({
286
+ agentId: agentA,
287
+ scope: "agent",
288
+ name: "stale-schema-embedding",
289
+ content: "stale schema embedding",
290
+ source: "manual",
291
+ });
292
+ getDb()
293
+ .prepare("UPDATE agent_memory SET embedding = ?, embeddingModel = ? WHERE id = ?")
294
+ .run(serializeEmbedding(vector({ 3: 1 })), "test-model", raw.id);
295
+
296
+ getDb().run("DROP TABLE memory_vec");
297
+ getDb().run(`
298
+ CREATE VIRTUAL TABLE memory_vec USING vec0(
299
+ memory_id TEXT PRIMARY KEY,
300
+ embedding float[512]
301
+ )
302
+ `);
303
+
304
+ const freshStore = new SqliteMemoryStore();
305
+ const health = freshStore.getHealth();
306
+
307
+ expect(health.sqliteVec.schema).toContain("distance_metric=cosine");
308
+ expect(health.counts.missingFromVec).toBe(0);
309
+ expect(health.retrievalMode).toBe("vec");
310
+ });
213
311
  });
214
312
 
215
313
  describe("delete()", () => {
@@ -326,4 +424,126 @@ describe("SqliteMemoryStore", () => {
326
424
  expect(filtered.length).toBeGreaterThan(0);
327
425
  });
328
426
  });
427
+
428
+ describe("purgeExpired()", () => {
429
+ const purgeAgent = "cccc0000-0000-4000-8000-000000000003";
430
+
431
+ beforeAll(() => {
432
+ try {
433
+ createAgent({ id: purgeAgent, name: "Purge Agent", isLead: false, status: "idle" });
434
+ } catch {
435
+ // agent may already exist from a prior run
436
+ }
437
+ });
438
+
439
+ test("deletes rows past their expiresAt and returns count", () => {
440
+ const db = getDb();
441
+
442
+ // Store a session_summary (3-day TTL) then backdateits expiresAt to the past
443
+ const mem = store.store({
444
+ agentId: purgeAgent,
445
+ scope: "agent",
446
+ name: "expired session",
447
+ content: "old session data",
448
+ source: "session_summary",
449
+ });
450
+ db.prepare("UPDATE agent_memory SET expiresAt = datetime('now', '-1 day') WHERE id = ?").run(
451
+ mem.id,
452
+ );
453
+
454
+ // Store a manual memory (never expires) — should survive
455
+ const keeper = store.store({
456
+ agentId: purgeAgent,
457
+ scope: "agent",
458
+ name: "keeper",
459
+ content: "manual content",
460
+ source: "manual",
461
+ });
462
+
463
+ const purged = store.purgeExpired();
464
+ expect(purged).toBeGreaterThanOrEqual(1);
465
+
466
+ expect(store.get(mem.id)).toBeNull();
467
+ expect(store.get(keeper.id)).not.toBeNull();
468
+ });
469
+
470
+ test("returns 0 when nothing is expired", () => {
471
+ const purged = store.purgeExpired();
472
+ expect(purged).toBe(0);
473
+ });
474
+
475
+ test("also removes corresponding vec rows", () => {
476
+ const db = getDb();
477
+ const emb = vector({ 0: 0.9, 100: 0.1 });
478
+
479
+ const mem = store.store({
480
+ agentId: purgeAgent,
481
+ scope: "agent",
482
+ name: "vec-purge-test",
483
+ content: "will be purged",
484
+ source: "task_completion",
485
+ });
486
+ store.updateEmbedding(mem.id, emb, "test-model");
487
+
488
+ const vecBefore = db
489
+ .prepare<{ count: number }, [string]>(
490
+ "SELECT COUNT(*) as count FROM memory_vec WHERE memory_id = ?",
491
+ )
492
+ .get(mem.id);
493
+
494
+ // Only check vec cleanup if the row was actually inserted
495
+ if (vecBefore && vecBefore.count > 0) {
496
+ db.prepare(
497
+ "UPDATE agent_memory SET expiresAt = datetime('now', '-1 day') WHERE id = ?",
498
+ ).run(mem.id);
499
+
500
+ store.purgeExpired();
501
+
502
+ const vecAfter = db
503
+ .prepare<{ count: number }, [string]>(
504
+ "SELECT COUNT(*) as count FROM memory_vec WHERE memory_id = ?",
505
+ )
506
+ .get(mem.id);
507
+ expect(vecAfter?.count ?? 0).toBe(0);
508
+ }
509
+ });
510
+ });
511
+
512
+ describe("knn-k cap", () => {
513
+ test("search does not throw when vec table exceeds 4096 rows", () => {
514
+ const db = getDb();
515
+ const emb = vector({ 0: 1.0 });
516
+ const embBuffer = serializeEmbedding(emb);
517
+
518
+ // Check if vec is available — if not, skip (test is meaningful only with vec)
519
+ const health = store.getHealth();
520
+ if (health.retrievalMode !== "vec") return;
521
+
522
+ // Insert enough rows to exceed 4096 in the vec table
523
+ const currentCount =
524
+ db.prepare<{ c: number }, []>("SELECT COUNT(*) as c FROM memory_vec").get()?.c ?? 0;
525
+ const needed = Math.max(0, 4097 - currentCount);
526
+
527
+ for (let i = 0; i < needed; i++) {
528
+ const id = `knn-test-${i}-${Date.now()}`;
529
+ db.prepare(
530
+ "INSERT INTO agent_memory (id, agentId, scope, name, content, source, embedding, chunkIndex, totalChunks, tags, alpha, beta, createdAt, accessedAt) VALUES (?, ?, 'agent', ?, 'knn test', 'manual', ?, 0, 1, '[]', 1, 1, datetime('now'), datetime('now'))",
531
+ ).run(id, agentA, `knn-${i}`, embBuffer);
532
+ const vecBuf = new Float32Array(emb);
533
+ db.prepare("INSERT INTO memory_vec (memory_id, embedding) VALUES (?, ?)").run(
534
+ id,
535
+ Buffer.from(vecBuf.buffer),
536
+ );
537
+ }
538
+
539
+ const vecCount =
540
+ db.prepare<{ c: number }, []>("SELECT COUNT(*) as c FROM memory_vec").get()?.c ?? 0;
541
+ expect(vecCount).toBeGreaterThanOrEqual(4097);
542
+
543
+ // This should NOT throw — it should clamp k to 4096
544
+ const results = store.search(emb, agentA, { scope: "agent", limit: 10 });
545
+ expect(results).toBeDefined();
546
+ expect(Array.isArray(results)).toBe(true);
547
+ });
548
+ });
329
549
  });
@@ -61,14 +61,13 @@ describe("Pages HTTP API", () => {
61
61
  }
62
62
  });
63
63
 
64
- test("POST /api/pages creates a page and returns {id, version}", async () => {
64
+ test("POST /api/pages creates an authed page by default and returns {id, version}", async () => {
65
65
  const res = await fetch(`${baseUrl}/api/pages`, {
66
66
  method: "POST",
67
67
  headers,
68
68
  body: JSON.stringify({
69
69
  title: "Hello",
70
70
  contentType: "text/html",
71
- authMode: "public",
72
71
  body: "<h1>hi</h1>",
73
72
  }),
74
73
  });
@@ -87,6 +86,25 @@ describe("Pages HTTP API", () => {
87
86
  expect(page.agentId).toBe(agentId);
88
87
  expect(page.slug).toBe("hello"); // auto-slug from title
89
88
  expect(page.contentType).toBe("text/html");
89
+ expect(page.authMode).toBe("authed");
90
+ });
91
+
92
+ test("POST /api/pages can explicitly opt in to public auth", async () => {
93
+ const res = await fetch(`${baseUrl}/api/pages`, {
94
+ method: "POST",
95
+ headers,
96
+ body: JSON.stringify({
97
+ title: "Public",
98
+ contentType: "text/html",
99
+ authMode: "public",
100
+ body: "<h1>public</h1>",
101
+ }),
102
+ });
103
+
104
+ expect(res.status).toBe(201);
105
+ const json = (await res.json()) as { id: string };
106
+ const got = await fetch(`${baseUrl}/api/pages/${json.id}`, { headers });
107
+ const page = (await got.json()) as Page;
90
108
  expect(page.authMode).toBe("public");
91
109
  });
92
110
 
@@ -5,6 +5,7 @@ import {
5
5
  closeDb,
6
6
  createPage,
7
7
  deletePage,
8
+ getDb,
8
9
  getPage,
9
10
  getPageBySlug,
10
11
  getPageVersion,
@@ -81,6 +82,31 @@ describe("pages storage CRUD", () => {
81
82
  expect(getPageVersions(created.id)).toHaveLength(0);
82
83
  });
83
84
 
85
+ test("createPage defaults authMode to authed when omitted", () => {
86
+ const agentId = makeAgentId();
87
+ const created = createPage({
88
+ agentId,
89
+ slug: "default-auth",
90
+ title: "Default Auth",
91
+ contentType: "text/html",
92
+ body: "<h1>default</h1>",
93
+ });
94
+
95
+ expect(created.authMode).toBe("authed");
96
+ });
97
+
98
+ test("pages table defaults authMode to authed when omitted", () => {
99
+ const agentId = makeAgentId();
100
+ const row = getDb()
101
+ .prepare<{ authMode: string }, [string, string, string, string, string]>(
102
+ `INSERT INTO pages (agentId, slug, title, contentType, body)
103
+ VALUES (?, ?, ?, ?, ?) RETURNING authMode`,
104
+ )
105
+ .get(agentId, "sql-default-auth", "SQL Default Auth", "text/html", "<h1>default</h1>");
106
+
107
+ expect(row?.authMode).toBe("authed");
108
+ });
109
+
84
110
  test("snapshotPage captures PRE-update content; post-update lives on parent", () => {
85
111
  const agentId = makeAgentId();
86
112
  const page = createPage({
@@ -54,7 +54,7 @@ describe("Session templates — registration", () => {
54
54
  await ensureTemplatesRegistered();
55
55
  });
56
56
 
57
- test("all 14 system templates are registered", () => {
57
+ test("all 15 system templates are registered", () => {
58
58
  const systemTemplates = [
59
59
  "system.agent.role",
60
60
  "system.agent.register",
@@ -66,6 +66,7 @@ describe("Session templates — registration", () => {
66
66
  "system.agent.agent_fs",
67
67
  "system.agent.self_awareness",
68
68
  "system.agent.context_mode",
69
+ "system.agent.seed_scripts",
69
70
 
70
71
  "system.agent.system",
71
72
  "system.agent.services",
@@ -90,12 +91,12 @@ describe("Session templates — registration", () => {
90
91
  }
91
92
  });
92
93
 
93
- test("total of 20 session/system templates registered", () => {
94
+ test("total of 21 session/system templates registered", () => {
94
95
  const all = getAllTemplateDefinitions();
95
96
  const sessionSystem = all.filter((d) => d.category === "system" || d.category === "session");
96
- // 20 = the original 19 + `system.session.worker.pi` (a pi-specific worker
97
- // composite that omits the context_mode block see session-templates.ts).
98
- expect(sessionSystem.length).toBe(20);
97
+ // 21 = the original 19 + `system.session.worker.pi` + `system.agent.seed_scripts`.
98
+ // The pi composite omits script nudges because pi workers do not have MCP.
99
+ expect(sessionSystem.length).toBe(21);
99
100
  });
100
101
  });
101
102
 
@@ -187,6 +188,15 @@ describe("Session templates — individual resolution", () => {
187
188
  expect(result.text).toContain("batch_execute");
188
189
  });
189
190
 
191
+ test("system.agent.seed_scripts points agents at task-start scripts", () => {
192
+ const result = resolveTemplate("system.agent.seed_scripts", {});
193
+ expect(result.text).toContain("Pre-built Seed Scripts");
194
+ expect(result.text).toContain("task-context-gathering");
195
+ expect(result.text).toContain("smart-recall");
196
+ expect(result.text).toContain("script-search");
197
+ expect(result.text).not.toContain("compound-insights");
198
+ });
199
+
190
200
  // system.agent.guidelines was removed — its content was redundant with worker/lead templates
191
201
 
192
202
  test("system.agent.system contains package info", () => {
@@ -240,6 +250,7 @@ describe("Session templates — composite resolution", () => {
240
250
 
241
251
  // Contains context_mode
242
252
  expect(result.text).toContain("Context Window Management");
253
+ expect(result.text).toContain("Pre-built Seed Scripts");
243
254
 
244
255
  // Guidelines template was removed (redundant with lead/worker templates)
245
256
 
@@ -275,6 +286,7 @@ describe("Session templates — composite resolution", () => {
275
286
 
276
287
  // Contains context_mode
277
288
  expect(result.text).toContain("Context Window Management");
289
+ expect(result.text).toContain("Pre-built Seed Scripts");
278
290
 
279
291
  // Guidelines template was removed (redundant with lead/worker templates)
280
292
 
@@ -309,6 +321,8 @@ describe("Session templates — composite resolution", () => {
309
321
  expect(workerResult.text).toContain("join-swarm");
310
322
  expect(leadResult.text).toContain("How You Are Built");
311
323
  expect(workerResult.text).toContain("How You Are Built");
324
+ expect(leadResult.text).toContain("Pre-built Seed Scripts");
325
+ expect(workerResult.text).toContain("Pre-built Seed Scripts");
312
326
 
313
327
  // Lead has lead content, not worker
314
328
  expect(leadResult.text).toContain("CRITICAL: You are a coordinator");
@@ -343,6 +357,7 @@ describe("Session templates — getBasePrompt integration", () => {
343
357
  expect(result).toContain("join-swarm");
344
358
  expect(result).toContain("store-progress");
345
359
  expect(result).toContain("How You Are Built");
360
+ expect(result).toContain("Pre-built Seed Scripts");
346
361
  expect(result).toContain("System packages available");
347
362
 
348
363
  // Conditional sections (services included by default)
@@ -365,8 +380,22 @@ describe("Session templates — getBasePrompt integration", () => {
365
380
  expect(result).toContain("your role is: lead");
366
381
  expect(result).toContain("integration-test-lead");
367
382
  expect(result).toContain("CRITICAL: You are a coordinator");
383
+ expect(result).toContain("Pre-built Seed Scripts");
368
384
 
369
385
  // Should NOT have worker content
370
386
  expect(result).not.toContain("task-action");
371
387
  });
388
+
389
+ test("getBasePrompt excludes seed_scripts for pi worker", async () => {
390
+ const { getBasePrompt } = await import("../prompts/base-prompt");
391
+ const result = await getBasePrompt({
392
+ role: "worker",
393
+ agentId: "integration-test-pi",
394
+ swarmUrl: "swarm.test.com",
395
+ provider: "pi",
396
+ });
397
+
398
+ expect(result).not.toContain("Pre-built Seed Scripts");
399
+ expect(result).not.toContain("task-context-gathering");
400
+ });
372
401
  });