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/index.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,96 @@ 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
|
+
});
|
|
29749
|
+
var mandateSchemas = {
|
|
29750
|
+
MandateContentTypeSchema,
|
|
29751
|
+
MandateStatusSchema,
|
|
29752
|
+
VoteTypeSchema,
|
|
29753
|
+
MandateEntrySchema,
|
|
29754
|
+
VoteSchema,
|
|
29755
|
+
MandateScoreSchema,
|
|
29756
|
+
CreateMandateArgsSchema,
|
|
29757
|
+
CastVoteArgsSchema,
|
|
29758
|
+
QueryMandatesArgsSchema,
|
|
29759
|
+
ScoreCalculationResultSchema
|
|
29760
|
+
};
|
|
29580
29761
|
// src/beads.ts
|
|
29581
29762
|
var beadsWorkingDirectory = null;
|
|
29582
29763
|
function setBeadsWorkingDirectory(directory) {
|
|
@@ -32378,6 +32559,89 @@ class InMemoryFeedbackStorage {
|
|
|
32378
32559
|
return [...this.events];
|
|
32379
32560
|
}
|
|
32380
32561
|
}
|
|
32562
|
+
var StrikeRecordSchema = exports_external.object({
|
|
32563
|
+
bead_id: exports_external.string(),
|
|
32564
|
+
strike_count: exports_external.number().int().min(0).max(3),
|
|
32565
|
+
failures: exports_external.array(exports_external.object({
|
|
32566
|
+
attempt: exports_external.string(),
|
|
32567
|
+
reason: exports_external.string(),
|
|
32568
|
+
timestamp: exports_external.string()
|
|
32569
|
+
})),
|
|
32570
|
+
first_strike_at: exports_external.string().optional(),
|
|
32571
|
+
last_strike_at: exports_external.string().optional()
|
|
32572
|
+
});
|
|
32573
|
+
|
|
32574
|
+
class InMemoryStrikeStorage {
|
|
32575
|
+
strikes = new Map;
|
|
32576
|
+
async store(record2) {
|
|
32577
|
+
this.strikes.set(record2.bead_id, record2);
|
|
32578
|
+
}
|
|
32579
|
+
async get(beadId) {
|
|
32580
|
+
return this.strikes.get(beadId) ?? null;
|
|
32581
|
+
}
|
|
32582
|
+
async getAll() {
|
|
32583
|
+
return Array.from(this.strikes.values());
|
|
32584
|
+
}
|
|
32585
|
+
async clear(beadId) {
|
|
32586
|
+
this.strikes.delete(beadId);
|
|
32587
|
+
}
|
|
32588
|
+
}
|
|
32589
|
+
async function addStrike(beadId, attempt, reason, storage = new InMemoryStrikeStorage) {
|
|
32590
|
+
const existing = await storage.get(beadId);
|
|
32591
|
+
const now = new Date().toISOString();
|
|
32592
|
+
const record2 = existing ?? {
|
|
32593
|
+
bead_id: beadId,
|
|
32594
|
+
strike_count: 0,
|
|
32595
|
+
failures: []
|
|
32596
|
+
};
|
|
32597
|
+
record2.strike_count = Math.min(3, record2.strike_count + 1);
|
|
32598
|
+
record2.failures.push({ attempt, reason, timestamp: now });
|
|
32599
|
+
record2.last_strike_at = now;
|
|
32600
|
+
if (!record2.first_strike_at) {
|
|
32601
|
+
record2.first_strike_at = now;
|
|
32602
|
+
}
|
|
32603
|
+
await storage.store(record2);
|
|
32604
|
+
return record2;
|
|
32605
|
+
}
|
|
32606
|
+
async function getStrikes(beadId, storage = new InMemoryStrikeStorage) {
|
|
32607
|
+
const record2 = await storage.get(beadId);
|
|
32608
|
+
return record2?.strike_count ?? 0;
|
|
32609
|
+
}
|
|
32610
|
+
async function isStrikedOut(beadId, storage = new InMemoryStrikeStorage) {
|
|
32611
|
+
const count = await getStrikes(beadId, storage);
|
|
32612
|
+
return count >= 3;
|
|
32613
|
+
}
|
|
32614
|
+
async function getArchitecturePrompt(beadId, storage = new InMemoryStrikeStorage) {
|
|
32615
|
+
const record2 = await storage.get(beadId);
|
|
32616
|
+
if (!record2 || record2.strike_count < 3) {
|
|
32617
|
+
return "";
|
|
32618
|
+
}
|
|
32619
|
+
const failuresList = record2.failures.map((f, i) => `${i + 1}. **${f.attempt}** - Failed: ${f.reason}`).join(`
|
|
32620
|
+
`);
|
|
32621
|
+
return `## Architecture Review Required
|
|
32622
|
+
|
|
32623
|
+
This bead (\`${beadId}\`) has failed 3 consecutive fix attempts:
|
|
32624
|
+
|
|
32625
|
+
${failuresList}
|
|
32626
|
+
|
|
32627
|
+
This pattern suggests an **architectural problem**, not a bug.
|
|
32628
|
+
|
|
32629
|
+
**Questions to consider:**
|
|
32630
|
+
- Is the current approach fundamentally sound?
|
|
32631
|
+
- Should we refactor the architecture instead?
|
|
32632
|
+
- Are we fixing symptoms instead of root cause?
|
|
32633
|
+
|
|
32634
|
+
**Options:**
|
|
32635
|
+
1. **Refactor architecture** (describe new approach)
|
|
32636
|
+
2. **Continue with Fix #4** (explain why this time is different)
|
|
32637
|
+
3. **Abandon this approach entirely**
|
|
32638
|
+
|
|
32639
|
+
**DO NOT attempt Fix #4 without answering these questions.**
|
|
32640
|
+
`;
|
|
32641
|
+
}
|
|
32642
|
+
async function clearStrikes(beadId, storage = new InMemoryStrikeStorage) {
|
|
32643
|
+
await storage.clear(beadId);
|
|
32644
|
+
}
|
|
32381
32645
|
|
|
32382
32646
|
class InMemoryErrorStorage {
|
|
32383
32647
|
errors = [];
|
|
@@ -32897,7 +33161,7 @@ You MUST keep your bead updated as you work:
|
|
|
32897
33161
|
|
|
32898
33162
|
**Never work silently.** Your bead status is how the swarm tracks progress.
|
|
32899
33163
|
|
|
32900
|
-
## MANDATORY:
|
|
33164
|
+
## MANDATORY: Swarm Mail Communication
|
|
32901
33165
|
|
|
32902
33166
|
You MUST communicate with other agents:
|
|
32903
33167
|
|
|
@@ -32906,9 +33170,9 @@ You MUST communicate with other agents:
|
|
|
32906
33170
|
3. **Announce blockers** immediately - don't spin trying to fix alone
|
|
32907
33171
|
4. **Coordinate on shared concerns** - if you see something affecting other agents, say so
|
|
32908
33172
|
|
|
32909
|
-
Use
|
|
33173
|
+
Use Swarm Mail for all communication:
|
|
32910
33174
|
\`\`\`
|
|
32911
|
-
|
|
33175
|
+
swarmmail_send(
|
|
32912
33176
|
to: ["coordinator" or specific agent],
|
|
32913
33177
|
subject: "Brief subject",
|
|
32914
33178
|
body: "Message content",
|
|
@@ -32920,7 +33184,7 @@ agentmail_send(
|
|
|
32920
33184
|
|
|
32921
33185
|
1. **Start**: Your bead is already marked in_progress
|
|
32922
33186
|
2. **Progress**: Use swarm_progress to report status updates
|
|
32923
|
-
3. **Blocked**: Report immediately via
|
|
33187
|
+
3. **Blocked**: Report immediately via Swarm Mail - don't spin
|
|
32924
33188
|
4. **Complete**: Use swarm_complete when done - it handles:
|
|
32925
33189
|
- Closing your bead with a summary
|
|
32926
33190
|
- Releasing file reservations
|
|
@@ -32942,7 +33206,7 @@ Before writing code:
|
|
|
32942
33206
|
1. **Read the files** you're assigned to understand current state
|
|
32943
33207
|
2. **Plan your approach** - what changes, in what order?
|
|
32944
33208
|
3. **Identify risks** - what could go wrong? What dependencies?
|
|
32945
|
-
4. **Communicate your plan** via
|
|
33209
|
+
4. **Communicate your plan** via Swarm Mail if non-trivial
|
|
32946
33210
|
|
|
32947
33211
|
Begin work on your subtask now.`;
|
|
32948
33212
|
var SUBTASK_PROMPT_V2 = `You are a swarm agent working on: **{subtask_title}**
|
|
@@ -33653,6 +33917,139 @@ var swarm_progress = tool({
|
|
|
33653
33917
|
return `Progress reported: ${args.status}${args.progress_percent !== undefined ? ` (${args.progress_percent}%)` : ""}`;
|
|
33654
33918
|
}
|
|
33655
33919
|
});
|
|
33920
|
+
async function runTypecheckVerification() {
|
|
33921
|
+
const step = {
|
|
33922
|
+
name: "typecheck",
|
|
33923
|
+
command: "tsc --noEmit",
|
|
33924
|
+
passed: false,
|
|
33925
|
+
exitCode: -1
|
|
33926
|
+
};
|
|
33927
|
+
try {
|
|
33928
|
+
const tsconfigExists = await Bun.file("tsconfig.json").exists();
|
|
33929
|
+
if (!tsconfigExists) {
|
|
33930
|
+
step.skipped = true;
|
|
33931
|
+
step.skipReason = "No tsconfig.json found";
|
|
33932
|
+
step.passed = true;
|
|
33933
|
+
return step;
|
|
33934
|
+
}
|
|
33935
|
+
const result = await Bun.$`tsc --noEmit`.quiet().nothrow();
|
|
33936
|
+
step.exitCode = result.exitCode;
|
|
33937
|
+
step.passed = result.exitCode === 0;
|
|
33938
|
+
if (!step.passed) {
|
|
33939
|
+
step.error = result.stderr.toString().slice(0, 1000);
|
|
33940
|
+
step.output = result.stdout.toString().slice(0, 1000);
|
|
33941
|
+
}
|
|
33942
|
+
} catch (error45) {
|
|
33943
|
+
step.skipped = true;
|
|
33944
|
+
step.skipReason = `tsc not available: ${error45 instanceof Error ? error45.message : String(error45)}`;
|
|
33945
|
+
step.passed = true;
|
|
33946
|
+
}
|
|
33947
|
+
return step;
|
|
33948
|
+
}
|
|
33949
|
+
async function runTestVerification(filesTouched) {
|
|
33950
|
+
const step = {
|
|
33951
|
+
name: "tests",
|
|
33952
|
+
command: "bun test <related-files>",
|
|
33953
|
+
passed: false,
|
|
33954
|
+
exitCode: -1
|
|
33955
|
+
};
|
|
33956
|
+
if (filesTouched.length === 0) {
|
|
33957
|
+
step.skipped = true;
|
|
33958
|
+
step.skipReason = "No files touched";
|
|
33959
|
+
step.passed = true;
|
|
33960
|
+
return step;
|
|
33961
|
+
}
|
|
33962
|
+
const testPatterns = [];
|
|
33963
|
+
for (const file2 of filesTouched) {
|
|
33964
|
+
if (file2.includes(".test.") || file2.includes(".spec.")) {
|
|
33965
|
+
testPatterns.push(file2);
|
|
33966
|
+
continue;
|
|
33967
|
+
}
|
|
33968
|
+
const baseName = file2.replace(/\.(ts|tsx|js|jsx)$/, "");
|
|
33969
|
+
testPatterns.push(`${baseName}.test.ts`);
|
|
33970
|
+
testPatterns.push(`${baseName}.test.tsx`);
|
|
33971
|
+
testPatterns.push(`${baseName}.spec.ts`);
|
|
33972
|
+
}
|
|
33973
|
+
const existingTests = [];
|
|
33974
|
+
for (const pattern of testPatterns) {
|
|
33975
|
+
try {
|
|
33976
|
+
const exists = await Bun.file(pattern).exists();
|
|
33977
|
+
if (exists) {
|
|
33978
|
+
existingTests.push(pattern);
|
|
33979
|
+
}
|
|
33980
|
+
} catch {}
|
|
33981
|
+
}
|
|
33982
|
+
if (existingTests.length === 0) {
|
|
33983
|
+
step.skipped = true;
|
|
33984
|
+
step.skipReason = "No related test files found";
|
|
33985
|
+
step.passed = true;
|
|
33986
|
+
return step;
|
|
33987
|
+
}
|
|
33988
|
+
try {
|
|
33989
|
+
step.command = `bun test ${existingTests.join(" ")}`;
|
|
33990
|
+
const result = await Bun.$`bun test ${existingTests}`.quiet().nothrow();
|
|
33991
|
+
step.exitCode = result.exitCode;
|
|
33992
|
+
step.passed = result.exitCode === 0;
|
|
33993
|
+
if (!step.passed) {
|
|
33994
|
+
step.error = result.stderr.toString().slice(0, 1000);
|
|
33995
|
+
step.output = result.stdout.toString().slice(0, 1000);
|
|
33996
|
+
}
|
|
33997
|
+
} catch (error45) {
|
|
33998
|
+
step.skipped = true;
|
|
33999
|
+
step.skipReason = `Test runner failed: ${error45 instanceof Error ? error45.message : String(error45)}`;
|
|
34000
|
+
step.passed = true;
|
|
34001
|
+
}
|
|
34002
|
+
return step;
|
|
34003
|
+
}
|
|
34004
|
+
async function runVerificationGate(filesTouched, skipUbs = false) {
|
|
34005
|
+
const steps = [];
|
|
34006
|
+
const blockers = [];
|
|
34007
|
+
if (!skipUbs && filesTouched.length > 0) {
|
|
34008
|
+
const ubsResult = await runUbsScan(filesTouched);
|
|
34009
|
+
if (ubsResult) {
|
|
34010
|
+
const ubsStep = {
|
|
34011
|
+
name: "ubs_scan",
|
|
34012
|
+
command: `ubs scan ${filesTouched.join(" ")}`,
|
|
34013
|
+
passed: ubsResult.summary.critical === 0,
|
|
34014
|
+
exitCode: ubsResult.exitCode
|
|
34015
|
+
};
|
|
34016
|
+
if (!ubsStep.passed) {
|
|
34017
|
+
ubsStep.error = `Found ${ubsResult.summary.critical} critical bugs`;
|
|
34018
|
+
blockers.push(`UBS: ${ubsResult.summary.critical} critical bugs found`);
|
|
34019
|
+
}
|
|
34020
|
+
steps.push(ubsStep);
|
|
34021
|
+
} else {
|
|
34022
|
+
steps.push({
|
|
34023
|
+
name: "ubs_scan",
|
|
34024
|
+
command: "ubs scan",
|
|
34025
|
+
passed: true,
|
|
34026
|
+
exitCode: 0,
|
|
34027
|
+
skipped: true,
|
|
34028
|
+
skipReason: "UBS not available"
|
|
34029
|
+
});
|
|
34030
|
+
}
|
|
34031
|
+
}
|
|
34032
|
+
const typecheckStep = await runTypecheckVerification();
|
|
34033
|
+
steps.push(typecheckStep);
|
|
34034
|
+
if (!typecheckStep.passed && !typecheckStep.skipped) {
|
|
34035
|
+
blockers.push(`Typecheck: ${typecheckStep.error?.slice(0, 100) || "failed"}`);
|
|
34036
|
+
}
|
|
34037
|
+
const testStep = await runTestVerification(filesTouched);
|
|
34038
|
+
steps.push(testStep);
|
|
34039
|
+
if (!testStep.passed && !testStep.skipped) {
|
|
34040
|
+
blockers.push(`Tests: ${testStep.error?.slice(0, 100) || "failed"}`);
|
|
34041
|
+
}
|
|
34042
|
+
const passedCount = steps.filter((s) => s.passed).length;
|
|
34043
|
+
const skippedCount = steps.filter((s) => s.skipped).length;
|
|
34044
|
+
const failedCount = steps.filter((s) => !s.passed && !s.skipped).length;
|
|
34045
|
+
const summary = failedCount === 0 ? `Verification passed: ${passedCount} checks passed, ${skippedCount} skipped` : `Verification FAILED: ${failedCount} checks failed, ${passedCount} passed, ${skippedCount} skipped`;
|
|
34046
|
+
return {
|
|
34047
|
+
passed: failedCount === 0,
|
|
34048
|
+
steps,
|
|
34049
|
+
summary,
|
|
34050
|
+
blockers
|
|
34051
|
+
};
|
|
34052
|
+
}
|
|
33656
34053
|
async function runUbsScan(files) {
|
|
33657
34054
|
if (files.length === 0) {
|
|
33658
34055
|
return null;
|
|
@@ -33755,19 +34152,44 @@ ${args.files_affected.map((f) => `- \`${f}\``).join(`
|
|
|
33755
34152
|
}
|
|
33756
34153
|
});
|
|
33757
34154
|
var swarm_complete = tool({
|
|
33758
|
-
description: "Mark subtask complete
|
|
34155
|
+
description: "Mark subtask complete with Verification Gate. Runs UBS scan, typecheck, and tests before allowing completion.",
|
|
33759
34156
|
args: {
|
|
33760
34157
|
project_key: tool.schema.string().describe("Project path"),
|
|
33761
34158
|
agent_name: tool.schema.string().describe("Your Agent Mail name"),
|
|
33762
34159
|
bead_id: tool.schema.string().describe("Subtask bead ID"),
|
|
33763
34160
|
summary: tool.schema.string().describe("Brief summary of work done"),
|
|
33764
34161
|
evaluation: tool.schema.string().optional().describe("Self-evaluation JSON (Evaluation schema)"),
|
|
33765
|
-
files_touched: tool.schema.array(tool.schema.string()).optional().describe("Files modified - will be
|
|
33766
|
-
skip_ubs_scan: tool.schema.boolean().optional().describe("Skip UBS bug scan (default: false)")
|
|
34162
|
+
files_touched: tool.schema.array(tool.schema.string()).optional().describe("Files modified - will be verified (UBS, typecheck, tests)"),
|
|
34163
|
+
skip_ubs_scan: tool.schema.boolean().optional().describe("Skip UBS bug scan (default: false)"),
|
|
34164
|
+
skip_verification: tool.schema.boolean().optional().describe("Skip ALL verification (UBS, typecheck, tests). Use sparingly! (default: false)")
|
|
33767
34165
|
},
|
|
33768
34166
|
async execute(args) {
|
|
34167
|
+
let verificationResult = null;
|
|
34168
|
+
if (!args.skip_verification && args.files_touched?.length) {
|
|
34169
|
+
verificationResult = await runVerificationGate(args.files_touched, args.skip_ubs_scan ?? false);
|
|
34170
|
+
if (!verificationResult.passed) {
|
|
34171
|
+
return JSON.stringify({
|
|
34172
|
+
success: false,
|
|
34173
|
+
error: "Verification Gate FAILED - fix issues before completing",
|
|
34174
|
+
verification: {
|
|
34175
|
+
passed: false,
|
|
34176
|
+
summary: verificationResult.summary,
|
|
34177
|
+
blockers: verificationResult.blockers,
|
|
34178
|
+
steps: verificationResult.steps.map((s) => ({
|
|
34179
|
+
name: s.name,
|
|
34180
|
+
passed: s.passed,
|
|
34181
|
+
skipped: s.skipped,
|
|
34182
|
+
skipReason: s.skipReason,
|
|
34183
|
+
error: s.error?.slice(0, 200)
|
|
34184
|
+
}))
|
|
34185
|
+
},
|
|
34186
|
+
hint: "Fix the failing checks and try again. Use skip_verification=true only as last resort.",
|
|
34187
|
+
gate_function: "IDENTIFY → RUN → READ → VERIFY → CLAIM (you are at VERIFY, claim blocked)"
|
|
34188
|
+
}, null, 2);
|
|
34189
|
+
}
|
|
34190
|
+
}
|
|
33769
34191
|
let ubsResult = null;
|
|
33770
|
-
if (args.
|
|
34192
|
+
if (!args.skip_verification && !verificationResult && args.files_touched?.length && !args.skip_ubs_scan) {
|
|
33771
34193
|
ubsResult = await runUbsScan(args.files_touched);
|
|
33772
34194
|
if (ubsResult && ubsResult.summary.critical > 0) {
|
|
33773
34195
|
return JSON.stringify({
|
|
@@ -33838,12 +34260,22 @@ var swarm_complete = tool({
|
|
|
33838
34260
|
closed: true,
|
|
33839
34261
|
reservations_released: true,
|
|
33840
34262
|
message_sent: true,
|
|
34263
|
+
verification_gate: verificationResult ? {
|
|
34264
|
+
passed: true,
|
|
34265
|
+
summary: verificationResult.summary,
|
|
34266
|
+
steps: verificationResult.steps.map((s) => ({
|
|
34267
|
+
name: s.name,
|
|
34268
|
+
passed: s.passed,
|
|
34269
|
+
skipped: s.skipped,
|
|
34270
|
+
skipReason: s.skipReason
|
|
34271
|
+
}))
|
|
34272
|
+
} : args.skip_verification ? { skipped: true, reason: "skip_verification=true" } : { skipped: true, reason: "no files_touched provided" },
|
|
33841
34273
|
ubs_scan: ubsResult ? {
|
|
33842
34274
|
ran: true,
|
|
33843
34275
|
bugs_found: ubsResult.summary.total,
|
|
33844
34276
|
summary: ubsResult.summary,
|
|
33845
34277
|
warnings: ubsResult.bugs.filter((b) => b.severity !== "critical")
|
|
33846
|
-
} : {
|
|
34278
|
+
} : verificationResult ? { ran: true, included_in_verification_gate: true } : {
|
|
33847
34279
|
ran: false,
|
|
33848
34280
|
reason: args.skip_ubs_scan ? "skipped" : "no files or ubs unavailable"
|
|
33849
34281
|
},
|
|
@@ -34411,6 +34843,77 @@ var swarmTools = {
|
|
|
34411
34843
|
swarm_get_error_context,
|
|
34412
34844
|
swarm_resolve_error
|
|
34413
34845
|
};
|
|
34846
|
+
var globalStrikeStorage = new InMemoryStrikeStorage;
|
|
34847
|
+
var swarm_check_strikes = tool({
|
|
34848
|
+
description: "Check 3-strike status for a bead. Records failures, detects architectural problems, generates architecture review prompts.",
|
|
34849
|
+
args: {
|
|
34850
|
+
bead_id: tool.schema.string().describe("Bead ID to check"),
|
|
34851
|
+
action: tool.schema.enum(["check", "add_strike", "clear", "get_prompt"]).describe("Action: check count, add strike, clear strikes, or get prompt"),
|
|
34852
|
+
attempt: tool.schema.string().optional().describe("Description of fix attempt (required for add_strike)"),
|
|
34853
|
+
reason: tool.schema.string().optional().describe("Why the fix failed (required for add_strike)")
|
|
34854
|
+
},
|
|
34855
|
+
async execute(args) {
|
|
34856
|
+
switch (args.action) {
|
|
34857
|
+
case "check": {
|
|
34858
|
+
const count = await getStrikes(args.bead_id, globalStrikeStorage);
|
|
34859
|
+
const strikedOut = await isStrikedOut(args.bead_id, globalStrikeStorage);
|
|
34860
|
+
return JSON.stringify({
|
|
34861
|
+
bead_id: args.bead_id,
|
|
34862
|
+
strike_count: count,
|
|
34863
|
+
is_striked_out: strikedOut,
|
|
34864
|
+
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.`,
|
|
34865
|
+
next_action: strikedOut ? "Call with action=get_prompt to get architecture review questions" : "Continue with fix attempt"
|
|
34866
|
+
}, null, 2);
|
|
34867
|
+
}
|
|
34868
|
+
case "add_strike": {
|
|
34869
|
+
if (!args.attempt || !args.reason) {
|
|
34870
|
+
return JSON.stringify({
|
|
34871
|
+
error: "add_strike requires 'attempt' and 'reason' parameters"
|
|
34872
|
+
}, null, 2);
|
|
34873
|
+
}
|
|
34874
|
+
const record2 = await addStrike(args.bead_id, args.attempt, args.reason, globalStrikeStorage);
|
|
34875
|
+
const strikedOut = record2.strike_count >= 3;
|
|
34876
|
+
return JSON.stringify({
|
|
34877
|
+
bead_id: args.bead_id,
|
|
34878
|
+
strike_count: record2.strike_count,
|
|
34879
|
+
is_striked_out: strikedOut,
|
|
34880
|
+
failures: record2.failures,
|
|
34881
|
+
message: strikedOut ? "⚠️ STRUCK OUT: 3 strikes reached. STOP and question the architecture." : `Strike ${record2.strike_count} recorded. ${3 - record2.strike_count} remaining.`,
|
|
34882
|
+
warning: strikedOut ? "DO NOT attempt Fix #4. Call with action=get_prompt for architecture review." : undefined
|
|
34883
|
+
}, null, 2);
|
|
34884
|
+
}
|
|
34885
|
+
case "clear": {
|
|
34886
|
+
await clearStrikes(args.bead_id, globalStrikeStorage);
|
|
34887
|
+
return JSON.stringify({
|
|
34888
|
+
bead_id: args.bead_id,
|
|
34889
|
+
strike_count: 0,
|
|
34890
|
+
is_striked_out: false,
|
|
34891
|
+
message: "Strikes cleared. Fresh start."
|
|
34892
|
+
}, null, 2);
|
|
34893
|
+
}
|
|
34894
|
+
case "get_prompt": {
|
|
34895
|
+
const prompt = await getArchitecturePrompt(args.bead_id, globalStrikeStorage);
|
|
34896
|
+
if (!prompt) {
|
|
34897
|
+
return JSON.stringify({
|
|
34898
|
+
bead_id: args.bead_id,
|
|
34899
|
+
has_prompt: false,
|
|
34900
|
+
message: "No architecture prompt (not struck out yet)"
|
|
34901
|
+
}, null, 2);
|
|
34902
|
+
}
|
|
34903
|
+
return JSON.stringify({
|
|
34904
|
+
bead_id: args.bead_id,
|
|
34905
|
+
has_prompt: true,
|
|
34906
|
+
architecture_review_prompt: prompt,
|
|
34907
|
+
message: "Architecture review required. Present this prompt to the human partner."
|
|
34908
|
+
}, null, 2);
|
|
34909
|
+
}
|
|
34910
|
+
default:
|
|
34911
|
+
return JSON.stringify({
|
|
34912
|
+
error: `Unknown action: ${args.action}`
|
|
34913
|
+
}, null, 2);
|
|
34914
|
+
}
|
|
34915
|
+
}
|
|
34916
|
+
});
|
|
34414
34917
|
|
|
34415
34918
|
// src/repo-crawl.ts
|
|
34416
34919
|
init_dist();
|
|
@@ -34737,6 +35240,762 @@ var repoCrawlTools = {
|
|
|
34737
35240
|
// src/index.ts
|
|
34738
35241
|
init_skills();
|
|
34739
35242
|
|
|
35243
|
+
// src/mandates.ts
|
|
35244
|
+
init_dist();
|
|
35245
|
+
|
|
35246
|
+
// src/mandate-storage.ts
|
|
35247
|
+
var cachedCommand = null;
|
|
35248
|
+
async function resolveSemanticMemoryCommand() {
|
|
35249
|
+
if (cachedCommand)
|
|
35250
|
+
return cachedCommand;
|
|
35251
|
+
const nativeResult = await Bun.$`which semantic-memory`.quiet().nothrow();
|
|
35252
|
+
if (nativeResult.exitCode === 0) {
|
|
35253
|
+
cachedCommand = ["semantic-memory"];
|
|
35254
|
+
return cachedCommand;
|
|
35255
|
+
}
|
|
35256
|
+
cachedCommand = ["bunx", "semantic-memory"];
|
|
35257
|
+
return cachedCommand;
|
|
35258
|
+
}
|
|
35259
|
+
async function execSemanticMemory(args) {
|
|
35260
|
+
try {
|
|
35261
|
+
const cmd = await resolveSemanticMemoryCommand();
|
|
35262
|
+
const fullCmd = [...cmd, ...args];
|
|
35263
|
+
const proc = Bun.spawn(fullCmd, {
|
|
35264
|
+
stdout: "pipe",
|
|
35265
|
+
stderr: "pipe"
|
|
35266
|
+
});
|
|
35267
|
+
try {
|
|
35268
|
+
const stdout = Buffer.from(await new Response(proc.stdout).arrayBuffer());
|
|
35269
|
+
const stderr = Buffer.from(await new Response(proc.stderr).arrayBuffer());
|
|
35270
|
+
const exitCode = await proc.exited;
|
|
35271
|
+
return { exitCode, stdout, stderr };
|
|
35272
|
+
} finally {
|
|
35273
|
+
proc.kill();
|
|
35274
|
+
}
|
|
35275
|
+
} catch (error45) {
|
|
35276
|
+
const errorMessage = error45 instanceof Error ? error45.message : String(error45);
|
|
35277
|
+
return {
|
|
35278
|
+
exitCode: 1,
|
|
35279
|
+
stdout: Buffer.from(""),
|
|
35280
|
+
stderr: Buffer.from(`Error executing semantic-memory: ${errorMessage}`)
|
|
35281
|
+
};
|
|
35282
|
+
}
|
|
35283
|
+
}
|
|
35284
|
+
var DEFAULT_MANDATE_STORAGE_CONFIG = {
|
|
35285
|
+
backend: "semantic-memory",
|
|
35286
|
+
collections: {
|
|
35287
|
+
mandates: "swarm-mandates",
|
|
35288
|
+
votes: "swarm-votes"
|
|
35289
|
+
},
|
|
35290
|
+
decay: DEFAULT_MANDATE_DECAY_CONFIG,
|
|
35291
|
+
useSemanticSearch: true
|
|
35292
|
+
};
|
|
35293
|
+
|
|
35294
|
+
class SemanticMemoryMandateStorage {
|
|
35295
|
+
config;
|
|
35296
|
+
constructor(config2 = {}) {
|
|
35297
|
+
this.config = { ...DEFAULT_MANDATE_STORAGE_CONFIG, ...config2 };
|
|
35298
|
+
}
|
|
35299
|
+
async storeInternal(collection, data, metadata) {
|
|
35300
|
+
const content = typeof data === "string" ? data : JSON.stringify(data);
|
|
35301
|
+
const args = ["store", content, "--collection", collection];
|
|
35302
|
+
if (metadata) {
|
|
35303
|
+
args.push("--metadata", JSON.stringify(metadata));
|
|
35304
|
+
}
|
|
35305
|
+
await execSemanticMemory(args);
|
|
35306
|
+
}
|
|
35307
|
+
async findInternal(collection, query, limit = 10, useFts = false) {
|
|
35308
|
+
const args = [
|
|
35309
|
+
"find",
|
|
35310
|
+
query,
|
|
35311
|
+
"--collection",
|
|
35312
|
+
collection,
|
|
35313
|
+
"--limit",
|
|
35314
|
+
String(limit),
|
|
35315
|
+
"--json"
|
|
35316
|
+
];
|
|
35317
|
+
if (useFts) {
|
|
35318
|
+
args.push("--fts");
|
|
35319
|
+
}
|
|
35320
|
+
const result = await execSemanticMemory(args);
|
|
35321
|
+
if (result.exitCode !== 0) {
|
|
35322
|
+
console.warn(`[mandate-storage] semantic-memory find() failed with exit code ${result.exitCode}: ${result.stderr.toString().trim()}`);
|
|
35323
|
+
return [];
|
|
35324
|
+
}
|
|
35325
|
+
try {
|
|
35326
|
+
const output = result.stdout.toString().trim();
|
|
35327
|
+
if (!output)
|
|
35328
|
+
return [];
|
|
35329
|
+
const parsed = JSON.parse(output);
|
|
35330
|
+
const results = Array.isArray(parsed) ? parsed : parsed.results || [];
|
|
35331
|
+
return results.map((r) => {
|
|
35332
|
+
const content = r.content || r.information || "";
|
|
35333
|
+
try {
|
|
35334
|
+
return JSON.parse(content);
|
|
35335
|
+
} catch {
|
|
35336
|
+
return content;
|
|
35337
|
+
}
|
|
35338
|
+
});
|
|
35339
|
+
} catch (error45) {
|
|
35340
|
+
console.warn(`[mandate-storage] Failed to parse semantic-memory find() output: ${error45 instanceof Error ? error45.message : String(error45)}`);
|
|
35341
|
+
return [];
|
|
35342
|
+
}
|
|
35343
|
+
}
|
|
35344
|
+
async listInternal(collection) {
|
|
35345
|
+
const result = await execSemanticMemory([
|
|
35346
|
+
"list",
|
|
35347
|
+
"--collection",
|
|
35348
|
+
collection,
|
|
35349
|
+
"--json"
|
|
35350
|
+
]);
|
|
35351
|
+
if (result.exitCode !== 0) {
|
|
35352
|
+
console.warn(`[mandate-storage] semantic-memory list() failed with exit code ${result.exitCode}: ${result.stderr.toString().trim()}`);
|
|
35353
|
+
return [];
|
|
35354
|
+
}
|
|
35355
|
+
try {
|
|
35356
|
+
const output = result.stdout.toString().trim();
|
|
35357
|
+
if (!output)
|
|
35358
|
+
return [];
|
|
35359
|
+
const parsed = JSON.parse(output);
|
|
35360
|
+
const items = Array.isArray(parsed) ? parsed : parsed.items || [];
|
|
35361
|
+
return items.map((item) => {
|
|
35362
|
+
const content = item.content || item.information || "";
|
|
35363
|
+
try {
|
|
35364
|
+
return JSON.parse(content);
|
|
35365
|
+
} catch {
|
|
35366
|
+
return content;
|
|
35367
|
+
}
|
|
35368
|
+
});
|
|
35369
|
+
} catch (error45) {
|
|
35370
|
+
console.warn(`[mandate-storage] Failed to parse semantic-memory list() output: ${error45 instanceof Error ? error45.message : String(error45)}`);
|
|
35371
|
+
return [];
|
|
35372
|
+
}
|
|
35373
|
+
}
|
|
35374
|
+
async store(entry) {
|
|
35375
|
+
await this.storeInternal(this.config.collections.mandates, entry, {
|
|
35376
|
+
id: entry.id,
|
|
35377
|
+
content_type: entry.content_type,
|
|
35378
|
+
author_agent: entry.author_agent,
|
|
35379
|
+
status: entry.status,
|
|
35380
|
+
tags: entry.tags.join(","),
|
|
35381
|
+
created_at: entry.created_at
|
|
35382
|
+
});
|
|
35383
|
+
}
|
|
35384
|
+
async get(id) {
|
|
35385
|
+
const all = await this.listInternal(this.config.collections.mandates);
|
|
35386
|
+
return all.find((entry) => entry.id === id) || null;
|
|
35387
|
+
}
|
|
35388
|
+
async find(query, limit = 10) {
|
|
35389
|
+
return this.findInternal(this.config.collections.mandates, query, limit, !this.config.useSemanticSearch);
|
|
35390
|
+
}
|
|
35391
|
+
async list(filter2) {
|
|
35392
|
+
const all = await this.listInternal(this.config.collections.mandates);
|
|
35393
|
+
if (!filter2)
|
|
35394
|
+
return all;
|
|
35395
|
+
return all.filter((entry) => {
|
|
35396
|
+
if (filter2.status && entry.status !== filter2.status)
|
|
35397
|
+
return false;
|
|
35398
|
+
if (filter2.content_type && entry.content_type !== filter2.content_type)
|
|
35399
|
+
return false;
|
|
35400
|
+
return true;
|
|
35401
|
+
});
|
|
35402
|
+
}
|
|
35403
|
+
async update(id, updates) {
|
|
35404
|
+
const existing = await this.get(id);
|
|
35405
|
+
if (!existing) {
|
|
35406
|
+
throw new Error(`Mandate ${id} not found`);
|
|
35407
|
+
}
|
|
35408
|
+
const updated = { ...existing, ...updates };
|
|
35409
|
+
await this.store(updated);
|
|
35410
|
+
}
|
|
35411
|
+
async vote(vote) {
|
|
35412
|
+
const existing = await this.hasVoted(vote.mandate_id, vote.agent_name);
|
|
35413
|
+
if (existing) {
|
|
35414
|
+
throw new Error(`Agent ${vote.agent_name} has already voted on mandate ${vote.mandate_id}`);
|
|
35415
|
+
}
|
|
35416
|
+
await this.storeInternal(this.config.collections.votes, vote, {
|
|
35417
|
+
id: vote.id,
|
|
35418
|
+
mandate_id: vote.mandate_id,
|
|
35419
|
+
agent_name: vote.agent_name,
|
|
35420
|
+
vote_type: vote.vote_type,
|
|
35421
|
+
timestamp: vote.timestamp,
|
|
35422
|
+
weight: vote.weight
|
|
35423
|
+
});
|
|
35424
|
+
}
|
|
35425
|
+
async getVotes(mandateId) {
|
|
35426
|
+
const all = await this.listInternal(this.config.collections.votes);
|
|
35427
|
+
return all.filter((vote) => vote.mandate_id === mandateId);
|
|
35428
|
+
}
|
|
35429
|
+
async hasVoted(mandateId, agentName) {
|
|
35430
|
+
const votes = await this.getVotes(mandateId);
|
|
35431
|
+
return votes.some((vote) => vote.agent_name === agentName);
|
|
35432
|
+
}
|
|
35433
|
+
async calculateScore(mandateId) {
|
|
35434
|
+
const votes = await this.getVotes(mandateId);
|
|
35435
|
+
const now = new Date;
|
|
35436
|
+
let rawUpvotes = 0;
|
|
35437
|
+
let rawDownvotes = 0;
|
|
35438
|
+
let decayedUpvotes = 0;
|
|
35439
|
+
let decayedDownvotes = 0;
|
|
35440
|
+
for (const vote of votes) {
|
|
35441
|
+
const decayed = calculateDecayedValue(vote.timestamp, now, this.config.decay.halfLifeDays);
|
|
35442
|
+
const value = vote.weight * decayed;
|
|
35443
|
+
if (vote.vote_type === "upvote") {
|
|
35444
|
+
rawUpvotes++;
|
|
35445
|
+
decayedUpvotes += value;
|
|
35446
|
+
} else {
|
|
35447
|
+
rawDownvotes++;
|
|
35448
|
+
decayedDownvotes += value;
|
|
35449
|
+
}
|
|
35450
|
+
}
|
|
35451
|
+
const totalDecayed = decayedUpvotes + decayedDownvotes;
|
|
35452
|
+
const voteRatio = totalDecayed > 0 ? decayedUpvotes / totalDecayed : 0;
|
|
35453
|
+
const netVotes = decayedUpvotes - decayedDownvotes;
|
|
35454
|
+
const decayedScore = netVotes * voteRatio;
|
|
35455
|
+
return {
|
|
35456
|
+
mandate_id: mandateId,
|
|
35457
|
+
net_votes: netVotes,
|
|
35458
|
+
vote_ratio: voteRatio,
|
|
35459
|
+
decayed_score: decayedScore,
|
|
35460
|
+
last_calculated: now.toISOString(),
|
|
35461
|
+
raw_upvotes: rawUpvotes,
|
|
35462
|
+
raw_downvotes: rawDownvotes,
|
|
35463
|
+
decayed_upvotes: decayedUpvotes,
|
|
35464
|
+
decayed_downvotes: decayedDownvotes
|
|
35465
|
+
};
|
|
35466
|
+
}
|
|
35467
|
+
async close() {}
|
|
35468
|
+
}
|
|
35469
|
+
|
|
35470
|
+
class InMemoryMandateStorage {
|
|
35471
|
+
entries = new Map;
|
|
35472
|
+
votes = new Map;
|
|
35473
|
+
config;
|
|
35474
|
+
constructor(config2 = {}) {
|
|
35475
|
+
const fullConfig = { ...DEFAULT_MANDATE_STORAGE_CONFIG, ...config2 };
|
|
35476
|
+
this.config = fullConfig.decay;
|
|
35477
|
+
}
|
|
35478
|
+
async store(entry) {
|
|
35479
|
+
this.entries.set(entry.id, entry);
|
|
35480
|
+
}
|
|
35481
|
+
async get(id) {
|
|
35482
|
+
return this.entries.get(id) || null;
|
|
35483
|
+
}
|
|
35484
|
+
async find(query, limit = 10) {
|
|
35485
|
+
const lowerQuery = query.toLowerCase();
|
|
35486
|
+
const results = Array.from(this.entries.values()).filter((entry) => entry.content.toLowerCase().includes(lowerQuery) || entry.tags.some((tag) => tag.toLowerCase().includes(lowerQuery)));
|
|
35487
|
+
return results.slice(0, limit);
|
|
35488
|
+
}
|
|
35489
|
+
async list(filter2) {
|
|
35490
|
+
let results = Array.from(this.entries.values());
|
|
35491
|
+
if (filter2) {
|
|
35492
|
+
results = results.filter((entry) => {
|
|
35493
|
+
if (filter2.status && entry.status !== filter2.status)
|
|
35494
|
+
return false;
|
|
35495
|
+
if (filter2.content_type && entry.content_type !== filter2.content_type)
|
|
35496
|
+
return false;
|
|
35497
|
+
return true;
|
|
35498
|
+
});
|
|
35499
|
+
}
|
|
35500
|
+
return results;
|
|
35501
|
+
}
|
|
35502
|
+
async update(id, updates) {
|
|
35503
|
+
const existing = await this.get(id);
|
|
35504
|
+
if (!existing) {
|
|
35505
|
+
throw new Error(`Mandate ${id} not found`);
|
|
35506
|
+
}
|
|
35507
|
+
const updated = { ...existing, ...updates };
|
|
35508
|
+
this.entries.set(id, updated);
|
|
35509
|
+
}
|
|
35510
|
+
async vote(vote) {
|
|
35511
|
+
const existing = await this.hasVoted(vote.mandate_id, vote.agent_name);
|
|
35512
|
+
if (existing) {
|
|
35513
|
+
throw new Error(`Agent ${vote.agent_name} has already voted on mandate ${vote.mandate_id}`);
|
|
35514
|
+
}
|
|
35515
|
+
this.votes.set(vote.id, vote);
|
|
35516
|
+
}
|
|
35517
|
+
async getVotes(mandateId) {
|
|
35518
|
+
return Array.from(this.votes.values()).filter((vote) => vote.mandate_id === mandateId);
|
|
35519
|
+
}
|
|
35520
|
+
async hasVoted(mandateId, agentName) {
|
|
35521
|
+
const votes = await this.getVotes(mandateId);
|
|
35522
|
+
return votes.some((vote) => vote.agent_name === agentName);
|
|
35523
|
+
}
|
|
35524
|
+
async calculateScore(mandateId) {
|
|
35525
|
+
const votes = await this.getVotes(mandateId);
|
|
35526
|
+
const now = new Date;
|
|
35527
|
+
let rawUpvotes = 0;
|
|
35528
|
+
let rawDownvotes = 0;
|
|
35529
|
+
let decayedUpvotes = 0;
|
|
35530
|
+
let decayedDownvotes = 0;
|
|
35531
|
+
for (const vote of votes) {
|
|
35532
|
+
const decayed = calculateDecayedValue(vote.timestamp, now, this.config.halfLifeDays);
|
|
35533
|
+
const value = vote.weight * decayed;
|
|
35534
|
+
if (vote.vote_type === "upvote") {
|
|
35535
|
+
rawUpvotes++;
|
|
35536
|
+
decayedUpvotes += value;
|
|
35537
|
+
} else {
|
|
35538
|
+
rawDownvotes++;
|
|
35539
|
+
decayedDownvotes += value;
|
|
35540
|
+
}
|
|
35541
|
+
}
|
|
35542
|
+
const totalDecayed = decayedUpvotes + decayedDownvotes;
|
|
35543
|
+
const voteRatio = totalDecayed > 0 ? decayedUpvotes / totalDecayed : 0;
|
|
35544
|
+
const netVotes = decayedUpvotes - decayedDownvotes;
|
|
35545
|
+
const decayedScore = netVotes * voteRatio;
|
|
35546
|
+
return {
|
|
35547
|
+
mandate_id: mandateId,
|
|
35548
|
+
net_votes: netVotes,
|
|
35549
|
+
vote_ratio: voteRatio,
|
|
35550
|
+
decayed_score: decayedScore,
|
|
35551
|
+
last_calculated: now.toISOString(),
|
|
35552
|
+
raw_upvotes: rawUpvotes,
|
|
35553
|
+
raw_downvotes: rawDownvotes,
|
|
35554
|
+
decayed_upvotes: decayedUpvotes,
|
|
35555
|
+
decayed_downvotes: decayedDownvotes
|
|
35556
|
+
};
|
|
35557
|
+
}
|
|
35558
|
+
async close() {}
|
|
35559
|
+
}
|
|
35560
|
+
function createMandateStorage(config2 = {}) {
|
|
35561
|
+
const fullConfig = { ...DEFAULT_MANDATE_STORAGE_CONFIG, ...config2 };
|
|
35562
|
+
switch (fullConfig.backend) {
|
|
35563
|
+
case "semantic-memory":
|
|
35564
|
+
return new SemanticMemoryMandateStorage(fullConfig);
|
|
35565
|
+
case "memory":
|
|
35566
|
+
return new InMemoryMandateStorage(fullConfig);
|
|
35567
|
+
default:
|
|
35568
|
+
throw new Error(`Unknown storage backend: ${fullConfig.backend}`);
|
|
35569
|
+
}
|
|
35570
|
+
}
|
|
35571
|
+
async function updateMandateStatus(mandateId, storage) {
|
|
35572
|
+
const entry = await storage.get(mandateId);
|
|
35573
|
+
if (!entry) {
|
|
35574
|
+
throw new Error(`Mandate ${mandateId} not found`);
|
|
35575
|
+
}
|
|
35576
|
+
const score = await storage.calculateScore(mandateId);
|
|
35577
|
+
const previousStatus = entry.status;
|
|
35578
|
+
let newStatus;
|
|
35579
|
+
const config2 = DEFAULT_MANDATE_DECAY_CONFIG;
|
|
35580
|
+
if (score.net_votes >= config2.mandateNetVotesThreshold && score.vote_ratio >= config2.mandateVoteRatioThreshold) {
|
|
35581
|
+
newStatus = "mandate";
|
|
35582
|
+
} else if (score.net_votes <= config2.rejectedNetVotesThreshold) {
|
|
35583
|
+
newStatus = "rejected";
|
|
35584
|
+
} else if (score.net_votes >= config2.establishedNetVotesThreshold) {
|
|
35585
|
+
newStatus = "established";
|
|
35586
|
+
} else {
|
|
35587
|
+
newStatus = "candidate";
|
|
35588
|
+
}
|
|
35589
|
+
if (newStatus !== previousStatus) {
|
|
35590
|
+
await storage.update(mandateId, { status: newStatus });
|
|
35591
|
+
}
|
|
35592
|
+
return {
|
|
35593
|
+
mandate_id: mandateId,
|
|
35594
|
+
previous_status: previousStatus,
|
|
35595
|
+
new_status: newStatus,
|
|
35596
|
+
score,
|
|
35597
|
+
status_changed: newStatus !== previousStatus
|
|
35598
|
+
};
|
|
35599
|
+
}
|
|
35600
|
+
async function updateAllMandateStatuses(storage) {
|
|
35601
|
+
const allEntries = await storage.list();
|
|
35602
|
+
const results = [];
|
|
35603
|
+
for (const entry of allEntries) {
|
|
35604
|
+
const result = await updateMandateStatus(entry.id, storage);
|
|
35605
|
+
results.push(result);
|
|
35606
|
+
}
|
|
35607
|
+
return results;
|
|
35608
|
+
}
|
|
35609
|
+
var globalMandateStorage = null;
|
|
35610
|
+
function getMandateStorage() {
|
|
35611
|
+
if (!globalMandateStorage) {
|
|
35612
|
+
globalMandateStorage = createMandateStorage();
|
|
35613
|
+
}
|
|
35614
|
+
return globalMandateStorage;
|
|
35615
|
+
}
|
|
35616
|
+
function setMandateStorage(storage) {
|
|
35617
|
+
globalMandateStorage = storage;
|
|
35618
|
+
}
|
|
35619
|
+
async function resetMandateStorage() {
|
|
35620
|
+
if (globalMandateStorage) {
|
|
35621
|
+
await globalMandateStorage.close();
|
|
35622
|
+
globalMandateStorage = null;
|
|
35623
|
+
}
|
|
35624
|
+
}
|
|
35625
|
+
|
|
35626
|
+
// src/mandate-promotion.ts
|
|
35627
|
+
function shouldPromote(score, currentStatus, config2 = DEFAULT_MANDATE_DECAY_CONFIG) {
|
|
35628
|
+
if (currentStatus === "rejected") {
|
|
35629
|
+
return "rejected";
|
|
35630
|
+
}
|
|
35631
|
+
if (currentStatus === "mandate") {
|
|
35632
|
+
return "mandate";
|
|
35633
|
+
}
|
|
35634
|
+
if (score.net_votes <= config2.rejectedNetVotesThreshold) {
|
|
35635
|
+
return "rejected";
|
|
35636
|
+
}
|
|
35637
|
+
if (currentStatus === "established") {
|
|
35638
|
+
if (score.net_votes >= config2.mandateNetVotesThreshold && score.vote_ratio >= config2.mandateVoteRatioThreshold) {
|
|
35639
|
+
return "mandate";
|
|
35640
|
+
}
|
|
35641
|
+
return "established";
|
|
35642
|
+
}
|
|
35643
|
+
if (score.net_votes >= config2.establishedNetVotesThreshold) {
|
|
35644
|
+
return "established";
|
|
35645
|
+
}
|
|
35646
|
+
return "candidate";
|
|
35647
|
+
}
|
|
35648
|
+
function evaluatePromotion(entry, score, config2 = DEFAULT_MANDATE_DECAY_CONFIG) {
|
|
35649
|
+
const previousStatus = entry.status;
|
|
35650
|
+
const newStatus = shouldPromote(score, previousStatus, config2);
|
|
35651
|
+
const promoted = newStatus !== previousStatus;
|
|
35652
|
+
let reason;
|
|
35653
|
+
if (newStatus === "rejected" && previousStatus === "rejected") {
|
|
35654
|
+
reason = `Remains rejected (permanent)`;
|
|
35655
|
+
} else if (newStatus === "rejected") {
|
|
35656
|
+
reason = `Rejected due to negative consensus (net_votes: ${score.net_votes.toFixed(2)} ≤ ${config2.rejectedNetVotesThreshold})`;
|
|
35657
|
+
} else if (newStatus === "mandate" && previousStatus === "mandate") {
|
|
35658
|
+
reason = `Remains mandate (no demotion)`;
|
|
35659
|
+
} else if (newStatus === "mandate" && previousStatus === "established") {
|
|
35660
|
+
reason = `Promoted to mandate (net_votes: ${score.net_votes.toFixed(2)} ≥ ${config2.mandateNetVotesThreshold}, ratio: ${score.vote_ratio.toFixed(2)} ≥ ${config2.mandateVoteRatioThreshold})`;
|
|
35661
|
+
} else if (newStatus === "established" && previousStatus === "established") {
|
|
35662
|
+
reason = `Remains established (net_votes: ${score.net_votes.toFixed(2)}, ratio: ${score.vote_ratio.toFixed(2)} below mandate threshold)`;
|
|
35663
|
+
} else if (newStatus === "established" && previousStatus === "candidate") {
|
|
35664
|
+
reason = `Promoted to established (net_votes: ${score.net_votes.toFixed(2)} ≥ ${config2.establishedNetVotesThreshold})`;
|
|
35665
|
+
} else if (newStatus === "candidate") {
|
|
35666
|
+
reason = `Remains candidate (net_votes: ${score.net_votes.toFixed(2)} below threshold)`;
|
|
35667
|
+
} else {
|
|
35668
|
+
reason = `No status change (current: ${previousStatus})`;
|
|
35669
|
+
}
|
|
35670
|
+
return {
|
|
35671
|
+
mandate_id: entry.id,
|
|
35672
|
+
previous_status: previousStatus,
|
|
35673
|
+
new_status: newStatus,
|
|
35674
|
+
score,
|
|
35675
|
+
promoted,
|
|
35676
|
+
reason
|
|
35677
|
+
};
|
|
35678
|
+
}
|
|
35679
|
+
function formatPromotionResult(result) {
|
|
35680
|
+
const arrow = result.promoted ? `${result.previous_status} → ${result.new_status}` : result.new_status;
|
|
35681
|
+
return `[${result.mandate_id}] ${arrow}: ${result.reason}`;
|
|
35682
|
+
}
|
|
35683
|
+
function evaluateBatchPromotions(entries, scores, config2 = DEFAULT_MANDATE_DECAY_CONFIG) {
|
|
35684
|
+
const results = [];
|
|
35685
|
+
for (const [id, entry] of entries) {
|
|
35686
|
+
const score = scores.get(id);
|
|
35687
|
+
if (!score) {
|
|
35688
|
+
continue;
|
|
35689
|
+
}
|
|
35690
|
+
const result = evaluatePromotion(entry, score, config2);
|
|
35691
|
+
results.push(result);
|
|
35692
|
+
}
|
|
35693
|
+
return results;
|
|
35694
|
+
}
|
|
35695
|
+
function getStatusChanges(results) {
|
|
35696
|
+
return results.filter((r) => r.promoted);
|
|
35697
|
+
}
|
|
35698
|
+
function groupByTransition(results) {
|
|
35699
|
+
const groups = new Map;
|
|
35700
|
+
for (const result of results) {
|
|
35701
|
+
const key = result.promoted ? `${result.previous_status}→${result.new_status}` : result.new_status;
|
|
35702
|
+
const existing = groups.get(key);
|
|
35703
|
+
if (existing) {
|
|
35704
|
+
existing.push(result);
|
|
35705
|
+
} else {
|
|
35706
|
+
groups.set(key, [result]);
|
|
35707
|
+
}
|
|
35708
|
+
}
|
|
35709
|
+
return groups;
|
|
35710
|
+
}
|
|
35711
|
+
|
|
35712
|
+
// src/mandates.ts
|
|
35713
|
+
class MandateError extends Error {
|
|
35714
|
+
operation;
|
|
35715
|
+
details;
|
|
35716
|
+
constructor(message, operation, details) {
|
|
35717
|
+
super(message);
|
|
35718
|
+
this.operation = operation;
|
|
35719
|
+
this.details = details;
|
|
35720
|
+
this.name = "MandateError";
|
|
35721
|
+
}
|
|
35722
|
+
}
|
|
35723
|
+
function generateMandateId() {
|
|
35724
|
+
const timestamp = Date.now().toString(36);
|
|
35725
|
+
const random = Math.random().toString(36).substring(2, 8);
|
|
35726
|
+
return `mandate-${timestamp}-${random}`;
|
|
35727
|
+
}
|
|
35728
|
+
function generateVoteId() {
|
|
35729
|
+
const timestamp = Date.now().toString(36);
|
|
35730
|
+
const random = Math.random().toString(36).substring(2, 8);
|
|
35731
|
+
return `vote-${timestamp}-${random}`;
|
|
35732
|
+
}
|
|
35733
|
+
var mandate_file = tool({
|
|
35734
|
+
description: "Submit a new idea, tip, lore, snippet, or feature request to the mandate system",
|
|
35735
|
+
args: {
|
|
35736
|
+
content: tool.schema.string().min(1).describe("The content to submit"),
|
|
35737
|
+
content_type: tool.schema.enum(["idea", "tip", "lore", "snippet", "feature_request"]).describe("Type of content"),
|
|
35738
|
+
tags: tool.schema.array(tool.schema.string()).optional().describe("Optional tags for categorization"),
|
|
35739
|
+
metadata: tool.schema.record(tool.schema.string(), tool.schema.unknown()).optional().describe("Optional metadata (e.g., code language for snippets)")
|
|
35740
|
+
},
|
|
35741
|
+
async execute(args) {
|
|
35742
|
+
const validated = CreateMandateArgsSchema.parse(args);
|
|
35743
|
+
const agentName = "system";
|
|
35744
|
+
const entry = {
|
|
35745
|
+
id: generateMandateId(),
|
|
35746
|
+
content: validated.content,
|
|
35747
|
+
content_type: validated.content_type,
|
|
35748
|
+
author_agent: agentName,
|
|
35749
|
+
created_at: new Date().toISOString(),
|
|
35750
|
+
status: "candidate",
|
|
35751
|
+
tags: validated.tags || [],
|
|
35752
|
+
metadata: validated.metadata
|
|
35753
|
+
};
|
|
35754
|
+
const validatedEntry = MandateEntrySchema.parse(entry);
|
|
35755
|
+
const storage = getMandateStorage();
|
|
35756
|
+
try {
|
|
35757
|
+
await storage.store(validatedEntry);
|
|
35758
|
+
} catch (error45) {
|
|
35759
|
+
throw new MandateError(`Failed to store mandate: ${error45 instanceof Error ? error45.message : String(error45)}`, "mandate_file", error45);
|
|
35760
|
+
}
|
|
35761
|
+
return JSON.stringify({
|
|
35762
|
+
success: true,
|
|
35763
|
+
mandate: validatedEntry,
|
|
35764
|
+
message: `Mandate ${validatedEntry.id} filed successfully`
|
|
35765
|
+
}, null, 2);
|
|
35766
|
+
}
|
|
35767
|
+
});
|
|
35768
|
+
var mandate_vote = tool({
|
|
35769
|
+
description: "Cast a vote (upvote or downvote) on an existing mandate",
|
|
35770
|
+
args: {
|
|
35771
|
+
mandate_id: tool.schema.string().describe("Mandate ID to vote on"),
|
|
35772
|
+
vote_type: tool.schema.enum(["upvote", "downvote"]).describe("Type of vote"),
|
|
35773
|
+
agent_name: tool.schema.string().describe("Agent name casting the vote")
|
|
35774
|
+
},
|
|
35775
|
+
async execute(args) {
|
|
35776
|
+
const validated = CastVoteArgsSchema.parse({
|
|
35777
|
+
mandate_id: args.mandate_id,
|
|
35778
|
+
vote_type: args.vote_type,
|
|
35779
|
+
weight: 1
|
|
35780
|
+
});
|
|
35781
|
+
const storage = getMandateStorage();
|
|
35782
|
+
const mandate = await storage.get(validated.mandate_id);
|
|
35783
|
+
if (!mandate) {
|
|
35784
|
+
throw new MandateError(`Mandate ${validated.mandate_id} not found`, "mandate_vote");
|
|
35785
|
+
}
|
|
35786
|
+
const hasVoted = await storage.hasVoted(validated.mandate_id, args.agent_name);
|
|
35787
|
+
if (hasVoted) {
|
|
35788
|
+
throw new MandateError(`Agent ${args.agent_name} has already voted on mandate ${validated.mandate_id}`, "mandate_vote");
|
|
35789
|
+
}
|
|
35790
|
+
const vote = {
|
|
35791
|
+
id: generateVoteId(),
|
|
35792
|
+
mandate_id: validated.mandate_id,
|
|
35793
|
+
agent_name: args.agent_name,
|
|
35794
|
+
vote_type: validated.vote_type,
|
|
35795
|
+
timestamp: new Date().toISOString(),
|
|
35796
|
+
weight: validated.weight
|
|
35797
|
+
};
|
|
35798
|
+
const validatedVote = VoteSchema.parse(vote);
|
|
35799
|
+
try {
|
|
35800
|
+
await storage.vote(validatedVote);
|
|
35801
|
+
} catch (error45) {
|
|
35802
|
+
throw new MandateError(`Failed to cast vote: ${error45 instanceof Error ? error45.message : String(error45)}`, "mandate_vote", error45);
|
|
35803
|
+
}
|
|
35804
|
+
const promotion = await updateMandateStatus(validated.mandate_id, storage);
|
|
35805
|
+
return JSON.stringify({
|
|
35806
|
+
success: true,
|
|
35807
|
+
vote: validatedVote,
|
|
35808
|
+
promotion: {
|
|
35809
|
+
previous_status: promotion.previous_status,
|
|
35810
|
+
new_status: promotion.new_status,
|
|
35811
|
+
status_changed: promotion.status_changed,
|
|
35812
|
+
score: promotion.score
|
|
35813
|
+
},
|
|
35814
|
+
message: formatPromotionResult({
|
|
35815
|
+
mandate_id: promotion.mandate_id,
|
|
35816
|
+
previous_status: promotion.previous_status,
|
|
35817
|
+
new_status: promotion.new_status,
|
|
35818
|
+
score: promotion.score,
|
|
35819
|
+
promoted: promotion.status_changed,
|
|
35820
|
+
reason: evaluatePromotion(mandate, promotion.score).reason || "Vote recorded"
|
|
35821
|
+
})
|
|
35822
|
+
}, null, 2);
|
|
35823
|
+
}
|
|
35824
|
+
});
|
|
35825
|
+
var mandate_query = tool({
|
|
35826
|
+
description: "Search for relevant mandates using semantic search (by meaning, not keywords)",
|
|
35827
|
+
args: {
|
|
35828
|
+
query: tool.schema.string().min(1).describe("Natural language query"),
|
|
35829
|
+
limit: tool.schema.number().int().positive().optional().describe("Max results to return (default: 5)"),
|
|
35830
|
+
status: tool.schema.enum(["candidate", "established", "mandate", "rejected"]).optional().describe("Filter by status"),
|
|
35831
|
+
content_type: tool.schema.enum(["idea", "tip", "lore", "snippet", "feature_request"]).optional().describe("Filter by content type")
|
|
35832
|
+
},
|
|
35833
|
+
async execute(args) {
|
|
35834
|
+
const storage = getMandateStorage();
|
|
35835
|
+
const limit = args.limit ?? 5;
|
|
35836
|
+
try {
|
|
35837
|
+
let results = await storage.find(args.query, limit * 2);
|
|
35838
|
+
if (args.status) {
|
|
35839
|
+
results = results.filter((m) => m.status === args.status);
|
|
35840
|
+
}
|
|
35841
|
+
if (args.content_type) {
|
|
35842
|
+
results = results.filter((m) => m.content_type === args.content_type);
|
|
35843
|
+
}
|
|
35844
|
+
results = results.slice(0, limit);
|
|
35845
|
+
const resultsWithScores = await Promise.all(results.map(async (mandate) => {
|
|
35846
|
+
const score = await storage.calculateScore(mandate.id);
|
|
35847
|
+
return { mandate, score };
|
|
35848
|
+
}));
|
|
35849
|
+
resultsWithScores.sort((a, b) => b.score.decayed_score - a.score.decayed_score);
|
|
35850
|
+
return JSON.stringify({
|
|
35851
|
+
query: args.query,
|
|
35852
|
+
count: resultsWithScores.length,
|
|
35853
|
+
results: resultsWithScores.map(({ mandate, score }) => ({
|
|
35854
|
+
id: mandate.id,
|
|
35855
|
+
content: mandate.content,
|
|
35856
|
+
content_type: mandate.content_type,
|
|
35857
|
+
status: mandate.status,
|
|
35858
|
+
author: mandate.author_agent,
|
|
35859
|
+
created_at: mandate.created_at,
|
|
35860
|
+
tags: mandate.tags,
|
|
35861
|
+
score: {
|
|
35862
|
+
net_votes: score.net_votes,
|
|
35863
|
+
vote_ratio: score.vote_ratio,
|
|
35864
|
+
decayed_score: score.decayed_score
|
|
35865
|
+
}
|
|
35866
|
+
}))
|
|
35867
|
+
}, null, 2);
|
|
35868
|
+
} catch (error45) {
|
|
35869
|
+
throw new MandateError(`Failed to query mandates: ${error45 instanceof Error ? error45.message : String(error45)}`, "mandate_query", error45);
|
|
35870
|
+
}
|
|
35871
|
+
}
|
|
35872
|
+
});
|
|
35873
|
+
var mandate_list = tool({
|
|
35874
|
+
description: "List mandates with optional filters (status, content type)",
|
|
35875
|
+
args: {
|
|
35876
|
+
status: tool.schema.enum(["candidate", "established", "mandate", "rejected"]).optional().describe("Filter by status"),
|
|
35877
|
+
content_type: tool.schema.enum(["idea", "tip", "lore", "snippet", "feature_request"]).optional().describe("Filter by content type"),
|
|
35878
|
+
limit: tool.schema.number().int().positive().optional().describe("Max results to return (default: 20)")
|
|
35879
|
+
},
|
|
35880
|
+
async execute(args) {
|
|
35881
|
+
const storage = getMandateStorage();
|
|
35882
|
+
const limit = args.limit ?? 20;
|
|
35883
|
+
try {
|
|
35884
|
+
let results = await storage.list({
|
|
35885
|
+
status: args.status,
|
|
35886
|
+
content_type: args.content_type
|
|
35887
|
+
});
|
|
35888
|
+
results = results.slice(0, limit);
|
|
35889
|
+
const resultsWithScores = await Promise.all(results.map(async (mandate) => {
|
|
35890
|
+
const score = await storage.calculateScore(mandate.id);
|
|
35891
|
+
return { mandate, score };
|
|
35892
|
+
}));
|
|
35893
|
+
resultsWithScores.sort((a, b) => b.score.decayed_score - a.score.decayed_score);
|
|
35894
|
+
return JSON.stringify({
|
|
35895
|
+
filters: {
|
|
35896
|
+
status: args.status || "all",
|
|
35897
|
+
content_type: args.content_type || "all"
|
|
35898
|
+
},
|
|
35899
|
+
count: resultsWithScores.length,
|
|
35900
|
+
results: resultsWithScores.map(({ mandate, score }) => ({
|
|
35901
|
+
id: mandate.id,
|
|
35902
|
+
content: mandate.content.slice(0, 200),
|
|
35903
|
+
content_type: mandate.content_type,
|
|
35904
|
+
status: mandate.status,
|
|
35905
|
+
author: mandate.author_agent,
|
|
35906
|
+
created_at: mandate.created_at,
|
|
35907
|
+
tags: mandate.tags,
|
|
35908
|
+
score: {
|
|
35909
|
+
net_votes: score.net_votes,
|
|
35910
|
+
vote_ratio: score.vote_ratio,
|
|
35911
|
+
decayed_score: score.decayed_score
|
|
35912
|
+
}
|
|
35913
|
+
}))
|
|
35914
|
+
}, null, 2);
|
|
35915
|
+
} catch (error45) {
|
|
35916
|
+
throw new MandateError(`Failed to list mandates: ${error45 instanceof Error ? error45.message : String(error45)}`, "mandate_list", error45);
|
|
35917
|
+
}
|
|
35918
|
+
}
|
|
35919
|
+
});
|
|
35920
|
+
var mandate_stats = tool({
|
|
35921
|
+
description: "Get voting statistics for a specific mandate or overall system",
|
|
35922
|
+
args: {
|
|
35923
|
+
mandate_id: tool.schema.string().optional().describe("Mandate ID (omit for overall stats)")
|
|
35924
|
+
},
|
|
35925
|
+
async execute(args) {
|
|
35926
|
+
const storage = getMandateStorage();
|
|
35927
|
+
try {
|
|
35928
|
+
if (args.mandate_id) {
|
|
35929
|
+
const mandate = await storage.get(args.mandate_id);
|
|
35930
|
+
if (!mandate) {
|
|
35931
|
+
throw new MandateError(`Mandate ${args.mandate_id} not found`, "mandate_stats");
|
|
35932
|
+
}
|
|
35933
|
+
const score = await storage.calculateScore(args.mandate_id);
|
|
35934
|
+
const votes = await storage.getVotes(args.mandate_id);
|
|
35935
|
+
return JSON.stringify({
|
|
35936
|
+
mandate_id: args.mandate_id,
|
|
35937
|
+
status: mandate.status,
|
|
35938
|
+
content_type: mandate.content_type,
|
|
35939
|
+
author: mandate.author_agent,
|
|
35940
|
+
created_at: mandate.created_at,
|
|
35941
|
+
votes: {
|
|
35942
|
+
total: votes.length,
|
|
35943
|
+
raw_upvotes: score.raw_upvotes,
|
|
35944
|
+
raw_downvotes: score.raw_downvotes,
|
|
35945
|
+
decayed_upvotes: score.decayed_upvotes,
|
|
35946
|
+
decayed_downvotes: score.decayed_downvotes,
|
|
35947
|
+
net_votes: score.net_votes,
|
|
35948
|
+
vote_ratio: score.vote_ratio,
|
|
35949
|
+
decayed_score: score.decayed_score
|
|
35950
|
+
},
|
|
35951
|
+
voters: votes.map((v) => ({
|
|
35952
|
+
agent: v.agent_name,
|
|
35953
|
+
vote_type: v.vote_type,
|
|
35954
|
+
timestamp: v.timestamp
|
|
35955
|
+
}))
|
|
35956
|
+
}, null, 2);
|
|
35957
|
+
} else {
|
|
35958
|
+
const allMandates = await storage.list();
|
|
35959
|
+
const stats = {
|
|
35960
|
+
total_mandates: allMandates.length,
|
|
35961
|
+
by_status: {
|
|
35962
|
+
candidate: 0,
|
|
35963
|
+
established: 0,
|
|
35964
|
+
mandate: 0,
|
|
35965
|
+
rejected: 0
|
|
35966
|
+
},
|
|
35967
|
+
by_content_type: {
|
|
35968
|
+
idea: 0,
|
|
35969
|
+
tip: 0,
|
|
35970
|
+
lore: 0,
|
|
35971
|
+
snippet: 0,
|
|
35972
|
+
feature_request: 0
|
|
35973
|
+
},
|
|
35974
|
+
total_votes: 0
|
|
35975
|
+
};
|
|
35976
|
+
for (const mandate of allMandates) {
|
|
35977
|
+
stats.by_status[mandate.status]++;
|
|
35978
|
+
stats.by_content_type[mandate.content_type]++;
|
|
35979
|
+
const votes = await storage.getVotes(mandate.id);
|
|
35980
|
+
stats.total_votes += votes.length;
|
|
35981
|
+
}
|
|
35982
|
+
return JSON.stringify(stats, null, 2);
|
|
35983
|
+
}
|
|
35984
|
+
} catch (error45) {
|
|
35985
|
+
if (error45 instanceof MandateError) {
|
|
35986
|
+
throw error45;
|
|
35987
|
+
}
|
|
35988
|
+
throw new MandateError(`Failed to get mandate stats: ${error45 instanceof Error ? error45.message : String(error45)}`, "mandate_stats", error45);
|
|
35989
|
+
}
|
|
35990
|
+
}
|
|
35991
|
+
});
|
|
35992
|
+
var mandateTools = {
|
|
35993
|
+
mandate_file,
|
|
35994
|
+
mandate_vote,
|
|
35995
|
+
mandate_query,
|
|
35996
|
+
mandate_list,
|
|
35997
|
+
mandate_stats
|
|
35998
|
+
};
|
|
34740
35999
|
// src/anti-patterns.ts
|
|
34741
36000
|
init_zod();
|
|
34742
36001
|
var PatternKindSchema = exports_external.enum(["pattern", "anti_pattern"]);
|
|
@@ -34828,21 +36087,21 @@ class InMemoryMaturityStorage {
|
|
|
34828
36087
|
}
|
|
34829
36088
|
|
|
34830
36089
|
// src/storage.ts
|
|
34831
|
-
var
|
|
34832
|
-
async function
|
|
34833
|
-
if (
|
|
34834
|
-
return
|
|
36090
|
+
var cachedCommand2 = null;
|
|
36091
|
+
async function resolveSemanticMemoryCommand2() {
|
|
36092
|
+
if (cachedCommand2)
|
|
36093
|
+
return cachedCommand2;
|
|
34835
36094
|
const nativeResult = await Bun.$`which semantic-memory`.quiet().nothrow();
|
|
34836
36095
|
if (nativeResult.exitCode === 0) {
|
|
34837
|
-
|
|
34838
|
-
return
|
|
36096
|
+
cachedCommand2 = ["semantic-memory"];
|
|
36097
|
+
return cachedCommand2;
|
|
34839
36098
|
}
|
|
34840
|
-
|
|
34841
|
-
return
|
|
36099
|
+
cachedCommand2 = ["bunx", "semantic-memory"];
|
|
36100
|
+
return cachedCommand2;
|
|
34842
36101
|
}
|
|
34843
|
-
async function
|
|
36102
|
+
async function execSemanticMemory2(args) {
|
|
34844
36103
|
try {
|
|
34845
|
-
const cmd = await
|
|
36104
|
+
const cmd = await resolveSemanticMemoryCommand2();
|
|
34846
36105
|
const fullCmd = [...cmd, ...args];
|
|
34847
36106
|
const proc = Bun.spawn(fullCmd, {
|
|
34848
36107
|
stdout: "pipe",
|
|
@@ -34886,7 +36145,7 @@ class SemanticMemoryStorage {
|
|
|
34886
36145
|
if (metadata) {
|
|
34887
36146
|
args.push("--metadata", JSON.stringify(metadata));
|
|
34888
36147
|
}
|
|
34889
|
-
await
|
|
36148
|
+
await execSemanticMemory2(args);
|
|
34890
36149
|
}
|
|
34891
36150
|
async find(collection, query, limit = 10, useFts = false) {
|
|
34892
36151
|
const args = [
|
|
@@ -34901,7 +36160,7 @@ class SemanticMemoryStorage {
|
|
|
34901
36160
|
if (useFts) {
|
|
34902
36161
|
args.push("--fts");
|
|
34903
36162
|
}
|
|
34904
|
-
const result = await
|
|
36163
|
+
const result = await execSemanticMemory2(args);
|
|
34905
36164
|
if (result.exitCode !== 0) {
|
|
34906
36165
|
console.warn(`[storage] semantic-memory find() failed with exit code ${result.exitCode}: ${result.stderr.toString().trim()}`);
|
|
34907
36166
|
return [];
|
|
@@ -34926,7 +36185,7 @@ class SemanticMemoryStorage {
|
|
|
34926
36185
|
}
|
|
34927
36186
|
}
|
|
34928
36187
|
async list(collection) {
|
|
34929
|
-
const result = await
|
|
36188
|
+
const result = await execSemanticMemory2([
|
|
34930
36189
|
"list",
|
|
34931
36190
|
"--collection",
|
|
34932
36191
|
collection,
|
|
@@ -35111,7 +36370,7 @@ function createStorage(config2 = {}) {
|
|
|
35111
36370
|
}
|
|
35112
36371
|
async function isSemanticMemoryAvailable() {
|
|
35113
36372
|
try {
|
|
35114
|
-
const result = await
|
|
36373
|
+
const result = await execSemanticMemory2(["stats"]);
|
|
35115
36374
|
return result.exitCode === 0;
|
|
35116
36375
|
} catch {
|
|
35117
36376
|
return false;
|
|
@@ -35196,7 +36455,8 @@ var SwarmPlugin = async (input) => {
|
|
|
35196
36455
|
...structuredTools,
|
|
35197
36456
|
...swarmTools,
|
|
35198
36457
|
...repoCrawlTools,
|
|
35199
|
-
...skillsTools
|
|
36458
|
+
...skillsTools,
|
|
36459
|
+
...mandateTools
|
|
35200
36460
|
},
|
|
35201
36461
|
event: async ({ event }) => {
|
|
35202
36462
|
if (event.type === "session.idle") {
|
|
@@ -35243,27 +36503,35 @@ var allTools = {
|
|
|
35243
36503
|
...structuredTools,
|
|
35244
36504
|
...swarmTools,
|
|
35245
36505
|
...repoCrawlTools,
|
|
35246
|
-
...skillsTools
|
|
36506
|
+
...skillsTools,
|
|
36507
|
+
...mandateTools
|
|
35247
36508
|
};
|
|
35248
36509
|
export {
|
|
35249
36510
|
withToolFallback,
|
|
35250
36511
|
warnMissingTool,
|
|
36512
|
+
updateMandateStatus,
|
|
36513
|
+
updateAllMandateStatuses,
|
|
35251
36514
|
swarmTools,
|
|
35252
36515
|
swarmMailTools,
|
|
35253
36516
|
structuredTools,
|
|
35254
36517
|
skillsTools,
|
|
36518
|
+
shouldPromote,
|
|
35255
36519
|
setSwarmMailProjectDirectory,
|
|
35256
36520
|
setStorage,
|
|
35257
36521
|
setSkillsProjectDirectory,
|
|
36522
|
+
setMandateStorage,
|
|
35258
36523
|
setBeadsWorkingDirectory,
|
|
35259
36524
|
setAgentMailProjectDirectory,
|
|
35260
36525
|
selectStrategy,
|
|
35261
36526
|
resetToolCache,
|
|
35262
36527
|
resetStorage,
|
|
36528
|
+
resetMandateStorage,
|
|
35263
36529
|
requireTool,
|
|
35264
36530
|
repoCrawlTools,
|
|
35265
36531
|
parseFrontmatter,
|
|
35266
36532
|
mcpCallWithAutoInit,
|
|
36533
|
+
mandateTools,
|
|
36534
|
+
mandateSchemas,
|
|
35267
36535
|
listSkills,
|
|
35268
36536
|
isToolAvailable,
|
|
35269
36537
|
isSemanticMemoryAvailable,
|
|
@@ -35271,12 +36539,15 @@ export {
|
|
|
35271
36539
|
isAgentNotFoundError,
|
|
35272
36540
|
invalidateSkillsCache,
|
|
35273
36541
|
ifToolAvailable,
|
|
36542
|
+
groupByTransition,
|
|
35274
36543
|
getToolAvailability,
|
|
35275
36544
|
getSwarmMailProjectDirectory,
|
|
35276
36545
|
getStorage,
|
|
36546
|
+
getStatusChanges,
|
|
35277
36547
|
getSkillsContextForSwarm,
|
|
35278
36548
|
getSkill,
|
|
35279
36549
|
getSchemaByName,
|
|
36550
|
+
getMandateStorage,
|
|
35280
36551
|
getBeadsWorkingDirectory,
|
|
35281
36552
|
getAgentMailProjectDirectory,
|
|
35282
36553
|
formatZodErrors,
|
|
@@ -35284,13 +36555,17 @@ export {
|
|
|
35284
36555
|
formatSubtaskPromptV2,
|
|
35285
36556
|
formatSubtaskPrompt,
|
|
35286
36557
|
formatStrategyGuidelines,
|
|
36558
|
+
formatPromotionResult,
|
|
35287
36559
|
formatEvaluationPrompt,
|
|
35288
36560
|
findRelevantSkills,
|
|
35289
36561
|
extractJsonFromText,
|
|
36562
|
+
evaluatePromotion,
|
|
36563
|
+
evaluateBatchPromotions,
|
|
35290
36564
|
discoverSkills,
|
|
35291
36565
|
src_default as default,
|
|
35292
36566
|
createStorageWithFallback,
|
|
35293
36567
|
createStorage,
|
|
36568
|
+
createMandateStorage,
|
|
35294
36569
|
createAgentMailError,
|
|
35295
36570
|
clearSessionState,
|
|
35296
36571
|
checkTool,
|
|
@@ -35309,6 +36584,8 @@ export {
|
|
|
35309
36584
|
agentMailTools,
|
|
35310
36585
|
WeightedEvaluationSchema,
|
|
35311
36586
|
WeightedCriterionEvaluationSchema,
|
|
36587
|
+
VoteTypeSchema,
|
|
36588
|
+
VoteSchema,
|
|
35312
36589
|
ValidationResultSchema,
|
|
35313
36590
|
TaskDecompositionSchema,
|
|
35314
36591
|
SwarmStatusSchema,
|
|
@@ -35320,10 +36597,19 @@ export {
|
|
|
35320
36597
|
SubtaskDependencySchema,
|
|
35321
36598
|
SpawnedAgentSchema,
|
|
35322
36599
|
SemanticMemoryStorage,
|
|
36600
|
+
SemanticMemoryMandateStorage,
|
|
36601
|
+
ScoreCalculationResultSchema,
|
|
35323
36602
|
SUBTASK_PROMPT_V2,
|
|
35324
36603
|
STRATEGIES,
|
|
35325
36604
|
RepoCrawlError,
|
|
36605
|
+
QueryMandatesArgsSchema,
|
|
36606
|
+
MandateStatusSchema,
|
|
36607
|
+
MandateScoreSchema,
|
|
36608
|
+
MandateError,
|
|
36609
|
+
MandateEntrySchema,
|
|
36610
|
+
MandateContentTypeSchema,
|
|
35326
36611
|
InMemoryStorage,
|
|
36612
|
+
InMemoryMandateStorage,
|
|
35327
36613
|
FileReservationConflictError,
|
|
35328
36614
|
EvaluationSchema,
|
|
35329
36615
|
EvaluationRequestSchema,
|
|
@@ -35335,8 +36621,12 @@ export {
|
|
|
35335
36621
|
DecomposedSubtaskSchema,
|
|
35336
36622
|
DecomposeArgsSchema,
|
|
35337
36623
|
DEFAULT_STORAGE_CONFIG,
|
|
36624
|
+
DEFAULT_MANDATE_STORAGE_CONFIG,
|
|
36625
|
+
DEFAULT_MANDATE_DECAY_CONFIG,
|
|
35338
36626
|
DEFAULT_CRITERIA,
|
|
35339
36627
|
CriterionEvaluationSchema,
|
|
36628
|
+
CreateMandateArgsSchema,
|
|
36629
|
+
CastVoteArgsSchema,
|
|
35340
36630
|
BeadValidationError,
|
|
35341
36631
|
BeadUpdateArgsSchema,
|
|
35342
36632
|
BeadTypeSchema,
|