opencode-swarm-plugin 0.15.0 → 0.17.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.
- package/.beads/issues.jsonl +11 -0
- package/README.md +41 -25
- package/bin/swarm.ts +35 -5
- package/dist/index.js +923 -46
- package/dist/plugin.js +822 -32
- package/examples/commands/swarm.md +43 -0
- package/global-skills/swarm-coordination/SKILL.md +58 -10
- package/package.json +1 -1
- package/src/beads.ts +30 -21
- package/src/index.ts +78 -0
- package/src/mandate-promotion.test.ts +473 -0
- package/src/mandate-promotion.ts +239 -0
- package/src/mandate-storage.test.ts +578 -0
- package/src/mandate-storage.ts +794 -0
- package/src/mandates.ts +540 -0
- package/src/schemas/index.ts +27 -0
- package/src/schemas/mandate.ts +232 -0
- package/src/swarm.ts +24 -9
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mandate schemas for voting system
|
|
3
|
+
*
|
|
4
|
+
* Agents file and vote on ideas, tips, lore, snippets, and feature requests.
|
|
5
|
+
* High-consensus items become "mandates" that influence future behavior.
|
|
6
|
+
*
|
|
7
|
+
* Vote decay and scoring patterns match learning.ts (90-day half-life).
|
|
8
|
+
*/
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Core Types
|
|
13
|
+
// ============================================================================
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Content types for mandate entries
|
|
17
|
+
*/
|
|
18
|
+
export const MandateContentTypeSchema = z.enum([
|
|
19
|
+
"idea",
|
|
20
|
+
"tip",
|
|
21
|
+
"lore",
|
|
22
|
+
"snippet",
|
|
23
|
+
"feature_request",
|
|
24
|
+
]);
|
|
25
|
+
export type MandateContentType = z.infer<typeof MandateContentTypeSchema>;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Mandate status lifecycle
|
|
29
|
+
*
|
|
30
|
+
* - candidate: New entry, collecting votes
|
|
31
|
+
* - established: Has some consensus but not enough for mandate status
|
|
32
|
+
* - mandate: High consensus (net_votes >= 5 AND vote_ratio >= 0.7)
|
|
33
|
+
* - rejected: Strong negative consensus or explicitly rejected
|
|
34
|
+
*/
|
|
35
|
+
export const MandateStatusSchema = z.enum([
|
|
36
|
+
"candidate",
|
|
37
|
+
"established",
|
|
38
|
+
"mandate",
|
|
39
|
+
"rejected",
|
|
40
|
+
]);
|
|
41
|
+
export type MandateStatus = z.infer<typeof MandateStatusSchema>;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Vote type
|
|
45
|
+
*/
|
|
46
|
+
export const VoteTypeSchema = z.enum(["upvote", "downvote"]);
|
|
47
|
+
export type VoteType = z.infer<typeof VoteTypeSchema>;
|
|
48
|
+
|
|
49
|
+
// ============================================================================
|
|
50
|
+
// Entry Schema
|
|
51
|
+
// ============================================================================
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* A mandate entry represents a proposal from an agent
|
|
55
|
+
*
|
|
56
|
+
* Entries can be ideas, tips, lore, code snippets, or feature requests.
|
|
57
|
+
* Other agents vote on entries to reach consensus.
|
|
58
|
+
*/
|
|
59
|
+
export const MandateEntrySchema = z.object({
|
|
60
|
+
/** Unique ID for this entry */
|
|
61
|
+
id: z.string(),
|
|
62
|
+
/** The actual content of the mandate */
|
|
63
|
+
content: z.string().min(1, "Content required"),
|
|
64
|
+
/** Type of content */
|
|
65
|
+
content_type: MandateContentTypeSchema,
|
|
66
|
+
/** Agent that created this entry */
|
|
67
|
+
author_agent: z.string(),
|
|
68
|
+
/** When this entry was created (ISO-8601) */
|
|
69
|
+
created_at: z.string().datetime({ offset: true }),
|
|
70
|
+
/** Current status */
|
|
71
|
+
status: MandateStatusSchema.default("candidate"),
|
|
72
|
+
/** Optional tags for categorization and search */
|
|
73
|
+
tags: z.array(z.string()).default([]),
|
|
74
|
+
/** Optional metadata (e.g., code language for snippets) */
|
|
75
|
+
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
76
|
+
});
|
|
77
|
+
export type MandateEntry = z.infer<typeof MandateEntrySchema>;
|
|
78
|
+
|
|
79
|
+
// ============================================================================
|
|
80
|
+
// Vote Schema
|
|
81
|
+
// ============================================================================
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* A vote on a mandate entry
|
|
85
|
+
*
|
|
86
|
+
* Each agent can vote once per entry (upvote or downvote).
|
|
87
|
+
* Votes decay with 90-day half-life matching learning.ts patterns.
|
|
88
|
+
*/
|
|
89
|
+
export const VoteSchema = z.object({
|
|
90
|
+
/** Unique ID for this vote */
|
|
91
|
+
id: z.string(),
|
|
92
|
+
/** The mandate entry this vote applies to */
|
|
93
|
+
mandate_id: z.string(),
|
|
94
|
+
/** Agent that cast this vote */
|
|
95
|
+
agent_name: z.string(),
|
|
96
|
+
/** Type of vote */
|
|
97
|
+
vote_type: VoteTypeSchema,
|
|
98
|
+
/** When this vote was cast (ISO-8601) */
|
|
99
|
+
timestamp: z.string().datetime({ offset: true }),
|
|
100
|
+
/** Raw vote weight before decay (default: 1.0) */
|
|
101
|
+
weight: z.number().min(0).max(1).default(1.0),
|
|
102
|
+
});
|
|
103
|
+
export type Vote = z.infer<typeof VoteSchema>;
|
|
104
|
+
|
|
105
|
+
// ============================================================================
|
|
106
|
+
// Score Schema
|
|
107
|
+
// ============================================================================
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Calculated score for a mandate entry
|
|
111
|
+
*
|
|
112
|
+
* Scores are recalculated periodically with decay applied.
|
|
113
|
+
* Uses same decay formula as learning.ts (90-day half-life).
|
|
114
|
+
*/
|
|
115
|
+
export const MandateScoreSchema = z.object({
|
|
116
|
+
/** The mandate entry this score applies to */
|
|
117
|
+
mandate_id: z.string(),
|
|
118
|
+
/** Net votes (upvotes - downvotes) with decay applied */
|
|
119
|
+
net_votes: z.number(),
|
|
120
|
+
/** Vote ratio: upvotes / (upvotes + downvotes) */
|
|
121
|
+
vote_ratio: z.number().min(0).max(1),
|
|
122
|
+
/** Final decayed score for ranking */
|
|
123
|
+
decayed_score: z.number(),
|
|
124
|
+
/** When this score was last calculated (ISO-8601) */
|
|
125
|
+
last_calculated: z.string().datetime({ offset: true }),
|
|
126
|
+
/** Raw vote counts (before decay) */
|
|
127
|
+
raw_upvotes: z.number().int().min(0),
|
|
128
|
+
raw_downvotes: z.number().int().min(0),
|
|
129
|
+
/** Decayed vote counts */
|
|
130
|
+
decayed_upvotes: z.number().min(0),
|
|
131
|
+
decayed_downvotes: z.number().min(0),
|
|
132
|
+
});
|
|
133
|
+
export type MandateScore = z.infer<typeof MandateScoreSchema>;
|
|
134
|
+
|
|
135
|
+
// ============================================================================
|
|
136
|
+
// Decay Configuration
|
|
137
|
+
// ============================================================================
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Configuration for mandate decay calculation
|
|
141
|
+
*
|
|
142
|
+
* Matches learning.ts decay patterns.
|
|
143
|
+
*/
|
|
144
|
+
export interface MandateDecayConfig {
|
|
145
|
+
/** Half-life for vote decay in days */
|
|
146
|
+
halfLifeDays: number;
|
|
147
|
+
/** Net votes threshold for mandate status */
|
|
148
|
+
mandateNetVotesThreshold: number;
|
|
149
|
+
/** Vote ratio threshold for mandate status */
|
|
150
|
+
mandateVoteRatioThreshold: number;
|
|
151
|
+
/** Net votes threshold for established status */
|
|
152
|
+
establishedNetVotesThreshold: number;
|
|
153
|
+
/** Negative net votes threshold for rejected status */
|
|
154
|
+
rejectedNetVotesThreshold: number;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export const DEFAULT_MANDATE_DECAY_CONFIG: MandateDecayConfig = {
|
|
158
|
+
halfLifeDays: 90,
|
|
159
|
+
mandateNetVotesThreshold: 5,
|
|
160
|
+
mandateVoteRatioThreshold: 0.7,
|
|
161
|
+
establishedNetVotesThreshold: 2,
|
|
162
|
+
rejectedNetVotesThreshold: -3,
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// ============================================================================
|
|
166
|
+
// API Schemas
|
|
167
|
+
// ============================================================================
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Arguments for creating a mandate entry
|
|
171
|
+
*/
|
|
172
|
+
export const CreateMandateArgsSchema = z.object({
|
|
173
|
+
content: z.string().min(1, "Content required"),
|
|
174
|
+
content_type: MandateContentTypeSchema,
|
|
175
|
+
tags: z.array(z.string()).default([]),
|
|
176
|
+
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
177
|
+
});
|
|
178
|
+
export type CreateMandateArgs = z.infer<typeof CreateMandateArgsSchema>;
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Arguments for casting a vote
|
|
182
|
+
*/
|
|
183
|
+
export const CastVoteArgsSchema = z.object({
|
|
184
|
+
mandate_id: z.string(),
|
|
185
|
+
vote_type: VoteTypeSchema,
|
|
186
|
+
weight: z.number().min(0).max(1).default(1.0),
|
|
187
|
+
});
|
|
188
|
+
export type CastVoteArgs = z.infer<typeof CastVoteArgsSchema>;
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Arguments for querying mandates
|
|
192
|
+
*/
|
|
193
|
+
export const QueryMandatesArgsSchema = z.object({
|
|
194
|
+
status: MandateStatusSchema.optional(),
|
|
195
|
+
content_type: MandateContentTypeSchema.optional(),
|
|
196
|
+
tags: z.array(z.string()).optional(),
|
|
197
|
+
author_agent: z.string().optional(),
|
|
198
|
+
limit: z.number().int().positive().default(20),
|
|
199
|
+
min_score: z.number().optional(),
|
|
200
|
+
});
|
|
201
|
+
export type QueryMandatesArgs = z.infer<typeof QueryMandatesArgsSchema>;
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Result of score calculation
|
|
205
|
+
*/
|
|
206
|
+
export const ScoreCalculationResultSchema = z.object({
|
|
207
|
+
mandate_id: z.string(),
|
|
208
|
+
previous_status: MandateStatusSchema,
|
|
209
|
+
new_status: MandateStatusSchema,
|
|
210
|
+
score: MandateScoreSchema,
|
|
211
|
+
status_changed: z.boolean(),
|
|
212
|
+
});
|
|
213
|
+
export type ScoreCalculationResult = z.infer<
|
|
214
|
+
typeof ScoreCalculationResultSchema
|
|
215
|
+
>;
|
|
216
|
+
|
|
217
|
+
// ============================================================================
|
|
218
|
+
// Exports
|
|
219
|
+
// ============================================================================
|
|
220
|
+
|
|
221
|
+
export const mandateSchemas = {
|
|
222
|
+
MandateContentTypeSchema,
|
|
223
|
+
MandateStatusSchema,
|
|
224
|
+
VoteTypeSchema,
|
|
225
|
+
MandateEntrySchema,
|
|
226
|
+
VoteSchema,
|
|
227
|
+
MandateScoreSchema,
|
|
228
|
+
CreateMandateArgsSchema,
|
|
229
|
+
CastVoteArgsSchema,
|
|
230
|
+
QueryMandatesArgsSchema,
|
|
231
|
+
ScoreCalculationResultSchema,
|
|
232
|
+
};
|
package/src/swarm.ts
CHANGED
|
@@ -2039,7 +2039,9 @@ async function runVerificationGate(
|
|
|
2039
2039
|
|
|
2040
2040
|
if (!ubsStep.passed) {
|
|
2041
2041
|
ubsStep.error = `Found ${ubsResult.summary.critical} critical bugs`;
|
|
2042
|
-
blockers.push(
|
|
2042
|
+
blockers.push(
|
|
2043
|
+
`UBS found ${ubsResult.summary.critical} critical bug(s). Try: Run 'ubs scan ${filesTouched.join(" ")}' to see details, fix critical bugs in reported files, or use skip_ubs_scan=true to bypass (not recommended).`,
|
|
2044
|
+
);
|
|
2043
2045
|
}
|
|
2044
2046
|
|
|
2045
2047
|
steps.push(ubsStep);
|
|
@@ -2060,7 +2062,7 @@ async function runVerificationGate(
|
|
|
2060
2062
|
steps.push(typecheckStep);
|
|
2061
2063
|
if (!typecheckStep.passed && !typecheckStep.skipped) {
|
|
2062
2064
|
blockers.push(
|
|
2063
|
-
`Typecheck: ${typecheckStep.error?.slice(0, 100) || "
|
|
2065
|
+
`Typecheck failed: ${typecheckStep.error?.slice(0, 100) || "type errors found"}. Try: Run 'tsc --noEmit' to see full errors, check tsconfig.json configuration, or fix reported type errors in modified files.`,
|
|
2064
2066
|
);
|
|
2065
2067
|
}
|
|
2066
2068
|
|
|
@@ -2068,7 +2070,9 @@ async function runVerificationGate(
|
|
|
2068
2070
|
const testStep = await runTestVerification(filesTouched);
|
|
2069
2071
|
steps.push(testStep);
|
|
2070
2072
|
if (!testStep.passed && !testStep.skipped) {
|
|
2071
|
-
blockers.push(
|
|
2073
|
+
blockers.push(
|
|
2074
|
+
`Tests failed: ${testStep.error?.slice(0, 100) || "test failures"}. Try: Run 'bun test ${testStep.command.split(" ").slice(2).join(" ")}' to see full output, check test assertions, or fix failing tests in modified files.`,
|
|
2075
|
+
);
|
|
2072
2076
|
}
|
|
2073
2077
|
|
|
2074
2078
|
// Build summary
|
|
@@ -2150,10 +2154,12 @@ async function runUbsScan(files: string[]): Promise<UbsScanResult | null> {
|
|
|
2150
2154
|
} catch (error) {
|
|
2151
2155
|
// UBS output wasn't JSON - this is an error condition
|
|
2152
2156
|
console.error(
|
|
2153
|
-
`[swarm] CRITICAL: UBS scan failed to parse JSON output:`,
|
|
2157
|
+
`[swarm] CRITICAL: UBS scan failed to parse JSON output because output is malformed:`,
|
|
2154
2158
|
error,
|
|
2155
2159
|
);
|
|
2156
|
-
console.error(
|
|
2160
|
+
console.error(
|
|
2161
|
+
`[swarm] Raw output: ${output}. Try: Run 'ubs doctor' to check installation, verify UBS version with 'ubs --version' (need v1.0.0+), or check if UBS supports --json flag.`,
|
|
2162
|
+
);
|
|
2157
2163
|
return {
|
|
2158
2164
|
exitCode: result.exitCode,
|
|
2159
2165
|
bugs: [],
|
|
@@ -2320,7 +2326,10 @@ export const swarm_complete = tool({
|
|
|
2320
2326
|
error: s.error?.slice(0, 200),
|
|
2321
2327
|
})),
|
|
2322
2328
|
},
|
|
2323
|
-
hint:
|
|
2329
|
+
hint:
|
|
2330
|
+
verificationResult.blockers.length > 0
|
|
2331
|
+
? `Fix these issues: ${verificationResult.blockers.map((b, i) => `${i + 1}. ${b}`).join(", ")}. Use skip_verification=true only as last resort.`
|
|
2332
|
+
: "Fix the failing checks and try again. Use skip_verification=true only as last resort.",
|
|
2324
2333
|
gate_function:
|
|
2325
2334
|
"IDENTIFY → RUN → READ → VERIFY → CLAIM (you are at VERIFY, claim blocked)",
|
|
2326
2335
|
},
|
|
@@ -2345,12 +2354,18 @@ export const swarm_complete = tool({
|
|
|
2345
2354
|
return JSON.stringify(
|
|
2346
2355
|
{
|
|
2347
2356
|
success: false,
|
|
2348
|
-
error:
|
|
2357
|
+
error: `UBS found ${ubsResult.summary.critical} critical bug(s) that must be fixed before completing`,
|
|
2349
2358
|
ubs_scan: {
|
|
2350
2359
|
critical_count: ubsResult.summary.critical,
|
|
2351
2360
|
bugs: ubsResult.bugs.filter((b) => b.severity === "critical"),
|
|
2352
2361
|
},
|
|
2353
|
-
hint:
|
|
2362
|
+
hint: `Fix these critical bugs: ${ubsResult.bugs
|
|
2363
|
+
.filter((b) => b.severity === "critical")
|
|
2364
|
+
.map((b) => `${b.file}:${b.line} - ${b.message}`)
|
|
2365
|
+
.slice(0, 3)
|
|
2366
|
+
.join(
|
|
2367
|
+
"; ",
|
|
2368
|
+
)}. 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).`,
|
|
2354
2369
|
},
|
|
2355
2370
|
null,
|
|
2356
2371
|
2,
|
|
@@ -2398,7 +2413,7 @@ export const swarm_complete = tool({
|
|
|
2398
2413
|
|
|
2399
2414
|
if (closeResult.exitCode !== 0) {
|
|
2400
2415
|
throw new SwarmError(
|
|
2401
|
-
`Failed to close bead: ${closeResult.stderr.toString()}
|
|
2416
|
+
`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.`,
|
|
2402
2417
|
"complete",
|
|
2403
2418
|
);
|
|
2404
2419
|
}
|