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.
@@ -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(`UBS: ${ubsResult.summary.critical} critical bugs found`);
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) || "failed"}`,
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(`Tests: ${testStep.error?.slice(0, 100) || "failed"}`);
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(`[swarm] Raw output:`, output);
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: "Fix the failing checks and try again. Use skip_verification=true only as last resort.",
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: "UBS found critical bugs - fix before completing",
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: "Fix the critical bugs and try again, or use skip_ubs_scan=true to bypass",
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
  }