@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
@@ -162,6 +162,9 @@ export interface SwarmSdk {
162
162
  script_upsert(args: { name: string; source: string; description?: string; intent?: string; scope?: ScriptScope; fsMode?: ScriptFsMode }): Promise<unknown>;
163
163
  script_delete(args: { name: string; scope?: ScriptScope }): Promise<unknown>;
164
164
  script_queryTypes(args: { name: string; scope?: ScriptScope }): Promise<unknown>;
165
+ script_launchRun(args: { source: string; args?: unknown; idempotencyKey?: string; scriptName?: string; requestedByUserId?: string }): Promise<unknown>;
166
+ script_getRun(args: { id: string }): Promise<unknown>;
167
+ script_listRuns(args?: { status?: "running" | "paused" | "completed" | "failed" | "cancelled" | "aborted_limit"; agentId?: string; limit?: number; offset?: number }): Promise<unknown>;
165
168
 
166
169
  // --- write: repos ---
167
170
  repo_update(args: Record<string, unknown>): Promise<unknown>;
@@ -209,7 +212,53 @@ export interface ScriptStdlib {
209
212
 
210
213
  export interface ScriptLogger extends Console {}
211
214
 
215
+ export interface ScriptRunContext {
216
+ id: string;
217
+ agentId: string;
218
+ args: unknown;
219
+ }
220
+
221
+ export interface ScriptWorkflowSteps {
222
+ rawLlm(
223
+ label: string,
224
+ config: { prompt: string; model?: string; schema?: Record<string, unknown> },
225
+ ): Promise<unknown>;
226
+ agentTask(
227
+ label: string,
228
+ config: {
229
+ template?: string;
230
+ task?: string;
231
+ agentId?: string;
232
+ tags?: string[];
233
+ priority?: number;
234
+ offerMode?: boolean;
235
+ dir?: string;
236
+ vcsRepo?: string;
237
+ model?: string;
238
+ parentTaskId?: string;
239
+ requestedByUserId?: string;
240
+ outputSchema?: Record<string, unknown>;
241
+ },
242
+ ): Promise<unknown>;
243
+ swarmScript(
244
+ label: string,
245
+ config: {
246
+ name?: string;
247
+ scriptName?: string;
248
+ source?: string;
249
+ args?: unknown;
250
+ scope?: ScriptScope;
251
+ fsMode?: ScriptFsMode;
252
+ intent?: string;
253
+ idempotencyKey?: string;
254
+ },
255
+ ): Promise<unknown>;
256
+ humanInTheLoop(): Promise<never>;
257
+ }
258
+
212
259
  export interface ScriptContext {
260
+ run?: ScriptRunContext;
261
+ step?: ScriptWorkflowSteps;
213
262
  swarm: SwarmSdk & { config: SwarmConfig };
214
263
  stdlib: ScriptStdlib;
215
264
  logger: ScriptLogger;
@@ -0,0 +1,221 @@
1
+ import { z } from "zod";
2
+
3
+ export const argsSchema = z.object({
4
+ nowIso: z.string().optional().describe("Triage clock override (default: current time)"),
5
+ failureLookbackMinutes: z
6
+ .number()
7
+ .int()
8
+ .positive()
9
+ .optional()
10
+ .describe("Look back this many minutes for real failures (default 60)"),
11
+ stuckMinutes: z
12
+ .number()
13
+ .int()
14
+ .positive()
15
+ .optional()
16
+ .describe("Flag in-progress tasks older than this on offline agents (default 5)"),
17
+ deployWindowMinutes: z
18
+ .number()
19
+ .int()
20
+ .positive()
21
+ .optional()
22
+ .describe("Window around now for merged agent-swarm PRs (default 15)"),
23
+ repo: z
24
+ .string()
25
+ .optional()
26
+ .describe("Optional GitHub repository in 'owner/name' form for restart/deploy PR detection"),
27
+ });
28
+
29
+ const BENIGN_FAILURE_RE = /^(superseded_workflow_task|cancelled|reboot-sweep)$/i;
30
+
31
+ function rowsToObjects(res: any): any[] {
32
+ const p = res?.data ?? res;
33
+ const cols: string[] = p?.columns ?? [];
34
+ return (p?.rows ?? []).map((r: any) =>
35
+ Array.isArray(r) ? Object.fromEntries(cols.map((c, i) => [c, r[i]])) : r,
36
+ );
37
+ }
38
+
39
+ async function query(ctx: any, sql: string, params?: unknown[]): Promise<any[]> {
40
+ try {
41
+ return rowsToObjects(await ctx.swarm.db_query({ sql, params }));
42
+ } catch (error) {
43
+ return [{ unavailable: error instanceof Error ? error.message : String(error) }];
44
+ }
45
+ }
46
+
47
+ function taskPreview(task: unknown): string {
48
+ return String(task || "").replace(/\s+/g, " ").trim().slice(0, 180);
49
+ }
50
+
51
+ function summarizeTask(row: any): any {
52
+ return {
53
+ id: row.id,
54
+ status: row.status,
55
+ taskType: row.taskType || null,
56
+ agentId: row.agentId || null,
57
+ agentName: row.agentName || null,
58
+ scheduleId: row.scheduleId || null,
59
+ parentTaskId: row.parentTaskId || null,
60
+ failureReason: row.failureReason || null,
61
+ createdAt: row.createdAt,
62
+ lastUpdatedAt: row.lastUpdatedAt,
63
+ taskPreview: taskPreview(row.task),
64
+ };
65
+ }
66
+
67
+ async function recentMergedPrs(
68
+ ctx: any,
69
+ repo: string | undefined,
70
+ nowMs: number,
71
+ windowMinutes: number,
72
+ ): Promise<any> {
73
+ if (!repo) return { skipped: "repo not provided" };
74
+ if (!/^[^/\s]+\/[^/\s]+$/.test(repo)) return { error: "repo must be in 'owner/name' form" };
75
+
76
+ const windowMs = windowMinutes * 60 * 1000;
77
+ try {
78
+ const response = await ctx.stdlib.fetch(
79
+ "https://api.github.com/repos/" +
80
+ repo +
81
+ "/pulls?state=closed&sort=updated&direction=desc&per_page=20",
82
+ {
83
+ headers: {
84
+ Accept: "application/vnd.github+json",
85
+ "User-Agent": "agent-swarm-boot-triage",
86
+ },
87
+ },
88
+ );
89
+ if (!response.ok) return { error: `GitHub API ${response.status}` };
90
+ const prs = (await response.json()) as any[];
91
+ return prs
92
+ .filter((pr) => pr?.merged_at)
93
+ .map((pr) => {
94
+ const mergedAtMs = Date.parse(pr.merged_at);
95
+ return {
96
+ repo,
97
+ number: pr.number,
98
+ title: pr.title,
99
+ url: pr.html_url,
100
+ mergedAt: pr.merged_at,
101
+ minutesFromRestart: Math.round((mergedAtMs - nowMs) / 60000),
102
+ };
103
+ })
104
+ .filter((pr) => Math.abs(pr.minutesFromRestart * 60000) <= windowMs);
105
+ } catch (error) {
106
+ return { error: error instanceof Error ? error.message : String(error) };
107
+ }
108
+ }
109
+
110
+ /** Read-only post-restart triage snapshot for the heartbeat.boot-triage prompt. */
111
+ export default async function bootTriage(args: any, ctx: any) {
112
+ const parsed = argsSchema.safeParse(args || {});
113
+ if (!parsed.success) return { error: "invalid args: " + parsed.error.message };
114
+
115
+ const now = parsed.data.nowIso ? new Date(parsed.data.nowIso) : new Date();
116
+ const nowMs = now.getTime();
117
+ const failureLookbackMinutes = parsed.data.failureLookbackMinutes || 60;
118
+ const stuckMinutes = parsed.data.stuckMinutes || 5;
119
+ const deployWindowMinutes = parsed.data.deployWindowMinutes || 15;
120
+ const repo = parsed.data.repo;
121
+
122
+ const mergedPrs = await recentMergedPrs(ctx, repo, nowMs, deployWindowMinutes);
123
+
124
+ const recentFailureRows = await query(
125
+ ctx,
126
+ `SELECT t.id, t.task, t.status, t.taskType, t.agentId, a.name as agentName,
127
+ t.scheduleId, t.parentTaskId, t.failureReason, t.createdAt, t.lastUpdatedAt
128
+ FROM agent_tasks t
129
+ LEFT JOIN agents a ON a.id = t.agentId
130
+ WHERE t.status = 'failed'
131
+ AND datetime(t.lastUpdatedAt) >= datetime(?, ?)
132
+ ORDER BY datetime(t.lastUpdatedAt) DESC
133
+ LIMIT 50`,
134
+ [now.toISOString(), `-${failureLookbackMinutes} minutes`],
135
+ );
136
+ const recentlyFailedTasks = recentFailureRows
137
+ .filter((row) => !row.unavailable)
138
+ .filter((row) => !BENIGN_FAILURE_RE.test(String(row.failureReason || "")))
139
+ .map(summarizeTask);
140
+
141
+ const stuckOfflineRows = await query(
142
+ ctx,
143
+ `SELECT t.id, t.task, t.status, t.taskType, t.agentId, a.name as agentName,
144
+ t.scheduleId, t.parentTaskId, t.failureReason, t.createdAt, t.lastUpdatedAt
145
+ FROM agent_tasks t
146
+ JOIN agents a ON a.id = t.agentId
147
+ WHERE t.status = 'in_progress'
148
+ AND a.status = 'offline'
149
+ AND datetime(t.lastUpdatedAt) <= datetime(?, ?)
150
+ ORDER BY datetime(t.lastUpdatedAt) ASC
151
+ LIMIT 50`,
152
+ [now.toISOString(), `-${stuckMinutes} minutes`],
153
+ );
154
+ const stuckInProgressOnOfflineAgents = stuckOfflineRows
155
+ .filter((row) => !row.unavailable)
156
+ .map(summarizeTask);
157
+
158
+ const orphanRows = await query(
159
+ ctx,
160
+ `SELECT t.id, t.task, t.status, t.taskType, t.agentId, a.name as agentName,
161
+ t.scheduleId, t.parentTaskId, t.failureReason, t.createdAt, t.lastUpdatedAt
162
+ FROM agent_tasks t
163
+ JOIN agents a ON a.id = t.agentId
164
+ WHERE t.status IN ('pending', 'offered')
165
+ AND a.status = 'offline'
166
+ ORDER BY datetime(t.lastUpdatedAt) ASC
167
+ LIMIT 50`,
168
+ );
169
+ const orphanedPendingOrOfferedOnOfflineWorkers = orphanRows
170
+ .filter((row) => !row.unavailable)
171
+ .map(summarizeTask);
172
+
173
+ const supersededRows = await query(
174
+ ctx,
175
+ `SELECT p.id, p.task, p.status, p.taskType, p.agentId, a.name as agentName,
176
+ p.scheduleId, p.parentTaskId, p.failureReason, p.createdAt, p.lastUpdatedAt
177
+ FROM agent_tasks p
178
+ LEFT JOIN agents a ON a.id = p.agentId
179
+ WHERE p.status = 'superseded'
180
+ AND datetime(p.lastUpdatedAt) >= datetime(?, ?)
181
+ AND NOT EXISTS (
182
+ SELECT 1
183
+ FROM agent_tasks c
184
+ WHERE c.parentTaskId = p.id
185
+ AND c.taskType = 'resume'
186
+ AND c.status NOT IN ('completed', 'failed', 'cancelled', 'superseded')
187
+ )
188
+ ORDER BY datetime(p.lastUpdatedAt) DESC
189
+ LIMIT 50`,
190
+ [now.toISOString(), `-${failureLookbackMinutes} minutes`],
191
+ );
192
+ const supersededTasksMissingResumeChild = supersededRows
193
+ .filter((row) => !row.unavailable)
194
+ .map(summarizeTask);
195
+
196
+ return {
197
+ generatedAt: now.toISOString(),
198
+ windows: {
199
+ failureLookbackMinutes,
200
+ stuckMinutes,
201
+ deployWindowMinutes,
202
+ },
203
+ deployRestartDetection: {
204
+ source: repo ? "github:" + repo : null,
205
+ mergedPrsWithinWindow: Array.isArray(mergedPrs) ? mergedPrs : [],
206
+ skipped: Array.isArray(mergedPrs) ? null : mergedPrs.skipped || null,
207
+ error: Array.isArray(mergedPrs) ? null : mergedPrs.error,
208
+ },
209
+ recentlyFailedTasks,
210
+ stuckInProgressOnOfflineAgents,
211
+ orphanedPendingOrOfferedOnOfflineWorkers,
212
+ supersededTasksMissingResumeChild,
213
+ summary: {
214
+ mergedPrsWithinWindow: Array.isArray(mergedPrs) ? mergedPrs.length : 0,
215
+ recentlyFailedTasks: recentlyFailedTasks.length,
216
+ stuckInProgressOnOfflineAgents: stuckInProgressOnOfflineAgents.length,
217
+ orphanedPendingOrOfferedOnOfflineWorkers: orphanedPendingOrOfferedOnOfflineWorkers.length,
218
+ supersededTasksMissingResumeChild: supersededTasksMissingResumeChild.length,
219
+ },
220
+ };
221
+ }