opencode-swarm-plugin 0.14.0 → 0.16.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/analysis/skill-architecture-meta-skills.md +1562 -0
- package/.beads/issues.jsonl +79 -0
- package/README.md +20 -18
- package/VERIFICATION_QUALITY_PATTERNS.md +565 -0
- package/bin/swarm.ts +5 -5
- package/dist/index.js +1318 -28
- package/dist/plugin.js +1218 -14
- package/docs/analysis/subagent-coordination-patterns.md +900 -0
- package/docs/analysis-socratic-planner-pattern.md +504 -0
- package/examples/commands/swarm.md +112 -7
- package/global-skills/swarm-coordination/SKILL.md +118 -20
- package/global-skills/swarm-coordination/references/coordinator-patterns.md +1 -1
- package/package.json +1 -1
- package/src/index.ts +78 -0
- package/src/learning.integration.test.ts +310 -0
- package/src/learning.ts +198 -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 +786 -0
- package/src/mandates.ts +540 -0
- package/src/schemas/index.ts +27 -0
- package/src/schemas/mandate.ts +232 -0
- package/src/skills.test.ts +194 -0
- package/src/skills.ts +184 -15
- package/src/swarm.integration.test.ts +4 -4
- package/src/swarm.ts +496 -19
- package/workflow-integration-analysis.md +876 -0
package/dist/plugin.js
CHANGED
|
@@ -28536,6 +28536,7 @@ var require_gray_matter = __commonJS((exports, module) => {
|
|
|
28536
28536
|
// src/skills.ts
|
|
28537
28537
|
var exports_skills = {};
|
|
28538
28538
|
__export(exports_skills, {
|
|
28539
|
+
validateCSOCompliance: () => validateCSOCompliance,
|
|
28539
28540
|
skills_use: () => skills_use,
|
|
28540
28541
|
skills_update: () => skills_update,
|
|
28541
28542
|
skills_read: () => skills_read,
|
|
@@ -28708,6 +28709,90 @@ async function listSkills() {
|
|
|
28708
28709
|
function invalidateSkillsCache() {
|
|
28709
28710
|
skillsCache = null;
|
|
28710
28711
|
}
|
|
28712
|
+
function validateCSOCompliance(name, description) {
|
|
28713
|
+
const warnings = {
|
|
28714
|
+
critical: [],
|
|
28715
|
+
suggestions: []
|
|
28716
|
+
};
|
|
28717
|
+
const hasUseWhen = /\buse when\b/i.test(description);
|
|
28718
|
+
if (!hasUseWhen) {
|
|
28719
|
+
warnings.critical.push("Description should include 'Use when...' to focus on triggering conditions");
|
|
28720
|
+
}
|
|
28721
|
+
if (description.length > 1024) {
|
|
28722
|
+
warnings.critical.push(`Description is ${description.length} chars (max 1024) - will be rejected`);
|
|
28723
|
+
} else if (description.length > 500) {
|
|
28724
|
+
warnings.suggestions.push(`Description is ${description.length} chars (aim for <500 for optimal discoverability)`);
|
|
28725
|
+
}
|
|
28726
|
+
const firstPersonPattern = /\b(I|I'm|I'll|my|mine|myself)\b/i;
|
|
28727
|
+
const secondPersonPattern = /\b(you|you're|you'll|your|yours|yourself)\b/i;
|
|
28728
|
+
if (firstPersonPattern.test(description)) {
|
|
28729
|
+
warnings.critical.push("Description uses first-person ('I', 'my') - skills are injected into system prompt, use third-person only");
|
|
28730
|
+
}
|
|
28731
|
+
if (secondPersonPattern.test(description)) {
|
|
28732
|
+
warnings.critical.push("Description uses second-person ('you', 'your') - use third-person voice (e.g., 'Handles X' not 'You can handle X')");
|
|
28733
|
+
}
|
|
28734
|
+
const nameWords = name.split("-");
|
|
28735
|
+
const firstWord = nameWords[0];
|
|
28736
|
+
const isGerund = /ing$/.test(firstWord);
|
|
28737
|
+
const isVerbForm = /(ing|ize|ify|ate)$/.test(firstWord);
|
|
28738
|
+
if (!isGerund && !isVerbForm) {
|
|
28739
|
+
const actionVerbs = [
|
|
28740
|
+
"test",
|
|
28741
|
+
"debug",
|
|
28742
|
+
"fix",
|
|
28743
|
+
"scan",
|
|
28744
|
+
"check",
|
|
28745
|
+
"validate",
|
|
28746
|
+
"create",
|
|
28747
|
+
"build",
|
|
28748
|
+
"deploy",
|
|
28749
|
+
"run",
|
|
28750
|
+
"load",
|
|
28751
|
+
"fetch",
|
|
28752
|
+
"parse"
|
|
28753
|
+
];
|
|
28754
|
+
const startsWithAction = actionVerbs.includes(firstWord);
|
|
28755
|
+
if (!startsWithAction) {
|
|
28756
|
+
warnings.suggestions.push(`Name '${name}' doesn't follow verb-first pattern. Consider gerunds (e.g., 'testing-skills' not 'test-skill') or action verbs for better clarity`);
|
|
28757
|
+
}
|
|
28758
|
+
}
|
|
28759
|
+
if (name.length > 64) {
|
|
28760
|
+
warnings.critical.push(`Name exceeds 64 character limit (${name.length} chars)`);
|
|
28761
|
+
}
|
|
28762
|
+
if (!/^[a-z0-9-]+$/.test(name)) {
|
|
28763
|
+
warnings.critical.push("Name must be lowercase letters, numbers, and hyphens only");
|
|
28764
|
+
}
|
|
28765
|
+
return warnings;
|
|
28766
|
+
}
|
|
28767
|
+
function formatCSOWarnings(warnings) {
|
|
28768
|
+
if (warnings.critical.length === 0 && warnings.suggestions.length === 0) {
|
|
28769
|
+
return null;
|
|
28770
|
+
}
|
|
28771
|
+
const parts = [];
|
|
28772
|
+
if (warnings.critical.length > 0) {
|
|
28773
|
+
parts.push("**CSO Critical Issues:**");
|
|
28774
|
+
for (const warning of warnings.critical) {
|
|
28775
|
+
parts.push(` ⚠️ ${warning}`);
|
|
28776
|
+
}
|
|
28777
|
+
}
|
|
28778
|
+
if (warnings.suggestions.length > 0) {
|
|
28779
|
+
parts.push(`
|
|
28780
|
+
**CSO Suggestions:**`);
|
|
28781
|
+
for (const suggestion of warnings.suggestions) {
|
|
28782
|
+
parts.push(` \uD83D\uDCA1 ${suggestion}`);
|
|
28783
|
+
}
|
|
28784
|
+
}
|
|
28785
|
+
parts.push(`
|
|
28786
|
+
**CSO Guide:**`);
|
|
28787
|
+
parts.push(" • Start description with 'Use when...' (focus on triggering conditions)");
|
|
28788
|
+
parts.push(" • Keep description <500 chars (max 1024)");
|
|
28789
|
+
parts.push(" • Use third-person voice only (injected into system prompt)");
|
|
28790
|
+
parts.push(" • Name: verb-first or gerunds (e.g., 'testing-async' not 'async-test')");
|
|
28791
|
+
parts.push(`
|
|
28792
|
+
Example: 'Use when tests have race conditions - replaces arbitrary timeouts with condition polling'`);
|
|
28793
|
+
return parts.join(`
|
|
28794
|
+
`);
|
|
28795
|
+
}
|
|
28711
28796
|
function quoteYamlScalar(value) {
|
|
28712
28797
|
const needsQuoting = /[:\n\r#"'`\[\]{}|>&*!?@]/.test(value) || value.startsWith(" ") || value.endsWith(" ") || value === "" || /^[0-9]/.test(value) || ["true", "false", "null", "yes", "no", "on", "off"].includes(value.toLowerCase());
|
|
28713
28798
|
if (!needsQuoting) {
|
|
@@ -29059,6 +29144,7 @@ Good skills have:
|
|
|
29059
29144
|
if (existing) {
|
|
29060
29145
|
return `Skill '${args.name}' already exists at ${existing.path}. Use skills_update to modify it.`;
|
|
29061
29146
|
}
|
|
29147
|
+
const csoWarnings = validateCSOCompliance(args.name, args.description);
|
|
29062
29148
|
let skillDir;
|
|
29063
29149
|
if (args.directory === "global") {
|
|
29064
29150
|
skillDir = join5(getGlobalSkillsDir(), args.name);
|
|
@@ -29074,7 +29160,7 @@ Good skills have:
|
|
|
29074
29160
|
const content = generateSkillContent(args.name, args.description, args.body, { tags: args.tags, tools: args.tools });
|
|
29075
29161
|
await writeFile(skillPath, content, "utf-8");
|
|
29076
29162
|
invalidateSkillsCache();
|
|
29077
|
-
|
|
29163
|
+
const response = {
|
|
29078
29164
|
success: true,
|
|
29079
29165
|
skill: args.name,
|
|
29080
29166
|
path: skillPath,
|
|
@@ -29084,7 +29170,12 @@ Good skills have:
|
|
|
29084
29170
|
"Add examples.md or reference.md for supplementary content",
|
|
29085
29171
|
"Add scripts/ directory for executable helpers"
|
|
29086
29172
|
]
|
|
29087
|
-
}
|
|
29173
|
+
};
|
|
29174
|
+
const warningsMessage = formatCSOWarnings(csoWarnings);
|
|
29175
|
+
if (warningsMessage) {
|
|
29176
|
+
response.cso_warnings = warningsMessage;
|
|
29177
|
+
}
|
|
29178
|
+
return JSON.stringify(response, null, 2);
|
|
29088
29179
|
} catch (error45) {
|
|
29089
29180
|
return `Failed to create skill: ${error45 instanceof Error ? error45.message : String(error45)}`;
|
|
29090
29181
|
}
|
|
@@ -29577,6 +29668,84 @@ var SwarmStatusSchema = exports_external.object({
|
|
|
29577
29668
|
agents: exports_external.array(SpawnedAgentSchema),
|
|
29578
29669
|
last_update: exports_external.string().datetime({ offset: true })
|
|
29579
29670
|
});
|
|
29671
|
+
// src/schemas/mandate.ts
|
|
29672
|
+
init_zod();
|
|
29673
|
+
var MandateContentTypeSchema = exports_external.enum([
|
|
29674
|
+
"idea",
|
|
29675
|
+
"tip",
|
|
29676
|
+
"lore",
|
|
29677
|
+
"snippet",
|
|
29678
|
+
"feature_request"
|
|
29679
|
+
]);
|
|
29680
|
+
var MandateStatusSchema = exports_external.enum([
|
|
29681
|
+
"candidate",
|
|
29682
|
+
"established",
|
|
29683
|
+
"mandate",
|
|
29684
|
+
"rejected"
|
|
29685
|
+
]);
|
|
29686
|
+
var VoteTypeSchema = exports_external.enum(["upvote", "downvote"]);
|
|
29687
|
+
var MandateEntrySchema = exports_external.object({
|
|
29688
|
+
id: exports_external.string(),
|
|
29689
|
+
content: exports_external.string().min(1, "Content required"),
|
|
29690
|
+
content_type: MandateContentTypeSchema,
|
|
29691
|
+
author_agent: exports_external.string(),
|
|
29692
|
+
created_at: exports_external.string().datetime({ offset: true }),
|
|
29693
|
+
status: MandateStatusSchema.default("candidate"),
|
|
29694
|
+
tags: exports_external.array(exports_external.string()).default([]),
|
|
29695
|
+
metadata: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
|
|
29696
|
+
});
|
|
29697
|
+
var VoteSchema = exports_external.object({
|
|
29698
|
+
id: exports_external.string(),
|
|
29699
|
+
mandate_id: exports_external.string(),
|
|
29700
|
+
agent_name: exports_external.string(),
|
|
29701
|
+
vote_type: VoteTypeSchema,
|
|
29702
|
+
timestamp: exports_external.string().datetime({ offset: true }),
|
|
29703
|
+
weight: exports_external.number().min(0).max(1).default(1)
|
|
29704
|
+
});
|
|
29705
|
+
var MandateScoreSchema = exports_external.object({
|
|
29706
|
+
mandate_id: exports_external.string(),
|
|
29707
|
+
net_votes: exports_external.number(),
|
|
29708
|
+
vote_ratio: exports_external.number().min(0).max(1),
|
|
29709
|
+
decayed_score: exports_external.number(),
|
|
29710
|
+
last_calculated: exports_external.string().datetime({ offset: true }),
|
|
29711
|
+
raw_upvotes: exports_external.number().int().min(0),
|
|
29712
|
+
raw_downvotes: exports_external.number().int().min(0),
|
|
29713
|
+
decayed_upvotes: exports_external.number().min(0),
|
|
29714
|
+
decayed_downvotes: exports_external.number().min(0)
|
|
29715
|
+
});
|
|
29716
|
+
var DEFAULT_MANDATE_DECAY_CONFIG = {
|
|
29717
|
+
halfLifeDays: 90,
|
|
29718
|
+
mandateNetVotesThreshold: 5,
|
|
29719
|
+
mandateVoteRatioThreshold: 0.7,
|
|
29720
|
+
establishedNetVotesThreshold: 2,
|
|
29721
|
+
rejectedNetVotesThreshold: -3
|
|
29722
|
+
};
|
|
29723
|
+
var CreateMandateArgsSchema = exports_external.object({
|
|
29724
|
+
content: exports_external.string().min(1, "Content required"),
|
|
29725
|
+
content_type: MandateContentTypeSchema,
|
|
29726
|
+
tags: exports_external.array(exports_external.string()).default([]),
|
|
29727
|
+
metadata: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
|
|
29728
|
+
});
|
|
29729
|
+
var CastVoteArgsSchema = exports_external.object({
|
|
29730
|
+
mandate_id: exports_external.string(),
|
|
29731
|
+
vote_type: VoteTypeSchema,
|
|
29732
|
+
weight: exports_external.number().min(0).max(1).default(1)
|
|
29733
|
+
});
|
|
29734
|
+
var QueryMandatesArgsSchema = exports_external.object({
|
|
29735
|
+
status: MandateStatusSchema.optional(),
|
|
29736
|
+
content_type: MandateContentTypeSchema.optional(),
|
|
29737
|
+
tags: exports_external.array(exports_external.string()).optional(),
|
|
29738
|
+
author_agent: exports_external.string().optional(),
|
|
29739
|
+
limit: exports_external.number().int().positive().default(20),
|
|
29740
|
+
min_score: exports_external.number().optional()
|
|
29741
|
+
});
|
|
29742
|
+
var ScoreCalculationResultSchema = exports_external.object({
|
|
29743
|
+
mandate_id: exports_external.string(),
|
|
29744
|
+
previous_status: MandateStatusSchema,
|
|
29745
|
+
new_status: MandateStatusSchema,
|
|
29746
|
+
score: MandateScoreSchema,
|
|
29747
|
+
status_changed: exports_external.boolean()
|
|
29748
|
+
});
|
|
29580
29749
|
// src/beads.ts
|
|
29581
29750
|
var beadsWorkingDirectory = null;
|
|
29582
29751
|
function setBeadsWorkingDirectory(directory) {
|
|
@@ -32229,6 +32398,90 @@ function outcomeToFeedback(outcome, criterion) {
|
|
|
32229
32398
|
raw_value: outcome.decayed_value
|
|
32230
32399
|
};
|
|
32231
32400
|
}
|
|
32401
|
+
var StrikeRecordSchema = exports_external.object({
|
|
32402
|
+
bead_id: exports_external.string(),
|
|
32403
|
+
strike_count: exports_external.number().int().min(0).max(3),
|
|
32404
|
+
failures: exports_external.array(exports_external.object({
|
|
32405
|
+
attempt: exports_external.string(),
|
|
32406
|
+
reason: exports_external.string(),
|
|
32407
|
+
timestamp: exports_external.string()
|
|
32408
|
+
})),
|
|
32409
|
+
first_strike_at: exports_external.string().optional(),
|
|
32410
|
+
last_strike_at: exports_external.string().optional()
|
|
32411
|
+
});
|
|
32412
|
+
|
|
32413
|
+
class InMemoryStrikeStorage {
|
|
32414
|
+
strikes = new Map;
|
|
32415
|
+
async store(record2) {
|
|
32416
|
+
this.strikes.set(record2.bead_id, record2);
|
|
32417
|
+
}
|
|
32418
|
+
async get(beadId) {
|
|
32419
|
+
return this.strikes.get(beadId) ?? null;
|
|
32420
|
+
}
|
|
32421
|
+
async getAll() {
|
|
32422
|
+
return Array.from(this.strikes.values());
|
|
32423
|
+
}
|
|
32424
|
+
async clear(beadId) {
|
|
32425
|
+
this.strikes.delete(beadId);
|
|
32426
|
+
}
|
|
32427
|
+
}
|
|
32428
|
+
async function addStrike(beadId, attempt, reason, storage = new InMemoryStrikeStorage) {
|
|
32429
|
+
const existing = await storage.get(beadId);
|
|
32430
|
+
const now = new Date().toISOString();
|
|
32431
|
+
const record2 = existing ?? {
|
|
32432
|
+
bead_id: beadId,
|
|
32433
|
+
strike_count: 0,
|
|
32434
|
+
failures: []
|
|
32435
|
+
};
|
|
32436
|
+
record2.strike_count = Math.min(3, record2.strike_count + 1);
|
|
32437
|
+
record2.failures.push({ attempt, reason, timestamp: now });
|
|
32438
|
+
record2.last_strike_at = now;
|
|
32439
|
+
if (!record2.first_strike_at) {
|
|
32440
|
+
record2.first_strike_at = now;
|
|
32441
|
+
}
|
|
32442
|
+
await storage.store(record2);
|
|
32443
|
+
return record2;
|
|
32444
|
+
}
|
|
32445
|
+
async function getStrikes(beadId, storage = new InMemoryStrikeStorage) {
|
|
32446
|
+
const record2 = await storage.get(beadId);
|
|
32447
|
+
return record2?.strike_count ?? 0;
|
|
32448
|
+
}
|
|
32449
|
+
async function isStrikedOut(beadId, storage = new InMemoryStrikeStorage) {
|
|
32450
|
+
const count = await getStrikes(beadId, storage);
|
|
32451
|
+
return count >= 3;
|
|
32452
|
+
}
|
|
32453
|
+
async function getArchitecturePrompt(beadId, storage = new InMemoryStrikeStorage) {
|
|
32454
|
+
const record2 = await storage.get(beadId);
|
|
32455
|
+
if (!record2 || record2.strike_count < 3) {
|
|
32456
|
+
return "";
|
|
32457
|
+
}
|
|
32458
|
+
const failuresList = record2.failures.map((f, i) => `${i + 1}. **${f.attempt}** - Failed: ${f.reason}`).join(`
|
|
32459
|
+
`);
|
|
32460
|
+
return `## Architecture Review Required
|
|
32461
|
+
|
|
32462
|
+
This bead (\`${beadId}\`) has failed 3 consecutive fix attempts:
|
|
32463
|
+
|
|
32464
|
+
${failuresList}
|
|
32465
|
+
|
|
32466
|
+
This pattern suggests an **architectural problem**, not a bug.
|
|
32467
|
+
|
|
32468
|
+
**Questions to consider:**
|
|
32469
|
+
- Is the current approach fundamentally sound?
|
|
32470
|
+
- Should we refactor the architecture instead?
|
|
32471
|
+
- Are we fixing symptoms instead of root cause?
|
|
32472
|
+
|
|
32473
|
+
**Options:**
|
|
32474
|
+
1. **Refactor architecture** (describe new approach)
|
|
32475
|
+
2. **Continue with Fix #4** (explain why this time is different)
|
|
32476
|
+
3. **Abandon this approach entirely**
|
|
32477
|
+
|
|
32478
|
+
**DO NOT attempt Fix #4 without answering these questions.**
|
|
32479
|
+
`;
|
|
32480
|
+
}
|
|
32481
|
+
async function clearStrikes(beadId, storage = new InMemoryStrikeStorage) {
|
|
32482
|
+
await storage.clear(beadId);
|
|
32483
|
+
}
|
|
32484
|
+
|
|
32232
32485
|
class InMemoryErrorStorage {
|
|
32233
32486
|
errors = [];
|
|
32234
32487
|
async store(entry) {
|
|
@@ -32747,7 +33000,7 @@ You MUST keep your bead updated as you work:
|
|
|
32747
33000
|
|
|
32748
33001
|
**Never work silently.** Your bead status is how the swarm tracks progress.
|
|
32749
33002
|
|
|
32750
|
-
## MANDATORY:
|
|
33003
|
+
## MANDATORY: Swarm Mail Communication
|
|
32751
33004
|
|
|
32752
33005
|
You MUST communicate with other agents:
|
|
32753
33006
|
|
|
@@ -32756,9 +33009,9 @@ You MUST communicate with other agents:
|
|
|
32756
33009
|
3. **Announce blockers** immediately - don't spin trying to fix alone
|
|
32757
33010
|
4. **Coordinate on shared concerns** - if you see something affecting other agents, say so
|
|
32758
33011
|
|
|
32759
|
-
Use
|
|
33012
|
+
Use Swarm Mail for all communication:
|
|
32760
33013
|
\`\`\`
|
|
32761
|
-
|
|
33014
|
+
swarmmail_send(
|
|
32762
33015
|
to: ["coordinator" or specific agent],
|
|
32763
33016
|
subject: "Brief subject",
|
|
32764
33017
|
body: "Message content",
|
|
@@ -32770,7 +33023,7 @@ agentmail_send(
|
|
|
32770
33023
|
|
|
32771
33024
|
1. **Start**: Your bead is already marked in_progress
|
|
32772
33025
|
2. **Progress**: Use swarm_progress to report status updates
|
|
32773
|
-
3. **Blocked**: Report immediately via
|
|
33026
|
+
3. **Blocked**: Report immediately via Swarm Mail - don't spin
|
|
32774
33027
|
4. **Complete**: Use swarm_complete when done - it handles:
|
|
32775
33028
|
- Closing your bead with a summary
|
|
32776
33029
|
- Releasing file reservations
|
|
@@ -32792,7 +33045,7 @@ Before writing code:
|
|
|
32792
33045
|
1. **Read the files** you're assigned to understand current state
|
|
32793
33046
|
2. **Plan your approach** - what changes, in what order?
|
|
32794
33047
|
3. **Identify risks** - what could go wrong? What dependencies?
|
|
32795
|
-
4. **Communicate your plan** via
|
|
33048
|
+
4. **Communicate your plan** via Swarm Mail if non-trivial
|
|
32796
33049
|
|
|
32797
33050
|
Begin work on your subtask now.`;
|
|
32798
33051
|
var SUBTASK_PROMPT_V2 = `You are a swarm agent working on: **{subtask_title}**
|
|
@@ -33495,6 +33748,139 @@ var swarm_progress = tool({
|
|
|
33495
33748
|
return `Progress reported: ${args.status}${args.progress_percent !== undefined ? ` (${args.progress_percent}%)` : ""}`;
|
|
33496
33749
|
}
|
|
33497
33750
|
});
|
|
33751
|
+
async function runTypecheckVerification() {
|
|
33752
|
+
const step = {
|
|
33753
|
+
name: "typecheck",
|
|
33754
|
+
command: "tsc --noEmit",
|
|
33755
|
+
passed: false,
|
|
33756
|
+
exitCode: -1
|
|
33757
|
+
};
|
|
33758
|
+
try {
|
|
33759
|
+
const tsconfigExists = await Bun.file("tsconfig.json").exists();
|
|
33760
|
+
if (!tsconfigExists) {
|
|
33761
|
+
step.skipped = true;
|
|
33762
|
+
step.skipReason = "No tsconfig.json found";
|
|
33763
|
+
step.passed = true;
|
|
33764
|
+
return step;
|
|
33765
|
+
}
|
|
33766
|
+
const result = await Bun.$`tsc --noEmit`.quiet().nothrow();
|
|
33767
|
+
step.exitCode = result.exitCode;
|
|
33768
|
+
step.passed = result.exitCode === 0;
|
|
33769
|
+
if (!step.passed) {
|
|
33770
|
+
step.error = result.stderr.toString().slice(0, 1000);
|
|
33771
|
+
step.output = result.stdout.toString().slice(0, 1000);
|
|
33772
|
+
}
|
|
33773
|
+
} catch (error45) {
|
|
33774
|
+
step.skipped = true;
|
|
33775
|
+
step.skipReason = `tsc not available: ${error45 instanceof Error ? error45.message : String(error45)}`;
|
|
33776
|
+
step.passed = true;
|
|
33777
|
+
}
|
|
33778
|
+
return step;
|
|
33779
|
+
}
|
|
33780
|
+
async function runTestVerification(filesTouched) {
|
|
33781
|
+
const step = {
|
|
33782
|
+
name: "tests",
|
|
33783
|
+
command: "bun test <related-files>",
|
|
33784
|
+
passed: false,
|
|
33785
|
+
exitCode: -1
|
|
33786
|
+
};
|
|
33787
|
+
if (filesTouched.length === 0) {
|
|
33788
|
+
step.skipped = true;
|
|
33789
|
+
step.skipReason = "No files touched";
|
|
33790
|
+
step.passed = true;
|
|
33791
|
+
return step;
|
|
33792
|
+
}
|
|
33793
|
+
const testPatterns = [];
|
|
33794
|
+
for (const file2 of filesTouched) {
|
|
33795
|
+
if (file2.includes(".test.") || file2.includes(".spec.")) {
|
|
33796
|
+
testPatterns.push(file2);
|
|
33797
|
+
continue;
|
|
33798
|
+
}
|
|
33799
|
+
const baseName = file2.replace(/\.(ts|tsx|js|jsx)$/, "");
|
|
33800
|
+
testPatterns.push(`${baseName}.test.ts`);
|
|
33801
|
+
testPatterns.push(`${baseName}.test.tsx`);
|
|
33802
|
+
testPatterns.push(`${baseName}.spec.ts`);
|
|
33803
|
+
}
|
|
33804
|
+
const existingTests = [];
|
|
33805
|
+
for (const pattern of testPatterns) {
|
|
33806
|
+
try {
|
|
33807
|
+
const exists = await Bun.file(pattern).exists();
|
|
33808
|
+
if (exists) {
|
|
33809
|
+
existingTests.push(pattern);
|
|
33810
|
+
}
|
|
33811
|
+
} catch {}
|
|
33812
|
+
}
|
|
33813
|
+
if (existingTests.length === 0) {
|
|
33814
|
+
step.skipped = true;
|
|
33815
|
+
step.skipReason = "No related test files found";
|
|
33816
|
+
step.passed = true;
|
|
33817
|
+
return step;
|
|
33818
|
+
}
|
|
33819
|
+
try {
|
|
33820
|
+
step.command = `bun test ${existingTests.join(" ")}`;
|
|
33821
|
+
const result = await Bun.$`bun test ${existingTests}`.quiet().nothrow();
|
|
33822
|
+
step.exitCode = result.exitCode;
|
|
33823
|
+
step.passed = result.exitCode === 0;
|
|
33824
|
+
if (!step.passed) {
|
|
33825
|
+
step.error = result.stderr.toString().slice(0, 1000);
|
|
33826
|
+
step.output = result.stdout.toString().slice(0, 1000);
|
|
33827
|
+
}
|
|
33828
|
+
} catch (error45) {
|
|
33829
|
+
step.skipped = true;
|
|
33830
|
+
step.skipReason = `Test runner failed: ${error45 instanceof Error ? error45.message : String(error45)}`;
|
|
33831
|
+
step.passed = true;
|
|
33832
|
+
}
|
|
33833
|
+
return step;
|
|
33834
|
+
}
|
|
33835
|
+
async function runVerificationGate(filesTouched, skipUbs = false) {
|
|
33836
|
+
const steps = [];
|
|
33837
|
+
const blockers = [];
|
|
33838
|
+
if (!skipUbs && filesTouched.length > 0) {
|
|
33839
|
+
const ubsResult = await runUbsScan(filesTouched);
|
|
33840
|
+
if (ubsResult) {
|
|
33841
|
+
const ubsStep = {
|
|
33842
|
+
name: "ubs_scan",
|
|
33843
|
+
command: `ubs scan ${filesTouched.join(" ")}`,
|
|
33844
|
+
passed: ubsResult.summary.critical === 0,
|
|
33845
|
+
exitCode: ubsResult.exitCode
|
|
33846
|
+
};
|
|
33847
|
+
if (!ubsStep.passed) {
|
|
33848
|
+
ubsStep.error = `Found ${ubsResult.summary.critical} critical bugs`;
|
|
33849
|
+
blockers.push(`UBS: ${ubsResult.summary.critical} critical bugs found`);
|
|
33850
|
+
}
|
|
33851
|
+
steps.push(ubsStep);
|
|
33852
|
+
} else {
|
|
33853
|
+
steps.push({
|
|
33854
|
+
name: "ubs_scan",
|
|
33855
|
+
command: "ubs scan",
|
|
33856
|
+
passed: true,
|
|
33857
|
+
exitCode: 0,
|
|
33858
|
+
skipped: true,
|
|
33859
|
+
skipReason: "UBS not available"
|
|
33860
|
+
});
|
|
33861
|
+
}
|
|
33862
|
+
}
|
|
33863
|
+
const typecheckStep = await runTypecheckVerification();
|
|
33864
|
+
steps.push(typecheckStep);
|
|
33865
|
+
if (!typecheckStep.passed && !typecheckStep.skipped) {
|
|
33866
|
+
blockers.push(`Typecheck: ${typecheckStep.error?.slice(0, 100) || "failed"}`);
|
|
33867
|
+
}
|
|
33868
|
+
const testStep = await runTestVerification(filesTouched);
|
|
33869
|
+
steps.push(testStep);
|
|
33870
|
+
if (!testStep.passed && !testStep.skipped) {
|
|
33871
|
+
blockers.push(`Tests: ${testStep.error?.slice(0, 100) || "failed"}`);
|
|
33872
|
+
}
|
|
33873
|
+
const passedCount = steps.filter((s) => s.passed).length;
|
|
33874
|
+
const skippedCount = steps.filter((s) => s.skipped).length;
|
|
33875
|
+
const failedCount = steps.filter((s) => !s.passed && !s.skipped).length;
|
|
33876
|
+
const summary = failedCount === 0 ? `Verification passed: ${passedCount} checks passed, ${skippedCount} skipped` : `Verification FAILED: ${failedCount} checks failed, ${passedCount} passed, ${skippedCount} skipped`;
|
|
33877
|
+
return {
|
|
33878
|
+
passed: failedCount === 0,
|
|
33879
|
+
steps,
|
|
33880
|
+
summary,
|
|
33881
|
+
blockers
|
|
33882
|
+
};
|
|
33883
|
+
}
|
|
33498
33884
|
async function runUbsScan(files) {
|
|
33499
33885
|
if (files.length === 0) {
|
|
33500
33886
|
return null;
|
|
@@ -33597,19 +33983,44 @@ ${args.files_affected.map((f) => `- \`${f}\``).join(`
|
|
|
33597
33983
|
}
|
|
33598
33984
|
});
|
|
33599
33985
|
var swarm_complete = tool({
|
|
33600
|
-
description: "Mark subtask complete
|
|
33986
|
+
description: "Mark subtask complete with Verification Gate. Runs UBS scan, typecheck, and tests before allowing completion.",
|
|
33601
33987
|
args: {
|
|
33602
33988
|
project_key: tool.schema.string().describe("Project path"),
|
|
33603
33989
|
agent_name: tool.schema.string().describe("Your Agent Mail name"),
|
|
33604
33990
|
bead_id: tool.schema.string().describe("Subtask bead ID"),
|
|
33605
33991
|
summary: tool.schema.string().describe("Brief summary of work done"),
|
|
33606
33992
|
evaluation: tool.schema.string().optional().describe("Self-evaluation JSON (Evaluation schema)"),
|
|
33607
|
-
files_touched: tool.schema.array(tool.schema.string()).optional().describe("Files modified - will be
|
|
33608
|
-
skip_ubs_scan: tool.schema.boolean().optional().describe("Skip UBS bug scan (default: false)")
|
|
33993
|
+
files_touched: tool.schema.array(tool.schema.string()).optional().describe("Files modified - will be verified (UBS, typecheck, tests)"),
|
|
33994
|
+
skip_ubs_scan: tool.schema.boolean().optional().describe("Skip UBS bug scan (default: false)"),
|
|
33995
|
+
skip_verification: tool.schema.boolean().optional().describe("Skip ALL verification (UBS, typecheck, tests). Use sparingly! (default: false)")
|
|
33609
33996
|
},
|
|
33610
33997
|
async execute(args) {
|
|
33998
|
+
let verificationResult = null;
|
|
33999
|
+
if (!args.skip_verification && args.files_touched?.length) {
|
|
34000
|
+
verificationResult = await runVerificationGate(args.files_touched, args.skip_ubs_scan ?? false);
|
|
34001
|
+
if (!verificationResult.passed) {
|
|
34002
|
+
return JSON.stringify({
|
|
34003
|
+
success: false,
|
|
34004
|
+
error: "Verification Gate FAILED - fix issues before completing",
|
|
34005
|
+
verification: {
|
|
34006
|
+
passed: false,
|
|
34007
|
+
summary: verificationResult.summary,
|
|
34008
|
+
blockers: verificationResult.blockers,
|
|
34009
|
+
steps: verificationResult.steps.map((s) => ({
|
|
34010
|
+
name: s.name,
|
|
34011
|
+
passed: s.passed,
|
|
34012
|
+
skipped: s.skipped,
|
|
34013
|
+
skipReason: s.skipReason,
|
|
34014
|
+
error: s.error?.slice(0, 200)
|
|
34015
|
+
}))
|
|
34016
|
+
},
|
|
34017
|
+
hint: "Fix the failing checks and try again. Use skip_verification=true only as last resort.",
|
|
34018
|
+
gate_function: "IDENTIFY → RUN → READ → VERIFY → CLAIM (you are at VERIFY, claim blocked)"
|
|
34019
|
+
}, null, 2);
|
|
34020
|
+
}
|
|
34021
|
+
}
|
|
33611
34022
|
let ubsResult = null;
|
|
33612
|
-
if (args.
|
|
34023
|
+
if (!args.skip_verification && !verificationResult && args.files_touched?.length && !args.skip_ubs_scan) {
|
|
33613
34024
|
ubsResult = await runUbsScan(args.files_touched);
|
|
33614
34025
|
if (ubsResult && ubsResult.summary.critical > 0) {
|
|
33615
34026
|
return JSON.stringify({
|
|
@@ -33680,12 +34091,22 @@ var swarm_complete = tool({
|
|
|
33680
34091
|
closed: true,
|
|
33681
34092
|
reservations_released: true,
|
|
33682
34093
|
message_sent: true,
|
|
34094
|
+
verification_gate: verificationResult ? {
|
|
34095
|
+
passed: true,
|
|
34096
|
+
summary: verificationResult.summary,
|
|
34097
|
+
steps: verificationResult.steps.map((s) => ({
|
|
34098
|
+
name: s.name,
|
|
34099
|
+
passed: s.passed,
|
|
34100
|
+
skipped: s.skipped,
|
|
34101
|
+
skipReason: s.skipReason
|
|
34102
|
+
}))
|
|
34103
|
+
} : args.skip_verification ? { skipped: true, reason: "skip_verification=true" } : { skipped: true, reason: "no files_touched provided" },
|
|
33683
34104
|
ubs_scan: ubsResult ? {
|
|
33684
34105
|
ran: true,
|
|
33685
34106
|
bugs_found: ubsResult.summary.total,
|
|
33686
34107
|
summary: ubsResult.summary,
|
|
33687
34108
|
warnings: ubsResult.bugs.filter((b) => b.severity !== "critical")
|
|
33688
|
-
} : {
|
|
34109
|
+
} : verificationResult ? { ran: true, included_in_verification_gate: true } : {
|
|
33689
34110
|
ran: false,
|
|
33690
34111
|
reason: args.skip_ubs_scan ? "skipped" : "no files or ubs unavailable"
|
|
33691
34112
|
},
|
|
@@ -34253,6 +34674,77 @@ var swarmTools = {
|
|
|
34253
34674
|
swarm_get_error_context,
|
|
34254
34675
|
swarm_resolve_error
|
|
34255
34676
|
};
|
|
34677
|
+
var globalStrikeStorage = new InMemoryStrikeStorage;
|
|
34678
|
+
var swarm_check_strikes = tool({
|
|
34679
|
+
description: "Check 3-strike status for a bead. Records failures, detects architectural problems, generates architecture review prompts.",
|
|
34680
|
+
args: {
|
|
34681
|
+
bead_id: tool.schema.string().describe("Bead ID to check"),
|
|
34682
|
+
action: tool.schema.enum(["check", "add_strike", "clear", "get_prompt"]).describe("Action: check count, add strike, clear strikes, or get prompt"),
|
|
34683
|
+
attempt: tool.schema.string().optional().describe("Description of fix attempt (required for add_strike)"),
|
|
34684
|
+
reason: tool.schema.string().optional().describe("Why the fix failed (required for add_strike)")
|
|
34685
|
+
},
|
|
34686
|
+
async execute(args) {
|
|
34687
|
+
switch (args.action) {
|
|
34688
|
+
case "check": {
|
|
34689
|
+
const count = await getStrikes(args.bead_id, globalStrikeStorage);
|
|
34690
|
+
const strikedOut = await isStrikedOut(args.bead_id, globalStrikeStorage);
|
|
34691
|
+
return JSON.stringify({
|
|
34692
|
+
bead_id: args.bead_id,
|
|
34693
|
+
strike_count: count,
|
|
34694
|
+
is_striked_out: strikedOut,
|
|
34695
|
+
message: strikedOut ? "⚠️ STRUCK OUT: 3 strikes reached. Use get_prompt action for architecture review." : count === 0 ? "No strikes. Clear to proceed." : `${count} strike${count > 1 ? "s" : ""}. ${3 - count} remaining before architecture review required.`,
|
|
34696
|
+
next_action: strikedOut ? "Call with action=get_prompt to get architecture review questions" : "Continue with fix attempt"
|
|
34697
|
+
}, null, 2);
|
|
34698
|
+
}
|
|
34699
|
+
case "add_strike": {
|
|
34700
|
+
if (!args.attempt || !args.reason) {
|
|
34701
|
+
return JSON.stringify({
|
|
34702
|
+
error: "add_strike requires 'attempt' and 'reason' parameters"
|
|
34703
|
+
}, null, 2);
|
|
34704
|
+
}
|
|
34705
|
+
const record2 = await addStrike(args.bead_id, args.attempt, args.reason, globalStrikeStorage);
|
|
34706
|
+
const strikedOut = record2.strike_count >= 3;
|
|
34707
|
+
return JSON.stringify({
|
|
34708
|
+
bead_id: args.bead_id,
|
|
34709
|
+
strike_count: record2.strike_count,
|
|
34710
|
+
is_striked_out: strikedOut,
|
|
34711
|
+
failures: record2.failures,
|
|
34712
|
+
message: strikedOut ? "⚠️ STRUCK OUT: 3 strikes reached. STOP and question the architecture." : `Strike ${record2.strike_count} recorded. ${3 - record2.strike_count} remaining.`,
|
|
34713
|
+
warning: strikedOut ? "DO NOT attempt Fix #4. Call with action=get_prompt for architecture review." : undefined
|
|
34714
|
+
}, null, 2);
|
|
34715
|
+
}
|
|
34716
|
+
case "clear": {
|
|
34717
|
+
await clearStrikes(args.bead_id, globalStrikeStorage);
|
|
34718
|
+
return JSON.stringify({
|
|
34719
|
+
bead_id: args.bead_id,
|
|
34720
|
+
strike_count: 0,
|
|
34721
|
+
is_striked_out: false,
|
|
34722
|
+
message: "Strikes cleared. Fresh start."
|
|
34723
|
+
}, null, 2);
|
|
34724
|
+
}
|
|
34725
|
+
case "get_prompt": {
|
|
34726
|
+
const prompt = await getArchitecturePrompt(args.bead_id, globalStrikeStorage);
|
|
34727
|
+
if (!prompt) {
|
|
34728
|
+
return JSON.stringify({
|
|
34729
|
+
bead_id: args.bead_id,
|
|
34730
|
+
has_prompt: false,
|
|
34731
|
+
message: "No architecture prompt (not struck out yet)"
|
|
34732
|
+
}, null, 2);
|
|
34733
|
+
}
|
|
34734
|
+
return JSON.stringify({
|
|
34735
|
+
bead_id: args.bead_id,
|
|
34736
|
+
has_prompt: true,
|
|
34737
|
+
architecture_review_prompt: prompt,
|
|
34738
|
+
message: "Architecture review required. Present this prompt to the human partner."
|
|
34739
|
+
}, null, 2);
|
|
34740
|
+
}
|
|
34741
|
+
default:
|
|
34742
|
+
return JSON.stringify({
|
|
34743
|
+
error: `Unknown action: ${args.action}`
|
|
34744
|
+
}, null, 2);
|
|
34745
|
+
}
|
|
34746
|
+
}
|
|
34747
|
+
});
|
|
34256
34748
|
|
|
34257
34749
|
// src/repo-crawl.ts
|
|
34258
34750
|
init_dist();
|
|
@@ -34579,6 +35071,716 @@ var repoCrawlTools = {
|
|
|
34579
35071
|
// src/index.ts
|
|
34580
35072
|
init_skills();
|
|
34581
35073
|
|
|
35074
|
+
// src/mandates.ts
|
|
35075
|
+
init_dist();
|
|
35076
|
+
|
|
35077
|
+
// src/mandate-storage.ts
|
|
35078
|
+
var cachedCommand = null;
|
|
35079
|
+
async function resolveSemanticMemoryCommand() {
|
|
35080
|
+
if (cachedCommand)
|
|
35081
|
+
return cachedCommand;
|
|
35082
|
+
const nativeResult = await Bun.$`which semantic-memory`.quiet().nothrow();
|
|
35083
|
+
if (nativeResult.exitCode === 0) {
|
|
35084
|
+
cachedCommand = ["semantic-memory"];
|
|
35085
|
+
return cachedCommand;
|
|
35086
|
+
}
|
|
35087
|
+
cachedCommand = ["bunx", "semantic-memory"];
|
|
35088
|
+
return cachedCommand;
|
|
35089
|
+
}
|
|
35090
|
+
async function execSemanticMemory(args) {
|
|
35091
|
+
try {
|
|
35092
|
+
const cmd = await resolveSemanticMemoryCommand();
|
|
35093
|
+
const fullCmd = [...cmd, ...args];
|
|
35094
|
+
const proc = Bun.spawn(fullCmd, {
|
|
35095
|
+
stdout: "pipe",
|
|
35096
|
+
stderr: "pipe"
|
|
35097
|
+
});
|
|
35098
|
+
try {
|
|
35099
|
+
const stdout = Buffer.from(await new Response(proc.stdout).arrayBuffer());
|
|
35100
|
+
const stderr = Buffer.from(await new Response(proc.stderr).arrayBuffer());
|
|
35101
|
+
const exitCode = await proc.exited;
|
|
35102
|
+
return { exitCode, stdout, stderr };
|
|
35103
|
+
} finally {
|
|
35104
|
+
proc.kill();
|
|
35105
|
+
}
|
|
35106
|
+
} catch (error45) {
|
|
35107
|
+
const errorMessage = error45 instanceof Error ? error45.message : String(error45);
|
|
35108
|
+
return {
|
|
35109
|
+
exitCode: 1,
|
|
35110
|
+
stdout: Buffer.from(""),
|
|
35111
|
+
stderr: Buffer.from(`Error executing semantic-memory: ${errorMessage}`)
|
|
35112
|
+
};
|
|
35113
|
+
}
|
|
35114
|
+
}
|
|
35115
|
+
var DEFAULT_MANDATE_STORAGE_CONFIG = {
|
|
35116
|
+
backend: "semantic-memory",
|
|
35117
|
+
collections: {
|
|
35118
|
+
mandates: "swarm-mandates",
|
|
35119
|
+
votes: "swarm-votes"
|
|
35120
|
+
},
|
|
35121
|
+
decay: DEFAULT_MANDATE_DECAY_CONFIG,
|
|
35122
|
+
useSemanticSearch: true
|
|
35123
|
+
};
|
|
35124
|
+
|
|
35125
|
+
class SemanticMemoryMandateStorage {
|
|
35126
|
+
config;
|
|
35127
|
+
constructor(config2 = {}) {
|
|
35128
|
+
this.config = { ...DEFAULT_MANDATE_STORAGE_CONFIG, ...config2 };
|
|
35129
|
+
}
|
|
35130
|
+
async storeInternal(collection, data, metadata) {
|
|
35131
|
+
const content = typeof data === "string" ? data : JSON.stringify(data);
|
|
35132
|
+
const args = ["store", content, "--collection", collection];
|
|
35133
|
+
if (metadata) {
|
|
35134
|
+
args.push("--metadata", JSON.stringify(metadata));
|
|
35135
|
+
}
|
|
35136
|
+
await execSemanticMemory(args);
|
|
35137
|
+
}
|
|
35138
|
+
async findInternal(collection, query, limit = 10, useFts = false) {
|
|
35139
|
+
const args = [
|
|
35140
|
+
"find",
|
|
35141
|
+
query,
|
|
35142
|
+
"--collection",
|
|
35143
|
+
collection,
|
|
35144
|
+
"--limit",
|
|
35145
|
+
String(limit),
|
|
35146
|
+
"--json"
|
|
35147
|
+
];
|
|
35148
|
+
if (useFts) {
|
|
35149
|
+
args.push("--fts");
|
|
35150
|
+
}
|
|
35151
|
+
const result = await execSemanticMemory(args);
|
|
35152
|
+
if (result.exitCode !== 0) {
|
|
35153
|
+
console.warn(`[mandate-storage] semantic-memory find() failed with exit code ${result.exitCode}: ${result.stderr.toString().trim()}`);
|
|
35154
|
+
return [];
|
|
35155
|
+
}
|
|
35156
|
+
try {
|
|
35157
|
+
const output = result.stdout.toString().trim();
|
|
35158
|
+
if (!output)
|
|
35159
|
+
return [];
|
|
35160
|
+
const parsed = JSON.parse(output);
|
|
35161
|
+
const results = Array.isArray(parsed) ? parsed : parsed.results || [];
|
|
35162
|
+
return results.map((r) => {
|
|
35163
|
+
const content = r.content || r.information || "";
|
|
35164
|
+
try {
|
|
35165
|
+
return JSON.parse(content);
|
|
35166
|
+
} catch {
|
|
35167
|
+
return content;
|
|
35168
|
+
}
|
|
35169
|
+
});
|
|
35170
|
+
} catch (error45) {
|
|
35171
|
+
console.warn(`[mandate-storage] Failed to parse semantic-memory find() output: ${error45 instanceof Error ? error45.message : String(error45)}`);
|
|
35172
|
+
return [];
|
|
35173
|
+
}
|
|
35174
|
+
}
|
|
35175
|
+
async listInternal(collection) {
|
|
35176
|
+
const result = await execSemanticMemory([
|
|
35177
|
+
"list",
|
|
35178
|
+
"--collection",
|
|
35179
|
+
collection,
|
|
35180
|
+
"--json"
|
|
35181
|
+
]);
|
|
35182
|
+
if (result.exitCode !== 0) {
|
|
35183
|
+
console.warn(`[mandate-storage] semantic-memory list() failed with exit code ${result.exitCode}: ${result.stderr.toString().trim()}`);
|
|
35184
|
+
return [];
|
|
35185
|
+
}
|
|
35186
|
+
try {
|
|
35187
|
+
const output = result.stdout.toString().trim();
|
|
35188
|
+
if (!output)
|
|
35189
|
+
return [];
|
|
35190
|
+
const parsed = JSON.parse(output);
|
|
35191
|
+
const items = Array.isArray(parsed) ? parsed : parsed.items || [];
|
|
35192
|
+
return items.map((item) => {
|
|
35193
|
+
const content = item.content || item.information || "";
|
|
35194
|
+
try {
|
|
35195
|
+
return JSON.parse(content);
|
|
35196
|
+
} catch {
|
|
35197
|
+
return content;
|
|
35198
|
+
}
|
|
35199
|
+
});
|
|
35200
|
+
} catch (error45) {
|
|
35201
|
+
console.warn(`[mandate-storage] Failed to parse semantic-memory list() output: ${error45 instanceof Error ? error45.message : String(error45)}`);
|
|
35202
|
+
return [];
|
|
35203
|
+
}
|
|
35204
|
+
}
|
|
35205
|
+
async store(entry) {
|
|
35206
|
+
await this.storeInternal(this.config.collections.mandates, entry, {
|
|
35207
|
+
id: entry.id,
|
|
35208
|
+
content_type: entry.content_type,
|
|
35209
|
+
author_agent: entry.author_agent,
|
|
35210
|
+
status: entry.status,
|
|
35211
|
+
tags: entry.tags.join(","),
|
|
35212
|
+
created_at: entry.created_at
|
|
35213
|
+
});
|
|
35214
|
+
}
|
|
35215
|
+
async get(id) {
|
|
35216
|
+
const all = await this.listInternal(this.config.collections.mandates);
|
|
35217
|
+
return all.find((entry) => entry.id === id) || null;
|
|
35218
|
+
}
|
|
35219
|
+
async find(query, limit = 10) {
|
|
35220
|
+
return this.findInternal(this.config.collections.mandates, query, limit, !this.config.useSemanticSearch);
|
|
35221
|
+
}
|
|
35222
|
+
async list(filter2) {
|
|
35223
|
+
const all = await this.listInternal(this.config.collections.mandates);
|
|
35224
|
+
if (!filter2)
|
|
35225
|
+
return all;
|
|
35226
|
+
return all.filter((entry) => {
|
|
35227
|
+
if (filter2.status && entry.status !== filter2.status)
|
|
35228
|
+
return false;
|
|
35229
|
+
if (filter2.content_type && entry.content_type !== filter2.content_type)
|
|
35230
|
+
return false;
|
|
35231
|
+
return true;
|
|
35232
|
+
});
|
|
35233
|
+
}
|
|
35234
|
+
async update(id, updates) {
|
|
35235
|
+
const existing = await this.get(id);
|
|
35236
|
+
if (!existing) {
|
|
35237
|
+
throw new Error(`Mandate ${id} not found`);
|
|
35238
|
+
}
|
|
35239
|
+
const updated = { ...existing, ...updates };
|
|
35240
|
+
await this.store(updated);
|
|
35241
|
+
}
|
|
35242
|
+
async vote(vote) {
|
|
35243
|
+
const existing = await this.hasVoted(vote.mandate_id, vote.agent_name);
|
|
35244
|
+
if (existing) {
|
|
35245
|
+
throw new Error(`Agent ${vote.agent_name} has already voted on mandate ${vote.mandate_id}`);
|
|
35246
|
+
}
|
|
35247
|
+
await this.storeInternal(this.config.collections.votes, vote, {
|
|
35248
|
+
id: vote.id,
|
|
35249
|
+
mandate_id: vote.mandate_id,
|
|
35250
|
+
agent_name: vote.agent_name,
|
|
35251
|
+
vote_type: vote.vote_type,
|
|
35252
|
+
timestamp: vote.timestamp,
|
|
35253
|
+
weight: vote.weight
|
|
35254
|
+
});
|
|
35255
|
+
}
|
|
35256
|
+
async getVotes(mandateId) {
|
|
35257
|
+
const all = await this.listInternal(this.config.collections.votes);
|
|
35258
|
+
return all.filter((vote) => vote.mandate_id === mandateId);
|
|
35259
|
+
}
|
|
35260
|
+
async hasVoted(mandateId, agentName) {
|
|
35261
|
+
const votes = await this.getVotes(mandateId);
|
|
35262
|
+
return votes.some((vote) => vote.agent_name === agentName);
|
|
35263
|
+
}
|
|
35264
|
+
async calculateScore(mandateId) {
|
|
35265
|
+
const votes = await this.getVotes(mandateId);
|
|
35266
|
+
const now = new Date;
|
|
35267
|
+
let rawUpvotes = 0;
|
|
35268
|
+
let rawDownvotes = 0;
|
|
35269
|
+
let decayedUpvotes = 0;
|
|
35270
|
+
let decayedDownvotes = 0;
|
|
35271
|
+
for (const vote of votes) {
|
|
35272
|
+
const decayed = calculateDecayedValue(vote.timestamp, now, this.config.decay.halfLifeDays);
|
|
35273
|
+
const value = vote.weight * decayed;
|
|
35274
|
+
if (vote.vote_type === "upvote") {
|
|
35275
|
+
rawUpvotes++;
|
|
35276
|
+
decayedUpvotes += value;
|
|
35277
|
+
} else {
|
|
35278
|
+
rawDownvotes++;
|
|
35279
|
+
decayedDownvotes += value;
|
|
35280
|
+
}
|
|
35281
|
+
}
|
|
35282
|
+
const totalDecayed = decayedUpvotes + decayedDownvotes;
|
|
35283
|
+
const voteRatio = totalDecayed > 0 ? decayedUpvotes / totalDecayed : 0;
|
|
35284
|
+
const netVotes = decayedUpvotes - decayedDownvotes;
|
|
35285
|
+
const decayedScore = netVotes * voteRatio;
|
|
35286
|
+
return {
|
|
35287
|
+
mandate_id: mandateId,
|
|
35288
|
+
net_votes: netVotes,
|
|
35289
|
+
vote_ratio: voteRatio,
|
|
35290
|
+
decayed_score: decayedScore,
|
|
35291
|
+
last_calculated: now.toISOString(),
|
|
35292
|
+
raw_upvotes: rawUpvotes,
|
|
35293
|
+
raw_downvotes: rawDownvotes,
|
|
35294
|
+
decayed_upvotes: decayedUpvotes,
|
|
35295
|
+
decayed_downvotes: decayedDownvotes
|
|
35296
|
+
};
|
|
35297
|
+
}
|
|
35298
|
+
async close() {}
|
|
35299
|
+
}
|
|
35300
|
+
|
|
35301
|
+
class InMemoryMandateStorage {
|
|
35302
|
+
entries = new Map;
|
|
35303
|
+
votes = new Map;
|
|
35304
|
+
config;
|
|
35305
|
+
constructor(config2 = {}) {
|
|
35306
|
+
const fullConfig = { ...DEFAULT_MANDATE_STORAGE_CONFIG, ...config2 };
|
|
35307
|
+
this.config = fullConfig.decay;
|
|
35308
|
+
}
|
|
35309
|
+
async store(entry) {
|
|
35310
|
+
this.entries.set(entry.id, entry);
|
|
35311
|
+
}
|
|
35312
|
+
async get(id) {
|
|
35313
|
+
return this.entries.get(id) || null;
|
|
35314
|
+
}
|
|
35315
|
+
async find(query, limit = 10) {
|
|
35316
|
+
const lowerQuery = query.toLowerCase();
|
|
35317
|
+
const results = Array.from(this.entries.values()).filter((entry) => entry.content.toLowerCase().includes(lowerQuery) || entry.tags.some((tag) => tag.toLowerCase().includes(lowerQuery)));
|
|
35318
|
+
return results.slice(0, limit);
|
|
35319
|
+
}
|
|
35320
|
+
async list(filter2) {
|
|
35321
|
+
let results = Array.from(this.entries.values());
|
|
35322
|
+
if (filter2) {
|
|
35323
|
+
results = results.filter((entry) => {
|
|
35324
|
+
if (filter2.status && entry.status !== filter2.status)
|
|
35325
|
+
return false;
|
|
35326
|
+
if (filter2.content_type && entry.content_type !== filter2.content_type)
|
|
35327
|
+
return false;
|
|
35328
|
+
return true;
|
|
35329
|
+
});
|
|
35330
|
+
}
|
|
35331
|
+
return results;
|
|
35332
|
+
}
|
|
35333
|
+
async update(id, updates) {
|
|
35334
|
+
const existing = await this.get(id);
|
|
35335
|
+
if (!existing) {
|
|
35336
|
+
throw new Error(`Mandate ${id} not found`);
|
|
35337
|
+
}
|
|
35338
|
+
const updated = { ...existing, ...updates };
|
|
35339
|
+
this.entries.set(id, updated);
|
|
35340
|
+
}
|
|
35341
|
+
async vote(vote) {
|
|
35342
|
+
const existing = await this.hasVoted(vote.mandate_id, vote.agent_name);
|
|
35343
|
+
if (existing) {
|
|
35344
|
+
throw new Error(`Agent ${vote.agent_name} has already voted on mandate ${vote.mandate_id}`);
|
|
35345
|
+
}
|
|
35346
|
+
this.votes.set(vote.id, vote);
|
|
35347
|
+
}
|
|
35348
|
+
async getVotes(mandateId) {
|
|
35349
|
+
return Array.from(this.votes.values()).filter((vote) => vote.mandate_id === mandateId);
|
|
35350
|
+
}
|
|
35351
|
+
async hasVoted(mandateId, agentName) {
|
|
35352
|
+
const votes = await this.getVotes(mandateId);
|
|
35353
|
+
return votes.some((vote) => vote.agent_name === agentName);
|
|
35354
|
+
}
|
|
35355
|
+
async calculateScore(mandateId) {
|
|
35356
|
+
const votes = await this.getVotes(mandateId);
|
|
35357
|
+
const now = new Date;
|
|
35358
|
+
let rawUpvotes = 0;
|
|
35359
|
+
let rawDownvotes = 0;
|
|
35360
|
+
let decayedUpvotes = 0;
|
|
35361
|
+
let decayedDownvotes = 0;
|
|
35362
|
+
for (const vote of votes) {
|
|
35363
|
+
const decayed = calculateDecayedValue(vote.timestamp, now, this.config.halfLifeDays);
|
|
35364
|
+
const value = vote.weight * decayed;
|
|
35365
|
+
if (vote.vote_type === "upvote") {
|
|
35366
|
+
rawUpvotes++;
|
|
35367
|
+
decayedUpvotes += value;
|
|
35368
|
+
} else {
|
|
35369
|
+
rawDownvotes++;
|
|
35370
|
+
decayedDownvotes += value;
|
|
35371
|
+
}
|
|
35372
|
+
}
|
|
35373
|
+
const totalDecayed = decayedUpvotes + decayedDownvotes;
|
|
35374
|
+
const voteRatio = totalDecayed > 0 ? decayedUpvotes / totalDecayed : 0;
|
|
35375
|
+
const netVotes = decayedUpvotes - decayedDownvotes;
|
|
35376
|
+
const decayedScore = netVotes * voteRatio;
|
|
35377
|
+
return {
|
|
35378
|
+
mandate_id: mandateId,
|
|
35379
|
+
net_votes: netVotes,
|
|
35380
|
+
vote_ratio: voteRatio,
|
|
35381
|
+
decayed_score: decayedScore,
|
|
35382
|
+
last_calculated: now.toISOString(),
|
|
35383
|
+
raw_upvotes: rawUpvotes,
|
|
35384
|
+
raw_downvotes: rawDownvotes,
|
|
35385
|
+
decayed_upvotes: decayedUpvotes,
|
|
35386
|
+
decayed_downvotes: decayedDownvotes
|
|
35387
|
+
};
|
|
35388
|
+
}
|
|
35389
|
+
async close() {}
|
|
35390
|
+
}
|
|
35391
|
+
function createMandateStorage(config2 = {}) {
|
|
35392
|
+
const fullConfig = { ...DEFAULT_MANDATE_STORAGE_CONFIG, ...config2 };
|
|
35393
|
+
switch (fullConfig.backend) {
|
|
35394
|
+
case "semantic-memory":
|
|
35395
|
+
return new SemanticMemoryMandateStorage(fullConfig);
|
|
35396
|
+
case "memory":
|
|
35397
|
+
return new InMemoryMandateStorage(fullConfig);
|
|
35398
|
+
default:
|
|
35399
|
+
throw new Error(`Unknown storage backend: ${fullConfig.backend}`);
|
|
35400
|
+
}
|
|
35401
|
+
}
|
|
35402
|
+
async function updateMandateStatus(mandateId, storage) {
|
|
35403
|
+
const entry = await storage.get(mandateId);
|
|
35404
|
+
if (!entry) {
|
|
35405
|
+
throw new Error(`Mandate ${mandateId} not found`);
|
|
35406
|
+
}
|
|
35407
|
+
const score = await storage.calculateScore(mandateId);
|
|
35408
|
+
const previousStatus = entry.status;
|
|
35409
|
+
let newStatus;
|
|
35410
|
+
const config2 = DEFAULT_MANDATE_DECAY_CONFIG;
|
|
35411
|
+
if (score.net_votes >= config2.mandateNetVotesThreshold && score.vote_ratio >= config2.mandateVoteRatioThreshold) {
|
|
35412
|
+
newStatus = "mandate";
|
|
35413
|
+
} else if (score.net_votes <= config2.rejectedNetVotesThreshold) {
|
|
35414
|
+
newStatus = "rejected";
|
|
35415
|
+
} else if (score.net_votes >= config2.establishedNetVotesThreshold) {
|
|
35416
|
+
newStatus = "established";
|
|
35417
|
+
} else {
|
|
35418
|
+
newStatus = "candidate";
|
|
35419
|
+
}
|
|
35420
|
+
if (newStatus !== previousStatus) {
|
|
35421
|
+
await storage.update(mandateId, { status: newStatus });
|
|
35422
|
+
}
|
|
35423
|
+
return {
|
|
35424
|
+
mandate_id: mandateId,
|
|
35425
|
+
previous_status: previousStatus,
|
|
35426
|
+
new_status: newStatus,
|
|
35427
|
+
score,
|
|
35428
|
+
status_changed: newStatus !== previousStatus
|
|
35429
|
+
};
|
|
35430
|
+
}
|
|
35431
|
+
var globalMandateStorage = null;
|
|
35432
|
+
function getMandateStorage() {
|
|
35433
|
+
if (!globalMandateStorage) {
|
|
35434
|
+
globalMandateStorage = createMandateStorage();
|
|
35435
|
+
}
|
|
35436
|
+
return globalMandateStorage;
|
|
35437
|
+
}
|
|
35438
|
+
|
|
35439
|
+
// src/mandate-promotion.ts
|
|
35440
|
+
function shouldPromote(score, currentStatus, config2 = DEFAULT_MANDATE_DECAY_CONFIG) {
|
|
35441
|
+
if (currentStatus === "rejected") {
|
|
35442
|
+
return "rejected";
|
|
35443
|
+
}
|
|
35444
|
+
if (currentStatus === "mandate") {
|
|
35445
|
+
return "mandate";
|
|
35446
|
+
}
|
|
35447
|
+
if (score.net_votes <= config2.rejectedNetVotesThreshold) {
|
|
35448
|
+
return "rejected";
|
|
35449
|
+
}
|
|
35450
|
+
if (currentStatus === "established") {
|
|
35451
|
+
if (score.net_votes >= config2.mandateNetVotesThreshold && score.vote_ratio >= config2.mandateVoteRatioThreshold) {
|
|
35452
|
+
return "mandate";
|
|
35453
|
+
}
|
|
35454
|
+
return "established";
|
|
35455
|
+
}
|
|
35456
|
+
if (score.net_votes >= config2.establishedNetVotesThreshold) {
|
|
35457
|
+
return "established";
|
|
35458
|
+
}
|
|
35459
|
+
return "candidate";
|
|
35460
|
+
}
|
|
35461
|
+
function evaluatePromotion(entry, score, config2 = DEFAULT_MANDATE_DECAY_CONFIG) {
|
|
35462
|
+
const previousStatus = entry.status;
|
|
35463
|
+
const newStatus = shouldPromote(score, previousStatus, config2);
|
|
35464
|
+
const promoted = newStatus !== previousStatus;
|
|
35465
|
+
let reason;
|
|
35466
|
+
if (newStatus === "rejected" && previousStatus === "rejected") {
|
|
35467
|
+
reason = `Remains rejected (permanent)`;
|
|
35468
|
+
} else if (newStatus === "rejected") {
|
|
35469
|
+
reason = `Rejected due to negative consensus (net_votes: ${score.net_votes.toFixed(2)} ≤ ${config2.rejectedNetVotesThreshold})`;
|
|
35470
|
+
} else if (newStatus === "mandate" && previousStatus === "mandate") {
|
|
35471
|
+
reason = `Remains mandate (no demotion)`;
|
|
35472
|
+
} else if (newStatus === "mandate" && previousStatus === "established") {
|
|
35473
|
+
reason = `Promoted to mandate (net_votes: ${score.net_votes.toFixed(2)} ≥ ${config2.mandateNetVotesThreshold}, ratio: ${score.vote_ratio.toFixed(2)} ≥ ${config2.mandateVoteRatioThreshold})`;
|
|
35474
|
+
} else if (newStatus === "established" && previousStatus === "established") {
|
|
35475
|
+
reason = `Remains established (net_votes: ${score.net_votes.toFixed(2)}, ratio: ${score.vote_ratio.toFixed(2)} below mandate threshold)`;
|
|
35476
|
+
} else if (newStatus === "established" && previousStatus === "candidate") {
|
|
35477
|
+
reason = `Promoted to established (net_votes: ${score.net_votes.toFixed(2)} ≥ ${config2.establishedNetVotesThreshold})`;
|
|
35478
|
+
} else if (newStatus === "candidate") {
|
|
35479
|
+
reason = `Remains candidate (net_votes: ${score.net_votes.toFixed(2)} below threshold)`;
|
|
35480
|
+
} else {
|
|
35481
|
+
reason = `No status change (current: ${previousStatus})`;
|
|
35482
|
+
}
|
|
35483
|
+
return {
|
|
35484
|
+
mandate_id: entry.id,
|
|
35485
|
+
previous_status: previousStatus,
|
|
35486
|
+
new_status: newStatus,
|
|
35487
|
+
score,
|
|
35488
|
+
promoted,
|
|
35489
|
+
reason
|
|
35490
|
+
};
|
|
35491
|
+
}
|
|
35492
|
+
function formatPromotionResult(result) {
|
|
35493
|
+
const arrow = result.promoted ? `${result.previous_status} → ${result.new_status}` : result.new_status;
|
|
35494
|
+
return `[${result.mandate_id}] ${arrow}: ${result.reason}`;
|
|
35495
|
+
}
|
|
35496
|
+
|
|
35497
|
+
// src/mandates.ts
|
|
35498
|
+
class MandateError extends Error {
|
|
35499
|
+
operation;
|
|
35500
|
+
details;
|
|
35501
|
+
constructor(message, operation, details) {
|
|
35502
|
+
super(message);
|
|
35503
|
+
this.operation = operation;
|
|
35504
|
+
this.details = details;
|
|
35505
|
+
this.name = "MandateError";
|
|
35506
|
+
}
|
|
35507
|
+
}
|
|
35508
|
+
function generateMandateId() {
|
|
35509
|
+
const timestamp = Date.now().toString(36);
|
|
35510
|
+
const random = Math.random().toString(36).substring(2, 8);
|
|
35511
|
+
return `mandate-${timestamp}-${random}`;
|
|
35512
|
+
}
|
|
35513
|
+
function generateVoteId() {
|
|
35514
|
+
const timestamp = Date.now().toString(36);
|
|
35515
|
+
const random = Math.random().toString(36).substring(2, 8);
|
|
35516
|
+
return `vote-${timestamp}-${random}`;
|
|
35517
|
+
}
|
|
35518
|
+
var mandate_file = tool({
|
|
35519
|
+
description: "Submit a new idea, tip, lore, snippet, or feature request to the mandate system",
|
|
35520
|
+
args: {
|
|
35521
|
+
content: tool.schema.string().min(1).describe("The content to submit"),
|
|
35522
|
+
content_type: tool.schema.enum(["idea", "tip", "lore", "snippet", "feature_request"]).describe("Type of content"),
|
|
35523
|
+
tags: tool.schema.array(tool.schema.string()).optional().describe("Optional tags for categorization"),
|
|
35524
|
+
metadata: tool.schema.record(tool.schema.string(), tool.schema.unknown()).optional().describe("Optional metadata (e.g., code language for snippets)")
|
|
35525
|
+
},
|
|
35526
|
+
async execute(args) {
|
|
35527
|
+
const validated = CreateMandateArgsSchema.parse(args);
|
|
35528
|
+
const agentName = "system";
|
|
35529
|
+
const entry = {
|
|
35530
|
+
id: generateMandateId(),
|
|
35531
|
+
content: validated.content,
|
|
35532
|
+
content_type: validated.content_type,
|
|
35533
|
+
author_agent: agentName,
|
|
35534
|
+
created_at: new Date().toISOString(),
|
|
35535
|
+
status: "candidate",
|
|
35536
|
+
tags: validated.tags || [],
|
|
35537
|
+
metadata: validated.metadata
|
|
35538
|
+
};
|
|
35539
|
+
const validatedEntry = MandateEntrySchema.parse(entry);
|
|
35540
|
+
const storage = getMandateStorage();
|
|
35541
|
+
try {
|
|
35542
|
+
await storage.store(validatedEntry);
|
|
35543
|
+
} catch (error45) {
|
|
35544
|
+
throw new MandateError(`Failed to store mandate: ${error45 instanceof Error ? error45.message : String(error45)}`, "mandate_file", error45);
|
|
35545
|
+
}
|
|
35546
|
+
return JSON.stringify({
|
|
35547
|
+
success: true,
|
|
35548
|
+
mandate: validatedEntry,
|
|
35549
|
+
message: `Mandate ${validatedEntry.id} filed successfully`
|
|
35550
|
+
}, null, 2);
|
|
35551
|
+
}
|
|
35552
|
+
});
|
|
35553
|
+
var mandate_vote = tool({
|
|
35554
|
+
description: "Cast a vote (upvote or downvote) on an existing mandate",
|
|
35555
|
+
args: {
|
|
35556
|
+
mandate_id: tool.schema.string().describe("Mandate ID to vote on"),
|
|
35557
|
+
vote_type: tool.schema.enum(["upvote", "downvote"]).describe("Type of vote"),
|
|
35558
|
+
agent_name: tool.schema.string().describe("Agent name casting the vote")
|
|
35559
|
+
},
|
|
35560
|
+
async execute(args) {
|
|
35561
|
+
const validated = CastVoteArgsSchema.parse({
|
|
35562
|
+
mandate_id: args.mandate_id,
|
|
35563
|
+
vote_type: args.vote_type,
|
|
35564
|
+
weight: 1
|
|
35565
|
+
});
|
|
35566
|
+
const storage = getMandateStorage();
|
|
35567
|
+
const mandate = await storage.get(validated.mandate_id);
|
|
35568
|
+
if (!mandate) {
|
|
35569
|
+
throw new MandateError(`Mandate ${validated.mandate_id} not found`, "mandate_vote");
|
|
35570
|
+
}
|
|
35571
|
+
const hasVoted = await storage.hasVoted(validated.mandate_id, args.agent_name);
|
|
35572
|
+
if (hasVoted) {
|
|
35573
|
+
throw new MandateError(`Agent ${args.agent_name} has already voted on mandate ${validated.mandate_id}`, "mandate_vote");
|
|
35574
|
+
}
|
|
35575
|
+
const vote = {
|
|
35576
|
+
id: generateVoteId(),
|
|
35577
|
+
mandate_id: validated.mandate_id,
|
|
35578
|
+
agent_name: args.agent_name,
|
|
35579
|
+
vote_type: validated.vote_type,
|
|
35580
|
+
timestamp: new Date().toISOString(),
|
|
35581
|
+
weight: validated.weight
|
|
35582
|
+
};
|
|
35583
|
+
const validatedVote = VoteSchema.parse(vote);
|
|
35584
|
+
try {
|
|
35585
|
+
await storage.vote(validatedVote);
|
|
35586
|
+
} catch (error45) {
|
|
35587
|
+
throw new MandateError(`Failed to cast vote: ${error45 instanceof Error ? error45.message : String(error45)}`, "mandate_vote", error45);
|
|
35588
|
+
}
|
|
35589
|
+
const promotion = await updateMandateStatus(validated.mandate_id, storage);
|
|
35590
|
+
return JSON.stringify({
|
|
35591
|
+
success: true,
|
|
35592
|
+
vote: validatedVote,
|
|
35593
|
+
promotion: {
|
|
35594
|
+
previous_status: promotion.previous_status,
|
|
35595
|
+
new_status: promotion.new_status,
|
|
35596
|
+
status_changed: promotion.status_changed,
|
|
35597
|
+
score: promotion.score
|
|
35598
|
+
},
|
|
35599
|
+
message: formatPromotionResult({
|
|
35600
|
+
mandate_id: promotion.mandate_id,
|
|
35601
|
+
previous_status: promotion.previous_status,
|
|
35602
|
+
new_status: promotion.new_status,
|
|
35603
|
+
score: promotion.score,
|
|
35604
|
+
promoted: promotion.status_changed,
|
|
35605
|
+
reason: evaluatePromotion(mandate, promotion.score).reason || "Vote recorded"
|
|
35606
|
+
})
|
|
35607
|
+
}, null, 2);
|
|
35608
|
+
}
|
|
35609
|
+
});
|
|
35610
|
+
var mandate_query = tool({
|
|
35611
|
+
description: "Search for relevant mandates using semantic search (by meaning, not keywords)",
|
|
35612
|
+
args: {
|
|
35613
|
+
query: tool.schema.string().min(1).describe("Natural language query"),
|
|
35614
|
+
limit: tool.schema.number().int().positive().optional().describe("Max results to return (default: 5)"),
|
|
35615
|
+
status: tool.schema.enum(["candidate", "established", "mandate", "rejected"]).optional().describe("Filter by status"),
|
|
35616
|
+
content_type: tool.schema.enum(["idea", "tip", "lore", "snippet", "feature_request"]).optional().describe("Filter by content type")
|
|
35617
|
+
},
|
|
35618
|
+
async execute(args) {
|
|
35619
|
+
const storage = getMandateStorage();
|
|
35620
|
+
const limit = args.limit ?? 5;
|
|
35621
|
+
try {
|
|
35622
|
+
let results = await storage.find(args.query, limit * 2);
|
|
35623
|
+
if (args.status) {
|
|
35624
|
+
results = results.filter((m) => m.status === args.status);
|
|
35625
|
+
}
|
|
35626
|
+
if (args.content_type) {
|
|
35627
|
+
results = results.filter((m) => m.content_type === args.content_type);
|
|
35628
|
+
}
|
|
35629
|
+
results = results.slice(0, limit);
|
|
35630
|
+
const resultsWithScores = await Promise.all(results.map(async (mandate) => {
|
|
35631
|
+
const score = await storage.calculateScore(mandate.id);
|
|
35632
|
+
return { mandate, score };
|
|
35633
|
+
}));
|
|
35634
|
+
resultsWithScores.sort((a, b) => b.score.decayed_score - a.score.decayed_score);
|
|
35635
|
+
return JSON.stringify({
|
|
35636
|
+
query: args.query,
|
|
35637
|
+
count: resultsWithScores.length,
|
|
35638
|
+
results: resultsWithScores.map(({ mandate, score }) => ({
|
|
35639
|
+
id: mandate.id,
|
|
35640
|
+
content: mandate.content,
|
|
35641
|
+
content_type: mandate.content_type,
|
|
35642
|
+
status: mandate.status,
|
|
35643
|
+
author: mandate.author_agent,
|
|
35644
|
+
created_at: mandate.created_at,
|
|
35645
|
+
tags: mandate.tags,
|
|
35646
|
+
score: {
|
|
35647
|
+
net_votes: score.net_votes,
|
|
35648
|
+
vote_ratio: score.vote_ratio,
|
|
35649
|
+
decayed_score: score.decayed_score
|
|
35650
|
+
}
|
|
35651
|
+
}))
|
|
35652
|
+
}, null, 2);
|
|
35653
|
+
} catch (error45) {
|
|
35654
|
+
throw new MandateError(`Failed to query mandates: ${error45 instanceof Error ? error45.message : String(error45)}`, "mandate_query", error45);
|
|
35655
|
+
}
|
|
35656
|
+
}
|
|
35657
|
+
});
|
|
35658
|
+
var mandate_list = tool({
|
|
35659
|
+
description: "List mandates with optional filters (status, content type)",
|
|
35660
|
+
args: {
|
|
35661
|
+
status: tool.schema.enum(["candidate", "established", "mandate", "rejected"]).optional().describe("Filter by status"),
|
|
35662
|
+
content_type: tool.schema.enum(["idea", "tip", "lore", "snippet", "feature_request"]).optional().describe("Filter by content type"),
|
|
35663
|
+
limit: tool.schema.number().int().positive().optional().describe("Max results to return (default: 20)")
|
|
35664
|
+
},
|
|
35665
|
+
async execute(args) {
|
|
35666
|
+
const storage = getMandateStorage();
|
|
35667
|
+
const limit = args.limit ?? 20;
|
|
35668
|
+
try {
|
|
35669
|
+
let results = await storage.list({
|
|
35670
|
+
status: args.status,
|
|
35671
|
+
content_type: args.content_type
|
|
35672
|
+
});
|
|
35673
|
+
results = results.slice(0, limit);
|
|
35674
|
+
const resultsWithScores = await Promise.all(results.map(async (mandate) => {
|
|
35675
|
+
const score = await storage.calculateScore(mandate.id);
|
|
35676
|
+
return { mandate, score };
|
|
35677
|
+
}));
|
|
35678
|
+
resultsWithScores.sort((a, b) => b.score.decayed_score - a.score.decayed_score);
|
|
35679
|
+
return JSON.stringify({
|
|
35680
|
+
filters: {
|
|
35681
|
+
status: args.status || "all",
|
|
35682
|
+
content_type: args.content_type || "all"
|
|
35683
|
+
},
|
|
35684
|
+
count: resultsWithScores.length,
|
|
35685
|
+
results: resultsWithScores.map(({ mandate, score }) => ({
|
|
35686
|
+
id: mandate.id,
|
|
35687
|
+
content: mandate.content.slice(0, 200),
|
|
35688
|
+
content_type: mandate.content_type,
|
|
35689
|
+
status: mandate.status,
|
|
35690
|
+
author: mandate.author_agent,
|
|
35691
|
+
created_at: mandate.created_at,
|
|
35692
|
+
tags: mandate.tags,
|
|
35693
|
+
score: {
|
|
35694
|
+
net_votes: score.net_votes,
|
|
35695
|
+
vote_ratio: score.vote_ratio,
|
|
35696
|
+
decayed_score: score.decayed_score
|
|
35697
|
+
}
|
|
35698
|
+
}))
|
|
35699
|
+
}, null, 2);
|
|
35700
|
+
} catch (error45) {
|
|
35701
|
+
throw new MandateError(`Failed to list mandates: ${error45 instanceof Error ? error45.message : String(error45)}`, "mandate_list", error45);
|
|
35702
|
+
}
|
|
35703
|
+
}
|
|
35704
|
+
});
|
|
35705
|
+
var mandate_stats = tool({
|
|
35706
|
+
description: "Get voting statistics for a specific mandate or overall system",
|
|
35707
|
+
args: {
|
|
35708
|
+
mandate_id: tool.schema.string().optional().describe("Mandate ID (omit for overall stats)")
|
|
35709
|
+
},
|
|
35710
|
+
async execute(args) {
|
|
35711
|
+
const storage = getMandateStorage();
|
|
35712
|
+
try {
|
|
35713
|
+
if (args.mandate_id) {
|
|
35714
|
+
const mandate = await storage.get(args.mandate_id);
|
|
35715
|
+
if (!mandate) {
|
|
35716
|
+
throw new MandateError(`Mandate ${args.mandate_id} not found`, "mandate_stats");
|
|
35717
|
+
}
|
|
35718
|
+
const score = await storage.calculateScore(args.mandate_id);
|
|
35719
|
+
const votes = await storage.getVotes(args.mandate_id);
|
|
35720
|
+
return JSON.stringify({
|
|
35721
|
+
mandate_id: args.mandate_id,
|
|
35722
|
+
status: mandate.status,
|
|
35723
|
+
content_type: mandate.content_type,
|
|
35724
|
+
author: mandate.author_agent,
|
|
35725
|
+
created_at: mandate.created_at,
|
|
35726
|
+
votes: {
|
|
35727
|
+
total: votes.length,
|
|
35728
|
+
raw_upvotes: score.raw_upvotes,
|
|
35729
|
+
raw_downvotes: score.raw_downvotes,
|
|
35730
|
+
decayed_upvotes: score.decayed_upvotes,
|
|
35731
|
+
decayed_downvotes: score.decayed_downvotes,
|
|
35732
|
+
net_votes: score.net_votes,
|
|
35733
|
+
vote_ratio: score.vote_ratio,
|
|
35734
|
+
decayed_score: score.decayed_score
|
|
35735
|
+
},
|
|
35736
|
+
voters: votes.map((v) => ({
|
|
35737
|
+
agent: v.agent_name,
|
|
35738
|
+
vote_type: v.vote_type,
|
|
35739
|
+
timestamp: v.timestamp
|
|
35740
|
+
}))
|
|
35741
|
+
}, null, 2);
|
|
35742
|
+
} else {
|
|
35743
|
+
const allMandates = await storage.list();
|
|
35744
|
+
const stats = {
|
|
35745
|
+
total_mandates: allMandates.length,
|
|
35746
|
+
by_status: {
|
|
35747
|
+
candidate: 0,
|
|
35748
|
+
established: 0,
|
|
35749
|
+
mandate: 0,
|
|
35750
|
+
rejected: 0
|
|
35751
|
+
},
|
|
35752
|
+
by_content_type: {
|
|
35753
|
+
idea: 0,
|
|
35754
|
+
tip: 0,
|
|
35755
|
+
lore: 0,
|
|
35756
|
+
snippet: 0,
|
|
35757
|
+
feature_request: 0
|
|
35758
|
+
},
|
|
35759
|
+
total_votes: 0
|
|
35760
|
+
};
|
|
35761
|
+
for (const mandate of allMandates) {
|
|
35762
|
+
stats.by_status[mandate.status]++;
|
|
35763
|
+
stats.by_content_type[mandate.content_type]++;
|
|
35764
|
+
const votes = await storage.getVotes(mandate.id);
|
|
35765
|
+
stats.total_votes += votes.length;
|
|
35766
|
+
}
|
|
35767
|
+
return JSON.stringify(stats, null, 2);
|
|
35768
|
+
}
|
|
35769
|
+
} catch (error45) {
|
|
35770
|
+
if (error45 instanceof MandateError) {
|
|
35771
|
+
throw error45;
|
|
35772
|
+
}
|
|
35773
|
+
throw new MandateError(`Failed to get mandate stats: ${error45 instanceof Error ? error45.message : String(error45)}`, "mandate_stats", error45);
|
|
35774
|
+
}
|
|
35775
|
+
}
|
|
35776
|
+
});
|
|
35777
|
+
var mandateTools = {
|
|
35778
|
+
mandate_file,
|
|
35779
|
+
mandate_vote,
|
|
35780
|
+
mandate_query,
|
|
35781
|
+
mandate_list,
|
|
35782
|
+
mandate_stats
|
|
35783
|
+
};
|
|
34582
35784
|
// src/anti-patterns.ts
|
|
34583
35785
|
init_zod();
|
|
34584
35786
|
var PatternKindSchema = exports_external.enum(["pattern", "anti_pattern"]);
|
|
@@ -34714,7 +35916,8 @@ var SwarmPlugin = async (input) => {
|
|
|
34714
35916
|
...structuredTools,
|
|
34715
35917
|
...swarmTools,
|
|
34716
35918
|
...repoCrawlTools,
|
|
34717
|
-
...skillsTools
|
|
35919
|
+
...skillsTools,
|
|
35920
|
+
...mandateTools
|
|
34718
35921
|
},
|
|
34719
35922
|
event: async ({ event }) => {
|
|
34720
35923
|
if (event.type === "session.idle") {
|
|
@@ -34760,7 +35963,8 @@ var allTools = {
|
|
|
34760
35963
|
...structuredTools,
|
|
34761
35964
|
...swarmTools,
|
|
34762
35965
|
...repoCrawlTools,
|
|
34763
|
-
...skillsTools
|
|
35966
|
+
...skillsTools,
|
|
35967
|
+
...mandateTools
|
|
34764
35968
|
};
|
|
34765
35969
|
export {
|
|
34766
35970
|
SwarmPlugin
|