opencode-swarm-plugin 0.12.6 → 0.12.7
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/.beads/issues.jsonl +9 -0
- package/dist/index.js +309 -25
- package/dist/plugin.js +308 -25
- package/package.json +1 -1
- package/src/agent-mail.ts +157 -26
- package/src/learning.ts +277 -0
- package/src/swarm.ts +176 -1
package/dist/plugin.js
CHANGED
|
@@ -22606,8 +22606,8 @@ var RETRY_CONFIG = {
|
|
|
22606
22606
|
jitterPercent: 20
|
|
22607
22607
|
};
|
|
22608
22608
|
var RECOVERY_CONFIG = {
|
|
22609
|
-
failureThreshold:
|
|
22610
|
-
restartCooldownMs:
|
|
22609
|
+
failureThreshold: 1,
|
|
22610
|
+
restartCooldownMs: 1e4,
|
|
22611
22611
|
enabled: process.env.OPENCODE_AGENT_MAIL_AUTO_RESTART !== "false"
|
|
22612
22612
|
};
|
|
22613
22613
|
var SESSION_STATE_DIR = process.env.SWARM_STATE_DIR || join2(tmpdir(), "swarm-sessions");
|
|
@@ -22981,24 +22981,57 @@ var agentmail_init = tool({
|
|
|
22981
22981
|
fallback: "Swarm will continue without multi-agent coordination. File conflicts possible if multiple agents active."
|
|
22982
22982
|
}, null, 2);
|
|
22983
22983
|
}
|
|
22984
|
-
const
|
|
22985
|
-
|
|
22986
|
-
|
|
22987
|
-
|
|
22988
|
-
|
|
22989
|
-
|
|
22990
|
-
|
|
22991
|
-
|
|
22992
|
-
|
|
22993
|
-
|
|
22994
|
-
|
|
22995
|
-
|
|
22996
|
-
|
|
22997
|
-
|
|
22998
|
-
|
|
22999
|
-
|
|
23000
|
-
|
|
23001
|
-
|
|
22984
|
+
const MAX_INIT_RETRIES = 3;
|
|
22985
|
+
let lastError = null;
|
|
22986
|
+
for (let attempt = 1;attempt <= MAX_INIT_RETRIES; attempt++) {
|
|
22987
|
+
try {
|
|
22988
|
+
const project = await mcpCall("ensure_project", {
|
|
22989
|
+
human_key: args.project_path
|
|
22990
|
+
});
|
|
22991
|
+
const agent = await mcpCall("register_agent", {
|
|
22992
|
+
project_key: args.project_path,
|
|
22993
|
+
program: "opencode",
|
|
22994
|
+
model: "claude-opus-4",
|
|
22995
|
+
name: args.agent_name,
|
|
22996
|
+
task_description: args.task_description || ""
|
|
22997
|
+
});
|
|
22998
|
+
const state = {
|
|
22999
|
+
projectKey: args.project_path,
|
|
23000
|
+
agentName: agent.name,
|
|
23001
|
+
reservations: [],
|
|
23002
|
+
startedAt: new Date().toISOString()
|
|
23003
|
+
};
|
|
23004
|
+
setState(ctx.sessionID, state);
|
|
23005
|
+
if (attempt > 1) {
|
|
23006
|
+
console.warn(`[agent-mail] Init succeeded on attempt ${attempt} after restart`);
|
|
23007
|
+
}
|
|
23008
|
+
return JSON.stringify({ project, agent, available: true }, null, 2);
|
|
23009
|
+
} catch (error45) {
|
|
23010
|
+
lastError = error45 instanceof Error ? error45 : new Error(String(error45));
|
|
23011
|
+
const isUnexpectedError = lastError.message.toLowerCase().includes("unexpected error");
|
|
23012
|
+
console.warn(`[agent-mail] Init attempt ${attempt}/${MAX_INIT_RETRIES} failed: ${lastError.message}`);
|
|
23013
|
+
if (isUnexpectedError && attempt < MAX_INIT_RETRIES) {
|
|
23014
|
+
console.warn("[agent-mail] Detected 'unexpected error', restarting server...");
|
|
23015
|
+
const restarted = await restartServer();
|
|
23016
|
+
if (restarted) {
|
|
23017
|
+
agentMailAvailable = null;
|
|
23018
|
+
consecutiveFailures = 0;
|
|
23019
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
23020
|
+
continue;
|
|
23021
|
+
}
|
|
23022
|
+
}
|
|
23023
|
+
if (!isUnexpectedError) {
|
|
23024
|
+
break;
|
|
23025
|
+
}
|
|
23026
|
+
}
|
|
23027
|
+
}
|
|
23028
|
+
return JSON.stringify({
|
|
23029
|
+
error: `Agent Mail init failed after ${MAX_INIT_RETRIES} attempts`,
|
|
23030
|
+
available: false,
|
|
23031
|
+
lastError: lastError?.message,
|
|
23032
|
+
hint: "Manually restart Agent Mail: pkill -f agent-mail && agent-mail serve",
|
|
23033
|
+
fallback: "Swarm will continue without multi-agent coordination."
|
|
23034
|
+
}, null, 2);
|
|
23002
23035
|
}
|
|
23003
23036
|
});
|
|
23004
23037
|
var agentmail_send = tool({
|
|
@@ -23204,7 +23237,11 @@ var agentmail_health = tool({
|
|
|
23204
23237
|
try {
|
|
23205
23238
|
const response = await fetch(`${AGENT_MAIL_URL}/health/liveness`);
|
|
23206
23239
|
if (response.ok) {
|
|
23207
|
-
|
|
23240
|
+
const functional = await isServerFunctional();
|
|
23241
|
+
if (functional) {
|
|
23242
|
+
return "Agent Mail is running and functional";
|
|
23243
|
+
}
|
|
23244
|
+
return "Agent Mail health OK but MCP not responding - consider restart";
|
|
23208
23245
|
}
|
|
23209
23246
|
return `Agent Mail returned status ${response.status}`;
|
|
23210
23247
|
} catch (error45) {
|
|
@@ -23212,6 +23249,41 @@ var agentmail_health = tool({
|
|
|
23212
23249
|
}
|
|
23213
23250
|
}
|
|
23214
23251
|
});
|
|
23252
|
+
var agentmail_restart = tool({
|
|
23253
|
+
description: "Manually restart Agent Mail server (use when getting 'unexpected error')",
|
|
23254
|
+
args: {
|
|
23255
|
+
force: tool.schema.boolean().optional().describe("Force restart even if server appears healthy (default: false)")
|
|
23256
|
+
},
|
|
23257
|
+
async execute(args) {
|
|
23258
|
+
if (!args.force) {
|
|
23259
|
+
const functional = await isServerFunctional();
|
|
23260
|
+
if (functional) {
|
|
23261
|
+
return JSON.stringify({
|
|
23262
|
+
restarted: false,
|
|
23263
|
+
reason: "Server is functional, no restart needed",
|
|
23264
|
+
hint: "Use force=true to restart anyway"
|
|
23265
|
+
}, null, 2);
|
|
23266
|
+
}
|
|
23267
|
+
}
|
|
23268
|
+
console.warn("[agent-mail] Manual restart requested...");
|
|
23269
|
+
const success2 = await restartServer();
|
|
23270
|
+
agentMailAvailable = null;
|
|
23271
|
+
consecutiveFailures = 0;
|
|
23272
|
+
if (success2) {
|
|
23273
|
+
return JSON.stringify({
|
|
23274
|
+
restarted: true,
|
|
23275
|
+
success: true,
|
|
23276
|
+
message: "Agent Mail server restarted successfully"
|
|
23277
|
+
}, null, 2);
|
|
23278
|
+
}
|
|
23279
|
+
return JSON.stringify({
|
|
23280
|
+
restarted: true,
|
|
23281
|
+
success: false,
|
|
23282
|
+
error: "Restart attempted but server did not come back up",
|
|
23283
|
+
hint: "Check server logs or manually start: agent-mail serve"
|
|
23284
|
+
}, null, 2);
|
|
23285
|
+
}
|
|
23286
|
+
});
|
|
23215
23287
|
var agentMailTools = {
|
|
23216
23288
|
agentmail_init,
|
|
23217
23289
|
agentmail_send,
|
|
@@ -23222,7 +23294,8 @@ var agentMailTools = {
|
|
|
23222
23294
|
agentmail_release,
|
|
23223
23295
|
agentmail_ack,
|
|
23224
23296
|
agentmail_search,
|
|
23225
|
-
agentmail_health
|
|
23297
|
+
agentmail_health,
|
|
23298
|
+
agentmail_restart
|
|
23226
23299
|
};
|
|
23227
23300
|
|
|
23228
23301
|
// src/structured.ts
|
|
@@ -23638,6 +23711,24 @@ var CriterionWeightSchema = exports_external.object({
|
|
|
23638
23711
|
last_validated: exports_external.string().optional(),
|
|
23639
23712
|
half_life_days: exports_external.number().positive().default(90)
|
|
23640
23713
|
});
|
|
23714
|
+
var ErrorTypeSchema = exports_external.enum([
|
|
23715
|
+
"validation",
|
|
23716
|
+
"timeout",
|
|
23717
|
+
"conflict",
|
|
23718
|
+
"tool_failure",
|
|
23719
|
+
"unknown"
|
|
23720
|
+
]);
|
|
23721
|
+
var ErrorEntrySchema = exports_external.object({
|
|
23722
|
+
id: exports_external.string(),
|
|
23723
|
+
bead_id: exports_external.string(),
|
|
23724
|
+
error_type: ErrorTypeSchema,
|
|
23725
|
+
message: exports_external.string(),
|
|
23726
|
+
stack_trace: exports_external.string().optional(),
|
|
23727
|
+
tool_name: exports_external.string().optional(),
|
|
23728
|
+
timestamp: exports_external.string(),
|
|
23729
|
+
resolved: exports_external.boolean().default(false),
|
|
23730
|
+
context: exports_external.string().optional()
|
|
23731
|
+
});
|
|
23641
23732
|
var DecompositionStrategySchema = exports_external.enum([
|
|
23642
23733
|
"file-based",
|
|
23643
23734
|
"feature-based",
|
|
@@ -23711,6 +23802,117 @@ function outcomeToFeedback(outcome, criterion) {
|
|
|
23711
23802
|
raw_value: outcome.decayed_value
|
|
23712
23803
|
};
|
|
23713
23804
|
}
|
|
23805
|
+
class InMemoryErrorStorage {
|
|
23806
|
+
errors = [];
|
|
23807
|
+
async store(entry) {
|
|
23808
|
+
this.errors.push(entry);
|
|
23809
|
+
}
|
|
23810
|
+
async getByBead(beadId) {
|
|
23811
|
+
return this.errors.filter((e) => e.bead_id === beadId);
|
|
23812
|
+
}
|
|
23813
|
+
async getUnresolvedByBead(beadId) {
|
|
23814
|
+
return this.errors.filter((e) => e.bead_id === beadId && !e.resolved);
|
|
23815
|
+
}
|
|
23816
|
+
async markResolved(id) {
|
|
23817
|
+
const error45 = this.errors.find((e) => e.id === id);
|
|
23818
|
+
if (error45) {
|
|
23819
|
+
error45.resolved = true;
|
|
23820
|
+
}
|
|
23821
|
+
}
|
|
23822
|
+
async getAll() {
|
|
23823
|
+
return [...this.errors];
|
|
23824
|
+
}
|
|
23825
|
+
}
|
|
23826
|
+
|
|
23827
|
+
class ErrorAccumulator {
|
|
23828
|
+
storage;
|
|
23829
|
+
constructor(storage) {
|
|
23830
|
+
this.storage = storage ?? new InMemoryErrorStorage;
|
|
23831
|
+
}
|
|
23832
|
+
async recordError(beadId, errorType, message, options) {
|
|
23833
|
+
const entry = {
|
|
23834
|
+
id: `${beadId}-${errorType}-${Date.now()}`,
|
|
23835
|
+
bead_id: beadId,
|
|
23836
|
+
error_type: errorType,
|
|
23837
|
+
message,
|
|
23838
|
+
stack_trace: options?.stack_trace,
|
|
23839
|
+
tool_name: options?.tool_name,
|
|
23840
|
+
timestamp: new Date().toISOString(),
|
|
23841
|
+
resolved: false,
|
|
23842
|
+
context: options?.context
|
|
23843
|
+
};
|
|
23844
|
+
const validated = ErrorEntrySchema.parse(entry);
|
|
23845
|
+
await this.storage.store(validated);
|
|
23846
|
+
return validated;
|
|
23847
|
+
}
|
|
23848
|
+
async getErrors(beadId) {
|
|
23849
|
+
return this.storage.getByBead(beadId);
|
|
23850
|
+
}
|
|
23851
|
+
async getUnresolvedErrors(beadId) {
|
|
23852
|
+
return this.storage.getUnresolvedByBead(beadId);
|
|
23853
|
+
}
|
|
23854
|
+
async resolveError(errorId) {
|
|
23855
|
+
await this.storage.markResolved(errorId);
|
|
23856
|
+
}
|
|
23857
|
+
async getErrorContext(beadId, includeResolved = false) {
|
|
23858
|
+
const errors3 = includeResolved ? await this.getErrors(beadId) : await this.getUnresolvedErrors(beadId);
|
|
23859
|
+
if (errors3.length === 0) {
|
|
23860
|
+
return "";
|
|
23861
|
+
}
|
|
23862
|
+
const byType = errors3.reduce((acc, err) => {
|
|
23863
|
+
const type = err.error_type;
|
|
23864
|
+
if (!acc[type]) {
|
|
23865
|
+
acc[type] = [];
|
|
23866
|
+
}
|
|
23867
|
+
acc[type].push(err);
|
|
23868
|
+
return acc;
|
|
23869
|
+
}, {});
|
|
23870
|
+
const lines = [
|
|
23871
|
+
"## Previous Errors",
|
|
23872
|
+
"",
|
|
23873
|
+
"The following errors were encountered during execution:",
|
|
23874
|
+
""
|
|
23875
|
+
];
|
|
23876
|
+
for (const [type, typeErrors] of Object.entries(byType)) {
|
|
23877
|
+
lines.push(`### ${type} (${typeErrors.length} error${typeErrors.length > 1 ? "s" : ""})`);
|
|
23878
|
+
lines.push("");
|
|
23879
|
+
for (const err of typeErrors) {
|
|
23880
|
+
lines.push(`- **${err.message}**`);
|
|
23881
|
+
if (err.context) {
|
|
23882
|
+
lines.push(` - Context: ${err.context}`);
|
|
23883
|
+
}
|
|
23884
|
+
if (err.tool_name) {
|
|
23885
|
+
lines.push(` - Tool: ${err.tool_name}`);
|
|
23886
|
+
}
|
|
23887
|
+
if (err.stack_trace) {
|
|
23888
|
+
lines.push(` - Stack: \`${err.stack_trace.slice(0, 100)}...\``);
|
|
23889
|
+
}
|
|
23890
|
+
lines.push(` - Time: ${new Date(err.timestamp).toLocaleString()}${err.resolved ? " (resolved)" : ""}`);
|
|
23891
|
+
lines.push("");
|
|
23892
|
+
}
|
|
23893
|
+
}
|
|
23894
|
+
lines.push("**Action Required**: Address these errors before proceeding. Consider:");
|
|
23895
|
+
lines.push("- What caused each error?");
|
|
23896
|
+
lines.push("- How can you prevent similar errors?");
|
|
23897
|
+
lines.push("- Are there patterns across error types?");
|
|
23898
|
+
lines.push("");
|
|
23899
|
+
return lines.join(`
|
|
23900
|
+
`);
|
|
23901
|
+
}
|
|
23902
|
+
async getErrorStats(beadId) {
|
|
23903
|
+
const allErrors = await this.getErrors(beadId);
|
|
23904
|
+
const unresolved = await this.getUnresolvedErrors(beadId);
|
|
23905
|
+
const byType = allErrors.reduce((acc, err) => {
|
|
23906
|
+
acc[err.error_type] = (acc[err.error_type] || 0) + 1;
|
|
23907
|
+
return acc;
|
|
23908
|
+
}, {});
|
|
23909
|
+
return {
|
|
23910
|
+
total: allErrors.length,
|
|
23911
|
+
unresolved: unresolved.length,
|
|
23912
|
+
by_type: byType
|
|
23913
|
+
};
|
|
23914
|
+
}
|
|
23915
|
+
}
|
|
23714
23916
|
|
|
23715
23917
|
// src/swarm.ts
|
|
23716
23918
|
var POSITIVE_MARKERS = [
|
|
@@ -24122,6 +24324,10 @@ Only modify these files. Need others? Message the coordinator.
|
|
|
24122
24324
|
## Context
|
|
24123
24325
|
{shared_context}
|
|
24124
24326
|
|
|
24327
|
+
{compressed_context}
|
|
24328
|
+
|
|
24329
|
+
{error_context}
|
|
24330
|
+
|
|
24125
24331
|
## MANDATORY: Use These Tools
|
|
24126
24332
|
|
|
24127
24333
|
### Agent Mail - communicate with the swarm
|
|
@@ -24155,7 +24361,9 @@ Begin now.`;
|
|
|
24155
24361
|
function formatSubtaskPromptV2(params) {
|
|
24156
24362
|
const fileList = params.files.length > 0 ? params.files.map((f) => `- \`${f}\``).join(`
|
|
24157
24363
|
`) : "(no specific files - use judgment)";
|
|
24158
|
-
|
|
24364
|
+
const compressedSection = params.compressed_context ? params.compressed_context : "";
|
|
24365
|
+
const errorSection = params.error_context ? params.error_context : "";
|
|
24366
|
+
return SUBTASK_PROMPT_V2.replace(/{bead_id}/g, params.bead_id).replace(/{epic_id}/g, params.epic_id).replace("{subtask_title}", params.subtask_title).replace("{subtask_description}", params.subtask_description || "(see title)").replace("{file_list}", fileList).replace("{shared_context}", params.shared_context || "(none)").replace("{compressed_context}", compressedSection).replace("{error_context}", errorSection);
|
|
24159
24367
|
}
|
|
24160
24368
|
var EVALUATION_PROMPT = `Evaluate the work completed for this subtask.
|
|
24161
24369
|
|
|
@@ -24853,6 +25061,7 @@ var swarm_record_outcome = tool({
|
|
|
24853
25061
|
};
|
|
24854
25062
|
const validated = OutcomeSignalsSchema.parse(signals);
|
|
24855
25063
|
const scored = scoreImplicitFeedback(validated, DEFAULT_LEARNING_CONFIG);
|
|
25064
|
+
const errorStats = await globalErrorAccumulator.getErrorStats(args.bead_id);
|
|
24856
25065
|
const criteriaToScore = args.criteria ?? [
|
|
24857
25066
|
"type_safe",
|
|
24858
25067
|
"no_bugs",
|
|
@@ -24864,6 +25073,10 @@ var swarm_record_outcome = tool({
|
|
|
24864
25073
|
if (args.strategy) {
|
|
24865
25074
|
event.context = `${event.context || ""} [strategy: ${args.strategy}]`.trim();
|
|
24866
25075
|
}
|
|
25076
|
+
if (errorStats.total > 0) {
|
|
25077
|
+
const errorSummary = Object.entries(errorStats.by_type).map(([type, count]) => `${type}:${count}`).join(", ");
|
|
25078
|
+
event.context = `${event.context || ""} [errors: ${errorSummary}]`.trim();
|
|
25079
|
+
}
|
|
24867
25080
|
return event;
|
|
24868
25081
|
});
|
|
24869
25082
|
return JSON.stringify({
|
|
@@ -24877,13 +25090,16 @@ var swarm_record_outcome = tool({
|
|
|
24877
25090
|
}
|
|
24878
25091
|
},
|
|
24879
25092
|
feedback_events: feedbackEvents,
|
|
25093
|
+
error_patterns: errorStats,
|
|
24880
25094
|
summary: {
|
|
24881
25095
|
feedback_type: scored.type,
|
|
24882
25096
|
duration_seconds: Math.round(args.duration_ms / 1000),
|
|
24883
25097
|
error_count: args.error_count ?? 0,
|
|
24884
25098
|
retry_count: args.retry_count ?? 0,
|
|
24885
25099
|
success: args.success,
|
|
24886
|
-
strategy: args.strategy
|
|
25100
|
+
strategy: args.strategy,
|
|
25101
|
+
accumulated_errors: errorStats.total,
|
|
25102
|
+
unresolved_errors: errorStats.unresolved
|
|
24887
25103
|
},
|
|
24888
25104
|
note: "Feedback events should be stored for criterion weight calculation. Use learning.ts functions to apply weights."
|
|
24889
25105
|
}, null, 2);
|
|
@@ -25053,6 +25269,70 @@ var swarm_evaluation_prompt = tool({
|
|
|
25053
25269
|
}, null, 2);
|
|
25054
25270
|
}
|
|
25055
25271
|
});
|
|
25272
|
+
var globalErrorAccumulator = new ErrorAccumulator;
|
|
25273
|
+
var swarm_accumulate_error = tool({
|
|
25274
|
+
description: "Record an error during subtask execution. Errors feed into retry prompts.",
|
|
25275
|
+
args: {
|
|
25276
|
+
bead_id: tool.schema.string().describe("Bead ID where error occurred"),
|
|
25277
|
+
error_type: tool.schema.enum(["validation", "timeout", "conflict", "tool_failure", "unknown"]).describe("Category of error"),
|
|
25278
|
+
message: tool.schema.string().describe("Human-readable error message"),
|
|
25279
|
+
stack_trace: tool.schema.string().optional().describe("Stack trace for debugging"),
|
|
25280
|
+
tool_name: tool.schema.string().optional().describe("Tool that failed"),
|
|
25281
|
+
context: tool.schema.string().optional().describe("What was happening when error occurred")
|
|
25282
|
+
},
|
|
25283
|
+
async execute(args) {
|
|
25284
|
+
const entry = await globalErrorAccumulator.recordError(args.bead_id, args.error_type, args.message, {
|
|
25285
|
+
stack_trace: args.stack_trace,
|
|
25286
|
+
tool_name: args.tool_name,
|
|
25287
|
+
context: args.context
|
|
25288
|
+
});
|
|
25289
|
+
return JSON.stringify({
|
|
25290
|
+
success: true,
|
|
25291
|
+
error_id: entry.id,
|
|
25292
|
+
bead_id: entry.bead_id,
|
|
25293
|
+
error_type: entry.error_type,
|
|
25294
|
+
message: entry.message,
|
|
25295
|
+
timestamp: entry.timestamp,
|
|
25296
|
+
note: "Error recorded for retry context. Use swarm_get_error_context to retrieve accumulated errors."
|
|
25297
|
+
}, null, 2);
|
|
25298
|
+
}
|
|
25299
|
+
});
|
|
25300
|
+
var swarm_get_error_context = tool({
|
|
25301
|
+
description: "Get accumulated errors for a bead. Returns formatted context for retry prompts.",
|
|
25302
|
+
args: {
|
|
25303
|
+
bead_id: tool.schema.string().describe("Bead ID to get errors for"),
|
|
25304
|
+
include_resolved: tool.schema.boolean().optional().describe("Include resolved errors (default: false)")
|
|
25305
|
+
},
|
|
25306
|
+
async execute(args) {
|
|
25307
|
+
const errorContext = await globalErrorAccumulator.getErrorContext(args.bead_id, args.include_resolved ?? false);
|
|
25308
|
+
const stats = await globalErrorAccumulator.getErrorStats(args.bead_id);
|
|
25309
|
+
return JSON.stringify({
|
|
25310
|
+
bead_id: args.bead_id,
|
|
25311
|
+
error_context: errorContext,
|
|
25312
|
+
stats: {
|
|
25313
|
+
total_errors: stats.total,
|
|
25314
|
+
unresolved: stats.unresolved,
|
|
25315
|
+
by_type: stats.by_type
|
|
25316
|
+
},
|
|
25317
|
+
has_errors: errorContext.length > 0,
|
|
25318
|
+
usage: "Inject error_context into retry prompt using {error_context} placeholder"
|
|
25319
|
+
}, null, 2);
|
|
25320
|
+
}
|
|
25321
|
+
});
|
|
25322
|
+
var swarm_resolve_error = tool({
|
|
25323
|
+
description: "Mark an error as resolved after fixing it. Updates error accumulator state.",
|
|
25324
|
+
args: {
|
|
25325
|
+
error_id: tool.schema.string().describe("Error ID to mark as resolved")
|
|
25326
|
+
},
|
|
25327
|
+
async execute(args) {
|
|
25328
|
+
await globalErrorAccumulator.resolveError(args.error_id);
|
|
25329
|
+
return JSON.stringify({
|
|
25330
|
+
success: true,
|
|
25331
|
+
error_id: args.error_id,
|
|
25332
|
+
resolved: true
|
|
25333
|
+
}, null, 2);
|
|
25334
|
+
}
|
|
25335
|
+
});
|
|
25056
25336
|
var swarm_init = tool({
|
|
25057
25337
|
description: "Initialize swarm session and check tool availability. Call at swarm start to see what features are available.",
|
|
25058
25338
|
args: {
|
|
@@ -25114,7 +25394,10 @@ var swarmTools = {
|
|
|
25114
25394
|
swarm_subtask_prompt,
|
|
25115
25395
|
swarm_spawn_subtask,
|
|
25116
25396
|
swarm_complete_subtask,
|
|
25117
|
-
swarm_evaluation_prompt
|
|
25397
|
+
swarm_evaluation_prompt,
|
|
25398
|
+
swarm_accumulate_error,
|
|
25399
|
+
swarm_get_error_context,
|
|
25400
|
+
swarm_resolve_error
|
|
25118
25401
|
};
|
|
25119
25402
|
// src/anti-patterns.ts
|
|
25120
25403
|
var PatternKindSchema = exports_external.enum(["pattern", "anti_pattern"]);
|
package/package.json
CHANGED
package/src/agent-mail.ts
CHANGED
|
@@ -38,10 +38,10 @@ const RETRY_CONFIG = {
|
|
|
38
38
|
|
|
39
39
|
// Server recovery configuration
|
|
40
40
|
const RECOVERY_CONFIG = {
|
|
41
|
-
/** Max consecutive failures before attempting restart */
|
|
42
|
-
failureThreshold:
|
|
43
|
-
/** Cooldown between restart attempts (ms) */
|
|
44
|
-
restartCooldownMs:
|
|
41
|
+
/** Max consecutive failures before attempting restart (1 = restart on first "unexpected error") */
|
|
42
|
+
failureThreshold: 1,
|
|
43
|
+
/** Cooldown between restart attempts (ms) - 10 seconds */
|
|
44
|
+
restartCooldownMs: 10000,
|
|
45
45
|
/** Whether auto-restart is enabled */
|
|
46
46
|
enabled: process.env.OPENCODE_AGENT_MAIL_AUTO_RESTART !== "false",
|
|
47
47
|
};
|
|
@@ -880,30 +880,88 @@ export const agentmail_init = tool({
|
|
|
880
880
|
);
|
|
881
881
|
}
|
|
882
882
|
|
|
883
|
-
//
|
|
884
|
-
const
|
|
885
|
-
|
|
886
|
-
});
|
|
883
|
+
// Retry loop with restart on failure
|
|
884
|
+
const MAX_INIT_RETRIES = 3;
|
|
885
|
+
let lastError: Error | null = null;
|
|
887
886
|
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
887
|
+
for (let attempt = 1; attempt <= MAX_INIT_RETRIES; attempt++) {
|
|
888
|
+
try {
|
|
889
|
+
// 1. Ensure project exists
|
|
890
|
+
const project = await mcpCall<ProjectInfo>("ensure_project", {
|
|
891
|
+
human_key: args.project_path,
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
// 2. Register agent
|
|
895
|
+
const agent = await mcpCall<AgentInfo>("register_agent", {
|
|
896
|
+
project_key: args.project_path,
|
|
897
|
+
program: "opencode",
|
|
898
|
+
model: "claude-opus-4",
|
|
899
|
+
name: args.agent_name, // undefined = auto-generate
|
|
900
|
+
task_description: args.task_description || "",
|
|
901
|
+
});
|
|
902
|
+
|
|
903
|
+
// 3. Store state using sessionID
|
|
904
|
+
const state: AgentMailState = {
|
|
905
|
+
projectKey: args.project_path,
|
|
906
|
+
agentName: agent.name,
|
|
907
|
+
reservations: [],
|
|
908
|
+
startedAt: new Date().toISOString(),
|
|
909
|
+
};
|
|
910
|
+
setState(ctx.sessionID, state);
|
|
911
|
+
|
|
912
|
+
// Success - if we retried, log it
|
|
913
|
+
if (attempt > 1) {
|
|
914
|
+
console.warn(
|
|
915
|
+
`[agent-mail] Init succeeded on attempt ${attempt} after restart`,
|
|
916
|
+
);
|
|
917
|
+
}
|
|
896
918
|
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
919
|
+
return JSON.stringify({ project, agent, available: true }, null, 2);
|
|
920
|
+
} catch (error) {
|
|
921
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
922
|
+
const isUnexpectedError = lastError.message
|
|
923
|
+
.toLowerCase()
|
|
924
|
+
.includes("unexpected error");
|
|
925
|
+
|
|
926
|
+
console.warn(
|
|
927
|
+
`[agent-mail] Init attempt ${attempt}/${MAX_INIT_RETRIES} failed: ${lastError.message}`,
|
|
928
|
+
);
|
|
929
|
+
|
|
930
|
+
// If it's an "unexpected error" and we have retries left, restart and retry
|
|
931
|
+
if (isUnexpectedError && attempt < MAX_INIT_RETRIES) {
|
|
932
|
+
console.warn(
|
|
933
|
+
"[agent-mail] Detected 'unexpected error', restarting server...",
|
|
934
|
+
);
|
|
935
|
+
const restarted = await restartServer();
|
|
936
|
+
if (restarted) {
|
|
937
|
+
// Clear cache and retry
|
|
938
|
+
agentMailAvailable = null;
|
|
939
|
+
consecutiveFailures = 0;
|
|
940
|
+
// Small delay to let server stabilize
|
|
941
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
942
|
+
continue;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
905
945
|
|
|
906
|
-
|
|
946
|
+
// For non-unexpected errors or if restart failed, don't retry
|
|
947
|
+
if (!isUnexpectedError) {
|
|
948
|
+
break;
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
// All retries exhausted
|
|
954
|
+
return JSON.stringify(
|
|
955
|
+
{
|
|
956
|
+
error: `Agent Mail init failed after ${MAX_INIT_RETRIES} attempts`,
|
|
957
|
+
available: false,
|
|
958
|
+
lastError: lastError?.message,
|
|
959
|
+
hint: "Manually restart Agent Mail: pkill -f agent-mail && agent-mail serve",
|
|
960
|
+
fallback: "Swarm will continue without multi-agent coordination.",
|
|
961
|
+
},
|
|
962
|
+
null,
|
|
963
|
+
2,
|
|
964
|
+
);
|
|
907
965
|
},
|
|
908
966
|
});
|
|
909
967
|
|
|
@@ -1270,7 +1328,12 @@ export const agentmail_health = tool({
|
|
|
1270
1328
|
try {
|
|
1271
1329
|
const response = await fetch(`${AGENT_MAIL_URL}/health/liveness`);
|
|
1272
1330
|
if (response.ok) {
|
|
1273
|
-
|
|
1331
|
+
// Also check if MCP is functional
|
|
1332
|
+
const functional = await isServerFunctional();
|
|
1333
|
+
if (functional) {
|
|
1334
|
+
return "Agent Mail is running and functional";
|
|
1335
|
+
}
|
|
1336
|
+
return "Agent Mail health OK but MCP not responding - consider restart";
|
|
1274
1337
|
}
|
|
1275
1338
|
return `Agent Mail returned status ${response.status}`;
|
|
1276
1339
|
} catch (error) {
|
|
@@ -1279,6 +1342,73 @@ export const agentmail_health = tool({
|
|
|
1279
1342
|
},
|
|
1280
1343
|
});
|
|
1281
1344
|
|
|
1345
|
+
/**
|
|
1346
|
+
* Manually restart Agent Mail server
|
|
1347
|
+
*
|
|
1348
|
+
* Use when server is in bad state (health OK but MCP failing).
|
|
1349
|
+
* This kills the existing process and starts a fresh one.
|
|
1350
|
+
*/
|
|
1351
|
+
export const agentmail_restart = tool({
|
|
1352
|
+
description:
|
|
1353
|
+
"Manually restart Agent Mail server (use when getting 'unexpected error')",
|
|
1354
|
+
args: {
|
|
1355
|
+
force: tool.schema
|
|
1356
|
+
.boolean()
|
|
1357
|
+
.optional()
|
|
1358
|
+
.describe(
|
|
1359
|
+
"Force restart even if server appears healthy (default: false)",
|
|
1360
|
+
),
|
|
1361
|
+
},
|
|
1362
|
+
async execute(args) {
|
|
1363
|
+
// Check if restart is needed
|
|
1364
|
+
if (!args.force) {
|
|
1365
|
+
const functional = await isServerFunctional();
|
|
1366
|
+
if (functional) {
|
|
1367
|
+
return JSON.stringify(
|
|
1368
|
+
{
|
|
1369
|
+
restarted: false,
|
|
1370
|
+
reason: "Server is functional, no restart needed",
|
|
1371
|
+
hint: "Use force=true to restart anyway",
|
|
1372
|
+
},
|
|
1373
|
+
null,
|
|
1374
|
+
2,
|
|
1375
|
+
);
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
// Attempt restart
|
|
1380
|
+
console.warn("[agent-mail] Manual restart requested...");
|
|
1381
|
+
const success = await restartServer();
|
|
1382
|
+
|
|
1383
|
+
// Clear caches
|
|
1384
|
+
agentMailAvailable = null;
|
|
1385
|
+
consecutiveFailures = 0;
|
|
1386
|
+
|
|
1387
|
+
if (success) {
|
|
1388
|
+
return JSON.stringify(
|
|
1389
|
+
{
|
|
1390
|
+
restarted: true,
|
|
1391
|
+
success: true,
|
|
1392
|
+
message: "Agent Mail server restarted successfully",
|
|
1393
|
+
},
|
|
1394
|
+
null,
|
|
1395
|
+
2,
|
|
1396
|
+
);
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
return JSON.stringify(
|
|
1400
|
+
{
|
|
1401
|
+
restarted: true,
|
|
1402
|
+
success: false,
|
|
1403
|
+
error: "Restart attempted but server did not come back up",
|
|
1404
|
+
hint: "Check server logs or manually start: agent-mail serve",
|
|
1405
|
+
},
|
|
1406
|
+
null,
|
|
1407
|
+
2,
|
|
1408
|
+
);
|
|
1409
|
+
},
|
|
1410
|
+
});
|
|
1411
|
+
|
|
1282
1412
|
// ============================================================================
|
|
1283
1413
|
// Export all tools
|
|
1284
1414
|
// ============================================================================
|
|
@@ -1294,6 +1424,7 @@ export const agentMailTools = {
|
|
|
1294
1424
|
agentmail_ack: agentmail_ack,
|
|
1295
1425
|
agentmail_search: agentmail_search,
|
|
1296
1426
|
agentmail_health: agentmail_health,
|
|
1427
|
+
agentmail_restart: agentmail_restart,
|
|
1297
1428
|
};
|
|
1298
1429
|
|
|
1299
1430
|
// ============================================================================
|