opencode-swarm-plugin 0.21.0 → 0.22.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.
@@ -1054,22 +1054,30 @@ export const swarm_complete = tool({
1054
1054
  .describe("Number of retry attempts during task"),
1055
1055
  },
1056
1056
  async execute(args) {
1057
- // Verify agent is registered in swarm-mail
1058
- // This catches agents who skipped swarmmail_init
1059
- const projectKey = args.project_key.replace(/\//g, "-").replace(/\\/g, "-");
1060
- let agentRegistered = false;
1061
- let registrationWarning = "";
1057
+ // Extract epic ID early for error notifications
1058
+ const epicId = args.bead_id.includes(".")
1059
+ ? args.bead_id.split(".")[0]
1060
+ : args.bead_id;
1062
1061
 
1063
1062
  try {
1064
- const agent = await getAgent(
1065
- projectKey,
1066
- args.agent_name,
1067
- args.project_key,
1068
- );
1069
- agentRegistered = agent !== null;
1063
+ // Verify agent is registered in swarm-mail
1064
+ // This catches agents who skipped swarmmail_init
1065
+ const projectKey = args.project_key
1066
+ .replace(/\//g, "-")
1067
+ .replace(/\\/g, "-");
1068
+ let agentRegistered = false;
1069
+ let registrationWarning = "";
1070
+
1071
+ try {
1072
+ const agent = await getAgent(
1073
+ projectKey,
1074
+ args.agent_name,
1075
+ args.project_key,
1076
+ );
1077
+ agentRegistered = agent !== null;
1070
1078
 
1071
- if (!agentRegistered) {
1072
- registrationWarning = `⚠️ WARNING: Agent '${args.agent_name}' was NOT registered in swarm-mail for project '${projectKey}'.
1079
+ if (!agentRegistered) {
1080
+ registrationWarning = `⚠️ WARNING: Agent '${args.agent_name}' was NOT registered in swarm-mail for project '${projectKey}'.
1073
1081
 
1074
1082
  This usually means you skipped the MANDATORY swarmmail_init step.
1075
1083
 
@@ -1083,251 +1091,301 @@ This usually means you skipped the MANDATORY swarmmail_init step.
1083
1091
 
1084
1092
  Continuing with completion, but this should be fixed for future subtasks.`;
1085
1093
 
1086
- console.warn(`[swarm_complete] ${registrationWarning}`);
1094
+ console.warn(`[swarm_complete] ${registrationWarning}`);
1095
+ }
1096
+ } catch (error) {
1097
+ // Non-fatal - agent might be using legacy workflow
1098
+ console.warn(
1099
+ `[swarm_complete] Could not verify agent registration:`,
1100
+ error,
1101
+ );
1102
+ registrationWarning = `ℹ️ Could not verify swarm-mail registration (database may not be available). Consider running swarmmail_init next time.`;
1087
1103
  }
1088
- } catch (error) {
1089
- // Non-fatal - agent might be using legacy workflow
1090
- console.warn(
1091
- `[swarm_complete] Could not verify agent registration:`,
1092
- error,
1093
- );
1094
- registrationWarning = `ℹ️ Could not verify swarm-mail registration (database may not be available). Consider running swarmmail_init next time.`;
1095
- }
1096
1104
 
1097
- // Run Verification Gate unless explicitly skipped
1098
- let verificationResult: VerificationGateResult | null = null;
1105
+ // Run Verification Gate unless explicitly skipped
1106
+ let verificationResult: VerificationGateResult | null = null;
1099
1107
 
1100
- if (!args.skip_verification && args.files_touched?.length) {
1101
- verificationResult = await runVerificationGate(
1102
- args.files_touched,
1103
- args.skip_ubs_scan ?? false,
1104
- );
1108
+ if (!args.skip_verification && args.files_touched?.length) {
1109
+ verificationResult = await runVerificationGate(
1110
+ args.files_touched,
1111
+ args.skip_ubs_scan ?? false,
1112
+ );
1105
1113
 
1106
- // Block completion if verification failed
1107
- if (!verificationResult.passed) {
1108
- return JSON.stringify(
1109
- {
1110
- success: false,
1111
- error: "Verification Gate FAILED - fix issues before completing",
1112
- verification: {
1113
- passed: false,
1114
- summary: verificationResult.summary,
1115
- blockers: verificationResult.blockers,
1116
- steps: verificationResult.steps.map((s) => ({
1117
- name: s.name,
1118
- passed: s.passed,
1119
- skipped: s.skipped,
1120
- skipReason: s.skipReason,
1121
- error: s.error?.slice(0, 200),
1122
- })),
1114
+ // Block completion if verification failed
1115
+ if (!verificationResult.passed) {
1116
+ return JSON.stringify(
1117
+ {
1118
+ success: false,
1119
+ error: "Verification Gate FAILED - fix issues before completing",
1120
+ verification: {
1121
+ passed: false,
1122
+ summary: verificationResult.summary,
1123
+ blockers: verificationResult.blockers,
1124
+ steps: verificationResult.steps.map((s) => ({
1125
+ name: s.name,
1126
+ passed: s.passed,
1127
+ skipped: s.skipped,
1128
+ skipReason: s.skipReason,
1129
+ error: s.error?.slice(0, 200),
1130
+ })),
1131
+ },
1132
+ hint:
1133
+ verificationResult.blockers.length > 0
1134
+ ? `Fix these issues: ${verificationResult.blockers.map((b, i) => `${i + 1}. ${b}`).join(", ")}. Use skip_verification=true only as last resort.`
1135
+ : "Fix the failing checks and try again. Use skip_verification=true only as last resort.",
1136
+ gate_function:
1137
+ "IDENTIFY → RUN → READ → VERIFY → CLAIM (you are at VERIFY, claim blocked)",
1123
1138
  },
1124
- hint:
1125
- verificationResult.blockers.length > 0
1126
- ? `Fix these issues: ${verificationResult.blockers.map((b, i) => `${i + 1}. ${b}`).join(", ")}. Use skip_verification=true only as last resort.`
1127
- : "Fix the failing checks and try again. Use skip_verification=true only as last resort.",
1128
- gate_function:
1129
- "IDENTIFY → RUN → READ → VERIFY → CLAIM (you are at VERIFY, claim blocked)",
1130
- },
1131
- null,
1132
- 2,
1133
- );
1139
+ null,
1140
+ 2,
1141
+ );
1142
+ }
1134
1143
  }
1135
- }
1136
1144
 
1137
- // Legacy UBS-only path for backward compatibility (when no files_touched)
1138
- let ubsResult: UbsScanResult | null = null;
1139
- if (
1140
- !args.skip_verification &&
1141
- !verificationResult &&
1142
- args.files_touched?.length &&
1143
- !args.skip_ubs_scan
1144
- ) {
1145
- ubsResult = await runUbsScan(args.files_touched);
1145
+ // Legacy UBS-only path for backward compatibility (when no files_touched)
1146
+ let ubsResult: UbsScanResult | null = null;
1147
+ if (
1148
+ !args.skip_verification &&
1149
+ !verificationResult &&
1150
+ args.files_touched?.length &&
1151
+ !args.skip_ubs_scan
1152
+ ) {
1153
+ ubsResult = await runUbsScan(args.files_touched);
1154
+
1155
+ // Block completion if critical bugs found
1156
+ if (ubsResult && ubsResult.summary.critical > 0) {
1157
+ return JSON.stringify(
1158
+ {
1159
+ success: false,
1160
+ error: `UBS found ${ubsResult.summary.critical} critical bug(s) that must be fixed before completing`,
1161
+ ubs_scan: {
1162
+ critical_count: ubsResult.summary.critical,
1163
+ bugs: ubsResult.bugs.filter((b) => b.severity === "critical"),
1164
+ },
1165
+ hint: `Fix these critical bugs: ${ubsResult.bugs
1166
+ .filter((b) => b.severity === "critical")
1167
+ .map((b) => `${b.file}:${b.line} - ${b.message}`)
1168
+ .slice(0, 3)
1169
+ .join(
1170
+ "; ",
1171
+ )}. Try: Run 'ubs scan ${args.files_touched?.join(" ") || "."} --json' for full report, fix reported issues, or use skip_ubs_scan=true to bypass (not recommended).`,
1172
+ },
1173
+ null,
1174
+ 2,
1175
+ );
1176
+ }
1177
+ }
1146
1178
 
1147
- // Block completion if critical bugs found
1148
- if (ubsResult && ubsResult.summary.critical > 0) {
1149
- return JSON.stringify(
1150
- {
1151
- success: false,
1152
- error: `UBS found ${ubsResult.summary.critical} critical bug(s) that must be fixed before completing`,
1153
- ubs_scan: {
1154
- critical_count: ubsResult.summary.critical,
1155
- bugs: ubsResult.bugs.filter((b) => b.severity === "critical"),
1179
+ // Parse and validate evaluation if provided
1180
+ let parsedEvaluation: Evaluation | undefined;
1181
+ if (args.evaluation) {
1182
+ try {
1183
+ parsedEvaluation = EvaluationSchema.parse(
1184
+ JSON.parse(args.evaluation),
1185
+ );
1186
+ } catch (error) {
1187
+ return JSON.stringify(
1188
+ {
1189
+ success: false,
1190
+ error: "Invalid evaluation format",
1191
+ details:
1192
+ error instanceof z.ZodError ? error.issues : String(error),
1156
1193
  },
1157
- hint: `Fix these critical bugs: ${ubsResult.bugs
1158
- .filter((b) => b.severity === "critical")
1159
- .map((b) => `${b.file}:${b.line} - ${b.message}`)
1160
- .slice(0, 3)
1161
- .join(
1162
- "; ",
1163
- )}. Try: Run 'ubs scan ${args.files_touched?.join(" ") || "."} --json' for full report, fix reported issues, or use skip_ubs_scan=true to bypass (not recommended).`,
1164
- },
1165
- null,
1166
- 2,
1167
- );
1194
+ null,
1195
+ 2,
1196
+ );
1197
+ }
1198
+
1199
+ // If evaluation failed, don't complete
1200
+ if (!parsedEvaluation.passed) {
1201
+ return JSON.stringify(
1202
+ {
1203
+ success: false,
1204
+ error: "Self-evaluation failed",
1205
+ retry_suggestion: parsedEvaluation.retry_suggestion,
1206
+ feedback: parsedEvaluation.overall_feedback,
1207
+ },
1208
+ null,
1209
+ 2,
1210
+ );
1211
+ }
1168
1212
  }
1169
- }
1170
1213
 
1171
- // Parse and validate evaluation if provided
1172
- let parsedEvaluation: Evaluation | undefined;
1173
- if (args.evaluation) {
1174
- try {
1175
- parsedEvaluation = EvaluationSchema.parse(JSON.parse(args.evaluation));
1176
- } catch (error) {
1177
- return JSON.stringify(
1178
- {
1179
- success: false,
1180
- error: "Invalid evaluation format",
1181
- details: error instanceof z.ZodError ? error.issues : String(error),
1182
- },
1183
- null,
1184
- 2,
1214
+ // Close the bead
1215
+ const closeResult =
1216
+ await Bun.$`bd close ${args.bead_id} --reason ${args.summary} --json`
1217
+ .quiet()
1218
+ .nothrow();
1219
+
1220
+ if (closeResult.exitCode !== 0) {
1221
+ throw new Error(
1222
+ `Failed to close bead because bd close command failed: ${closeResult.stderr.toString()}. Try: Verify bead exists and is not already closed with 'bd show ${args.bead_id}', check if bead ID is correct with 'beads_query()', or use beads_close tool directly.`,
1185
1223
  );
1186
1224
  }
1187
1225
 
1188
- // If evaluation failed, don't complete
1189
- if (!parsedEvaluation.passed) {
1190
- return JSON.stringify(
1191
- {
1192
- success: false,
1193
- error: "Self-evaluation failed",
1194
- retry_suggestion: parsedEvaluation.retry_suggestion,
1195
- feedback: parsedEvaluation.overall_feedback,
1196
- },
1197
- null,
1198
- 2,
1226
+ // Emit SubtaskOutcomeEvent for learning system
1227
+ try {
1228
+ const epicId = args.bead_id.includes(".")
1229
+ ? args.bead_id.split(".")[0]
1230
+ : args.bead_id;
1231
+
1232
+ const durationMs = args.start_time ? Date.now() - args.start_time : 0;
1233
+
1234
+ const event = createEvent("subtask_outcome", {
1235
+ project_key: args.project_key,
1236
+ epic_id: epicId,
1237
+ bead_id: args.bead_id,
1238
+ planned_files: args.planned_files || [],
1239
+ actual_files: args.files_touched || [],
1240
+ duration_ms: durationMs,
1241
+ error_count: args.error_count || 0,
1242
+ retry_count: args.retry_count || 0,
1243
+ success: true,
1244
+ });
1245
+ await appendEvent(event, args.project_key);
1246
+ } catch (error) {
1247
+ // Non-fatal - log and continue
1248
+ console.warn(
1249
+ "[swarm_complete] Failed to emit SubtaskOutcomeEvent:",
1250
+ error,
1199
1251
  );
1200
1252
  }
1201
- }
1202
1253
 
1203
- // Close the bead
1204
- const closeResult =
1205
- await Bun.$`bd close ${args.bead_id} --reason ${args.summary} --json`
1206
- .quiet()
1207
- .nothrow();
1254
+ // Automatic memory capture (MANDATORY on successful completion)
1255
+ // Extract strategy from bead metadata if available
1256
+ let capturedStrategy: LearningDecompositionStrategy | undefined;
1257
+ const durationMs = args.start_time ? Date.now() - args.start_time : 0;
1208
1258
 
1209
- if (closeResult.exitCode !== 0) {
1210
- throw new Error(
1211
- `Failed to close bead because bd close command failed: ${closeResult.stderr.toString()}. Try: Verify bead exists and is not already closed with 'bd show ${args.bead_id}', check if bead ID is correct with 'beads_query()', or use beads_close tool directly.`,
1259
+ // Build memory information from task completion
1260
+ const memoryInfo = formatMemoryStoreOnSuccess(
1261
+ args.bead_id,
1262
+ args.summary,
1263
+ args.files_touched || [],
1264
+ capturedStrategy,
1212
1265
  );
1213
- }
1214
1266
 
1215
- // Emit SubtaskOutcomeEvent for learning system
1216
- try {
1267
+ let memoryStored = false;
1268
+ let memoryError: string | undefined;
1269
+
1270
+ // Attempt to store in semantic-memory (non-blocking)
1271
+ try {
1272
+ const memoryAvailable = await isToolAvailable("semantic-memory");
1273
+ if (memoryAvailable) {
1274
+ // Call semantic-memory store command
1275
+ const storeResult =
1276
+ await Bun.$`semantic-memory store ${memoryInfo.information} --metadata ${memoryInfo.metadata}`
1277
+ .quiet()
1278
+ .nothrow();
1279
+
1280
+ if (storeResult.exitCode === 0) {
1281
+ memoryStored = true;
1282
+ console.log(
1283
+ `[swarm_complete] Stored learning for ${args.bead_id} in semantic-memory`,
1284
+ );
1285
+ } else {
1286
+ memoryError = `semantic-memory store failed: ${storeResult.stderr.toString().slice(0, 200)}`;
1287
+ console.warn(`[swarm_complete] ${memoryError}`);
1288
+ }
1289
+ } else {
1290
+ memoryError =
1291
+ "semantic-memory not available - learning stored in-memory only";
1292
+ warnMissingTool("semantic-memory");
1293
+ }
1294
+ } catch (error) {
1295
+ memoryError = `Failed to store memory: ${error instanceof Error ? error.message : String(error)}`;
1296
+ console.warn(`[swarm_complete] ${memoryError}`);
1297
+ }
1298
+
1299
+ // Release file reservations for this agent using embedded swarm-mail
1300
+ try {
1301
+ await releaseSwarmFiles({
1302
+ projectPath: args.project_key,
1303
+ agentName: args.agent_name,
1304
+ // Release all reservations for this agent
1305
+ });
1306
+ } catch (error) {
1307
+ // Release might fail (e.g., no reservations existed)
1308
+ // This is non-fatal - log and continue
1309
+ console.warn(
1310
+ `[swarm] Failed to release file reservations for ${args.agent_name}:`,
1311
+ error,
1312
+ );
1313
+ }
1314
+
1315
+ // Extract epic ID
1217
1316
  const epicId = args.bead_id.includes(".")
1218
1317
  ? args.bead_id.split(".")[0]
1219
1318
  : args.bead_id;
1220
1319
 
1221
- const durationMs = args.start_time ? Date.now() - args.start_time : 0;
1222
-
1223
- const event = createEvent("subtask_outcome", {
1224
- project_key: args.project_key,
1225
- epic_id: epicId,
1226
- bead_id: args.bead_id,
1227
- planned_files: args.planned_files || [],
1228
- actual_files: args.files_touched || [],
1229
- duration_ms: durationMs,
1230
- error_count: args.error_count || 0,
1231
- retry_count: args.retry_count || 0,
1232
- success: true,
1233
- });
1234
- await appendEvent(event, args.project_key);
1235
- } catch (error) {
1236
- // Non-fatal - log and continue
1237
- console.warn(
1238
- "[swarm_complete] Failed to emit SubtaskOutcomeEvent:",
1239
- error,
1240
- );
1241
- }
1242
-
1243
- // Release file reservations for this agent using embedded swarm-mail
1244
- try {
1245
- await releaseSwarmFiles({
1320
+ // Send completion message using embedded swarm-mail with memory capture status
1321
+ const completionBody = [
1322
+ `## Subtask Complete: ${args.bead_id}`,
1323
+ "",
1324
+ `**Summary**: ${args.summary}`,
1325
+ "",
1326
+ parsedEvaluation
1327
+ ? `**Self-Evaluation**: ${parsedEvaluation.passed ? "PASSED" : "FAILED"}`
1328
+ : "",
1329
+ parsedEvaluation?.overall_feedback
1330
+ ? `**Feedback**: ${parsedEvaluation.overall_feedback}`
1331
+ : "",
1332
+ "",
1333
+ `**Memory Capture**: ${memoryStored ? "✓ Stored in semantic-memory" : `✗ ${memoryError || "Failed"}`}`,
1334
+ ]
1335
+ .filter(Boolean)
1336
+ .join("\n");
1337
+
1338
+ await sendSwarmMessage({
1246
1339
  projectPath: args.project_key,
1247
- agentName: args.agent_name,
1248
- // Release all reservations for this agent
1340
+ fromAgent: args.agent_name,
1341
+ toAgents: [], // Thread broadcast
1342
+ subject: `Complete: ${args.bead_id}`,
1343
+ body: completionBody,
1344
+ threadId: epicId,
1345
+ importance: "normal",
1249
1346
  });
1250
- } catch (error) {
1251
- // Release might fail (e.g., no reservations existed)
1252
- // This is non-fatal - log and continue
1253
- console.warn(
1254
- `[swarm] Failed to release file reservations for ${args.agent_name}:`,
1255
- error,
1256
- );
1257
- }
1258
1347
 
1259
- // Extract epic ID
1260
- const epicId = args.bead_id.includes(".")
1261
- ? args.bead_id.split(".")[0]
1262
- : args.bead_id;
1263
-
1264
- // Send completion message using embedded swarm-mail
1265
- const completionBody = [
1266
- `## Subtask Complete: ${args.bead_id}`,
1267
- "",
1268
- `**Summary**: ${args.summary}`,
1269
- "",
1270
- parsedEvaluation
1271
- ? `**Self-Evaluation**: ${parsedEvaluation.passed ? "PASSED" : "FAILED"}`
1272
- : "",
1273
- parsedEvaluation?.overall_feedback
1274
- ? `**Feedback**: ${parsedEvaluation.overall_feedback}`
1275
- : "",
1276
- ]
1277
- .filter(Boolean)
1278
- .join("\n");
1279
-
1280
- await sendSwarmMessage({
1281
- projectPath: args.project_key,
1282
- fromAgent: args.agent_name,
1283
- toAgents: [], // Thread broadcast
1284
- subject: `Complete: ${args.bead_id}`,
1285
- body: completionBody,
1286
- threadId: epicId,
1287
- importance: "normal",
1288
- });
1289
-
1290
- // Build success response with semantic-memory integration
1291
- const response = {
1292
- success: true,
1293
- bead_id: args.bead_id,
1294
- closed: true,
1295
- reservations_released: true,
1296
- message_sent: true,
1297
- agent_registration: {
1298
- verified: agentRegistered,
1299
- warning: registrationWarning || undefined,
1300
- },
1301
- verification_gate: verificationResult
1302
- ? {
1303
- passed: true,
1304
- summary: verificationResult.summary,
1305
- steps: verificationResult.steps.map((s) => ({
1306
- name: s.name,
1307
- passed: s.passed,
1308
- skipped: s.skipped,
1309
- skipReason: s.skipReason,
1310
- })),
1311
- }
1312
- : args.skip_verification
1313
- ? { skipped: true, reason: "skip_verification=true" }
1314
- : { skipped: true, reason: "no files_touched provided" },
1315
- ubs_scan: ubsResult
1316
- ? {
1317
- ran: true,
1318
- bugs_found: ubsResult.summary.total,
1319
- summary: ubsResult.summary,
1320
- warnings: ubsResult.bugs.filter((b) => b.severity !== "critical"),
1321
- }
1322
- : verificationResult
1323
- ? { ran: true, included_in_verification_gate: true }
1324
- : {
1325
- ran: false,
1326
- reason: args.skip_ubs_scan
1327
- ? "skipped"
1328
- : "no files or ubs unavailable",
1329
- },
1330
- learning_prompt: `## Reflection
1348
+ // Build success response with semantic-memory integration
1349
+ const response = {
1350
+ success: true,
1351
+ bead_id: args.bead_id,
1352
+ closed: true,
1353
+ reservations_released: true,
1354
+ message_sent: true,
1355
+ agent_registration: {
1356
+ verified: agentRegistered,
1357
+ warning: registrationWarning || undefined,
1358
+ },
1359
+ verification_gate: verificationResult
1360
+ ? {
1361
+ passed: true,
1362
+ summary: verificationResult.summary,
1363
+ steps: verificationResult.steps.map((s) => ({
1364
+ name: s.name,
1365
+ passed: s.passed,
1366
+ skipped: s.skipped,
1367
+ skipReason: s.skipReason,
1368
+ })),
1369
+ }
1370
+ : args.skip_verification
1371
+ ? { skipped: true, reason: "skip_verification=true" }
1372
+ : { skipped: true, reason: "no files_touched provided" },
1373
+ ubs_scan: ubsResult
1374
+ ? {
1375
+ ran: true,
1376
+ bugs_found: ubsResult.summary.total,
1377
+ summary: ubsResult.summary,
1378
+ warnings: ubsResult.bugs.filter((b) => b.severity !== "critical"),
1379
+ }
1380
+ : verificationResult
1381
+ ? { ran: true, included_in_verification_gate: true }
1382
+ : {
1383
+ ran: false,
1384
+ reason: args.skip_ubs_scan
1385
+ ? "skipped"
1386
+ : "no files or ubs unavailable",
1387
+ },
1388
+ learning_prompt: `## Reflection
1331
1389
 
1332
1390
  Did you learn anything reusable during this subtask? Consider:
1333
1391
 
@@ -1339,15 +1397,110 @@ Did you learn anything reusable during this subtask? Consider:
1339
1397
  If you discovered something valuable, use \`swarm_learn\` or \`skills_create\` to preserve it as a skill for future swarms.
1340
1398
 
1341
1399
  Files touched: ${args.files_touched?.join(", ") || "none recorded"}`,
1342
- // Add semantic-memory integration on success
1343
- memory_store: formatMemoryStoreOnSuccess(
1344
- args.bead_id,
1345
- args.summary,
1346
- args.files_touched || [],
1347
- ),
1348
- };
1400
+ // Automatic memory capture (MANDATORY)
1401
+ memory_capture: {
1402
+ attempted: true,
1403
+ stored: memoryStored,
1404
+ error: memoryError,
1405
+ information: memoryInfo.information,
1406
+ metadata: memoryInfo.metadata,
1407
+ note: memoryStored
1408
+ ? "Learning automatically stored in semantic-memory"
1409
+ : `Failed to store: ${memoryError}. Learning lost unless semantic-memory is available.`,
1410
+ },
1411
+ };
1412
+
1413
+ return JSON.stringify(response, null, 2);
1414
+ } catch (error) {
1415
+ // CRITICAL: Notify coordinator of failure via swarm mail
1416
+ const errorMessage =
1417
+ error instanceof Error ? error.message : String(error);
1418
+ const errorStack = error instanceof Error ? error.stack : undefined;
1419
+
1420
+ // Determine which step failed
1421
+ let failedStep = "unknown";
1422
+ if (errorMessage.includes("verification")) {
1423
+ failedStep = "Verification Gate (UBS/typecheck/tests)";
1424
+ } else if (errorMessage.includes("UBS") || errorMessage.includes("ubs")) {
1425
+ failedStep = "UBS scan";
1426
+ } else if (errorMessage.includes("evaluation")) {
1427
+ failedStep = "Self-evaluation parsing";
1428
+ } else if (
1429
+ errorMessage.includes("bead") ||
1430
+ errorMessage.includes("close")
1431
+ ) {
1432
+ failedStep = "Bead close";
1433
+ } else if (
1434
+ errorMessage.includes("memory") ||
1435
+ errorMessage.includes("semantic")
1436
+ ) {
1437
+ failedStep = "Memory storage (non-fatal)";
1438
+ } else if (
1439
+ errorMessage.includes("reservation") ||
1440
+ errorMessage.includes("release")
1441
+ ) {
1442
+ failedStep = "File reservation release";
1443
+ } else if (
1444
+ errorMessage.includes("message") ||
1445
+ errorMessage.includes("mail")
1446
+ ) {
1447
+ failedStep = "Swarm mail notification";
1448
+ }
1349
1449
 
1350
- return JSON.stringify(response, null, 2);
1450
+ // Build error notification body
1451
+ const errorBody = [
1452
+ `## ⚠️ SWARM_COMPLETE FAILED`,
1453
+ "",
1454
+ `**Bead**: ${args.bead_id}`,
1455
+ `**Agent**: ${args.agent_name}`,
1456
+ `**Failed Step**: ${failedStep}`,
1457
+ "",
1458
+ `### Error Message`,
1459
+ "```",
1460
+ errorMessage,
1461
+ "```",
1462
+ "",
1463
+ errorStack
1464
+ ? `### Stack Trace\n\`\`\`\n${errorStack.slice(0, 1000)}\n\`\`\`\n`
1465
+ : "",
1466
+ `### Context`,
1467
+ `- **Summary**: ${args.summary}`,
1468
+ `- **Files touched**: ${args.files_touched?.length ? args.files_touched.join(", ") : "none"}`,
1469
+ `- **Skip UBS**: ${args.skip_ubs_scan ?? false}`,
1470
+ `- **Skip verification**: ${args.skip_verification ?? false}`,
1471
+ "",
1472
+ `### Recovery Actions`,
1473
+ "1. Check error message for specific issue",
1474
+ "2. Review failed step (UBS scan, typecheck, bead close, etc.)",
1475
+ "3. Fix underlying issue or use skip flags if appropriate",
1476
+ "4. Retry swarm_complete after fixing",
1477
+ ]
1478
+ .filter(Boolean)
1479
+ .join("\n");
1480
+
1481
+ // Send urgent notification to coordinator
1482
+ try {
1483
+ await sendSwarmMessage({
1484
+ projectPath: args.project_key,
1485
+ fromAgent: args.agent_name,
1486
+ toAgents: [], // Thread broadcast to coordinator
1487
+ subject: `FAILED: swarm_complete for ${args.bead_id}`,
1488
+ body: errorBody,
1489
+ threadId: epicId,
1490
+ importance: "urgent",
1491
+ });
1492
+ } catch (mailError) {
1493
+ // Even swarm mail failed - log to console as last resort
1494
+ console.error(
1495
+ `[swarm_complete] CRITICAL: Failed to notify coordinator of failure for ${args.bead_id}:`,
1496
+ mailError,
1497
+ );
1498
+ console.error(`[swarm_complete] Original error:`, error);
1499
+ }
1500
+
1501
+ // Re-throw the original error after notifying
1502
+ throw error;
1503
+ }
1351
1504
  },
1352
1505
  });
1353
1506