opencode-swarm-plugin 0.20.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.beads/issues.jsonl +213 -0
- package/INTEGRATION_EXAMPLE.md +66 -0
- package/README.md +352 -522
- package/dist/index.js +2046 -984
- package/dist/plugin.js +2051 -1017
- package/docs/analysis/subagent-coordination-patterns.md +2 -0
- package/docs/semantic-memory-cli-syntax.md +123 -0
- package/docs/swarm-mail-architecture.md +1147 -0
- package/evals/README.md +116 -0
- package/evals/evalite.config.ts +15 -0
- package/evals/example.eval.ts +32 -0
- package/evals/fixtures/decomposition-cases.ts +105 -0
- package/evals/lib/data-loader.test.ts +288 -0
- package/evals/lib/data-loader.ts +111 -0
- package/evals/lib/llm.ts +115 -0
- package/evals/scorers/index.ts +200 -0
- package/evals/scorers/outcome-scorers.test.ts +27 -0
- package/evals/scorers/outcome-scorers.ts +349 -0
- package/evals/swarm-decomposition.eval.ts +112 -0
- package/package.json +8 -1
- package/scripts/cleanup-test-memories.ts +346 -0
- package/src/beads.ts +49 -0
- package/src/eval-capture.ts +487 -0
- package/src/index.ts +45 -3
- package/src/learning.integration.test.ts +19 -4
- package/src/output-guardrails.test.ts +438 -0
- package/src/output-guardrails.ts +381 -0
- package/src/schemas/index.ts +18 -0
- package/src/schemas/swarm-context.ts +115 -0
- package/src/storage.ts +117 -5
- package/src/streams/events.test.ts +296 -0
- package/src/streams/events.ts +93 -0
- package/src/streams/migrations.test.ts +24 -20
- package/src/streams/migrations.ts +51 -0
- package/src/streams/projections.ts +187 -0
- package/src/streams/store.ts +275 -0
- package/src/swarm-orchestrate.ts +771 -189
- package/src/swarm-prompts.ts +84 -12
- package/src/swarm.integration.test.ts +124 -0
- package/vitest.integration.config.ts +6 -0
- package/vitest.integration.setup.ts +48 -0
package/dist/plugin.js
CHANGED
|
@@ -12675,7 +12675,7 @@ var init_zod = __esm(() => {
|
|
|
12675
12675
|
init_external();
|
|
12676
12676
|
});
|
|
12677
12677
|
|
|
12678
|
-
// node_modules/.pnpm/@opencode-ai+plugin@1.0.
|
|
12678
|
+
// node_modules/.pnpm/@opencode-ai+plugin@1.0.152/node_modules/@opencode-ai/plugin/dist/tool.js
|
|
12679
12679
|
function tool(input) {
|
|
12680
12680
|
return input;
|
|
12681
12681
|
}
|
|
@@ -12684,7 +12684,7 @@ var init_tool = __esm(() => {
|
|
|
12684
12684
|
tool.schema = exports_external;
|
|
12685
12685
|
});
|
|
12686
12686
|
|
|
12687
|
-
// node_modules/.pnpm/@opencode-ai+plugin@1.0.
|
|
12687
|
+
// node_modules/.pnpm/@opencode-ai+plugin@1.0.152/node_modules/@opencode-ai/plugin/dist/index.js
|
|
12688
12688
|
var init_dist = __esm(() => {
|
|
12689
12689
|
init_tool();
|
|
12690
12690
|
});
|
|
@@ -12705,7 +12705,7 @@ function createEvent(type, data) {
|
|
|
12705
12705
|
function isEventType(event, type) {
|
|
12706
12706
|
return event.type === type;
|
|
12707
12707
|
}
|
|
12708
|
-
var BaseEventSchema, AgentRegisteredEventSchema, AgentActiveEventSchema, MessageSentEventSchema, MessageReadEventSchema, MessageAckedEventSchema, FileReservedEventSchema, FileReleasedEventSchema, TaskStartedEventSchema, TaskProgressEventSchema, TaskCompletedEventSchema, TaskBlockedEventSchema, AgentEventSchema;
|
|
12708
|
+
var BaseEventSchema, AgentRegisteredEventSchema, AgentActiveEventSchema, MessageSentEventSchema, MessageReadEventSchema, MessageAckedEventSchema, FileReservedEventSchema, FileReleasedEventSchema, TaskStartedEventSchema, TaskProgressEventSchema, TaskCompletedEventSchema, TaskBlockedEventSchema, DecompositionGeneratedEventSchema, SubtaskOutcomeEventSchema, HumanFeedbackEventSchema, SwarmCheckpointedEventSchema, SwarmRecoveredEventSchema, AgentEventSchema;
|
|
12709
12709
|
var init_events = __esm(() => {
|
|
12710
12710
|
init_zod();
|
|
12711
12711
|
BaseEventSchema = exports_external.object({
|
|
@@ -12791,6 +12791,68 @@ var init_events = __esm(() => {
|
|
|
12791
12791
|
bead_id: exports_external.string(),
|
|
12792
12792
|
reason: exports_external.string()
|
|
12793
12793
|
});
|
|
12794
|
+
DecompositionGeneratedEventSchema = BaseEventSchema.extend({
|
|
12795
|
+
type: exports_external.literal("decomposition_generated"),
|
|
12796
|
+
epic_id: exports_external.string(),
|
|
12797
|
+
task: exports_external.string(),
|
|
12798
|
+
context: exports_external.string().optional(),
|
|
12799
|
+
strategy: exports_external.enum(["file-based", "feature-based", "risk-based"]),
|
|
12800
|
+
epic_title: exports_external.string(),
|
|
12801
|
+
subtasks: exports_external.array(exports_external.object({
|
|
12802
|
+
title: exports_external.string(),
|
|
12803
|
+
files: exports_external.array(exports_external.string()),
|
|
12804
|
+
priority: exports_external.number().min(0).max(3).optional()
|
|
12805
|
+
})),
|
|
12806
|
+
recovery_context: exports_external.object({
|
|
12807
|
+
shared_context: exports_external.string().optional(),
|
|
12808
|
+
skills_to_load: exports_external.array(exports_external.string()).optional(),
|
|
12809
|
+
coordinator_notes: exports_external.string().optional()
|
|
12810
|
+
}).optional()
|
|
12811
|
+
});
|
|
12812
|
+
SubtaskOutcomeEventSchema = BaseEventSchema.extend({
|
|
12813
|
+
type: exports_external.literal("subtask_outcome"),
|
|
12814
|
+
epic_id: exports_external.string(),
|
|
12815
|
+
bead_id: exports_external.string(),
|
|
12816
|
+
planned_files: exports_external.array(exports_external.string()),
|
|
12817
|
+
actual_files: exports_external.array(exports_external.string()),
|
|
12818
|
+
duration_ms: exports_external.number().min(0),
|
|
12819
|
+
error_count: exports_external.number().min(0).default(0),
|
|
12820
|
+
retry_count: exports_external.number().min(0).default(0),
|
|
12821
|
+
success: exports_external.boolean()
|
|
12822
|
+
});
|
|
12823
|
+
HumanFeedbackEventSchema = BaseEventSchema.extend({
|
|
12824
|
+
type: exports_external.literal("human_feedback"),
|
|
12825
|
+
epic_id: exports_external.string(),
|
|
12826
|
+
accepted: exports_external.boolean(),
|
|
12827
|
+
modified: exports_external.boolean().default(false),
|
|
12828
|
+
notes: exports_external.string().optional()
|
|
12829
|
+
});
|
|
12830
|
+
SwarmCheckpointedEventSchema = BaseEventSchema.extend({
|
|
12831
|
+
type: exports_external.literal("swarm_checkpointed"),
|
|
12832
|
+
epic_id: exports_external.string(),
|
|
12833
|
+
bead_id: exports_external.string(),
|
|
12834
|
+
strategy: exports_external.enum(["file-based", "feature-based", "risk-based"]),
|
|
12835
|
+
files: exports_external.array(exports_external.string()),
|
|
12836
|
+
dependencies: exports_external.array(exports_external.string()),
|
|
12837
|
+
directives: exports_external.object({
|
|
12838
|
+
shared_context: exports_external.string().optional(),
|
|
12839
|
+
skills_to_load: exports_external.array(exports_external.string()).optional(),
|
|
12840
|
+
coordinator_notes: exports_external.string().optional()
|
|
12841
|
+
}),
|
|
12842
|
+
recovery: exports_external.object({
|
|
12843
|
+
last_checkpoint: exports_external.number(),
|
|
12844
|
+
files_modified: exports_external.array(exports_external.string()),
|
|
12845
|
+
progress_percent: exports_external.number().min(0).max(100),
|
|
12846
|
+
last_message: exports_external.string().optional(),
|
|
12847
|
+
error_context: exports_external.string().optional()
|
|
12848
|
+
})
|
|
12849
|
+
});
|
|
12850
|
+
SwarmRecoveredEventSchema = BaseEventSchema.extend({
|
|
12851
|
+
type: exports_external.literal("swarm_recovered"),
|
|
12852
|
+
epic_id: exports_external.string(),
|
|
12853
|
+
bead_id: exports_external.string(),
|
|
12854
|
+
recovered_from_checkpoint: exports_external.number()
|
|
12855
|
+
});
|
|
12794
12856
|
AgentEventSchema = exports_external.discriminatedUnion("type", [
|
|
12795
12857
|
AgentRegisteredEventSchema,
|
|
12796
12858
|
AgentActiveEventSchema,
|
|
@@ -12802,609 +12864,206 @@ var init_events = __esm(() => {
|
|
|
12802
12864
|
TaskStartedEventSchema,
|
|
12803
12865
|
TaskProgressEventSchema,
|
|
12804
12866
|
TaskCompletedEventSchema,
|
|
12805
|
-
TaskBlockedEventSchema
|
|
12867
|
+
TaskBlockedEventSchema,
|
|
12868
|
+
DecompositionGeneratedEventSchema,
|
|
12869
|
+
SubtaskOutcomeEventSchema,
|
|
12870
|
+
HumanFeedbackEventSchema,
|
|
12871
|
+
SwarmCheckpointedEventSchema,
|
|
12872
|
+
SwarmRecoveredEventSchema
|
|
12806
12873
|
]);
|
|
12807
12874
|
});
|
|
12808
12875
|
|
|
12809
|
-
//
|
|
12810
|
-
|
|
12811
|
-
const
|
|
12812
|
-
|
|
12813
|
-
|
|
12876
|
+
// node_modules/.pnpm/@isaacs+balanced-match@4.0.1/node_modules/@isaacs/balanced-match/dist/esm/index.js
|
|
12877
|
+
var balanced = (a, b, str2) => {
|
|
12878
|
+
const ma = a instanceof RegExp ? maybeMatch(a, str2) : a;
|
|
12879
|
+
const mb = b instanceof RegExp ? maybeMatch(b, str2) : b;
|
|
12880
|
+
const r = ma !== null && mb != null && range(ma, mb, str2);
|
|
12881
|
+
return r && {
|
|
12882
|
+
start: r[0],
|
|
12883
|
+
end: r[1],
|
|
12884
|
+
pre: str2.slice(0, r[0]),
|
|
12885
|
+
body: str2.slice(r[0] + ma.length, r[1]),
|
|
12886
|
+
post: str2.slice(r[1] + mb.length)
|
|
12887
|
+
};
|
|
12888
|
+
}, maybeMatch = (reg, str2) => {
|
|
12889
|
+
const m = str2.match(reg);
|
|
12890
|
+
return m ? m[0] : null;
|
|
12891
|
+
}, range = (a, b, str2) => {
|
|
12892
|
+
let begs, beg, left, right = undefined, result;
|
|
12893
|
+
let ai = str2.indexOf(a);
|
|
12894
|
+
let bi = str2.indexOf(b, ai + 1);
|
|
12895
|
+
let i = ai;
|
|
12896
|
+
if (ai >= 0 && bi > 0) {
|
|
12897
|
+
if (a === b) {
|
|
12898
|
+
return [ai, bi];
|
|
12899
|
+
}
|
|
12900
|
+
begs = [];
|
|
12901
|
+
left = str2.length;
|
|
12902
|
+
while (i >= 0 && !result) {
|
|
12903
|
+
if (i === ai) {
|
|
12904
|
+
begs.push(i);
|
|
12905
|
+
ai = str2.indexOf(a, i + 1);
|
|
12906
|
+
} else if (begs.length === 1) {
|
|
12907
|
+
const r = begs.pop();
|
|
12908
|
+
if (r !== undefined)
|
|
12909
|
+
result = [r, bi];
|
|
12910
|
+
} else {
|
|
12911
|
+
beg = begs.pop();
|
|
12912
|
+
if (beg !== undefined && beg < left) {
|
|
12913
|
+
left = beg;
|
|
12914
|
+
right = bi;
|
|
12915
|
+
}
|
|
12916
|
+
bi = str2.indexOf(b, i + 1);
|
|
12917
|
+
}
|
|
12918
|
+
i = ai < bi && ai >= 0 ? ai : bi;
|
|
12919
|
+
}
|
|
12920
|
+
if (begs.length && right !== undefined) {
|
|
12921
|
+
result = [left, right];
|
|
12922
|
+
}
|
|
12814
12923
|
}
|
|
12815
|
-
|
|
12816
|
-
|
|
12924
|
+
return result;
|
|
12925
|
+
};
|
|
12926
|
+
|
|
12927
|
+
// node_modules/.pnpm/@isaacs+brace-expansion@5.0.0/node_modules/@isaacs/brace-expansion/dist/esm/index.js
|
|
12928
|
+
function numeric(str2) {
|
|
12929
|
+
return !isNaN(str2) ? parseInt(str2, 10) : str2.charCodeAt(0);
|
|
12930
|
+
}
|
|
12931
|
+
function escapeBraces(str2) {
|
|
12932
|
+
return str2.replace(slashPattern, escSlash).replace(openPattern, escOpen).replace(closePattern, escClose).replace(commaPattern, escComma).replace(periodPattern, escPeriod);
|
|
12933
|
+
}
|
|
12934
|
+
function unescapeBraces(str2) {
|
|
12935
|
+
return str2.replace(escSlashPattern, "\\").replace(escOpenPattern, "{").replace(escClosePattern, "}").replace(escCommaPattern, ",").replace(escPeriodPattern, ".");
|
|
12936
|
+
}
|
|
12937
|
+
function parseCommaParts(str2) {
|
|
12938
|
+
if (!str2) {
|
|
12939
|
+
return [""];
|
|
12817
12940
|
}
|
|
12818
|
-
|
|
12941
|
+
const parts = [];
|
|
12942
|
+
const m = balanced("{", "}", str2);
|
|
12943
|
+
if (!m) {
|
|
12944
|
+
return str2.split(",");
|
|
12945
|
+
}
|
|
12946
|
+
const { pre, body, post } = m;
|
|
12947
|
+
const p = pre.split(",");
|
|
12948
|
+
p[p.length - 1] += "{" + body + "}";
|
|
12949
|
+
const postParts = parseCommaParts(post);
|
|
12950
|
+
if (post.length) {
|
|
12951
|
+
p[p.length - 1] += postParts.shift();
|
|
12952
|
+
p.push.apply(p, postParts);
|
|
12953
|
+
}
|
|
12954
|
+
parts.push.apply(parts, p);
|
|
12955
|
+
return parts;
|
|
12819
12956
|
}
|
|
12820
|
-
|
|
12821
|
-
|
|
12822
|
-
|
|
12823
|
-
console.log("[SwarmMail] Appending event", {
|
|
12824
|
-
type,
|
|
12825
|
-
projectKey: project_key,
|
|
12826
|
-
timestamp
|
|
12827
|
-
});
|
|
12828
|
-
const result = await db.query(`INSERT INTO events (type, project_key, timestamp, data)
|
|
12829
|
-
VALUES ($1, $2, $3, $4)
|
|
12830
|
-
RETURNING id, sequence`, [type, project_key, timestamp, JSON.stringify(rest)]);
|
|
12831
|
-
const row = result.rows[0];
|
|
12832
|
-
if (!row) {
|
|
12833
|
-
throw new Error("Failed to insert event - no row returned");
|
|
12957
|
+
function expand(str2) {
|
|
12958
|
+
if (!str2) {
|
|
12959
|
+
return [];
|
|
12834
12960
|
}
|
|
12835
|
-
|
|
12836
|
-
|
|
12837
|
-
|
|
12838
|
-
|
|
12839
|
-
sequence,
|
|
12840
|
-
projectKey: project_key
|
|
12841
|
-
});
|
|
12842
|
-
console.debug("[SwarmMail] Updating materialized views", { type, id });
|
|
12843
|
-
await updateMaterializedViews(db, { ...event, id, sequence });
|
|
12844
|
-
return { ...event, id, sequence };
|
|
12961
|
+
if (str2.slice(0, 2) === "{}") {
|
|
12962
|
+
str2 = "\\{\\}" + str2.slice(2);
|
|
12963
|
+
}
|
|
12964
|
+
return expand_(escapeBraces(str2), true).map(unescapeBraces);
|
|
12845
12965
|
}
|
|
12846
|
-
|
|
12847
|
-
return
|
|
12848
|
-
|
|
12849
|
-
|
|
12850
|
-
|
|
12851
|
-
|
|
12852
|
-
|
|
12853
|
-
|
|
12854
|
-
|
|
12855
|
-
|
|
12856
|
-
|
|
12857
|
-
|
|
12858
|
-
|
|
12859
|
-
|
|
12966
|
+
function embrace(str2) {
|
|
12967
|
+
return "{" + str2 + "}";
|
|
12968
|
+
}
|
|
12969
|
+
function isPadded(el) {
|
|
12970
|
+
return /^-?0\d/.test(el);
|
|
12971
|
+
}
|
|
12972
|
+
function lte(i, y) {
|
|
12973
|
+
return i <= y;
|
|
12974
|
+
}
|
|
12975
|
+
function gte(i, y) {
|
|
12976
|
+
return i >= y;
|
|
12977
|
+
}
|
|
12978
|
+
function expand_(str2, isTop) {
|
|
12979
|
+
const expansions = [];
|
|
12980
|
+
const m = balanced("{", "}", str2);
|
|
12981
|
+
if (!m)
|
|
12982
|
+
return [str2];
|
|
12983
|
+
const pre = m.pre;
|
|
12984
|
+
const post = m.post.length ? expand_(m.post, false) : [""];
|
|
12985
|
+
if (/\$$/.test(m.pre)) {
|
|
12986
|
+
for (let k = 0;k < post.length; k++) {
|
|
12987
|
+
const expansion = pre + "{" + m.body + "}" + post[k];
|
|
12988
|
+
expansions.push(expansion);
|
|
12989
|
+
}
|
|
12990
|
+
} else {
|
|
12991
|
+
const isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
|
|
12992
|
+
const isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
|
|
12993
|
+
const isSequence = isNumericSequence || isAlphaSequence;
|
|
12994
|
+
const isOptions = m.body.indexOf(",") >= 0;
|
|
12995
|
+
if (!isSequence && !isOptions) {
|
|
12996
|
+
if (m.post.match(/,(?!,).*\}/)) {
|
|
12997
|
+
str2 = m.pre + "{" + m.body + escClose + m.post;
|
|
12998
|
+
return expand_(str2);
|
|
12999
|
+
}
|
|
13000
|
+
return [str2];
|
|
13001
|
+
}
|
|
13002
|
+
let n;
|
|
13003
|
+
if (isSequence) {
|
|
13004
|
+
n = m.body.split(/\.\./);
|
|
13005
|
+
} else {
|
|
13006
|
+
n = parseCommaParts(m.body);
|
|
13007
|
+
if (n.length === 1 && n[0] !== undefined) {
|
|
13008
|
+
n = expand_(n[0], false).map(embrace);
|
|
13009
|
+
if (n.length === 1) {
|
|
13010
|
+
return post.map((p) => m.pre + n[0] + p);
|
|
12860
13011
|
}
|
|
12861
|
-
const { id, sequence } = row;
|
|
12862
|
-
const enrichedEvent = { ...event, id, sequence };
|
|
12863
|
-
await updateMaterializedViews(db, enrichedEvent);
|
|
12864
|
-
results.push(enrichedEvent);
|
|
12865
13012
|
}
|
|
12866
|
-
|
|
12867
|
-
|
|
12868
|
-
|
|
12869
|
-
|
|
12870
|
-
|
|
12871
|
-
|
|
12872
|
-
|
|
12873
|
-
|
|
13013
|
+
}
|
|
13014
|
+
let N;
|
|
13015
|
+
if (isSequence && n[0] !== undefined && n[1] !== undefined) {
|
|
13016
|
+
const x = numeric(n[0]);
|
|
13017
|
+
const y = numeric(n[1]);
|
|
13018
|
+
const width = Math.max(n[0].length, n[1].length);
|
|
13019
|
+
let incr = n.length === 3 && n[2] !== undefined ? Math.abs(numeric(n[2])) : 1;
|
|
13020
|
+
let test = lte;
|
|
13021
|
+
const reverse = y < x;
|
|
13022
|
+
if (reverse) {
|
|
13023
|
+
incr *= -1;
|
|
13024
|
+
test = gte;
|
|
12874
13025
|
}
|
|
12875
|
-
|
|
12876
|
-
|
|
12877
|
-
|
|
12878
|
-
|
|
12879
|
-
|
|
13026
|
+
const pad = n.some(isPadded);
|
|
13027
|
+
N = [];
|
|
13028
|
+
for (let i = x;test(i, y); i += incr) {
|
|
13029
|
+
let c;
|
|
13030
|
+
if (isAlphaSequence) {
|
|
13031
|
+
c = String.fromCharCode(i);
|
|
13032
|
+
if (c === "\\") {
|
|
13033
|
+
c = "";
|
|
13034
|
+
}
|
|
13035
|
+
} else {
|
|
13036
|
+
c = String(i);
|
|
13037
|
+
if (pad) {
|
|
13038
|
+
const need = width - c.length;
|
|
13039
|
+
if (need > 0) {
|
|
13040
|
+
const z = new Array(need + 1).join("0");
|
|
13041
|
+
if (i < 0) {
|
|
13042
|
+
c = "-" + z + c.slice(1);
|
|
13043
|
+
} else {
|
|
13044
|
+
c = z + c;
|
|
13045
|
+
}
|
|
13046
|
+
}
|
|
13047
|
+
}
|
|
13048
|
+
}
|
|
13049
|
+
N.push(c);
|
|
13050
|
+
}
|
|
13051
|
+
} else {
|
|
13052
|
+
N = [];
|
|
13053
|
+
for (let j = 0;j < n.length; j++) {
|
|
13054
|
+
N.push.apply(N, expand_(n[j], false));
|
|
12880
13055
|
}
|
|
12881
|
-
throw e;
|
|
12882
13056
|
}
|
|
12883
|
-
|
|
12884
|
-
|
|
12885
|
-
|
|
12886
|
-
|
|
12887
|
-
|
|
12888
|
-
|
|
12889
|
-
|
|
12890
|
-
|
|
12891
|
-
|
|
12892
|
-
|
|
12893
|
-
conditions.push(`project_key = $${paramIndex++}`);
|
|
12894
|
-
params.push(options2.projectKey);
|
|
12895
|
-
}
|
|
12896
|
-
if (options2.types && options2.types.length > 0) {
|
|
12897
|
-
conditions.push(`type = ANY($${paramIndex++})`);
|
|
12898
|
-
params.push(options2.types);
|
|
12899
|
-
}
|
|
12900
|
-
if (options2.since !== undefined) {
|
|
12901
|
-
conditions.push(`timestamp >= $${paramIndex++}`);
|
|
12902
|
-
params.push(options2.since);
|
|
12903
|
-
}
|
|
12904
|
-
if (options2.until !== undefined) {
|
|
12905
|
-
conditions.push(`timestamp <= $${paramIndex++}`);
|
|
12906
|
-
params.push(options2.until);
|
|
12907
|
-
}
|
|
12908
|
-
if (options2.afterSequence !== undefined) {
|
|
12909
|
-
conditions.push(`sequence > $${paramIndex++}`);
|
|
12910
|
-
params.push(options2.afterSequence);
|
|
12911
|
-
}
|
|
12912
|
-
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
12913
|
-
let query = `
|
|
12914
|
-
SELECT id, type, project_key, timestamp, sequence, data
|
|
12915
|
-
FROM events
|
|
12916
|
-
${whereClause}
|
|
12917
|
-
ORDER BY sequence ASC
|
|
12918
|
-
`;
|
|
12919
|
-
if (options2.limit) {
|
|
12920
|
-
query += ` LIMIT $${paramIndex++}`;
|
|
12921
|
-
params.push(options2.limit);
|
|
12922
|
-
}
|
|
12923
|
-
if (options2.offset) {
|
|
12924
|
-
query += ` OFFSET $${paramIndex++}`;
|
|
12925
|
-
params.push(options2.offset);
|
|
12926
|
-
}
|
|
12927
|
-
const result = await db.query(query, params);
|
|
12928
|
-
return result.rows.map((row) => {
|
|
12929
|
-
const data = typeof row.data === "string" ? JSON.parse(row.data) : row.data;
|
|
12930
|
-
return {
|
|
12931
|
-
id: row.id,
|
|
12932
|
-
type: row.type,
|
|
12933
|
-
project_key: row.project_key,
|
|
12934
|
-
timestamp: parseTimestamp(row.timestamp),
|
|
12935
|
-
sequence: row.sequence,
|
|
12936
|
-
...data
|
|
12937
|
-
};
|
|
12938
|
-
});
|
|
12939
|
-
});
|
|
12940
|
-
}
|
|
12941
|
-
async function getLatestSequence(projectKey, projectPath) {
|
|
12942
|
-
const db = await getDatabase(projectPath);
|
|
12943
|
-
const query = projectKey ? "SELECT MAX(sequence) as seq FROM events WHERE project_key = $1" : "SELECT MAX(sequence) as seq FROM events";
|
|
12944
|
-
const params = projectKey ? [projectKey] : [];
|
|
12945
|
-
const result = await db.query(query, params);
|
|
12946
|
-
return result.rows[0]?.seq ?? 0;
|
|
12947
|
-
}
|
|
12948
|
-
async function replayEvents(options2 = {}, projectPath) {
|
|
12949
|
-
return withTiming("replayEvents", async () => {
|
|
12950
|
-
const startTime = Date.now();
|
|
12951
|
-
const db = await getDatabase(projectPath);
|
|
12952
|
-
if (options2.clearViews) {
|
|
12953
|
-
if (options2.projectKey) {
|
|
12954
|
-
await db.query(`DELETE FROM message_recipients WHERE message_id IN (
|
|
12955
|
-
SELECT id FROM messages WHERE project_key = $1
|
|
12956
|
-
)`, [options2.projectKey]);
|
|
12957
|
-
await db.query(`DELETE FROM messages WHERE project_key = $1`, [
|
|
12958
|
-
options2.projectKey
|
|
12959
|
-
]);
|
|
12960
|
-
await db.query(`DELETE FROM reservations WHERE project_key = $1`, [
|
|
12961
|
-
options2.projectKey
|
|
12962
|
-
]);
|
|
12963
|
-
await db.query(`DELETE FROM agents WHERE project_key = $1`, [
|
|
12964
|
-
options2.projectKey
|
|
12965
|
-
]);
|
|
12966
|
-
} else {
|
|
12967
|
-
await db.exec(`
|
|
12968
|
-
DELETE FROM message_recipients;
|
|
12969
|
-
DELETE FROM messages;
|
|
12970
|
-
DELETE FROM reservations;
|
|
12971
|
-
DELETE FROM agents;
|
|
12972
|
-
`);
|
|
12973
|
-
}
|
|
12974
|
-
}
|
|
12975
|
-
const events = await readEvents({
|
|
12976
|
-
projectKey: options2.projectKey,
|
|
12977
|
-
afterSequence: options2.fromSequence
|
|
12978
|
-
}, projectPath);
|
|
12979
|
-
for (const event of events) {
|
|
12980
|
-
await updateMaterializedViews(db, event);
|
|
12981
|
-
}
|
|
12982
|
-
return {
|
|
12983
|
-
eventsReplayed: events.length,
|
|
12984
|
-
duration: Date.now() - startTime
|
|
12985
|
-
};
|
|
12986
|
-
});
|
|
12987
|
-
}
|
|
12988
|
-
async function replayEventsBatched(projectKey, onBatch, options2 = {}, projectPath) {
|
|
12989
|
-
return withTiming("replayEventsBatched", async () => {
|
|
12990
|
-
const startTime = Date.now();
|
|
12991
|
-
const batchSize = options2.batchSize ?? 1000;
|
|
12992
|
-
const fromSequence = options2.fromSequence ?? 0;
|
|
12993
|
-
const db = await getDatabase(projectPath);
|
|
12994
|
-
if (options2.clearViews) {
|
|
12995
|
-
await db.query(`DELETE FROM message_recipients WHERE message_id IN (
|
|
12996
|
-
SELECT id FROM messages WHERE project_key = $1
|
|
12997
|
-
)`, [projectKey]);
|
|
12998
|
-
await db.query(`DELETE FROM messages WHERE project_key = $1`, [
|
|
12999
|
-
projectKey
|
|
13000
|
-
]);
|
|
13001
|
-
await db.query(`DELETE FROM reservations WHERE project_key = $1`, [
|
|
13002
|
-
projectKey
|
|
13003
|
-
]);
|
|
13004
|
-
await db.query(`DELETE FROM agents WHERE project_key = $1`, [projectKey]);
|
|
13005
|
-
}
|
|
13006
|
-
const countResult = await db.query(`SELECT COUNT(*) as count FROM events WHERE project_key = $1 AND sequence > $2`, [projectKey, fromSequence]);
|
|
13007
|
-
const total = parseInt(countResult.rows[0]?.count ?? "0");
|
|
13008
|
-
if (total === 0) {
|
|
13009
|
-
return { eventsReplayed: 0, duration: Date.now() - startTime };
|
|
13010
|
-
}
|
|
13011
|
-
let processed = 0;
|
|
13012
|
-
let offset = 0;
|
|
13013
|
-
while (processed < total) {
|
|
13014
|
-
const events = await readEvents({
|
|
13015
|
-
projectKey,
|
|
13016
|
-
afterSequence: fromSequence,
|
|
13017
|
-
limit: batchSize,
|
|
13018
|
-
offset
|
|
13019
|
-
}, projectPath);
|
|
13020
|
-
if (events.length === 0)
|
|
13021
|
-
break;
|
|
13022
|
-
for (const event of events) {
|
|
13023
|
-
await updateMaterializedViews(db, event);
|
|
13024
|
-
}
|
|
13025
|
-
processed += events.length;
|
|
13026
|
-
const percent = Math.round(processed / total * 100);
|
|
13027
|
-
await onBatch(events, { processed, total, percent });
|
|
13028
|
-
console.log(`[SwarmMail] Replaying events: ${processed}/${total} (${percent}%)`);
|
|
13029
|
-
offset += batchSize;
|
|
13030
|
-
}
|
|
13031
|
-
return {
|
|
13032
|
-
eventsReplayed: processed,
|
|
13033
|
-
duration: Date.now() - startTime
|
|
13034
|
-
};
|
|
13035
|
-
});
|
|
13036
|
-
}
|
|
13037
|
-
async function updateMaterializedViews(db, event) {
|
|
13038
|
-
try {
|
|
13039
|
-
switch (event.type) {
|
|
13040
|
-
case "agent_registered":
|
|
13041
|
-
await handleAgentRegistered(db, event);
|
|
13042
|
-
break;
|
|
13043
|
-
case "agent_active":
|
|
13044
|
-
await db.query(`UPDATE agents SET last_active_at = $1 WHERE project_key = $2 AND name = $3`, [event.timestamp, event.project_key, event.agent_name]);
|
|
13045
|
-
break;
|
|
13046
|
-
case "message_sent":
|
|
13047
|
-
await handleMessageSent(db, event);
|
|
13048
|
-
break;
|
|
13049
|
-
case "message_read":
|
|
13050
|
-
await db.query(`UPDATE message_recipients SET read_at = $1 WHERE message_id = $2 AND agent_name = $3`, [event.timestamp, event.message_id, event.agent_name]);
|
|
13051
|
-
break;
|
|
13052
|
-
case "message_acked":
|
|
13053
|
-
await db.query(`UPDATE message_recipients SET acked_at = $1 WHERE message_id = $2 AND agent_name = $3`, [event.timestamp, event.message_id, event.agent_name]);
|
|
13054
|
-
break;
|
|
13055
|
-
case "file_reserved":
|
|
13056
|
-
await handleFileReserved(db, event);
|
|
13057
|
-
break;
|
|
13058
|
-
case "file_released":
|
|
13059
|
-
await handleFileReleased(db, event);
|
|
13060
|
-
break;
|
|
13061
|
-
case "task_started":
|
|
13062
|
-
case "task_progress":
|
|
13063
|
-
case "task_completed":
|
|
13064
|
-
case "task_blocked":
|
|
13065
|
-
break;
|
|
13066
|
-
}
|
|
13067
|
-
} catch (error45) {
|
|
13068
|
-
console.error("[SwarmMail] Failed to update materialized views", {
|
|
13069
|
-
eventType: event.type,
|
|
13070
|
-
eventId: event.id,
|
|
13071
|
-
error: error45
|
|
13072
|
-
});
|
|
13073
|
-
throw error45;
|
|
13074
|
-
}
|
|
13075
|
-
}
|
|
13076
|
-
async function handleAgentRegistered(db, event) {
|
|
13077
|
-
await db.query(`INSERT INTO agents (project_key, name, program, model, task_description, registered_at, last_active_at)
|
|
13078
|
-
VALUES ($1, $2, $3, $4, $5, $6, $6)
|
|
13079
|
-
ON CONFLICT (project_key, name) DO UPDATE SET
|
|
13080
|
-
program = EXCLUDED.program,
|
|
13081
|
-
model = EXCLUDED.model,
|
|
13082
|
-
task_description = EXCLUDED.task_description,
|
|
13083
|
-
last_active_at = EXCLUDED.last_active_at`, [
|
|
13084
|
-
event.project_key,
|
|
13085
|
-
event.agent_name,
|
|
13086
|
-
event.program,
|
|
13087
|
-
event.model,
|
|
13088
|
-
event.task_description || null,
|
|
13089
|
-
event.timestamp
|
|
13090
|
-
]);
|
|
13091
|
-
}
|
|
13092
|
-
async function handleMessageSent(db, event) {
|
|
13093
|
-
console.log("[SwarmMail] Handling message sent event", {
|
|
13094
|
-
from: event.from_agent,
|
|
13095
|
-
to: event.to_agents,
|
|
13096
|
-
subject: event.subject,
|
|
13097
|
-
projectKey: event.project_key
|
|
13098
|
-
});
|
|
13099
|
-
const result = await db.query(`INSERT INTO messages (project_key, from_agent, subject, body, thread_id, importance, ack_required, created_at)
|
|
13100
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
13101
|
-
RETURNING id`, [
|
|
13102
|
-
event.project_key,
|
|
13103
|
-
event.from_agent,
|
|
13104
|
-
event.subject,
|
|
13105
|
-
event.body,
|
|
13106
|
-
event.thread_id || null,
|
|
13107
|
-
event.importance,
|
|
13108
|
-
event.ack_required,
|
|
13109
|
-
event.timestamp
|
|
13110
|
-
]);
|
|
13111
|
-
const msgRow = result.rows[0];
|
|
13112
|
-
if (!msgRow) {
|
|
13113
|
-
throw new Error("Failed to insert message - no row returned");
|
|
13114
|
-
}
|
|
13115
|
-
const messageId = msgRow.id;
|
|
13116
|
-
if (event.to_agents.length > 0) {
|
|
13117
|
-
const values = event.to_agents.map((_, i) => `($1, $${i + 2})`).join(", ");
|
|
13118
|
-
const params = [messageId, ...event.to_agents];
|
|
13119
|
-
await db.query(`INSERT INTO message_recipients (message_id, agent_name)
|
|
13120
|
-
VALUES ${values}
|
|
13121
|
-
ON CONFLICT DO NOTHING`, params);
|
|
13122
|
-
console.log("[SwarmMail] Message recipients inserted", {
|
|
13123
|
-
messageId,
|
|
13124
|
-
recipientCount: event.to_agents.length
|
|
13125
|
-
});
|
|
13126
|
-
}
|
|
13127
|
-
}
|
|
13128
|
-
async function handleFileReserved(db, event) {
|
|
13129
|
-
console.log("[SwarmMail] Handling file reservation event", {
|
|
13130
|
-
agent: event.agent_name,
|
|
13131
|
-
paths: event.paths,
|
|
13132
|
-
exclusive: event.exclusive,
|
|
13133
|
-
projectKey: event.project_key
|
|
13134
|
-
});
|
|
13135
|
-
if (event.paths.length > 0) {
|
|
13136
|
-
const values = event.paths.map((_, i) => `($1, $2, $${i + 3}, $${event.paths.length + 3}, $${event.paths.length + 4}, $${event.paths.length + 5}, $${event.paths.length + 6})`).join(", ");
|
|
13137
|
-
const params = [
|
|
13138
|
-
event.project_key,
|
|
13139
|
-
event.agent_name,
|
|
13140
|
-
...event.paths,
|
|
13141
|
-
event.exclusive,
|
|
13142
|
-
event.reason || null,
|
|
13143
|
-
event.timestamp,
|
|
13144
|
-
event.expires_at
|
|
13145
|
-
];
|
|
13146
|
-
if (event.paths.length > 0) {
|
|
13147
|
-
await db.query(`DELETE FROM reservations
|
|
13148
|
-
WHERE project_key = $1
|
|
13149
|
-
AND agent_name = $2
|
|
13150
|
-
AND path_pattern = ANY($3)
|
|
13151
|
-
AND released_at IS NULL`, [event.project_key, event.agent_name, event.paths]);
|
|
13152
|
-
}
|
|
13153
|
-
await db.query(`INSERT INTO reservations (project_key, agent_name, path_pattern, exclusive, reason, created_at, expires_at)
|
|
13154
|
-
VALUES ${values}`, params);
|
|
13155
|
-
console.log("[SwarmMail] File reservations inserted", {
|
|
13156
|
-
agent: event.agent_name,
|
|
13157
|
-
reservationCount: event.paths.length
|
|
13158
|
-
});
|
|
13159
|
-
}
|
|
13160
|
-
}
|
|
13161
|
-
async function handleFileReleased(db, event) {
|
|
13162
|
-
if (event.type !== "file_released")
|
|
13163
|
-
return;
|
|
13164
|
-
if (event.reservation_ids && event.reservation_ids.length > 0) {
|
|
13165
|
-
await db.query(`UPDATE reservations SET released_at = $1 WHERE id = ANY($2)`, [event.timestamp, event.reservation_ids]);
|
|
13166
|
-
} else if (event.paths && event.paths.length > 0) {
|
|
13167
|
-
await db.query(`UPDATE reservations SET released_at = $1
|
|
13168
|
-
WHERE project_key = $2 AND agent_name = $3 AND path_pattern = ANY($4) AND released_at IS NULL`, [event.timestamp, event.project_key, event.agent_name, event.paths]);
|
|
13169
|
-
} else {
|
|
13170
|
-
await db.query(`UPDATE reservations SET released_at = $1
|
|
13171
|
-
WHERE project_key = $2 AND agent_name = $3 AND released_at IS NULL`, [event.timestamp, event.project_key, event.agent_name]);
|
|
13172
|
-
}
|
|
13173
|
-
}
|
|
13174
|
-
async function registerAgent(projectKey, agentName, options2 = {}, projectPath) {
|
|
13175
|
-
const event = createEvent("agent_registered", {
|
|
13176
|
-
project_key: projectKey,
|
|
13177
|
-
agent_name: agentName,
|
|
13178
|
-
program: options2.program || "opencode",
|
|
13179
|
-
model: options2.model || "unknown",
|
|
13180
|
-
task_description: options2.taskDescription
|
|
13181
|
-
});
|
|
13182
|
-
return appendEvent(event, projectPath);
|
|
13183
|
-
}
|
|
13184
|
-
async function sendMessage(projectKey, fromAgent, toAgents, subject, body, options2 = {}, projectPath) {
|
|
13185
|
-
const event = createEvent("message_sent", {
|
|
13186
|
-
project_key: projectKey,
|
|
13187
|
-
from_agent: fromAgent,
|
|
13188
|
-
to_agents: toAgents,
|
|
13189
|
-
subject,
|
|
13190
|
-
body,
|
|
13191
|
-
thread_id: options2.threadId,
|
|
13192
|
-
importance: options2.importance || "normal",
|
|
13193
|
-
ack_required: options2.ackRequired || false
|
|
13194
|
-
});
|
|
13195
|
-
return appendEvent(event, projectPath);
|
|
13196
|
-
}
|
|
13197
|
-
async function reserveFiles(projectKey, agentName, paths, options2 = {}, projectPath) {
|
|
13198
|
-
const ttlSeconds = options2.ttlSeconds || 3600;
|
|
13199
|
-
const event = createEvent("file_reserved", {
|
|
13200
|
-
project_key: projectKey,
|
|
13201
|
-
agent_name: agentName,
|
|
13202
|
-
paths,
|
|
13203
|
-
reason: options2.reason,
|
|
13204
|
-
exclusive: options2.exclusive ?? true,
|
|
13205
|
-
ttl_seconds: ttlSeconds,
|
|
13206
|
-
expires_at: Date.now() + ttlSeconds * 1000
|
|
13207
|
-
});
|
|
13208
|
-
return appendEvent(event, projectPath);
|
|
13209
|
-
}
|
|
13210
|
-
var TIMESTAMP_SAFE_UNTIL;
|
|
13211
|
-
var init_store = __esm(() => {
|
|
13212
|
-
init_streams();
|
|
13213
|
-
init_events();
|
|
13214
|
-
TIMESTAMP_SAFE_UNTIL = new Date("2286-01-01").getTime();
|
|
13215
|
-
});
|
|
13216
|
-
|
|
13217
|
-
// node_modules/.pnpm/@isaacs+balanced-match@4.0.1/node_modules/@isaacs/balanced-match/dist/esm/index.js
|
|
13218
|
-
var balanced = (a, b, str2) => {
|
|
13219
|
-
const ma = a instanceof RegExp ? maybeMatch(a, str2) : a;
|
|
13220
|
-
const mb = b instanceof RegExp ? maybeMatch(b, str2) : b;
|
|
13221
|
-
const r = ma !== null && mb != null && range(ma, mb, str2);
|
|
13222
|
-
return r && {
|
|
13223
|
-
start: r[0],
|
|
13224
|
-
end: r[1],
|
|
13225
|
-
pre: str2.slice(0, r[0]),
|
|
13226
|
-
body: str2.slice(r[0] + ma.length, r[1]),
|
|
13227
|
-
post: str2.slice(r[1] + mb.length)
|
|
13228
|
-
};
|
|
13229
|
-
}, maybeMatch = (reg, str2) => {
|
|
13230
|
-
const m = str2.match(reg);
|
|
13231
|
-
return m ? m[0] : null;
|
|
13232
|
-
}, range = (a, b, str2) => {
|
|
13233
|
-
let begs, beg, left, right = undefined, result;
|
|
13234
|
-
let ai = str2.indexOf(a);
|
|
13235
|
-
let bi = str2.indexOf(b, ai + 1);
|
|
13236
|
-
let i = ai;
|
|
13237
|
-
if (ai >= 0 && bi > 0) {
|
|
13238
|
-
if (a === b) {
|
|
13239
|
-
return [ai, bi];
|
|
13240
|
-
}
|
|
13241
|
-
begs = [];
|
|
13242
|
-
left = str2.length;
|
|
13243
|
-
while (i >= 0 && !result) {
|
|
13244
|
-
if (i === ai) {
|
|
13245
|
-
begs.push(i);
|
|
13246
|
-
ai = str2.indexOf(a, i + 1);
|
|
13247
|
-
} else if (begs.length === 1) {
|
|
13248
|
-
const r = begs.pop();
|
|
13249
|
-
if (r !== undefined)
|
|
13250
|
-
result = [r, bi];
|
|
13251
|
-
} else {
|
|
13252
|
-
beg = begs.pop();
|
|
13253
|
-
if (beg !== undefined && beg < left) {
|
|
13254
|
-
left = beg;
|
|
13255
|
-
right = bi;
|
|
13256
|
-
}
|
|
13257
|
-
bi = str2.indexOf(b, i + 1);
|
|
13258
|
-
}
|
|
13259
|
-
i = ai < bi && ai >= 0 ? ai : bi;
|
|
13260
|
-
}
|
|
13261
|
-
if (begs.length && right !== undefined) {
|
|
13262
|
-
result = [left, right];
|
|
13263
|
-
}
|
|
13264
|
-
}
|
|
13265
|
-
return result;
|
|
13266
|
-
};
|
|
13267
|
-
|
|
13268
|
-
// node_modules/.pnpm/@isaacs+brace-expansion@5.0.0/node_modules/@isaacs/brace-expansion/dist/esm/index.js
|
|
13269
|
-
function numeric(str2) {
|
|
13270
|
-
return !isNaN(str2) ? parseInt(str2, 10) : str2.charCodeAt(0);
|
|
13271
|
-
}
|
|
13272
|
-
function escapeBraces(str2) {
|
|
13273
|
-
return str2.replace(slashPattern, escSlash).replace(openPattern, escOpen).replace(closePattern, escClose).replace(commaPattern, escComma).replace(periodPattern, escPeriod);
|
|
13274
|
-
}
|
|
13275
|
-
function unescapeBraces(str2) {
|
|
13276
|
-
return str2.replace(escSlashPattern, "\\").replace(escOpenPattern, "{").replace(escClosePattern, "}").replace(escCommaPattern, ",").replace(escPeriodPattern, ".");
|
|
13277
|
-
}
|
|
13278
|
-
function parseCommaParts(str2) {
|
|
13279
|
-
if (!str2) {
|
|
13280
|
-
return [""];
|
|
13281
|
-
}
|
|
13282
|
-
const parts = [];
|
|
13283
|
-
const m = balanced("{", "}", str2);
|
|
13284
|
-
if (!m) {
|
|
13285
|
-
return str2.split(",");
|
|
13286
|
-
}
|
|
13287
|
-
const { pre, body, post } = m;
|
|
13288
|
-
const p = pre.split(",");
|
|
13289
|
-
p[p.length - 1] += "{" + body + "}";
|
|
13290
|
-
const postParts = parseCommaParts(post);
|
|
13291
|
-
if (post.length) {
|
|
13292
|
-
p[p.length - 1] += postParts.shift();
|
|
13293
|
-
p.push.apply(p, postParts);
|
|
13294
|
-
}
|
|
13295
|
-
parts.push.apply(parts, p);
|
|
13296
|
-
return parts;
|
|
13297
|
-
}
|
|
13298
|
-
function expand(str2) {
|
|
13299
|
-
if (!str2) {
|
|
13300
|
-
return [];
|
|
13301
|
-
}
|
|
13302
|
-
if (str2.slice(0, 2) === "{}") {
|
|
13303
|
-
str2 = "\\{\\}" + str2.slice(2);
|
|
13304
|
-
}
|
|
13305
|
-
return expand_(escapeBraces(str2), true).map(unescapeBraces);
|
|
13306
|
-
}
|
|
13307
|
-
function embrace(str2) {
|
|
13308
|
-
return "{" + str2 + "}";
|
|
13309
|
-
}
|
|
13310
|
-
function isPadded(el) {
|
|
13311
|
-
return /^-?0\d/.test(el);
|
|
13312
|
-
}
|
|
13313
|
-
function lte(i, y) {
|
|
13314
|
-
return i <= y;
|
|
13315
|
-
}
|
|
13316
|
-
function gte(i, y) {
|
|
13317
|
-
return i >= y;
|
|
13318
|
-
}
|
|
13319
|
-
function expand_(str2, isTop) {
|
|
13320
|
-
const expansions = [];
|
|
13321
|
-
const m = balanced("{", "}", str2);
|
|
13322
|
-
if (!m)
|
|
13323
|
-
return [str2];
|
|
13324
|
-
const pre = m.pre;
|
|
13325
|
-
const post = m.post.length ? expand_(m.post, false) : [""];
|
|
13326
|
-
if (/\$$/.test(m.pre)) {
|
|
13327
|
-
for (let k = 0;k < post.length; k++) {
|
|
13328
|
-
const expansion = pre + "{" + m.body + "}" + post[k];
|
|
13329
|
-
expansions.push(expansion);
|
|
13330
|
-
}
|
|
13331
|
-
} else {
|
|
13332
|
-
const isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
|
|
13333
|
-
const isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
|
|
13334
|
-
const isSequence = isNumericSequence || isAlphaSequence;
|
|
13335
|
-
const isOptions = m.body.indexOf(",") >= 0;
|
|
13336
|
-
if (!isSequence && !isOptions) {
|
|
13337
|
-
if (m.post.match(/,(?!,).*\}/)) {
|
|
13338
|
-
str2 = m.pre + "{" + m.body + escClose + m.post;
|
|
13339
|
-
return expand_(str2);
|
|
13340
|
-
}
|
|
13341
|
-
return [str2];
|
|
13342
|
-
}
|
|
13343
|
-
let n;
|
|
13344
|
-
if (isSequence) {
|
|
13345
|
-
n = m.body.split(/\.\./);
|
|
13346
|
-
} else {
|
|
13347
|
-
n = parseCommaParts(m.body);
|
|
13348
|
-
if (n.length === 1 && n[0] !== undefined) {
|
|
13349
|
-
n = expand_(n[0], false).map(embrace);
|
|
13350
|
-
if (n.length === 1) {
|
|
13351
|
-
return post.map((p) => m.pre + n[0] + p);
|
|
13352
|
-
}
|
|
13353
|
-
}
|
|
13354
|
-
}
|
|
13355
|
-
let N;
|
|
13356
|
-
if (isSequence && n[0] !== undefined && n[1] !== undefined) {
|
|
13357
|
-
const x = numeric(n[0]);
|
|
13358
|
-
const y = numeric(n[1]);
|
|
13359
|
-
const width = Math.max(n[0].length, n[1].length);
|
|
13360
|
-
let incr = n.length === 3 && n[2] !== undefined ? Math.abs(numeric(n[2])) : 1;
|
|
13361
|
-
let test = lte;
|
|
13362
|
-
const reverse = y < x;
|
|
13363
|
-
if (reverse) {
|
|
13364
|
-
incr *= -1;
|
|
13365
|
-
test = gte;
|
|
13366
|
-
}
|
|
13367
|
-
const pad = n.some(isPadded);
|
|
13368
|
-
N = [];
|
|
13369
|
-
for (let i = x;test(i, y); i += incr) {
|
|
13370
|
-
let c;
|
|
13371
|
-
if (isAlphaSequence) {
|
|
13372
|
-
c = String.fromCharCode(i);
|
|
13373
|
-
if (c === "\\") {
|
|
13374
|
-
c = "";
|
|
13375
|
-
}
|
|
13376
|
-
} else {
|
|
13377
|
-
c = String(i);
|
|
13378
|
-
if (pad) {
|
|
13379
|
-
const need = width - c.length;
|
|
13380
|
-
if (need > 0) {
|
|
13381
|
-
const z = new Array(need + 1).join("0");
|
|
13382
|
-
if (i < 0) {
|
|
13383
|
-
c = "-" + z + c.slice(1);
|
|
13384
|
-
} else {
|
|
13385
|
-
c = z + c;
|
|
13386
|
-
}
|
|
13387
|
-
}
|
|
13388
|
-
}
|
|
13389
|
-
}
|
|
13390
|
-
N.push(c);
|
|
13391
|
-
}
|
|
13392
|
-
} else {
|
|
13393
|
-
N = [];
|
|
13394
|
-
for (let j = 0;j < n.length; j++) {
|
|
13395
|
-
N.push.apply(N, expand_(n[j], false));
|
|
13396
|
-
}
|
|
13397
|
-
}
|
|
13398
|
-
for (let j = 0;j < N.length; j++) {
|
|
13399
|
-
for (let k = 0;k < post.length; k++) {
|
|
13400
|
-
const expansion = pre + N[j] + post[k];
|
|
13401
|
-
if (!isTop || isSequence || expansion) {
|
|
13402
|
-
expansions.push(expansion);
|
|
13403
|
-
}
|
|
13404
|
-
}
|
|
13405
|
-
}
|
|
13406
|
-
}
|
|
13407
|
-
return expansions;
|
|
13057
|
+
for (let j = 0;j < N.length; j++) {
|
|
13058
|
+
for (let k = 0;k < post.length; k++) {
|
|
13059
|
+
const expansion = pre + N[j] + post[k];
|
|
13060
|
+
if (!isTop || isSequence || expansion) {
|
|
13061
|
+
expansions.push(expansion);
|
|
13062
|
+
}
|
|
13063
|
+
}
|
|
13064
|
+
}
|
|
13065
|
+
}
|
|
13066
|
+
return expansions;
|
|
13408
13067
|
}
|
|
13409
13068
|
var escSlash, escOpen, escClose, escComma, escPeriod, escSlashPattern, escOpenPattern, escClosePattern, escCommaPattern, escPeriodPattern, slashPattern, openPattern, closePattern, commaPattern, periodPattern;
|
|
13410
13069
|
var init_esm = __esm(() => {
|
|
@@ -14753,6 +14412,79 @@ function pathMatches(path2, pattern) {
|
|
|
14753
14412
|
}
|
|
14754
14413
|
return minimatch(path2, pattern);
|
|
14755
14414
|
}
|
|
14415
|
+
async function getEvalRecords(projectKey, options2, projectPath) {
|
|
14416
|
+
const db = await getDatabase(projectPath);
|
|
14417
|
+
const conditions = ["project_key = $1"];
|
|
14418
|
+
const params = [projectKey];
|
|
14419
|
+
let paramIndex = 2;
|
|
14420
|
+
if (options2?.strategy) {
|
|
14421
|
+
conditions.push(`strategy = $${paramIndex++}`);
|
|
14422
|
+
params.push(options2.strategy);
|
|
14423
|
+
}
|
|
14424
|
+
const whereClause = conditions.join(" AND ");
|
|
14425
|
+
let query = `
|
|
14426
|
+
SELECT id, project_key, task, context, strategy, epic_title, subtasks,
|
|
14427
|
+
outcomes, overall_success, total_duration_ms, total_errors,
|
|
14428
|
+
human_accepted, human_modified, human_notes,
|
|
14429
|
+
file_overlap_count, scope_accuracy, time_balance_ratio,
|
|
14430
|
+
created_at, updated_at
|
|
14431
|
+
FROM eval_records
|
|
14432
|
+
WHERE ${whereClause}
|
|
14433
|
+
ORDER BY created_at DESC
|
|
14434
|
+
`;
|
|
14435
|
+
if (options2?.limit) {
|
|
14436
|
+
query += ` LIMIT $${paramIndex}`;
|
|
14437
|
+
params.push(options2.limit);
|
|
14438
|
+
}
|
|
14439
|
+
const result = await db.query(query, params);
|
|
14440
|
+
return result.rows.map((row) => ({
|
|
14441
|
+
id: row.id,
|
|
14442
|
+
project_key: row.project_key,
|
|
14443
|
+
task: row.task,
|
|
14444
|
+
context: row.context,
|
|
14445
|
+
strategy: row.strategy,
|
|
14446
|
+
epic_title: row.epic_title,
|
|
14447
|
+
subtasks: typeof row.subtasks === "string" ? JSON.parse(row.subtasks) : row.subtasks,
|
|
14448
|
+
outcomes: row.outcomes ? typeof row.outcomes === "string" ? JSON.parse(row.outcomes) : row.outcomes : undefined,
|
|
14449
|
+
overall_success: row.overall_success,
|
|
14450
|
+
total_duration_ms: row.total_duration_ms,
|
|
14451
|
+
total_errors: row.total_errors,
|
|
14452
|
+
human_accepted: row.human_accepted,
|
|
14453
|
+
human_modified: row.human_modified,
|
|
14454
|
+
human_notes: row.human_notes,
|
|
14455
|
+
file_overlap_count: row.file_overlap_count,
|
|
14456
|
+
scope_accuracy: row.scope_accuracy,
|
|
14457
|
+
time_balance_ratio: row.time_balance_ratio,
|
|
14458
|
+
created_at: parseInt(row.created_at),
|
|
14459
|
+
updated_at: parseInt(row.updated_at)
|
|
14460
|
+
}));
|
|
14461
|
+
}
|
|
14462
|
+
async function getEvalStats(projectKey, projectPath) {
|
|
14463
|
+
const db = await getDatabase(projectPath);
|
|
14464
|
+
const overallResult = await db.query(`SELECT
|
|
14465
|
+
COUNT(*) as total_records,
|
|
14466
|
+
COUNT(*) FILTER (WHERE overall_success = true) as success_count,
|
|
14467
|
+
AVG(total_duration_ms) as avg_duration
|
|
14468
|
+
FROM eval_records
|
|
14469
|
+
WHERE project_key = $1`, [projectKey]);
|
|
14470
|
+
const totalRecords = parseInt(overallResult.rows[0]?.total_records || "0");
|
|
14471
|
+
const successCount = parseInt(overallResult.rows[0]?.success_count || "0");
|
|
14472
|
+
const avgDurationMs = parseFloat(overallResult.rows[0]?.avg_duration || "0");
|
|
14473
|
+
const strategyResult = await db.query(`SELECT strategy, COUNT(*) as count
|
|
14474
|
+
FROM eval_records
|
|
14475
|
+
WHERE project_key = $1
|
|
14476
|
+
GROUP BY strategy`, [projectKey]);
|
|
14477
|
+
const byStrategy = {};
|
|
14478
|
+
for (const row of strategyResult.rows) {
|
|
14479
|
+
byStrategy[row.strategy] = parseInt(row.count);
|
|
14480
|
+
}
|
|
14481
|
+
return {
|
|
14482
|
+
totalRecords,
|
|
14483
|
+
successRate: totalRecords > 0 ? successCount / totalRecords : 0,
|
|
14484
|
+
avgDurationMs,
|
|
14485
|
+
byStrategy
|
|
14486
|
+
};
|
|
14487
|
+
}
|
|
14756
14488
|
var init_projections = __esm(() => {
|
|
14757
14489
|
init_streams();
|
|
14758
14490
|
init_esm2();
|
|
@@ -15438,45 +15170,314 @@ async function getPendingMigrations(db) {
|
|
|
15438
15170
|
const currentVersion = await getCurrentVersion(db);
|
|
15439
15171
|
return migrations.filter((m) => m.version > currentVersion).sort((a, b) => a.version - b.version);
|
|
15440
15172
|
}
|
|
15441
|
-
var migrations;
|
|
15442
|
-
var init_migrations = __esm(() => {
|
|
15443
|
-
migrations = [
|
|
15444
|
-
{
|
|
15445
|
-
version: 1,
|
|
15446
|
-
description: "Add cursors table for DurableCursor",
|
|
15447
|
-
up: `
|
|
15448
|
-
CREATE TABLE IF NOT EXISTS cursors (
|
|
15449
|
-
id SERIAL PRIMARY KEY,
|
|
15450
|
-
stream TEXT NOT NULL,
|
|
15451
|
-
checkpoint TEXT NOT NULL,
|
|
15452
|
-
position BIGINT NOT NULL DEFAULT 0,
|
|
15453
|
-
updated_at BIGINT NOT NULL,
|
|
15454
|
-
UNIQUE(stream, checkpoint)
|
|
15455
|
-
);
|
|
15456
|
-
CREATE INDEX IF NOT EXISTS idx_cursors_checkpoint ON cursors(checkpoint);
|
|
15457
|
-
CREATE INDEX IF NOT EXISTS idx_cursors_stream ON cursors(stream);
|
|
15458
|
-
`,
|
|
15459
|
-
down: `DROP TABLE IF EXISTS cursors;`
|
|
15460
|
-
},
|
|
15461
|
-
{
|
|
15462
|
-
version: 2,
|
|
15463
|
-
description: "Add deferred table for DurableDeferred",
|
|
15464
|
-
up: `
|
|
15465
|
-
CREATE TABLE IF NOT EXISTS deferred (
|
|
15466
|
-
id SERIAL PRIMARY KEY,
|
|
15467
|
-
url TEXT NOT NULL UNIQUE,
|
|
15468
|
-
resolved BOOLEAN NOT NULL DEFAULT FALSE,
|
|
15469
|
-
value JSONB,
|
|
15470
|
-
error TEXT,
|
|
15471
|
-
expires_at BIGINT NOT NULL,
|
|
15472
|
-
created_at BIGINT NOT NULL
|
|
15473
|
-
);
|
|
15474
|
-
CREATE INDEX IF NOT EXISTS idx_deferred_url ON deferred(url);
|
|
15475
|
-
CREATE INDEX IF NOT EXISTS idx_deferred_expires ON deferred(expires_at);
|
|
15476
|
-
CREATE INDEX IF NOT EXISTS idx_deferred_resolved ON deferred(resolved);
|
|
15477
|
-
`,
|
|
15478
|
-
down: `DROP TABLE IF EXISTS deferred;`
|
|
15479
|
-
}
|
|
15173
|
+
var migrations;
|
|
15174
|
+
var init_migrations = __esm(() => {
|
|
15175
|
+
migrations = [
|
|
15176
|
+
{
|
|
15177
|
+
version: 1,
|
|
15178
|
+
description: "Add cursors table for DurableCursor",
|
|
15179
|
+
up: `
|
|
15180
|
+
CREATE TABLE IF NOT EXISTS cursors (
|
|
15181
|
+
id SERIAL PRIMARY KEY,
|
|
15182
|
+
stream TEXT NOT NULL,
|
|
15183
|
+
checkpoint TEXT NOT NULL,
|
|
15184
|
+
position BIGINT NOT NULL DEFAULT 0,
|
|
15185
|
+
updated_at BIGINT NOT NULL,
|
|
15186
|
+
UNIQUE(stream, checkpoint)
|
|
15187
|
+
);
|
|
15188
|
+
CREATE INDEX IF NOT EXISTS idx_cursors_checkpoint ON cursors(checkpoint);
|
|
15189
|
+
CREATE INDEX IF NOT EXISTS idx_cursors_stream ON cursors(stream);
|
|
15190
|
+
`,
|
|
15191
|
+
down: `DROP TABLE IF EXISTS cursors;`
|
|
15192
|
+
},
|
|
15193
|
+
{
|
|
15194
|
+
version: 2,
|
|
15195
|
+
description: "Add deferred table for DurableDeferred",
|
|
15196
|
+
up: `
|
|
15197
|
+
CREATE TABLE IF NOT EXISTS deferred (
|
|
15198
|
+
id SERIAL PRIMARY KEY,
|
|
15199
|
+
url TEXT NOT NULL UNIQUE,
|
|
15200
|
+
resolved BOOLEAN NOT NULL DEFAULT FALSE,
|
|
15201
|
+
value JSONB,
|
|
15202
|
+
error TEXT,
|
|
15203
|
+
expires_at BIGINT NOT NULL,
|
|
15204
|
+
created_at BIGINT NOT NULL
|
|
15205
|
+
);
|
|
15206
|
+
CREATE INDEX IF NOT EXISTS idx_deferred_url ON deferred(url);
|
|
15207
|
+
CREATE INDEX IF NOT EXISTS idx_deferred_expires ON deferred(expires_at);
|
|
15208
|
+
CREATE INDEX IF NOT EXISTS idx_deferred_resolved ON deferred(resolved);
|
|
15209
|
+
`,
|
|
15210
|
+
down: `DROP TABLE IF EXISTS deferred;`
|
|
15211
|
+
},
|
|
15212
|
+
{
|
|
15213
|
+
version: 3,
|
|
15214
|
+
description: "Add eval_records table for learning system",
|
|
15215
|
+
up: `
|
|
15216
|
+
CREATE TABLE IF NOT EXISTS eval_records (
|
|
15217
|
+
id TEXT PRIMARY KEY,
|
|
15218
|
+
project_key TEXT NOT NULL,
|
|
15219
|
+
task TEXT NOT NULL,
|
|
15220
|
+
context TEXT,
|
|
15221
|
+
strategy TEXT NOT NULL,
|
|
15222
|
+
epic_title TEXT NOT NULL,
|
|
15223
|
+
subtasks JSONB NOT NULL,
|
|
15224
|
+
outcomes JSONB,
|
|
15225
|
+
overall_success BOOLEAN,
|
|
15226
|
+
total_duration_ms INTEGER,
|
|
15227
|
+
total_errors INTEGER,
|
|
15228
|
+
human_accepted BOOLEAN,
|
|
15229
|
+
human_modified BOOLEAN,
|
|
15230
|
+
human_notes TEXT,
|
|
15231
|
+
file_overlap_count INTEGER,
|
|
15232
|
+
scope_accuracy REAL,
|
|
15233
|
+
time_balance_ratio REAL,
|
|
15234
|
+
created_at BIGINT NOT NULL,
|
|
15235
|
+
updated_at BIGINT NOT NULL
|
|
15236
|
+
);
|
|
15237
|
+
CREATE INDEX IF NOT EXISTS idx_eval_records_project ON eval_records(project_key);
|
|
15238
|
+
CREATE INDEX IF NOT EXISTS idx_eval_records_strategy ON eval_records(strategy);
|
|
15239
|
+
`,
|
|
15240
|
+
down: `DROP TABLE IF EXISTS eval_records;`
|
|
15241
|
+
},
|
|
15242
|
+
{
|
|
15243
|
+
version: 4,
|
|
15244
|
+
description: "Add swarm_contexts table for context recovery",
|
|
15245
|
+
up: `
|
|
15246
|
+
CREATE TABLE IF NOT EXISTS swarm_contexts (
|
|
15247
|
+
id TEXT PRIMARY KEY,
|
|
15248
|
+
epic_id TEXT NOT NULL,
|
|
15249
|
+
bead_id TEXT NOT NULL,
|
|
15250
|
+
strategy TEXT NOT NULL,
|
|
15251
|
+
files JSONB NOT NULL,
|
|
15252
|
+
dependencies JSONB NOT NULL,
|
|
15253
|
+
directives JSONB NOT NULL,
|
|
15254
|
+
recovery JSONB NOT NULL,
|
|
15255
|
+
created_at BIGINT NOT NULL,
|
|
15256
|
+
updated_at BIGINT NOT NULL
|
|
15257
|
+
);
|
|
15258
|
+
CREATE INDEX IF NOT EXISTS idx_swarm_contexts_epic ON swarm_contexts(epic_id);
|
|
15259
|
+
CREATE INDEX IF NOT EXISTS idx_swarm_contexts_bead ON swarm_contexts(bead_id);
|
|
15260
|
+
`,
|
|
15261
|
+
down: `DROP TABLE IF EXISTS swarm_contexts;`
|
|
15262
|
+
}
|
|
15263
|
+
];
|
|
15264
|
+
});
|
|
15265
|
+
|
|
15266
|
+
// src/streams/swarm-mail.ts
|
|
15267
|
+
function generateSwarmAgentName() {
|
|
15268
|
+
const adj = ADJECTIVES2[Math.floor(Math.random() * ADJECTIVES2.length)];
|
|
15269
|
+
const noun = NOUNS2[Math.floor(Math.random() * NOUNS2.length)];
|
|
15270
|
+
return `${adj}${noun}`;
|
|
15271
|
+
}
|
|
15272
|
+
async function initSwarmAgent(options2) {
|
|
15273
|
+
const {
|
|
15274
|
+
projectPath,
|
|
15275
|
+
agentName = generateSwarmAgentName(),
|
|
15276
|
+
program = "opencode",
|
|
15277
|
+
model = "unknown",
|
|
15278
|
+
taskDescription
|
|
15279
|
+
} = options2;
|
|
15280
|
+
await registerAgent(projectPath, agentName, { program, model, taskDescription }, projectPath);
|
|
15281
|
+
return {
|
|
15282
|
+
projectKey: projectPath,
|
|
15283
|
+
agentName
|
|
15284
|
+
};
|
|
15285
|
+
}
|
|
15286
|
+
async function sendSwarmMessage(options2) {
|
|
15287
|
+
const {
|
|
15288
|
+
projectPath,
|
|
15289
|
+
fromAgent,
|
|
15290
|
+
toAgents,
|
|
15291
|
+
subject,
|
|
15292
|
+
body,
|
|
15293
|
+
threadId,
|
|
15294
|
+
importance = "normal",
|
|
15295
|
+
ackRequired = false
|
|
15296
|
+
} = options2;
|
|
15297
|
+
await sendMessage(projectPath, fromAgent, toAgents, subject, body, { threadId, importance, ackRequired }, projectPath);
|
|
15298
|
+
const { getDatabase: getDatabase2 } = await Promise.resolve().then(() => (init_streams(), exports_streams));
|
|
15299
|
+
const db = await getDatabase2(projectPath);
|
|
15300
|
+
const result = await db.query(`SELECT id FROM messages
|
|
15301
|
+
WHERE project_key = $1 AND from_agent = $2 AND subject = $3
|
|
15302
|
+
ORDER BY created_at DESC LIMIT 1`, [projectPath, fromAgent, subject]);
|
|
15303
|
+
const messageId = result.rows[0]?.id ?? 0;
|
|
15304
|
+
return {
|
|
15305
|
+
success: true,
|
|
15306
|
+
messageId,
|
|
15307
|
+
threadId,
|
|
15308
|
+
recipientCount: toAgents.length
|
|
15309
|
+
};
|
|
15310
|
+
}
|
|
15311
|
+
async function getSwarmInbox(options2) {
|
|
15312
|
+
const {
|
|
15313
|
+
projectPath,
|
|
15314
|
+
agentName,
|
|
15315
|
+
limit = MAX_INBOX_LIMIT2,
|
|
15316
|
+
urgentOnly = false,
|
|
15317
|
+
unreadOnly = false,
|
|
15318
|
+
includeBodies = false
|
|
15319
|
+
} = options2;
|
|
15320
|
+
const effectiveLimit = Math.min(limit, MAX_INBOX_LIMIT2);
|
|
15321
|
+
const messages = await getInbox(projectPath, agentName, {
|
|
15322
|
+
limit: effectiveLimit,
|
|
15323
|
+
urgentOnly,
|
|
15324
|
+
unreadOnly,
|
|
15325
|
+
includeBodies
|
|
15326
|
+
}, projectPath);
|
|
15327
|
+
return {
|
|
15328
|
+
messages: messages.map((m) => ({
|
|
15329
|
+
id: m.id,
|
|
15330
|
+
from_agent: m.from_agent,
|
|
15331
|
+
subject: m.subject,
|
|
15332
|
+
body: includeBodies ? m.body : undefined,
|
|
15333
|
+
thread_id: m.thread_id,
|
|
15334
|
+
importance: m.importance,
|
|
15335
|
+
created_at: m.created_at
|
|
15336
|
+
})),
|
|
15337
|
+
total: messages.length
|
|
15338
|
+
};
|
|
15339
|
+
}
|
|
15340
|
+
async function readSwarmMessage(options2) {
|
|
15341
|
+
const { projectPath, messageId, agentName, markAsRead = false } = options2;
|
|
15342
|
+
const message = await getMessage(projectPath, messageId, projectPath);
|
|
15343
|
+
if (!message) {
|
|
15344
|
+
return null;
|
|
15345
|
+
}
|
|
15346
|
+
if (markAsRead && agentName) {
|
|
15347
|
+
await appendEvent(createEvent("message_read", {
|
|
15348
|
+
project_key: projectPath,
|
|
15349
|
+
message_id: messageId,
|
|
15350
|
+
agent_name: agentName
|
|
15351
|
+
}), projectPath);
|
|
15352
|
+
}
|
|
15353
|
+
return {
|
|
15354
|
+
id: message.id,
|
|
15355
|
+
from_agent: message.from_agent,
|
|
15356
|
+
subject: message.subject,
|
|
15357
|
+
body: message.body,
|
|
15358
|
+
thread_id: message.thread_id,
|
|
15359
|
+
importance: message.importance,
|
|
15360
|
+
created_at: message.created_at
|
|
15361
|
+
};
|
|
15362
|
+
}
|
|
15363
|
+
async function reserveSwarmFiles(options2) {
|
|
15364
|
+
const {
|
|
15365
|
+
projectPath,
|
|
15366
|
+
agentName,
|
|
15367
|
+
paths,
|
|
15368
|
+
reason,
|
|
15369
|
+
exclusive = true,
|
|
15370
|
+
ttlSeconds = DEFAULT_TTL_SECONDS2
|
|
15371
|
+
} = options2;
|
|
15372
|
+
const conflicts = await checkConflicts(projectPath, agentName, paths, projectPath);
|
|
15373
|
+
await reserveFiles(projectPath, agentName, paths, { reason, exclusive, ttlSeconds }, projectPath);
|
|
15374
|
+
const reservations = await getActiveReservations(projectPath, projectPath, agentName);
|
|
15375
|
+
const granted = reservations.filter((r) => paths.includes(r.path_pattern)).map((r) => ({
|
|
15376
|
+
id: r.id,
|
|
15377
|
+
path_pattern: r.path_pattern,
|
|
15378
|
+
exclusive: r.exclusive,
|
|
15379
|
+
expiresAt: r.expires_at
|
|
15380
|
+
}));
|
|
15381
|
+
return {
|
|
15382
|
+
granted,
|
|
15383
|
+
conflicts: conflicts.map((c) => ({
|
|
15384
|
+
path: c.path,
|
|
15385
|
+
holder: c.holder,
|
|
15386
|
+
pattern: c.pattern
|
|
15387
|
+
}))
|
|
15388
|
+
};
|
|
15389
|
+
}
|
|
15390
|
+
async function releaseSwarmFiles(options2) {
|
|
15391
|
+
const { projectPath, agentName, paths, reservationIds } = options2;
|
|
15392
|
+
const currentReservations = await getActiveReservations(projectPath, projectPath, agentName);
|
|
15393
|
+
let releaseCount = 0;
|
|
15394
|
+
if (paths && paths.length > 0) {
|
|
15395
|
+
releaseCount = currentReservations.filter((r) => paths.includes(r.path_pattern)).length;
|
|
15396
|
+
} else if (reservationIds && reservationIds.length > 0) {
|
|
15397
|
+
releaseCount = currentReservations.filter((r) => reservationIds.includes(r.id)).length;
|
|
15398
|
+
} else {
|
|
15399
|
+
releaseCount = currentReservations.length;
|
|
15400
|
+
}
|
|
15401
|
+
await appendEvent(createEvent("file_released", {
|
|
15402
|
+
project_key: projectPath,
|
|
15403
|
+
agent_name: agentName,
|
|
15404
|
+
paths,
|
|
15405
|
+
reservation_ids: reservationIds
|
|
15406
|
+
}), projectPath);
|
|
15407
|
+
return {
|
|
15408
|
+
released: releaseCount,
|
|
15409
|
+
releasedAt: Date.now()
|
|
15410
|
+
};
|
|
15411
|
+
}
|
|
15412
|
+
async function acknowledgeSwarmMessage(options2) {
|
|
15413
|
+
const { projectPath, messageId, agentName } = options2;
|
|
15414
|
+
const timestamp = Date.now();
|
|
15415
|
+
await appendEvent(createEvent("message_acked", {
|
|
15416
|
+
project_key: projectPath,
|
|
15417
|
+
message_id: messageId,
|
|
15418
|
+
agent_name: agentName
|
|
15419
|
+
}), projectPath);
|
|
15420
|
+
return {
|
|
15421
|
+
acknowledged: true,
|
|
15422
|
+
acknowledgedAt: new Date(timestamp).toISOString()
|
|
15423
|
+
};
|
|
15424
|
+
}
|
|
15425
|
+
async function checkSwarmHealth(projectPath) {
|
|
15426
|
+
const healthy = await isDatabaseHealthy(projectPath);
|
|
15427
|
+
if (!healthy) {
|
|
15428
|
+
return {
|
|
15429
|
+
healthy: false,
|
|
15430
|
+
database: "disconnected"
|
|
15431
|
+
};
|
|
15432
|
+
}
|
|
15433
|
+
const stats = await getDatabaseStats(projectPath);
|
|
15434
|
+
return {
|
|
15435
|
+
healthy: true,
|
|
15436
|
+
database: "connected",
|
|
15437
|
+
stats
|
|
15438
|
+
};
|
|
15439
|
+
}
|
|
15440
|
+
var MAX_INBOX_LIMIT2 = 5, DEFAULT_TTL_SECONDS2 = 3600, ADJECTIVES2, NOUNS2;
|
|
15441
|
+
var init_swarm_mail = __esm(() => {
|
|
15442
|
+
init_events();
|
|
15443
|
+
init_streams();
|
|
15444
|
+
init_projections();
|
|
15445
|
+
init_store();
|
|
15446
|
+
ADJECTIVES2 = [
|
|
15447
|
+
"Blue",
|
|
15448
|
+
"Red",
|
|
15449
|
+
"Green",
|
|
15450
|
+
"Gold",
|
|
15451
|
+
"Silver",
|
|
15452
|
+
"Swift",
|
|
15453
|
+
"Bright",
|
|
15454
|
+
"Dark",
|
|
15455
|
+
"Calm",
|
|
15456
|
+
"Bold",
|
|
15457
|
+
"Wise",
|
|
15458
|
+
"Quick",
|
|
15459
|
+
"Warm",
|
|
15460
|
+
"Cool",
|
|
15461
|
+
"Pure",
|
|
15462
|
+
"Wild"
|
|
15463
|
+
];
|
|
15464
|
+
NOUNS2 = [
|
|
15465
|
+
"Lake",
|
|
15466
|
+
"Stone",
|
|
15467
|
+
"River",
|
|
15468
|
+
"Mountain",
|
|
15469
|
+
"Forest",
|
|
15470
|
+
"Ocean",
|
|
15471
|
+
"Star",
|
|
15472
|
+
"Moon",
|
|
15473
|
+
"Wind",
|
|
15474
|
+
"Fire",
|
|
15475
|
+
"Cloud",
|
|
15476
|
+
"Storm",
|
|
15477
|
+
"Dawn",
|
|
15478
|
+
"Dusk",
|
|
15479
|
+
"Hawk",
|
|
15480
|
+
"Wolf"
|
|
15480
15481
|
];
|
|
15481
15482
|
});
|
|
15482
15483
|
|
|
@@ -15494,7 +15495,7 @@ __export(exports_streams, {
|
|
|
15494
15495
|
reserveSwarmFiles: () => reserveSwarmFiles,
|
|
15495
15496
|
reserveFiles: () => reserveFiles,
|
|
15496
15497
|
reserveAgentFiles: () => reserveAgentFiles,
|
|
15497
|
-
replayEventsBatched: () =>
|
|
15498
|
+
replayEventsBatched: () => replayEventsBatched2,
|
|
15498
15499
|
replayEvents: () => replayEvents,
|
|
15499
15500
|
releaseSwarmFiles: () => releaseSwarmFiles,
|
|
15500
15501
|
releaseAgentFiles: () => releaseAgentFiles,
|
|
@@ -15516,6 +15517,8 @@ __export(exports_streams, {
|
|
|
15516
15517
|
getLatestSequence: () => getLatestSequence,
|
|
15517
15518
|
getInbox: () => getInbox,
|
|
15518
15519
|
getEventTimeline: () => getEventTimeline,
|
|
15520
|
+
getEvalStats: () => getEvalStats,
|
|
15521
|
+
getEvalRecords: () => getEvalRecords,
|
|
15519
15522
|
getDatabaseStats: () => getDatabaseStats,
|
|
15520
15523
|
getDatabasePath: () => getDatabasePath,
|
|
15521
15524
|
getDatabase: () => getDatabase,
|
|
@@ -15543,12 +15546,17 @@ __export(exports_streams, {
|
|
|
15543
15546
|
TaskProgressEventSchema: () => TaskProgressEventSchema,
|
|
15544
15547
|
TaskCompletedEventSchema: () => TaskCompletedEventSchema,
|
|
15545
15548
|
TaskBlockedEventSchema: () => TaskBlockedEventSchema,
|
|
15549
|
+
SwarmRecoveredEventSchema: () => SwarmRecoveredEventSchema,
|
|
15550
|
+
SwarmCheckpointedEventSchema: () => SwarmCheckpointedEventSchema,
|
|
15551
|
+
SubtaskOutcomeEventSchema: () => SubtaskOutcomeEventSchema,
|
|
15546
15552
|
PGlite: () => PGlite,
|
|
15547
15553
|
MessageSentEventSchema: () => MessageSentEventSchema,
|
|
15548
15554
|
MessageReadEventSchema: () => MessageReadEventSchema,
|
|
15549
15555
|
MessageAckedEventSchema: () => MessageAckedEventSchema,
|
|
15556
|
+
HumanFeedbackEventSchema: () => HumanFeedbackEventSchema,
|
|
15550
15557
|
FileReservedEventSchema: () => FileReservedEventSchema,
|
|
15551
15558
|
FileReleasedEventSchema: () => FileReleasedEventSchema,
|
|
15559
|
+
DecompositionGeneratedEventSchema: () => DecompositionGeneratedEventSchema,
|
|
15552
15560
|
BaseEventSchema: () => BaseEventSchema,
|
|
15553
15561
|
AgentRegisteredEventSchema: () => AgentRegisteredEventSchema,
|
|
15554
15562
|
AgentEventSchema: () => AgentEventSchema,
|
|
@@ -15829,271 +15837,627 @@ async function isDatabaseHealthy(projectPath) {
|
|
|
15829
15837
|
return false;
|
|
15830
15838
|
}
|
|
15831
15839
|
}
|
|
15832
|
-
async function getDatabaseStats(projectPath) {
|
|
15840
|
+
async function getDatabaseStats(projectPath) {
|
|
15841
|
+
const db = await getDatabase(projectPath);
|
|
15842
|
+
const [events2, agents, messages, reservations] = await Promise.all([
|
|
15843
|
+
db.query("SELECT COUNT(*) as count FROM events"),
|
|
15844
|
+
db.query("SELECT COUNT(*) as count FROM agents"),
|
|
15845
|
+
db.query("SELECT COUNT(*) as count FROM messages"),
|
|
15846
|
+
db.query("SELECT COUNT(*) as count FROM reservations WHERE released_at IS NULL")
|
|
15847
|
+
]);
|
|
15848
|
+
return {
|
|
15849
|
+
events: parseInt(events2.rows[0]?.count || "0"),
|
|
15850
|
+
agents: parseInt(agents.rows[0]?.count || "0"),
|
|
15851
|
+
messages: parseInt(messages.rows[0]?.count || "0"),
|
|
15852
|
+
reservations: parseInt(reservations.rows[0]?.count || "0")
|
|
15853
|
+
};
|
|
15854
|
+
}
|
|
15855
|
+
function handleExit() {
|
|
15856
|
+
const dbsToClose = Array.from(instances.values());
|
|
15857
|
+
for (const db of dbsToClose) {
|
|
15858
|
+
try {
|
|
15859
|
+
db.close().catch(() => {});
|
|
15860
|
+
} catch {}
|
|
15861
|
+
}
|
|
15862
|
+
}
|
|
15863
|
+
var SLOW_QUERY_THRESHOLD_MS = 100, DEBUG_LOG_PATH, instances, pendingInstances, schemaInitialized, degradedInstances, lastAccess, MAX_CACHE_SIZE = 10;
|
|
15864
|
+
var init_streams = __esm(() => {
|
|
15865
|
+
init_agent_mail();
|
|
15866
|
+
init_debug();
|
|
15867
|
+
init_events();
|
|
15868
|
+
init_migrations();
|
|
15869
|
+
init_projections();
|
|
15870
|
+
init_store();
|
|
15871
|
+
init_swarm_mail();
|
|
15872
|
+
DEBUG_LOG_PATH = join(homedir(), ".opencode", "streams-debug.log");
|
|
15873
|
+
instances = new Map;
|
|
15874
|
+
pendingInstances = new Map;
|
|
15875
|
+
schemaInitialized = new Map;
|
|
15876
|
+
degradedInstances = new Map;
|
|
15877
|
+
lastAccess = new Map;
|
|
15878
|
+
process.on("exit", handleExit);
|
|
15879
|
+
process.on("SIGINT", () => {
|
|
15880
|
+
handleExit();
|
|
15881
|
+
process.exit(0);
|
|
15882
|
+
});
|
|
15883
|
+
process.on("SIGTERM", () => {
|
|
15884
|
+
handleExit();
|
|
15885
|
+
process.exit(0);
|
|
15886
|
+
});
|
|
15887
|
+
});
|
|
15888
|
+
|
|
15889
|
+
// src/streams/store.ts
|
|
15890
|
+
function parseTimestamp(timestamp) {
|
|
15891
|
+
const ts = parseInt(timestamp, 10);
|
|
15892
|
+
if (Number.isNaN(ts)) {
|
|
15893
|
+
throw new Error(`[SwarmMail] Invalid timestamp: ${timestamp}`);
|
|
15894
|
+
}
|
|
15895
|
+
if (ts > Number.MAX_SAFE_INTEGER) {
|
|
15896
|
+
console.warn(`[SwarmMail] Timestamp ${timestamp} exceeds MAX_SAFE_INTEGER (year 2286+), precision may be lost`);
|
|
15897
|
+
}
|
|
15898
|
+
return ts;
|
|
15899
|
+
}
|
|
15900
|
+
async function appendEvent(event, projectPath) {
|
|
15901
|
+
const db = await getDatabase(projectPath);
|
|
15902
|
+
const { type, project_key, timestamp, ...rest } = event;
|
|
15903
|
+
console.log("[SwarmMail] Appending event", {
|
|
15904
|
+
type,
|
|
15905
|
+
projectKey: project_key,
|
|
15906
|
+
timestamp
|
|
15907
|
+
});
|
|
15908
|
+
const result = await db.query(`INSERT INTO events (type, project_key, timestamp, data)
|
|
15909
|
+
VALUES ($1, $2, $3, $4)
|
|
15910
|
+
RETURNING id, sequence`, [type, project_key, timestamp, JSON.stringify(rest)]);
|
|
15911
|
+
const row = result.rows[0];
|
|
15912
|
+
if (!row) {
|
|
15913
|
+
throw new Error("Failed to insert event - no row returned");
|
|
15914
|
+
}
|
|
15915
|
+
const { id, sequence } = row;
|
|
15916
|
+
console.log("[SwarmMail] Event appended", {
|
|
15917
|
+
type,
|
|
15918
|
+
id,
|
|
15919
|
+
sequence,
|
|
15920
|
+
projectKey: project_key
|
|
15921
|
+
});
|
|
15922
|
+
console.debug("[SwarmMail] Updating materialized views", { type, id });
|
|
15923
|
+
await updateMaterializedViews(db, { ...event, id, sequence });
|
|
15924
|
+
return { ...event, id, sequence };
|
|
15925
|
+
}
|
|
15926
|
+
async function appendEvents(events2, projectPath) {
|
|
15927
|
+
return withTiming("appendEvents", async () => {
|
|
15928
|
+
const db = await getDatabase(projectPath);
|
|
15929
|
+
const results = [];
|
|
15930
|
+
await db.exec("BEGIN");
|
|
15931
|
+
try {
|
|
15932
|
+
for (const event of events2) {
|
|
15933
|
+
const { type, project_key, timestamp, ...rest } = event;
|
|
15934
|
+
const result = await db.query(`INSERT INTO events (type, project_key, timestamp, data)
|
|
15935
|
+
VALUES ($1, $2, $3, $4)
|
|
15936
|
+
RETURNING id, sequence`, [type, project_key, timestamp, JSON.stringify(rest)]);
|
|
15937
|
+
const row = result.rows[0];
|
|
15938
|
+
if (!row) {
|
|
15939
|
+
throw new Error("Failed to insert event - no row returned");
|
|
15940
|
+
}
|
|
15941
|
+
const { id, sequence } = row;
|
|
15942
|
+
const enrichedEvent = { ...event, id, sequence };
|
|
15943
|
+
await updateMaterializedViews(db, enrichedEvent);
|
|
15944
|
+
results.push(enrichedEvent);
|
|
15945
|
+
}
|
|
15946
|
+
await db.exec("COMMIT");
|
|
15947
|
+
} catch (e) {
|
|
15948
|
+
let rollbackError = null;
|
|
15949
|
+
try {
|
|
15950
|
+
await db.exec("ROLLBACK");
|
|
15951
|
+
} catch (rbErr) {
|
|
15952
|
+
rollbackError = rbErr;
|
|
15953
|
+
console.error("[SwarmMail] ROLLBACK failed:", rbErr);
|
|
15954
|
+
}
|
|
15955
|
+
if (rollbackError) {
|
|
15956
|
+
const compositeError = new Error(`Transaction failed: ${e instanceof Error ? e.message : String(e)}. ` + `ROLLBACK also failed: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}. ` + `Database may be in inconsistent state.`);
|
|
15957
|
+
compositeError.originalError = e;
|
|
15958
|
+
compositeError.rollbackError = rollbackError;
|
|
15959
|
+
throw compositeError;
|
|
15960
|
+
}
|
|
15961
|
+
throw e;
|
|
15962
|
+
}
|
|
15963
|
+
return results;
|
|
15964
|
+
});
|
|
15965
|
+
}
|
|
15966
|
+
async function readEvents(options2 = {}, projectPath) {
|
|
15967
|
+
return withTiming("readEvents", async () => {
|
|
15968
|
+
const db = await getDatabase(projectPath);
|
|
15969
|
+
const conditions = [];
|
|
15970
|
+
const params = [];
|
|
15971
|
+
let paramIndex = 1;
|
|
15972
|
+
if (options2.projectKey) {
|
|
15973
|
+
conditions.push(`project_key = $${paramIndex++}`);
|
|
15974
|
+
params.push(options2.projectKey);
|
|
15975
|
+
}
|
|
15976
|
+
if (options2.types && options2.types.length > 0) {
|
|
15977
|
+
conditions.push(`type = ANY($${paramIndex++})`);
|
|
15978
|
+
params.push(options2.types);
|
|
15979
|
+
}
|
|
15980
|
+
if (options2.since !== undefined) {
|
|
15981
|
+
conditions.push(`timestamp >= $${paramIndex++}`);
|
|
15982
|
+
params.push(options2.since);
|
|
15983
|
+
}
|
|
15984
|
+
if (options2.until !== undefined) {
|
|
15985
|
+
conditions.push(`timestamp <= $${paramIndex++}`);
|
|
15986
|
+
params.push(options2.until);
|
|
15987
|
+
}
|
|
15988
|
+
if (options2.afterSequence !== undefined) {
|
|
15989
|
+
conditions.push(`sequence > $${paramIndex++}`);
|
|
15990
|
+
params.push(options2.afterSequence);
|
|
15991
|
+
}
|
|
15992
|
+
const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
15993
|
+
let query = `
|
|
15994
|
+
SELECT id, type, project_key, timestamp, sequence, data
|
|
15995
|
+
FROM events
|
|
15996
|
+
${whereClause}
|
|
15997
|
+
ORDER BY sequence ASC
|
|
15998
|
+
`;
|
|
15999
|
+
if (options2.limit) {
|
|
16000
|
+
query += ` LIMIT $${paramIndex++}`;
|
|
16001
|
+
params.push(options2.limit);
|
|
16002
|
+
}
|
|
16003
|
+
if (options2.offset) {
|
|
16004
|
+
query += ` OFFSET $${paramIndex++}`;
|
|
16005
|
+
params.push(options2.offset);
|
|
16006
|
+
}
|
|
16007
|
+
const result = await db.query(query, params);
|
|
16008
|
+
return result.rows.map((row) => {
|
|
16009
|
+
const data = typeof row.data === "string" ? JSON.parse(row.data) : row.data;
|
|
16010
|
+
return {
|
|
16011
|
+
id: row.id,
|
|
16012
|
+
type: row.type,
|
|
16013
|
+
project_key: row.project_key,
|
|
16014
|
+
timestamp: parseTimestamp(row.timestamp),
|
|
16015
|
+
sequence: row.sequence,
|
|
16016
|
+
...data
|
|
16017
|
+
};
|
|
16018
|
+
});
|
|
16019
|
+
});
|
|
16020
|
+
}
|
|
16021
|
+
async function getLatestSequence(projectKey, projectPath) {
|
|
15833
16022
|
const db = await getDatabase(projectPath);
|
|
15834
|
-
const
|
|
15835
|
-
|
|
15836
|
-
|
|
15837
|
-
|
|
15838
|
-
db.query("SELECT COUNT(*) as count FROM reservations WHERE released_at IS NULL")
|
|
15839
|
-
]);
|
|
15840
|
-
return {
|
|
15841
|
-
events: parseInt(events2.rows[0]?.count || "0"),
|
|
15842
|
-
agents: parseInt(agents.rows[0]?.count || "0"),
|
|
15843
|
-
messages: parseInt(messages.rows[0]?.count || "0"),
|
|
15844
|
-
reservations: parseInt(reservations.rows[0]?.count || "0")
|
|
15845
|
-
};
|
|
15846
|
-
}
|
|
15847
|
-
function handleExit() {
|
|
15848
|
-
const dbsToClose = Array.from(instances.values());
|
|
15849
|
-
for (const db of dbsToClose) {
|
|
15850
|
-
try {
|
|
15851
|
-
db.close().catch(() => {});
|
|
15852
|
-
} catch {}
|
|
15853
|
-
}
|
|
16023
|
+
const query = projectKey ? "SELECT MAX(sequence) as seq FROM events WHERE project_key = $1" : "SELECT MAX(sequence) as seq FROM events";
|
|
16024
|
+
const params = projectKey ? [projectKey] : [];
|
|
16025
|
+
const result = await db.query(query, params);
|
|
16026
|
+
return result.rows[0]?.seq ?? 0;
|
|
15854
16027
|
}
|
|
15855
|
-
|
|
15856
|
-
|
|
15857
|
-
|
|
15858
|
-
|
|
15859
|
-
|
|
15860
|
-
|
|
15861
|
-
|
|
15862
|
-
|
|
15863
|
-
|
|
15864
|
-
|
|
15865
|
-
|
|
15866
|
-
|
|
15867
|
-
|
|
15868
|
-
|
|
15869
|
-
|
|
15870
|
-
|
|
15871
|
-
|
|
15872
|
-
|
|
15873
|
-
|
|
15874
|
-
|
|
15875
|
-
|
|
15876
|
-
|
|
15877
|
-
|
|
16028
|
+
async function replayEvents(options2 = {}, projectPath) {
|
|
16029
|
+
return withTiming("replayEvents", async () => {
|
|
16030
|
+
const startTime = Date.now();
|
|
16031
|
+
const db = await getDatabase(projectPath);
|
|
16032
|
+
if (options2.clearViews) {
|
|
16033
|
+
if (options2.projectKey) {
|
|
16034
|
+
await db.query(`DELETE FROM message_recipients WHERE message_id IN (
|
|
16035
|
+
SELECT id FROM messages WHERE project_key = $1
|
|
16036
|
+
)`, [options2.projectKey]);
|
|
16037
|
+
await db.query(`DELETE FROM messages WHERE project_key = $1`, [
|
|
16038
|
+
options2.projectKey
|
|
16039
|
+
]);
|
|
16040
|
+
await db.query(`DELETE FROM reservations WHERE project_key = $1`, [
|
|
16041
|
+
options2.projectKey
|
|
16042
|
+
]);
|
|
16043
|
+
await db.query(`DELETE FROM agents WHERE project_key = $1`, [
|
|
16044
|
+
options2.projectKey
|
|
16045
|
+
]);
|
|
16046
|
+
} else {
|
|
16047
|
+
await db.exec(`
|
|
16048
|
+
DELETE FROM message_recipients;
|
|
16049
|
+
DELETE FROM messages;
|
|
16050
|
+
DELETE FROM reservations;
|
|
16051
|
+
DELETE FROM agents;
|
|
16052
|
+
`);
|
|
16053
|
+
}
|
|
16054
|
+
}
|
|
16055
|
+
const events2 = await readEvents({
|
|
16056
|
+
projectKey: options2.projectKey,
|
|
16057
|
+
afterSequence: options2.fromSequence
|
|
16058
|
+
}, projectPath);
|
|
16059
|
+
for (const event of events2) {
|
|
16060
|
+
await updateMaterializedViews(db, event);
|
|
16061
|
+
}
|
|
16062
|
+
return {
|
|
16063
|
+
eventsReplayed: events2.length,
|
|
16064
|
+
duration: Date.now() - startTime
|
|
16065
|
+
};
|
|
15878
16066
|
});
|
|
15879
|
-
});
|
|
15880
|
-
|
|
15881
|
-
// src/streams/swarm-mail.ts
|
|
15882
|
-
function generateSwarmAgentName() {
|
|
15883
|
-
const adj = ADJECTIVES2[Math.floor(Math.random() * ADJECTIVES2.length)];
|
|
15884
|
-
const noun = NOUNS2[Math.floor(Math.random() * NOUNS2.length)];
|
|
15885
|
-
return `${adj}${noun}`;
|
|
15886
16067
|
}
|
|
15887
|
-
async function
|
|
15888
|
-
|
|
15889
|
-
|
|
15890
|
-
|
|
15891
|
-
|
|
15892
|
-
|
|
15893
|
-
|
|
15894
|
-
|
|
15895
|
-
|
|
15896
|
-
|
|
15897
|
-
|
|
15898
|
-
|
|
15899
|
-
|
|
16068
|
+
async function replayEventsBatched2(projectKey, onBatch, options2 = {}, projectPath) {
|
|
16069
|
+
return withTiming("replayEventsBatched", async () => {
|
|
16070
|
+
const startTime = Date.now();
|
|
16071
|
+
const batchSize = options2.batchSize ?? 1000;
|
|
16072
|
+
const fromSequence = options2.fromSequence ?? 0;
|
|
16073
|
+
const db = await getDatabase(projectPath);
|
|
16074
|
+
if (options2.clearViews) {
|
|
16075
|
+
await db.query(`DELETE FROM message_recipients WHERE message_id IN (
|
|
16076
|
+
SELECT id FROM messages WHERE project_key = $1
|
|
16077
|
+
)`, [projectKey]);
|
|
16078
|
+
await db.query(`DELETE FROM messages WHERE project_key = $1`, [
|
|
16079
|
+
projectKey
|
|
16080
|
+
]);
|
|
16081
|
+
await db.query(`DELETE FROM reservations WHERE project_key = $1`, [
|
|
16082
|
+
projectKey
|
|
16083
|
+
]);
|
|
16084
|
+
await db.query(`DELETE FROM agents WHERE project_key = $1`, [projectKey]);
|
|
16085
|
+
}
|
|
16086
|
+
const countResult = await db.query(`SELECT COUNT(*) as count FROM events WHERE project_key = $1 AND sequence > $2`, [projectKey, fromSequence]);
|
|
16087
|
+
const total = parseInt(countResult.rows[0]?.count ?? "0");
|
|
16088
|
+
if (total === 0) {
|
|
16089
|
+
return { eventsReplayed: 0, duration: Date.now() - startTime };
|
|
16090
|
+
}
|
|
16091
|
+
let processed = 0;
|
|
16092
|
+
let offset = 0;
|
|
16093
|
+
while (processed < total) {
|
|
16094
|
+
const events2 = await readEvents({
|
|
16095
|
+
projectKey,
|
|
16096
|
+
afterSequence: fromSequence,
|
|
16097
|
+
limit: batchSize,
|
|
16098
|
+
offset
|
|
16099
|
+
}, projectPath);
|
|
16100
|
+
if (events2.length === 0)
|
|
16101
|
+
break;
|
|
16102
|
+
for (const event of events2) {
|
|
16103
|
+
await updateMaterializedViews(db, event);
|
|
16104
|
+
}
|
|
16105
|
+
processed += events2.length;
|
|
16106
|
+
const percent = Math.round(processed / total * 100);
|
|
16107
|
+
await onBatch(events2, { processed, total, percent });
|
|
16108
|
+
console.log(`[SwarmMail] Replaying events: ${processed}/${total} (${percent}%)`);
|
|
16109
|
+
offset += batchSize;
|
|
16110
|
+
}
|
|
16111
|
+
return {
|
|
16112
|
+
eventsReplayed: processed,
|
|
16113
|
+
duration: Date.now() - startTime
|
|
16114
|
+
};
|
|
16115
|
+
});
|
|
15900
16116
|
}
|
|
15901
|
-
async function
|
|
15902
|
-
|
|
15903
|
-
|
|
15904
|
-
|
|
15905
|
-
|
|
15906
|
-
|
|
15907
|
-
|
|
15908
|
-
|
|
15909
|
-
|
|
15910
|
-
|
|
15911
|
-
|
|
15912
|
-
|
|
15913
|
-
|
|
15914
|
-
|
|
15915
|
-
|
|
15916
|
-
|
|
15917
|
-
|
|
15918
|
-
|
|
15919
|
-
|
|
15920
|
-
|
|
15921
|
-
|
|
15922
|
-
|
|
15923
|
-
|
|
15924
|
-
|
|
16117
|
+
async function updateMaterializedViews(db, event) {
|
|
16118
|
+
try {
|
|
16119
|
+
switch (event.type) {
|
|
16120
|
+
case "agent_registered":
|
|
16121
|
+
await handleAgentRegistered(db, event);
|
|
16122
|
+
break;
|
|
16123
|
+
case "agent_active":
|
|
16124
|
+
await db.query(`UPDATE agents SET last_active_at = $1 WHERE project_key = $2 AND name = $3`, [event.timestamp, event.project_key, event.agent_name]);
|
|
16125
|
+
break;
|
|
16126
|
+
case "message_sent":
|
|
16127
|
+
await handleMessageSent(db, event);
|
|
16128
|
+
break;
|
|
16129
|
+
case "message_read":
|
|
16130
|
+
await db.query(`UPDATE message_recipients SET read_at = $1 WHERE message_id = $2 AND agent_name = $3`, [event.timestamp, event.message_id, event.agent_name]);
|
|
16131
|
+
break;
|
|
16132
|
+
case "message_acked":
|
|
16133
|
+
await db.query(`UPDATE message_recipients SET acked_at = $1 WHERE message_id = $2 AND agent_name = $3`, [event.timestamp, event.message_id, event.agent_name]);
|
|
16134
|
+
break;
|
|
16135
|
+
case "file_reserved":
|
|
16136
|
+
await handleFileReserved(db, event);
|
|
16137
|
+
break;
|
|
16138
|
+
case "file_released":
|
|
16139
|
+
await handleFileReleased(db, event);
|
|
16140
|
+
break;
|
|
16141
|
+
case "task_started":
|
|
16142
|
+
case "task_progress":
|
|
16143
|
+
case "task_completed":
|
|
16144
|
+
case "task_blocked":
|
|
16145
|
+
break;
|
|
16146
|
+
case "decomposition_generated":
|
|
16147
|
+
await handleDecompositionGenerated(db, event);
|
|
16148
|
+
break;
|
|
16149
|
+
case "subtask_outcome":
|
|
16150
|
+
await handleSubtaskOutcome(db, event);
|
|
16151
|
+
break;
|
|
16152
|
+
case "human_feedback":
|
|
16153
|
+
await handleHumanFeedback(db, event);
|
|
16154
|
+
break;
|
|
16155
|
+
case "swarm_checkpointed":
|
|
16156
|
+
await handleSwarmCheckpointed(db, event);
|
|
16157
|
+
break;
|
|
16158
|
+
case "swarm_recovered":
|
|
16159
|
+
await handleSwarmRecovered(db, event);
|
|
16160
|
+
break;
|
|
16161
|
+
}
|
|
16162
|
+
} catch (error45) {
|
|
16163
|
+
console.error("[SwarmMail] Failed to update materialized views", {
|
|
16164
|
+
eventType: event.type,
|
|
16165
|
+
eventId: event.id,
|
|
16166
|
+
error: error45
|
|
16167
|
+
});
|
|
16168
|
+
throw error45;
|
|
16169
|
+
}
|
|
15925
16170
|
}
|
|
15926
|
-
async function
|
|
15927
|
-
|
|
15928
|
-
|
|
15929
|
-
|
|
15930
|
-
|
|
15931
|
-
|
|
15932
|
-
|
|
15933
|
-
|
|
15934
|
-
|
|
15935
|
-
|
|
15936
|
-
|
|
15937
|
-
|
|
15938
|
-
|
|
15939
|
-
|
|
15940
|
-
|
|
15941
|
-
}, projectPath);
|
|
15942
|
-
return {
|
|
15943
|
-
messages: messages.map((m) => ({
|
|
15944
|
-
id: m.id,
|
|
15945
|
-
from_agent: m.from_agent,
|
|
15946
|
-
subject: m.subject,
|
|
15947
|
-
body: includeBodies ? m.body : undefined,
|
|
15948
|
-
thread_id: m.thread_id,
|
|
15949
|
-
importance: m.importance,
|
|
15950
|
-
created_at: m.created_at
|
|
15951
|
-
})),
|
|
15952
|
-
total: messages.length
|
|
15953
|
-
};
|
|
16171
|
+
async function handleAgentRegistered(db, event) {
|
|
16172
|
+
await db.query(`INSERT INTO agents (project_key, name, program, model, task_description, registered_at, last_active_at)
|
|
16173
|
+
VALUES ($1, $2, $3, $4, $5, $6, $6)
|
|
16174
|
+
ON CONFLICT (project_key, name) DO UPDATE SET
|
|
16175
|
+
program = EXCLUDED.program,
|
|
16176
|
+
model = EXCLUDED.model,
|
|
16177
|
+
task_description = EXCLUDED.task_description,
|
|
16178
|
+
last_active_at = EXCLUDED.last_active_at`, [
|
|
16179
|
+
event.project_key,
|
|
16180
|
+
event.agent_name,
|
|
16181
|
+
event.program,
|
|
16182
|
+
event.model,
|
|
16183
|
+
event.task_description || null,
|
|
16184
|
+
event.timestamp
|
|
16185
|
+
]);
|
|
15954
16186
|
}
|
|
15955
|
-
async function
|
|
15956
|
-
|
|
15957
|
-
|
|
15958
|
-
|
|
15959
|
-
|
|
16187
|
+
async function handleMessageSent(db, event) {
|
|
16188
|
+
console.log("[SwarmMail] Handling message sent event", {
|
|
16189
|
+
from: event.from_agent,
|
|
16190
|
+
to: event.to_agents,
|
|
16191
|
+
subject: event.subject,
|
|
16192
|
+
projectKey: event.project_key
|
|
16193
|
+
});
|
|
16194
|
+
const result = await db.query(`INSERT INTO messages (project_key, from_agent, subject, body, thread_id, importance, ack_required, created_at)
|
|
16195
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
16196
|
+
RETURNING id`, [
|
|
16197
|
+
event.project_key,
|
|
16198
|
+
event.from_agent,
|
|
16199
|
+
event.subject,
|
|
16200
|
+
event.body,
|
|
16201
|
+
event.thread_id || null,
|
|
16202
|
+
event.importance,
|
|
16203
|
+
event.ack_required,
|
|
16204
|
+
event.timestamp
|
|
16205
|
+
]);
|
|
16206
|
+
const msgRow = result.rows[0];
|
|
16207
|
+
if (!msgRow) {
|
|
16208
|
+
throw new Error("Failed to insert message - no row returned");
|
|
15960
16209
|
}
|
|
15961
|
-
|
|
15962
|
-
|
|
15963
|
-
|
|
15964
|
-
|
|
15965
|
-
|
|
15966
|
-
|
|
16210
|
+
const messageId = msgRow.id;
|
|
16211
|
+
if (event.to_agents.length > 0) {
|
|
16212
|
+
const values = event.to_agents.map((_, i) => `($1, $${i + 2})`).join(", ");
|
|
16213
|
+
const params = [messageId, ...event.to_agents];
|
|
16214
|
+
await db.query(`INSERT INTO message_recipients (message_id, agent_name)
|
|
16215
|
+
VALUES ${values}
|
|
16216
|
+
ON CONFLICT DO NOTHING`, params);
|
|
16217
|
+
console.log("[SwarmMail] Message recipients inserted", {
|
|
16218
|
+
messageId,
|
|
16219
|
+
recipientCount: event.to_agents.length
|
|
16220
|
+
});
|
|
15967
16221
|
}
|
|
15968
|
-
return {
|
|
15969
|
-
id: message.id,
|
|
15970
|
-
from_agent: message.from_agent,
|
|
15971
|
-
subject: message.subject,
|
|
15972
|
-
body: message.body,
|
|
15973
|
-
thread_id: message.thread_id,
|
|
15974
|
-
importance: message.importance,
|
|
15975
|
-
created_at: message.created_at
|
|
15976
|
-
};
|
|
15977
16222
|
}
|
|
15978
|
-
async function
|
|
15979
|
-
|
|
15980
|
-
|
|
15981
|
-
|
|
15982
|
-
|
|
15983
|
-
|
|
15984
|
-
|
|
15985
|
-
|
|
15986
|
-
|
|
15987
|
-
|
|
15988
|
-
|
|
15989
|
-
|
|
15990
|
-
|
|
15991
|
-
|
|
15992
|
-
|
|
15993
|
-
|
|
15994
|
-
|
|
15995
|
-
|
|
15996
|
-
|
|
15997
|
-
|
|
15998
|
-
|
|
15999
|
-
|
|
16000
|
-
|
|
16001
|
-
|
|
16002
|
-
}
|
|
16003
|
-
|
|
16223
|
+
async function handleFileReserved(db, event) {
|
|
16224
|
+
console.log("[SwarmMail] Handling file reservation event", {
|
|
16225
|
+
agent: event.agent_name,
|
|
16226
|
+
paths: event.paths,
|
|
16227
|
+
exclusive: event.exclusive,
|
|
16228
|
+
projectKey: event.project_key
|
|
16229
|
+
});
|
|
16230
|
+
if (event.paths.length > 0) {
|
|
16231
|
+
const values = event.paths.map((_, i) => `($1, $2, $${i + 3}, $${event.paths.length + 3}, $${event.paths.length + 4}, $${event.paths.length + 5}, $${event.paths.length + 6})`).join(", ");
|
|
16232
|
+
const params = [
|
|
16233
|
+
event.project_key,
|
|
16234
|
+
event.agent_name,
|
|
16235
|
+
...event.paths,
|
|
16236
|
+
event.exclusive,
|
|
16237
|
+
event.reason || null,
|
|
16238
|
+
event.timestamp,
|
|
16239
|
+
event.expires_at
|
|
16240
|
+
];
|
|
16241
|
+
if (event.paths.length > 0) {
|
|
16242
|
+
await db.query(`DELETE FROM reservations
|
|
16243
|
+
WHERE project_key = $1
|
|
16244
|
+
AND agent_name = $2
|
|
16245
|
+
AND path_pattern = ANY($3)
|
|
16246
|
+
AND released_at IS NULL`, [event.project_key, event.agent_name, event.paths]);
|
|
16247
|
+
}
|
|
16248
|
+
await db.query(`INSERT INTO reservations (project_key, agent_name, path_pattern, exclusive, reason, created_at, expires_at)
|
|
16249
|
+
VALUES ${values}`, params);
|
|
16250
|
+
console.log("[SwarmMail] File reservations inserted", {
|
|
16251
|
+
agent: event.agent_name,
|
|
16252
|
+
reservationCount: event.paths.length
|
|
16253
|
+
});
|
|
16254
|
+
}
|
|
16004
16255
|
}
|
|
16005
|
-
async function
|
|
16006
|
-
|
|
16007
|
-
|
|
16008
|
-
|
|
16009
|
-
|
|
16010
|
-
|
|
16011
|
-
|
|
16012
|
-
|
|
16256
|
+
async function handleFileReleased(db, event) {
|
|
16257
|
+
if (event.type !== "file_released")
|
|
16258
|
+
return;
|
|
16259
|
+
if (event.reservation_ids && event.reservation_ids.length > 0) {
|
|
16260
|
+
await db.query(`UPDATE reservations SET released_at = $1 WHERE id = ANY($2)`, [event.timestamp, event.reservation_ids]);
|
|
16261
|
+
} else if (event.paths && event.paths.length > 0) {
|
|
16262
|
+
await db.query(`UPDATE reservations SET released_at = $1
|
|
16263
|
+
WHERE project_key = $2 AND agent_name = $3 AND path_pattern = ANY($4) AND released_at IS NULL`, [event.timestamp, event.project_key, event.agent_name, event.paths]);
|
|
16013
16264
|
} else {
|
|
16014
|
-
|
|
16265
|
+
await db.query(`UPDATE reservations SET released_at = $1
|
|
16266
|
+
WHERE project_key = $2 AND agent_name = $3 AND released_at IS NULL`, [event.timestamp, event.project_key, event.agent_name]);
|
|
16015
16267
|
}
|
|
16016
|
-
await appendEvent(createEvent("file_released", {
|
|
16017
|
-
project_key: projectPath,
|
|
16018
|
-
agent_name: agentName,
|
|
16019
|
-
paths,
|
|
16020
|
-
reservation_ids: reservationIds
|
|
16021
|
-
}), projectPath);
|
|
16022
|
-
return {
|
|
16023
|
-
released: releaseCount,
|
|
16024
|
-
releasedAt: Date.now()
|
|
16025
|
-
};
|
|
16026
16268
|
}
|
|
16027
|
-
async function
|
|
16028
|
-
|
|
16029
|
-
|
|
16030
|
-
await
|
|
16031
|
-
|
|
16032
|
-
|
|
16033
|
-
|
|
16034
|
-
|
|
16035
|
-
|
|
16036
|
-
|
|
16037
|
-
|
|
16038
|
-
|
|
16269
|
+
async function handleDecompositionGenerated(db, event) {
|
|
16270
|
+
if (event.type !== "decomposition_generated")
|
|
16271
|
+
return;
|
|
16272
|
+
await db.query(`INSERT INTO eval_records (
|
|
16273
|
+
id, project_key, task, context, strategy, epic_title, subtasks,
|
|
16274
|
+
created_at, updated_at
|
|
16275
|
+
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $8)
|
|
16276
|
+
ON CONFLICT (id) DO NOTHING`, [
|
|
16277
|
+
event.epic_id,
|
|
16278
|
+
event.project_key,
|
|
16279
|
+
event.task,
|
|
16280
|
+
event.context || null,
|
|
16281
|
+
event.strategy,
|
|
16282
|
+
event.epic_title,
|
|
16283
|
+
JSON.stringify(event.subtasks),
|
|
16284
|
+
event.timestamp
|
|
16285
|
+
]);
|
|
16039
16286
|
}
|
|
16040
|
-
async function
|
|
16041
|
-
|
|
16042
|
-
|
|
16043
|
-
|
|
16044
|
-
|
|
16045
|
-
|
|
16046
|
-
|
|
16287
|
+
async function handleSubtaskOutcome(db, event) {
|
|
16288
|
+
if (event.type !== "subtask_outcome")
|
|
16289
|
+
return;
|
|
16290
|
+
const result = await db.query(`SELECT outcomes, subtasks FROM eval_records WHERE id = $1`, [
|
|
16291
|
+
event.epic_id
|
|
16292
|
+
]);
|
|
16293
|
+
if (!result.rows[0]) {
|
|
16294
|
+
console.warn(`[SwarmMail] No eval_record found for epic_id ${event.epic_id}`);
|
|
16295
|
+
return;
|
|
16047
16296
|
}
|
|
16048
|
-
const
|
|
16049
|
-
|
|
16050
|
-
|
|
16051
|
-
|
|
16052
|
-
|
|
16053
|
-
|
|
16297
|
+
const row = result.rows[0];
|
|
16298
|
+
const subtasks = typeof row.subtasks === "string" ? JSON.parse(row.subtasks) : row.subtasks;
|
|
16299
|
+
const outcomes = row.outcomes ? typeof row.outcomes === "string" ? JSON.parse(row.outcomes) : row.outcomes : [];
|
|
16300
|
+
const newOutcome = {
|
|
16301
|
+
bead_id: event.bead_id,
|
|
16302
|
+
planned_files: event.planned_files,
|
|
16303
|
+
actual_files: event.actual_files,
|
|
16304
|
+
duration_ms: event.duration_ms,
|
|
16305
|
+
error_count: event.error_count,
|
|
16306
|
+
retry_count: event.retry_count,
|
|
16307
|
+
success: event.success
|
|
16308
|
+
};
|
|
16309
|
+
const updatedOutcomes = [...outcomes, newOutcome];
|
|
16310
|
+
const fileOverlapCount = computeFileOverlap(subtasks);
|
|
16311
|
+
const scopeAccuracy = computeScopeAccuracy(event.planned_files, event.actual_files);
|
|
16312
|
+
const timeBalanceRatio = computeTimeBalanceRatio(updatedOutcomes);
|
|
16313
|
+
const overallSuccess = updatedOutcomes.every((o) => o.success);
|
|
16314
|
+
const totalDurationMs = updatedOutcomes.reduce((sum, o) => sum + o.duration_ms, 0);
|
|
16315
|
+
const totalErrors = updatedOutcomes.reduce((sum, o) => sum + o.error_count, 0);
|
|
16316
|
+
await db.query(`UPDATE eval_records SET
|
|
16317
|
+
outcomes = $1,
|
|
16318
|
+
file_overlap_count = $2,
|
|
16319
|
+
scope_accuracy = $3,
|
|
16320
|
+
time_balance_ratio = $4,
|
|
16321
|
+
overall_success = $5,
|
|
16322
|
+
total_duration_ms = $6,
|
|
16323
|
+
total_errors = $7,
|
|
16324
|
+
updated_at = $8
|
|
16325
|
+
WHERE id = $9`, [
|
|
16326
|
+
JSON.stringify(updatedOutcomes),
|
|
16327
|
+
fileOverlapCount,
|
|
16328
|
+
scopeAccuracy,
|
|
16329
|
+
timeBalanceRatio,
|
|
16330
|
+
overallSuccess,
|
|
16331
|
+
totalDurationMs,
|
|
16332
|
+
totalErrors,
|
|
16333
|
+
event.timestamp,
|
|
16334
|
+
event.epic_id
|
|
16335
|
+
]);
|
|
16054
16336
|
}
|
|
16055
|
-
|
|
16056
|
-
|
|
16057
|
-
|
|
16058
|
-
|
|
16059
|
-
|
|
16060
|
-
|
|
16061
|
-
|
|
16062
|
-
|
|
16063
|
-
|
|
16064
|
-
|
|
16065
|
-
|
|
16066
|
-
|
|
16067
|
-
|
|
16068
|
-
|
|
16069
|
-
|
|
16070
|
-
|
|
16071
|
-
|
|
16072
|
-
|
|
16073
|
-
|
|
16074
|
-
|
|
16075
|
-
|
|
16076
|
-
|
|
16077
|
-
|
|
16078
|
-
|
|
16079
|
-
|
|
16080
|
-
|
|
16081
|
-
|
|
16082
|
-
|
|
16083
|
-
|
|
16084
|
-
|
|
16085
|
-
|
|
16086
|
-
|
|
16087
|
-
|
|
16088
|
-
|
|
16089
|
-
|
|
16090
|
-
|
|
16091
|
-
|
|
16092
|
-
|
|
16093
|
-
|
|
16094
|
-
|
|
16095
|
-
|
|
16096
|
-
|
|
16337
|
+
async function handleHumanFeedback(db, event) {
|
|
16338
|
+
if (event.type !== "human_feedback")
|
|
16339
|
+
return;
|
|
16340
|
+
await db.query(`UPDATE eval_records SET
|
|
16341
|
+
human_accepted = $1,
|
|
16342
|
+
human_modified = $2,
|
|
16343
|
+
human_notes = $3,
|
|
16344
|
+
updated_at = $4
|
|
16345
|
+
WHERE id = $5`, [
|
|
16346
|
+
event.accepted,
|
|
16347
|
+
event.modified,
|
|
16348
|
+
event.notes || null,
|
|
16349
|
+
event.timestamp,
|
|
16350
|
+
event.epic_id
|
|
16351
|
+
]);
|
|
16352
|
+
}
|
|
16353
|
+
async function handleSwarmCheckpointed(db, event) {
|
|
16354
|
+
if (event.type !== "swarm_checkpointed")
|
|
16355
|
+
return;
|
|
16356
|
+
await db.query(`INSERT INTO swarm_contexts (
|
|
16357
|
+
project_key, epic_id, bead_id, strategy, files, dependencies,
|
|
16358
|
+
directives, recovery, checkpointed_at, updated_at
|
|
16359
|
+
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $9)
|
|
16360
|
+
ON CONFLICT (project_key, epic_id, bead_id) DO UPDATE SET
|
|
16361
|
+
strategy = EXCLUDED.strategy,
|
|
16362
|
+
files = EXCLUDED.files,
|
|
16363
|
+
dependencies = EXCLUDED.dependencies,
|
|
16364
|
+
directives = EXCLUDED.directives,
|
|
16365
|
+
recovery = EXCLUDED.recovery,
|
|
16366
|
+
checkpointed_at = EXCLUDED.checkpointed_at,
|
|
16367
|
+
updated_at = EXCLUDED.updated_at`, [
|
|
16368
|
+
event.project_key,
|
|
16369
|
+
event.epic_id,
|
|
16370
|
+
event.bead_id,
|
|
16371
|
+
event.strategy,
|
|
16372
|
+
JSON.stringify(event.files),
|
|
16373
|
+
JSON.stringify(event.dependencies),
|
|
16374
|
+
JSON.stringify(event.directives),
|
|
16375
|
+
JSON.stringify(event.recovery),
|
|
16376
|
+
event.timestamp
|
|
16377
|
+
]);
|
|
16378
|
+
}
|
|
16379
|
+
async function handleSwarmRecovered(db, event) {
|
|
16380
|
+
if (event.type !== "swarm_recovered")
|
|
16381
|
+
return;
|
|
16382
|
+
await db.query(`UPDATE swarm_contexts SET
|
|
16383
|
+
recovered_at = $1,
|
|
16384
|
+
recovered_from_checkpoint = $2,
|
|
16385
|
+
updated_at = $1
|
|
16386
|
+
WHERE project_key = $3 AND epic_id = $4 AND bead_id = $5`, [
|
|
16387
|
+
event.timestamp,
|
|
16388
|
+
event.recovered_from_checkpoint,
|
|
16389
|
+
event.project_key,
|
|
16390
|
+
event.epic_id,
|
|
16391
|
+
event.bead_id
|
|
16392
|
+
]);
|
|
16393
|
+
}
|
|
16394
|
+
function computeFileOverlap(subtasks) {
|
|
16395
|
+
const fileCount = new Map;
|
|
16396
|
+
for (const subtask of subtasks) {
|
|
16397
|
+
for (const file2 of subtask.files) {
|
|
16398
|
+
fileCount.set(file2, (fileCount.get(file2) || 0) + 1);
|
|
16399
|
+
}
|
|
16400
|
+
}
|
|
16401
|
+
return Array.from(fileCount.values()).filter((count) => count > 1).length;
|
|
16402
|
+
}
|
|
16403
|
+
function computeScopeAccuracy(planned, actual) {
|
|
16404
|
+
if (planned.length === 0)
|
|
16405
|
+
return 1;
|
|
16406
|
+
const plannedSet = new Set(planned);
|
|
16407
|
+
const intersection2 = actual.filter((file2) => plannedSet.has(file2));
|
|
16408
|
+
return intersection2.length / planned.length;
|
|
16409
|
+
}
|
|
16410
|
+
function computeTimeBalanceRatio(outcomes) {
|
|
16411
|
+
if (outcomes.length === 0)
|
|
16412
|
+
return null;
|
|
16413
|
+
const durations = outcomes.map((o) => o.duration_ms);
|
|
16414
|
+
const max = Math.max(...durations);
|
|
16415
|
+
const min = Math.min(...durations);
|
|
16416
|
+
if (min === 0)
|
|
16417
|
+
return null;
|
|
16418
|
+
return max / min;
|
|
16419
|
+
}
|
|
16420
|
+
async function registerAgent(projectKey, agentName, options2 = {}, projectPath) {
|
|
16421
|
+
const event = createEvent("agent_registered", {
|
|
16422
|
+
project_key: projectKey,
|
|
16423
|
+
agent_name: agentName,
|
|
16424
|
+
program: options2.program || "opencode",
|
|
16425
|
+
model: options2.model || "unknown",
|
|
16426
|
+
task_description: options2.taskDescription
|
|
16427
|
+
});
|
|
16428
|
+
return appendEvent(event, projectPath);
|
|
16429
|
+
}
|
|
16430
|
+
async function sendMessage(projectKey, fromAgent, toAgents, subject, body, options2 = {}, projectPath) {
|
|
16431
|
+
const event = createEvent("message_sent", {
|
|
16432
|
+
project_key: projectKey,
|
|
16433
|
+
from_agent: fromAgent,
|
|
16434
|
+
to_agents: toAgents,
|
|
16435
|
+
subject,
|
|
16436
|
+
body,
|
|
16437
|
+
thread_id: options2.threadId,
|
|
16438
|
+
importance: options2.importance || "normal",
|
|
16439
|
+
ack_required: options2.ackRequired || false
|
|
16440
|
+
});
|
|
16441
|
+
return appendEvent(event, projectPath);
|
|
16442
|
+
}
|
|
16443
|
+
async function reserveFiles(projectKey, agentName, paths, options2 = {}, projectPath) {
|
|
16444
|
+
const ttlSeconds = options2.ttlSeconds || 3600;
|
|
16445
|
+
const event = createEvent("file_reserved", {
|
|
16446
|
+
project_key: projectKey,
|
|
16447
|
+
agent_name: agentName,
|
|
16448
|
+
paths,
|
|
16449
|
+
reason: options2.reason,
|
|
16450
|
+
exclusive: options2.exclusive ?? true,
|
|
16451
|
+
ttl_seconds: ttlSeconds,
|
|
16452
|
+
expires_at: Date.now() + ttlSeconds * 1000
|
|
16453
|
+
});
|
|
16454
|
+
return appendEvent(event, projectPath);
|
|
16455
|
+
}
|
|
16456
|
+
var TIMESTAMP_SAFE_UNTIL;
|
|
16457
|
+
var init_store = __esm(() => {
|
|
16458
|
+
init_streams();
|
|
16459
|
+
init_events();
|
|
16460
|
+
TIMESTAMP_SAFE_UNTIL = new Date("2286-01-01").getTime();
|
|
16097
16461
|
});
|
|
16098
16462
|
|
|
16099
16463
|
// node_modules/.pnpm/@ioredis+commands@1.4.0/node_modules/@ioredis/commands/built/commands.json
|
|
@@ -30762,7 +31126,58 @@ var ScoreCalculationResultSchema = exports_external.object({
|
|
|
30762
31126
|
score: MandateScoreSchema,
|
|
30763
31127
|
status_changed: exports_external.boolean()
|
|
30764
31128
|
});
|
|
31129
|
+
// src/schemas/swarm-context.ts
|
|
31130
|
+
init_zod();
|
|
31131
|
+
var SwarmStrategySchema = exports_external.enum([
|
|
31132
|
+
"file-based",
|
|
31133
|
+
"feature-based",
|
|
31134
|
+
"risk-based"
|
|
31135
|
+
]);
|
|
31136
|
+
var SwarmDirectivesSchema = exports_external.object({
|
|
31137
|
+
shared_context: exports_external.string(),
|
|
31138
|
+
skills_to_load: exports_external.array(exports_external.string()).default([]),
|
|
31139
|
+
coordinator_notes: exports_external.string().default("")
|
|
31140
|
+
});
|
|
31141
|
+
var SwarmRecoverySchema = exports_external.object({
|
|
31142
|
+
last_checkpoint: exports_external.string(),
|
|
31143
|
+
files_modified: exports_external.array(exports_external.string()).default([]),
|
|
31144
|
+
progress_percent: exports_external.number().min(0).max(100).default(0),
|
|
31145
|
+
last_message: exports_external.string().default(""),
|
|
31146
|
+
error_context: exports_external.string().optional()
|
|
31147
|
+
});
|
|
31148
|
+
var SwarmBeadContextSchema = exports_external.object({
|
|
31149
|
+
id: exports_external.string(),
|
|
31150
|
+
epic_id: exports_external.string(),
|
|
31151
|
+
bead_id: exports_external.string(),
|
|
31152
|
+
strategy: SwarmStrategySchema,
|
|
31153
|
+
files: exports_external.array(exports_external.string()),
|
|
31154
|
+
dependencies: exports_external.array(exports_external.string()).default([]),
|
|
31155
|
+
directives: SwarmDirectivesSchema,
|
|
31156
|
+
recovery: SwarmRecoverySchema,
|
|
31157
|
+
created_at: exports_external.number().int().positive(),
|
|
31158
|
+
updated_at: exports_external.number().int().positive()
|
|
31159
|
+
});
|
|
31160
|
+
var CreateSwarmContextArgsSchema = SwarmBeadContextSchema.omit({
|
|
31161
|
+
id: true,
|
|
31162
|
+
created_at: true,
|
|
31163
|
+
updated_at: true
|
|
31164
|
+
});
|
|
31165
|
+
var UpdateSwarmContextArgsSchema = exports_external.object({
|
|
31166
|
+
id: exports_external.string(),
|
|
31167
|
+
recovery: SwarmRecoverySchema.partial().optional(),
|
|
31168
|
+
files: exports_external.array(exports_external.string()).optional(),
|
|
31169
|
+
dependencies: exports_external.array(exports_external.string()).optional(),
|
|
31170
|
+
directives: SwarmDirectivesSchema.partial().optional()
|
|
31171
|
+
});
|
|
31172
|
+
var QuerySwarmContextsArgsSchema = exports_external.object({
|
|
31173
|
+
epic_id: exports_external.string().optional(),
|
|
31174
|
+
bead_id: exports_external.string().optional(),
|
|
31175
|
+
strategy: SwarmStrategySchema.optional(),
|
|
31176
|
+
has_errors: exports_external.boolean().optional()
|
|
31177
|
+
});
|
|
30765
31178
|
// src/beads.ts
|
|
31179
|
+
init_events();
|
|
31180
|
+
init_store();
|
|
30766
31181
|
var beadsWorkingDirectory = null;
|
|
30767
31182
|
function setBeadsWorkingDirectory(directory) {
|
|
30768
31183
|
beadsWorkingDirectory = directory;
|
|
@@ -30907,7 +31322,15 @@ var beads_create_epic = tool({
|
|
|
30907
31322
|
priority: tool.schema.number().min(0).max(3).optional(),
|
|
30908
31323
|
files: tool.schema.array(tool.schema.string()).optional(),
|
|
30909
31324
|
id_suffix: tool.schema.string().optional().describe("Custom ID suffix (e.g., 'e2e-test' becomes 'phase-0.e2e-test')")
|
|
30910
|
-
})).describe("Subtasks to create under the epic")
|
|
31325
|
+
})).describe("Subtasks to create under the epic"),
|
|
31326
|
+
strategy: tool.schema.enum(["file-based", "feature-based", "risk-based"]).optional().describe("Decomposition strategy used (default: feature-based)"),
|
|
31327
|
+
task: tool.schema.string().optional().describe("Original task description that was decomposed"),
|
|
31328
|
+
project_key: tool.schema.string().optional().describe("Project path for event emission"),
|
|
31329
|
+
recovery_context: tool.schema.object({
|
|
31330
|
+
shared_context: tool.schema.string().optional(),
|
|
31331
|
+
skills_to_load: tool.schema.array(tool.schema.string()).optional(),
|
|
31332
|
+
coordinator_notes: tool.schema.string().optional()
|
|
31333
|
+
}).optional().describe("Recovery context from checkpoint compaction")
|
|
30911
31334
|
},
|
|
30912
31335
|
async execute(args, ctx) {
|
|
30913
31336
|
const validated = EpicCreateArgsSchema.parse(args);
|
|
@@ -30950,6 +31373,27 @@ var beads_create_epic = tool({
|
|
|
30950
31373
|
epic,
|
|
30951
31374
|
subtasks: created.slice(1)
|
|
30952
31375
|
};
|
|
31376
|
+
if (args.project_key) {
|
|
31377
|
+
try {
|
|
31378
|
+
const event = createEvent("decomposition_generated", {
|
|
31379
|
+
project_key: args.project_key,
|
|
31380
|
+
epic_id: epic.id,
|
|
31381
|
+
task: args.task || validated.epic_title,
|
|
31382
|
+
context: validated.epic_description,
|
|
31383
|
+
strategy: args.strategy || "feature-based",
|
|
31384
|
+
epic_title: validated.epic_title,
|
|
31385
|
+
subtasks: validated.subtasks.map((st) => ({
|
|
31386
|
+
title: st.title,
|
|
31387
|
+
files: st.files || [],
|
|
31388
|
+
priority: st.priority
|
|
31389
|
+
})),
|
|
31390
|
+
recovery_context: args.recovery_context
|
|
31391
|
+
});
|
|
31392
|
+
await appendEvent(event, args.project_key);
|
|
31393
|
+
} catch (error45) {
|
|
31394
|
+
console.warn("[beads_create_epic] Failed to emit DecompositionGeneratedEvent:", error45);
|
|
31395
|
+
}
|
|
31396
|
+
}
|
|
30953
31397
|
return JSON.stringify(result, null, 2);
|
|
30954
31398
|
} catch (error45) {
|
|
30955
31399
|
const rollbackCommands = [];
|
|
@@ -31128,7 +31572,7 @@ var beads_sync = tool({
|
|
|
31128
31572
|
async execute(args, ctx) {
|
|
31129
31573
|
const autoPull = args.auto_pull ?? true;
|
|
31130
31574
|
const TIMEOUT_MS = 30000;
|
|
31131
|
-
const
|
|
31575
|
+
const withTimeout2 = async (promise2, timeoutMs, operation) => {
|
|
31132
31576
|
let timeoutId;
|
|
31133
31577
|
const timeoutPromise = new Promise((_, reject) => {
|
|
31134
31578
|
timeoutId = setTimeout(() => reject(new BeadError(`Operation timed out after ${timeoutMs}ms`, operation)), timeoutMs);
|
|
@@ -31141,7 +31585,7 @@ var beads_sync = tool({
|
|
|
31141
31585
|
}
|
|
31142
31586
|
}
|
|
31143
31587
|
};
|
|
31144
|
-
const flushResult = await
|
|
31588
|
+
const flushResult = await withTimeout2(runBdCommand(["sync", "--flush-only"]), TIMEOUT_MS, "bd sync --flush-only");
|
|
31145
31589
|
if (flushResult.exitCode !== 0) {
|
|
31146
31590
|
throw new BeadError(`Failed to flush beads because bd sync failed: ${flushResult.stderr}. Try: Check if .beads/ directory is writable, verify no corrupted JSONL files, or run 'bd list' to test basic beads functionality.`, "bd sync --flush-only", flushResult.exitCode);
|
|
31147
31591
|
}
|
|
@@ -31156,7 +31600,7 @@ var beads_sync = tool({
|
|
|
31156
31600
|
if (addResult.exitCode !== 0) {
|
|
31157
31601
|
throw new BeadError(`Failed to stage beads because git add failed: ${addResult.stderr}. Try: Check if .beads/ directory exists, verify git is initialized with 'git status', or check for .gitignore patterns blocking .beads/.`, "git add .beads/", addResult.exitCode);
|
|
31158
31602
|
}
|
|
31159
|
-
const commitResult = await
|
|
31603
|
+
const commitResult = await withTimeout2(runGitCommand(["commit", "-m", "chore: sync beads"]), TIMEOUT_MS, "git commit");
|
|
31160
31604
|
if (commitResult.exitCode !== 0 && !commitResult.stdout.includes("nothing to commit")) {
|
|
31161
31605
|
throw new BeadError(`Failed to commit beads because git commit failed: ${commitResult.stderr}. Try: Check git config (user.name, user.email) with 'git config --list', verify working tree is clean, or check for pre-commit hooks blocking commit.`, "git commit", commitResult.exitCode);
|
|
31162
31606
|
}
|
|
@@ -31185,7 +31629,7 @@ var beads_sync = tool({
|
|
|
31185
31629
|
console.warn(`[beads] Stash failed (${stashResult.stderr}), attempting pull anyway...`);
|
|
31186
31630
|
}
|
|
31187
31631
|
}
|
|
31188
|
-
const pullResult = await
|
|
31632
|
+
const pullResult = await withTimeout2(runGitCommand(["pull", "--rebase"]), TIMEOUT_MS, "git pull --rebase");
|
|
31189
31633
|
if (didStash) {
|
|
31190
31634
|
console.warn("[beads] Restoring stashed changes...");
|
|
31191
31635
|
const unstashResult = await runGitCommand(["stash", "pop"]);
|
|
@@ -31199,12 +31643,12 @@ var beads_sync = tool({
|
|
|
31199
31643
|
if (pullResult.exitCode !== 0) {
|
|
31200
31644
|
throw new BeadError(`Failed to pull because git pull --rebase failed: ${pullResult.stderr}. Try: Resolve merge conflicts manually with 'git status', check if remote is accessible with 'git remote -v', or use skip_verification to bypass automatic pull.`, "git pull --rebase", pullResult.exitCode);
|
|
31201
31645
|
}
|
|
31202
|
-
const importResult = await
|
|
31646
|
+
const importResult = await withTimeout2(runBdCommand(["sync", "--import-only"]), TIMEOUT_MS, "bd sync --import-only");
|
|
31203
31647
|
if (importResult.exitCode !== 0) {
|
|
31204
31648
|
console.warn(`[beads] Import warning: ${importResult.stderr}`);
|
|
31205
31649
|
}
|
|
31206
31650
|
}
|
|
31207
|
-
const pushResult = await
|
|
31651
|
+
const pushResult = await withTimeout2(runGitCommand(["push"]), TIMEOUT_MS, "git push");
|
|
31208
31652
|
if (pushResult.exitCode !== 0) {
|
|
31209
31653
|
throw new BeadError(`Failed to push because git push failed: ${pushResult.stderr}. Try: Check if remote branch is up to date with 'git pull --rebase', verify push permissions, check remote URL with 'git remote -v', or force push with 'git push --force-with-lease' if safe.`, "git push", pushResult.exitCode);
|
|
31210
31654
|
}
|
|
@@ -34018,20 +34462,29 @@ Only modify these files. Need others? Message the coordinator.
|
|
|
34018
34462
|
|
|
34019
34463
|
{error_context}
|
|
34020
34464
|
|
|
34021
|
-
## [MANDATORY: SWARM MAIL]
|
|
34465
|
+
## [MANDATORY: SWARM MAIL INITIALIZATION]
|
|
34022
34466
|
|
|
34023
|
-
**YOU MUST
|
|
34467
|
+
**CRITICAL: YOU MUST INITIALIZE SWARM MAIL BEFORE DOING ANY WORK.**
|
|
34024
34468
|
|
|
34025
|
-
|
|
34026
|
-
\`\`\`
|
|
34027
|
-
swarmmail_init(project_path="$PWD", task_description="{subtask_title}")
|
|
34028
|
-
\`\`\`
|
|
34469
|
+
This is your FIRST step - before reading files, before planning, before ANY other action.
|
|
34029
34470
|
|
|
34030
|
-
###
|
|
34471
|
+
### Step 1: Initialize (REQUIRED - DO THIS FIRST)
|
|
34031
34472
|
\`\`\`
|
|
34032
|
-
|
|
34473
|
+
swarmmail_init(project_path="{project_path}", task_description="{bead_id}: {subtask_title}")
|
|
34033
34474
|
\`\`\`
|
|
34034
34475
|
|
|
34476
|
+
**This registers you with the coordination system and enables:**
|
|
34477
|
+
- File reservation tracking
|
|
34478
|
+
- Inter-agent communication
|
|
34479
|
+
- Progress monitoring
|
|
34480
|
+
- Conflict detection
|
|
34481
|
+
|
|
34482
|
+
**If you skip this step, your work will not be tracked and swarm_complete will fail.**
|
|
34483
|
+
|
|
34484
|
+
## [SWARM MAIL USAGE]
|
|
34485
|
+
|
|
34486
|
+
After initialization, use Swarm Mail for coordination:
|
|
34487
|
+
|
|
34035
34488
|
### Check Inbox Regularly
|
|
34036
34489
|
\`\`\`
|
|
34037
34490
|
swarmmail_inbox() # Check for coordinator messages
|
|
@@ -34085,14 +34538,17 @@ As you work, note reusable patterns, best practices, or domain insights:
|
|
|
34085
34538
|
- Skills make swarms smarter over time
|
|
34086
34539
|
|
|
34087
34540
|
## [WORKFLOW]
|
|
34088
|
-
1. **swarmmail_init** - Initialize session FIRST
|
|
34541
|
+
1. **swarmmail_init** - Initialize session (MANDATORY FIRST STEP)
|
|
34089
34542
|
2. Read assigned files
|
|
34090
34543
|
3. Implement changes
|
|
34091
34544
|
4. **swarmmail_send** - Report progress to coordinator
|
|
34092
34545
|
5. Verify (typecheck)
|
|
34093
34546
|
6. **swarm_complete** - Mark done, release reservations
|
|
34094
34547
|
|
|
34095
|
-
**CRITICAL
|
|
34548
|
+
**CRITICAL REQUIREMENTS:**
|
|
34549
|
+
- Step 1 (swarmmail_init) is NON-NEGOTIABLE - do it before anything else
|
|
34550
|
+
- Never work silently - send progress updates via swarmmail_send every significant milestone
|
|
34551
|
+
- If you complete without initializing, swarm_complete will detect this and warn/fail
|
|
34096
34552
|
|
|
34097
34553
|
Begin now.`;
|
|
34098
34554
|
var EVALUATION_PROMPT = `Evaluate the work completed for this subtask.
|
|
@@ -34136,7 +34592,33 @@ function formatSubtaskPromptV2(params) {
|
|
|
34136
34592
|
`) : "(no specific files - use judgment)";
|
|
34137
34593
|
const compressedSection = params.compressed_context ? params.compressed_context : "";
|
|
34138
34594
|
const errorSection = params.error_context ? params.error_context : "";
|
|
34139
|
-
|
|
34595
|
+
let recoverySection = "";
|
|
34596
|
+
if (params.recovery_context) {
|
|
34597
|
+
const sections = [];
|
|
34598
|
+
if (params.recovery_context.shared_context) {
|
|
34599
|
+
sections.push(`### Recovery Context
|
|
34600
|
+
${params.recovery_context.shared_context}`);
|
|
34601
|
+
}
|
|
34602
|
+
if (params.recovery_context.skills_to_load && params.recovery_context.skills_to_load.length > 0) {
|
|
34603
|
+
sections.push(`### Skills to Load
|
|
34604
|
+
Before starting work, load these skills for specialized guidance:
|
|
34605
|
+
${params.recovery_context.skills_to_load.map((s) => `- skills_use(name="${s}")`).join(`
|
|
34606
|
+
`)}`);
|
|
34607
|
+
}
|
|
34608
|
+
if (params.recovery_context.coordinator_notes) {
|
|
34609
|
+
sections.push(`### Coordinator Notes
|
|
34610
|
+
${params.recovery_context.coordinator_notes}`);
|
|
34611
|
+
}
|
|
34612
|
+
if (sections.length > 0) {
|
|
34613
|
+
recoverySection = `
|
|
34614
|
+
## [RECOVERY CONTEXT]
|
|
34615
|
+
|
|
34616
|
+
${sections.join(`
|
|
34617
|
+
|
|
34618
|
+
`)}`;
|
|
34619
|
+
}
|
|
34620
|
+
}
|
|
34621
|
+
return SUBTASK_PROMPT_V2.replace(/{bead_id}/g, params.bead_id).replace(/{epic_id}/g, params.epic_id).replace(/{project_path}/g, params.project_path || "$PWD").replace("{subtask_title}", params.subtask_title).replace("{subtask_description}", params.subtask_description || "(see title)").replace("{file_list}", fileList).replace("{shared_context}", params.shared_context || "(none)").replace("{compressed_context}", compressedSection).replace("{error_context}", errorSection + recoverySection);
|
|
34140
34622
|
}
|
|
34141
34623
|
function formatSubtaskPrompt(params) {
|
|
34142
34624
|
const fileList = params.files.map((f) => `- \`${f}\``).join(`
|
|
@@ -34157,7 +34639,8 @@ var swarm_subtask_prompt = tool({
|
|
|
34157
34639
|
subtask_title: tool.schema.string().describe("Subtask title"),
|
|
34158
34640
|
subtask_description: tool.schema.string().optional().describe("Detailed subtask instructions"),
|
|
34159
34641
|
files: tool.schema.array(tool.schema.string()).describe("Files assigned to this subtask"),
|
|
34160
|
-
shared_context: tool.schema.string().optional().describe("Context shared across all agents")
|
|
34642
|
+
shared_context: tool.schema.string().optional().describe("Context shared across all agents"),
|
|
34643
|
+
project_path: tool.schema.string().optional().describe("Absolute project path for swarmmail_init")
|
|
34161
34644
|
},
|
|
34162
34645
|
async execute(args) {
|
|
34163
34646
|
const prompt = formatSubtaskPrompt({
|
|
@@ -34173,14 +34656,20 @@ var swarm_subtask_prompt = tool({
|
|
|
34173
34656
|
}
|
|
34174
34657
|
});
|
|
34175
34658
|
var swarm_spawn_subtask = tool({
|
|
34176
|
-
description: "Prepare a subtask for spawning. Returns prompt with Agent Mail/beads instructions.",
|
|
34659
|
+
description: "Prepare a subtask for spawning. Returns prompt with Agent Mail/beads instructions. IMPORTANT: Pass project_path for swarmmail_init.",
|
|
34177
34660
|
args: {
|
|
34178
34661
|
bead_id: tool.schema.string().describe("Subtask bead ID"),
|
|
34179
34662
|
epic_id: tool.schema.string().describe("Parent epic bead ID"),
|
|
34180
34663
|
subtask_title: tool.schema.string().describe("Subtask title"),
|
|
34181
34664
|
subtask_description: tool.schema.string().optional().describe("Detailed subtask instructions"),
|
|
34182
34665
|
files: tool.schema.array(tool.schema.string()).describe("Files assigned to this subtask"),
|
|
34183
|
-
shared_context: tool.schema.string().optional().describe("Context shared across all agents")
|
|
34666
|
+
shared_context: tool.schema.string().optional().describe("Context shared across all agents"),
|
|
34667
|
+
project_path: tool.schema.string().optional().describe("Absolute project path for swarmmail_init (REQUIRED for tracking)"),
|
|
34668
|
+
recovery_context: tool.schema.object({
|
|
34669
|
+
shared_context: tool.schema.string().optional(),
|
|
34670
|
+
skills_to_load: tool.schema.array(tool.schema.string()).optional(),
|
|
34671
|
+
coordinator_notes: tool.schema.string().optional()
|
|
34672
|
+
}).optional().describe("Recovery context from checkpoint compaction")
|
|
34184
34673
|
},
|
|
34185
34674
|
async execute(args) {
|
|
34186
34675
|
const prompt = formatSubtaskPromptV2({
|
|
@@ -34189,13 +34678,17 @@ var swarm_spawn_subtask = tool({
|
|
|
34189
34678
|
subtask_title: args.subtask_title,
|
|
34190
34679
|
subtask_description: args.subtask_description || "",
|
|
34191
34680
|
files: args.files,
|
|
34192
|
-
shared_context: args.shared_context
|
|
34681
|
+
shared_context: args.shared_context,
|
|
34682
|
+
project_path: args.project_path,
|
|
34683
|
+
recovery_context: args.recovery_context
|
|
34193
34684
|
});
|
|
34194
34685
|
return JSON.stringify({
|
|
34195
34686
|
prompt,
|
|
34196
34687
|
bead_id: args.bead_id,
|
|
34197
34688
|
epic_id: args.epic_id,
|
|
34198
|
-
files: args.files
|
|
34689
|
+
files: args.files,
|
|
34690
|
+
project_path: args.project_path,
|
|
34691
|
+
recovery_context: args.recovery_context
|
|
34199
34692
|
}, null, 2);
|
|
34200
34693
|
}
|
|
34201
34694
|
});
|
|
@@ -34317,6 +34810,9 @@ var promptTools = {
|
|
|
34317
34810
|
init_dist();
|
|
34318
34811
|
init_zod();
|
|
34319
34812
|
init_swarm_mail();
|
|
34813
|
+
init_projections();
|
|
34814
|
+
init_events();
|
|
34815
|
+
init_store();
|
|
34320
34816
|
init_learning();
|
|
34321
34817
|
init_skills();
|
|
34322
34818
|
async function queryEpicSubtasks(epicId) {
|
|
@@ -34746,7 +35242,57 @@ var swarm_progress = tool({
|
|
|
34746
35242
|
threadId: epicId,
|
|
34747
35243
|
importance: args.status === "blocked" ? "high" : "normal"
|
|
34748
35244
|
});
|
|
34749
|
-
|
|
35245
|
+
let checkpointCreated = false;
|
|
35246
|
+
if (args.progress_percent !== undefined && args.files_touched && args.files_touched.length > 0) {
|
|
35247
|
+
const milestones = [25, 50, 75];
|
|
35248
|
+
if (milestones.includes(args.progress_percent)) {
|
|
35249
|
+
try {
|
|
35250
|
+
const checkpoint = {
|
|
35251
|
+
epic_id: epicId,
|
|
35252
|
+
bead_id: args.bead_id,
|
|
35253
|
+
strategy: "file-based",
|
|
35254
|
+
files: args.files_touched,
|
|
35255
|
+
dependencies: [],
|
|
35256
|
+
directives: {},
|
|
35257
|
+
recovery: {
|
|
35258
|
+
last_checkpoint: Date.now(),
|
|
35259
|
+
files_modified: args.files_touched,
|
|
35260
|
+
progress_percent: args.progress_percent,
|
|
35261
|
+
last_message: args.message
|
|
35262
|
+
}
|
|
35263
|
+
};
|
|
35264
|
+
const event = createEvent("swarm_checkpointed", {
|
|
35265
|
+
project_key: args.project_key,
|
|
35266
|
+
...checkpoint
|
|
35267
|
+
});
|
|
35268
|
+
await appendEvent(event, args.project_key);
|
|
35269
|
+
const { getDatabase: getDatabase2 } = await Promise.resolve().then(() => (init_streams(), exports_streams));
|
|
35270
|
+
const db = await getDatabase2(args.project_key);
|
|
35271
|
+
const now = Date.now();
|
|
35272
|
+
await db.query(`INSERT INTO swarm_contexts (id, epic_id, bead_id, strategy, files, dependencies, directives, recovery, created_at, updated_at)
|
|
35273
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
|
35274
|
+
ON CONFLICT (id) DO UPDATE SET
|
|
35275
|
+
files = EXCLUDED.files,
|
|
35276
|
+
recovery = EXCLUDED.recovery,
|
|
35277
|
+
updated_at = EXCLUDED.updated_at`, [
|
|
35278
|
+
args.bead_id,
|
|
35279
|
+
epicId,
|
|
35280
|
+
args.bead_id,
|
|
35281
|
+
checkpoint.strategy,
|
|
35282
|
+
JSON.stringify(checkpoint.files),
|
|
35283
|
+
JSON.stringify(checkpoint.dependencies),
|
|
35284
|
+
JSON.stringify(checkpoint.directives),
|
|
35285
|
+
JSON.stringify(checkpoint.recovery),
|
|
35286
|
+
now,
|
|
35287
|
+
now
|
|
35288
|
+
]);
|
|
35289
|
+
checkpointCreated = true;
|
|
35290
|
+
} catch (error45) {
|
|
35291
|
+
console.warn(`[swarm_progress] Auto-checkpoint failed at ${args.progress_percent}%:`, error45);
|
|
35292
|
+
}
|
|
35293
|
+
}
|
|
35294
|
+
}
|
|
35295
|
+
return `Progress reported: ${args.status}${args.progress_percent !== undefined ? ` (${args.progress_percent}%)` : ""}${checkpointCreated ? " [checkpoint created]" : ""}`;
|
|
34750
35296
|
}
|
|
34751
35297
|
});
|
|
34752
35298
|
var swarm_broadcast = tool({
|
|
@@ -34806,125 +35352,205 @@ var swarm_complete = tool({
|
|
|
34806
35352
|
evaluation: tool.schema.string().optional().describe("Self-evaluation JSON (Evaluation schema)"),
|
|
34807
35353
|
files_touched: tool.schema.array(tool.schema.string()).optional().describe("Files modified - will be verified (UBS, typecheck, tests)"),
|
|
34808
35354
|
skip_ubs_scan: tool.schema.boolean().optional().describe("Skip UBS bug scan (default: false)"),
|
|
34809
|
-
skip_verification: tool.schema.boolean().optional().describe("Skip ALL verification (UBS, typecheck, tests). Use sparingly! (default: false)")
|
|
35355
|
+
skip_verification: tool.schema.boolean().optional().describe("Skip ALL verification (UBS, typecheck, tests). Use sparingly! (default: false)"),
|
|
35356
|
+
planned_files: tool.schema.array(tool.schema.string()).optional().describe("Files that were originally planned to be modified"),
|
|
35357
|
+
start_time: tool.schema.number().optional().describe("Task start timestamp (Unix ms) for duration calculation"),
|
|
35358
|
+
error_count: tool.schema.number().optional().describe("Number of errors encountered during task"),
|
|
35359
|
+
retry_count: tool.schema.number().optional().describe("Number of retry attempts during task")
|
|
34810
35360
|
},
|
|
34811
35361
|
async execute(args) {
|
|
34812
|
-
|
|
34813
|
-
|
|
34814
|
-
|
|
34815
|
-
|
|
34816
|
-
|
|
34817
|
-
|
|
34818
|
-
|
|
34819
|
-
|
|
34820
|
-
|
|
34821
|
-
|
|
34822
|
-
|
|
34823
|
-
|
|
34824
|
-
|
|
34825
|
-
|
|
34826
|
-
|
|
34827
|
-
|
|
34828
|
-
|
|
34829
|
-
|
|
34830
|
-
|
|
34831
|
-
|
|
34832
|
-
|
|
34833
|
-
|
|
35362
|
+
const epicId = args.bead_id.includes(".") ? args.bead_id.split(".")[0] : args.bead_id;
|
|
35363
|
+
try {
|
|
35364
|
+
const projectKey = args.project_key.replace(/\//g, "-").replace(/\\/g, "-");
|
|
35365
|
+
let agentRegistered = false;
|
|
35366
|
+
let registrationWarning = "";
|
|
35367
|
+
try {
|
|
35368
|
+
const agent = await getAgent(projectKey, args.agent_name, args.project_key);
|
|
35369
|
+
agentRegistered = agent !== null;
|
|
35370
|
+
if (!agentRegistered) {
|
|
35371
|
+
registrationWarning = `⚠️ WARNING: Agent '${args.agent_name}' was NOT registered in swarm-mail for project '${projectKey}'.
|
|
35372
|
+
|
|
35373
|
+
This usually means you skipped the MANDATORY swarmmail_init step.
|
|
35374
|
+
|
|
35375
|
+
**Impact:**
|
|
35376
|
+
- Your work was not tracked in the coordination system
|
|
35377
|
+
- File reservations may not have been managed
|
|
35378
|
+
- Other agents couldn't coordinate with you
|
|
35379
|
+
- Learning/eval data may be incomplete
|
|
35380
|
+
|
|
35381
|
+
**Next time:** Run swarmmail_init(project_path="${args.project_key}", task_description="<task>") FIRST, before any other work.
|
|
35382
|
+
|
|
35383
|
+
Continuing with completion, but this should be fixed for future subtasks.`;
|
|
35384
|
+
console.warn(`[swarm_complete] ${registrationWarning}`);
|
|
35385
|
+
}
|
|
35386
|
+
} catch (error45) {
|
|
35387
|
+
console.warn(`[swarm_complete] Could not verify agent registration:`, error45);
|
|
35388
|
+
registrationWarning = `ℹ️ Could not verify swarm-mail registration (database may not be available). Consider running swarmmail_init next time.`;
|
|
34834
35389
|
}
|
|
34835
|
-
|
|
34836
|
-
|
|
34837
|
-
|
|
34838
|
-
|
|
34839
|
-
|
|
34840
|
-
|
|
34841
|
-
|
|
34842
|
-
|
|
34843
|
-
|
|
34844
|
-
|
|
34845
|
-
|
|
34846
|
-
|
|
34847
|
-
|
|
34848
|
-
|
|
35390
|
+
let verificationResult = null;
|
|
35391
|
+
if (!args.skip_verification && args.files_touched?.length) {
|
|
35392
|
+
verificationResult = await runVerificationGate(args.files_touched, args.skip_ubs_scan ?? false);
|
|
35393
|
+
if (!verificationResult.passed) {
|
|
35394
|
+
return JSON.stringify({
|
|
35395
|
+
success: false,
|
|
35396
|
+
error: "Verification Gate FAILED - fix issues before completing",
|
|
35397
|
+
verification: {
|
|
35398
|
+
passed: false,
|
|
35399
|
+
summary: verificationResult.summary,
|
|
35400
|
+
blockers: verificationResult.blockers,
|
|
35401
|
+
steps: verificationResult.steps.map((s) => ({
|
|
35402
|
+
name: s.name,
|
|
35403
|
+
passed: s.passed,
|
|
35404
|
+
skipped: s.skipped,
|
|
35405
|
+
skipReason: s.skipReason,
|
|
35406
|
+
error: s.error?.slice(0, 200)
|
|
35407
|
+
}))
|
|
35408
|
+
},
|
|
35409
|
+
hint: verificationResult.blockers.length > 0 ? `Fix these issues: ${verificationResult.blockers.map((b, i) => `${i + 1}. ${b}`).join(", ")}. Use skip_verification=true only as last resort.` : "Fix the failing checks and try again. Use skip_verification=true only as last resort.",
|
|
35410
|
+
gate_function: "IDENTIFY → RUN → READ → VERIFY → CLAIM (you are at VERIFY, claim blocked)"
|
|
35411
|
+
}, null, 2);
|
|
35412
|
+
}
|
|
35413
|
+
}
|
|
35414
|
+
let ubsResult = null;
|
|
35415
|
+
if (!args.skip_verification && !verificationResult && args.files_touched?.length && !args.skip_ubs_scan) {
|
|
35416
|
+
ubsResult = await runUbsScan(args.files_touched);
|
|
35417
|
+
if (ubsResult && ubsResult.summary.critical > 0) {
|
|
35418
|
+
return JSON.stringify({
|
|
35419
|
+
success: false,
|
|
35420
|
+
error: `UBS found ${ubsResult.summary.critical} critical bug(s) that must be fixed before completing`,
|
|
35421
|
+
ubs_scan: {
|
|
35422
|
+
critical_count: ubsResult.summary.critical,
|
|
35423
|
+
bugs: ubsResult.bugs.filter((b) => b.severity === "critical")
|
|
35424
|
+
},
|
|
35425
|
+
hint: `Fix these critical bugs: ${ubsResult.bugs.filter((b) => b.severity === "critical").map((b) => `${b.file}:${b.line} - ${b.message}`).slice(0, 3).join("; ")}. Try: Run 'ubs scan ${args.files_touched?.join(" ") || "."} --json' for full report, fix reported issues, or use skip_ubs_scan=true to bypass (not recommended).`
|
|
35426
|
+
}, null, 2);
|
|
35427
|
+
}
|
|
35428
|
+
}
|
|
35429
|
+
let parsedEvaluation;
|
|
35430
|
+
if (args.evaluation) {
|
|
35431
|
+
try {
|
|
35432
|
+
parsedEvaluation = EvaluationSchema.parse(JSON.parse(args.evaluation));
|
|
35433
|
+
} catch (error45) {
|
|
35434
|
+
return JSON.stringify({
|
|
35435
|
+
success: false,
|
|
35436
|
+
error: "Invalid evaluation format",
|
|
35437
|
+
details: error45 instanceof exports_external.ZodError ? error45.issues : String(error45)
|
|
35438
|
+
}, null, 2);
|
|
35439
|
+
}
|
|
35440
|
+
if (!parsedEvaluation.passed) {
|
|
35441
|
+
return JSON.stringify({
|
|
35442
|
+
success: false,
|
|
35443
|
+
error: "Self-evaluation failed",
|
|
35444
|
+
retry_suggestion: parsedEvaluation.retry_suggestion,
|
|
35445
|
+
feedback: parsedEvaluation.overall_feedback
|
|
35446
|
+
}, null, 2);
|
|
35447
|
+
}
|
|
35448
|
+
}
|
|
35449
|
+
const closeResult = await Bun.$`bd close ${args.bead_id} --reason ${args.summary} --json`.quiet().nothrow();
|
|
35450
|
+
if (closeResult.exitCode !== 0) {
|
|
35451
|
+
throw new Error(`Failed to close bead because bd close command failed: ${closeResult.stderr.toString()}. Try: Verify bead exists and is not already closed with 'bd show ${args.bead_id}', check if bead ID is correct with 'beads_query()', or use beads_close tool directly.`);
|
|
34849
35452
|
}
|
|
34850
|
-
}
|
|
34851
|
-
let parsedEvaluation;
|
|
34852
|
-
if (args.evaluation) {
|
|
34853
35453
|
try {
|
|
34854
|
-
|
|
35454
|
+
const epicId3 = args.bead_id.includes(".") ? args.bead_id.split(".")[0] : args.bead_id;
|
|
35455
|
+
const durationMs2 = args.start_time ? Date.now() - args.start_time : 0;
|
|
35456
|
+
const event = createEvent("subtask_outcome", {
|
|
35457
|
+
project_key: args.project_key,
|
|
35458
|
+
epic_id: epicId3,
|
|
35459
|
+
bead_id: args.bead_id,
|
|
35460
|
+
planned_files: args.planned_files || [],
|
|
35461
|
+
actual_files: args.files_touched || [],
|
|
35462
|
+
duration_ms: durationMs2,
|
|
35463
|
+
error_count: args.error_count || 0,
|
|
35464
|
+
retry_count: args.retry_count || 0,
|
|
35465
|
+
success: true
|
|
35466
|
+
});
|
|
35467
|
+
await appendEvent(event, args.project_key);
|
|
34855
35468
|
} catch (error45) {
|
|
34856
|
-
|
|
34857
|
-
success: false,
|
|
34858
|
-
error: "Invalid evaluation format",
|
|
34859
|
-
details: error45 instanceof exports_external.ZodError ? error45.issues : String(error45)
|
|
34860
|
-
}, null, 2);
|
|
35469
|
+
console.warn("[swarm_complete] Failed to emit SubtaskOutcomeEvent:", error45);
|
|
34861
35470
|
}
|
|
34862
|
-
|
|
34863
|
-
|
|
34864
|
-
|
|
34865
|
-
|
|
34866
|
-
|
|
34867
|
-
|
|
34868
|
-
|
|
35471
|
+
let capturedStrategy;
|
|
35472
|
+
const durationMs = args.start_time ? Date.now() - args.start_time : 0;
|
|
35473
|
+
const memoryInfo = formatMemoryStoreOnSuccess(args.bead_id, args.summary, args.files_touched || [], capturedStrategy);
|
|
35474
|
+
let memoryStored = false;
|
|
35475
|
+
let memoryError;
|
|
35476
|
+
try {
|
|
35477
|
+
const memoryAvailable = await isToolAvailable("semantic-memory");
|
|
35478
|
+
if (memoryAvailable) {
|
|
35479
|
+
const storeResult = await Bun.$`semantic-memory store ${memoryInfo.information} --metadata ${memoryInfo.metadata}`.quiet().nothrow();
|
|
35480
|
+
if (storeResult.exitCode === 0) {
|
|
35481
|
+
memoryStored = true;
|
|
35482
|
+
console.log(`[swarm_complete] Stored learning for ${args.bead_id} in semantic-memory`);
|
|
35483
|
+
} else {
|
|
35484
|
+
memoryError = `semantic-memory store failed: ${storeResult.stderr.toString().slice(0, 200)}`;
|
|
35485
|
+
console.warn(`[swarm_complete] ${memoryError}`);
|
|
35486
|
+
}
|
|
35487
|
+
} else {
|
|
35488
|
+
memoryError = "semantic-memory not available - learning stored in-memory only";
|
|
35489
|
+
warnMissingTool("semantic-memory");
|
|
35490
|
+
}
|
|
35491
|
+
} catch (error45) {
|
|
35492
|
+
memoryError = `Failed to store memory: ${error45 instanceof Error ? error45.message : String(error45)}`;
|
|
35493
|
+
console.warn(`[swarm_complete] ${memoryError}`);
|
|
34869
35494
|
}
|
|
34870
|
-
|
|
34871
|
-
|
|
34872
|
-
|
|
34873
|
-
|
|
34874
|
-
|
|
34875
|
-
|
|
34876
|
-
|
|
35495
|
+
try {
|
|
35496
|
+
await releaseSwarmFiles({
|
|
35497
|
+
projectPath: args.project_key,
|
|
35498
|
+
agentName: args.agent_name
|
|
35499
|
+
});
|
|
35500
|
+
} catch (error45) {
|
|
35501
|
+
console.warn(`[swarm] Failed to release file reservations for ${args.agent_name}:`, error45);
|
|
35502
|
+
}
|
|
35503
|
+
const epicId2 = args.bead_id.includes(".") ? args.bead_id.split(".")[0] : args.bead_id;
|
|
35504
|
+
const completionBody = [
|
|
35505
|
+
`## Subtask Complete: ${args.bead_id}`,
|
|
35506
|
+
"",
|
|
35507
|
+
`**Summary**: ${args.summary}`,
|
|
35508
|
+
"",
|
|
35509
|
+
parsedEvaluation ? `**Self-Evaluation**: ${parsedEvaluation.passed ? "PASSED" : "FAILED"}` : "",
|
|
35510
|
+
parsedEvaluation?.overall_feedback ? `**Feedback**: ${parsedEvaluation.overall_feedback}` : "",
|
|
35511
|
+
"",
|
|
35512
|
+
`**Memory Capture**: ${memoryStored ? "✓ Stored in semantic-memory" : `✗ ${memoryError || "Failed"}`}`
|
|
35513
|
+
].filter(Boolean).join(`
|
|
35514
|
+
`);
|
|
35515
|
+
await sendSwarmMessage({
|
|
34877
35516
|
projectPath: args.project_key,
|
|
34878
|
-
|
|
35517
|
+
fromAgent: args.agent_name,
|
|
35518
|
+
toAgents: [],
|
|
35519
|
+
subject: `Complete: ${args.bead_id}`,
|
|
35520
|
+
body: completionBody,
|
|
35521
|
+
threadId: epicId2,
|
|
35522
|
+
importance: "normal"
|
|
34879
35523
|
});
|
|
34880
|
-
|
|
34881
|
-
|
|
34882
|
-
|
|
34883
|
-
|
|
34884
|
-
|
|
34885
|
-
|
|
34886
|
-
|
|
34887
|
-
|
|
34888
|
-
|
|
34889
|
-
|
|
34890
|
-
|
|
34891
|
-
|
|
34892
|
-
|
|
34893
|
-
|
|
34894
|
-
|
|
34895
|
-
|
|
34896
|
-
|
|
34897
|
-
|
|
34898
|
-
|
|
34899
|
-
|
|
34900
|
-
|
|
34901
|
-
|
|
34902
|
-
|
|
34903
|
-
|
|
34904
|
-
|
|
34905
|
-
|
|
34906
|
-
|
|
34907
|
-
|
|
34908
|
-
|
|
34909
|
-
|
|
34910
|
-
summary: verificationResult.summary,
|
|
34911
|
-
steps: verificationResult.steps.map((s) => ({
|
|
34912
|
-
name: s.name,
|
|
34913
|
-
passed: s.passed,
|
|
34914
|
-
skipped: s.skipped,
|
|
34915
|
-
skipReason: s.skipReason
|
|
34916
|
-
}))
|
|
34917
|
-
} : args.skip_verification ? { skipped: true, reason: "skip_verification=true" } : { skipped: true, reason: "no files_touched provided" },
|
|
34918
|
-
ubs_scan: ubsResult ? {
|
|
34919
|
-
ran: true,
|
|
34920
|
-
bugs_found: ubsResult.summary.total,
|
|
34921
|
-
summary: ubsResult.summary,
|
|
34922
|
-
warnings: ubsResult.bugs.filter((b) => b.severity !== "critical")
|
|
34923
|
-
} : verificationResult ? { ran: true, included_in_verification_gate: true } : {
|
|
34924
|
-
ran: false,
|
|
34925
|
-
reason: args.skip_ubs_scan ? "skipped" : "no files or ubs unavailable"
|
|
34926
|
-
},
|
|
34927
|
-
learning_prompt: `## Reflection
|
|
35524
|
+
const response = {
|
|
35525
|
+
success: true,
|
|
35526
|
+
bead_id: args.bead_id,
|
|
35527
|
+
closed: true,
|
|
35528
|
+
reservations_released: true,
|
|
35529
|
+
message_sent: true,
|
|
35530
|
+
agent_registration: {
|
|
35531
|
+
verified: agentRegistered,
|
|
35532
|
+
warning: registrationWarning || undefined
|
|
35533
|
+
},
|
|
35534
|
+
verification_gate: verificationResult ? {
|
|
35535
|
+
passed: true,
|
|
35536
|
+
summary: verificationResult.summary,
|
|
35537
|
+
steps: verificationResult.steps.map((s) => ({
|
|
35538
|
+
name: s.name,
|
|
35539
|
+
passed: s.passed,
|
|
35540
|
+
skipped: s.skipped,
|
|
35541
|
+
skipReason: s.skipReason
|
|
35542
|
+
}))
|
|
35543
|
+
} : args.skip_verification ? { skipped: true, reason: "skip_verification=true" } : { skipped: true, reason: "no files_touched provided" },
|
|
35544
|
+
ubs_scan: ubsResult ? {
|
|
35545
|
+
ran: true,
|
|
35546
|
+
bugs_found: ubsResult.summary.total,
|
|
35547
|
+
summary: ubsResult.summary,
|
|
35548
|
+
warnings: ubsResult.bugs.filter((b) => b.severity !== "critical")
|
|
35549
|
+
} : verificationResult ? { ran: true, included_in_verification_gate: true } : {
|
|
35550
|
+
ran: false,
|
|
35551
|
+
reason: args.skip_ubs_scan ? "skipped" : "no files or ubs unavailable"
|
|
35552
|
+
},
|
|
35553
|
+
learning_prompt: `## Reflection
|
|
34928
35554
|
|
|
34929
35555
|
Did you learn anything reusable during this subtask? Consider:
|
|
34930
35556
|
|
|
@@ -34936,9 +35562,81 @@ Did you learn anything reusable during this subtask? Consider:
|
|
|
34936
35562
|
If you discovered something valuable, use \`swarm_learn\` or \`skills_create\` to preserve it as a skill for future swarms.
|
|
34937
35563
|
|
|
34938
35564
|
Files touched: ${args.files_touched?.join(", ") || "none recorded"}`,
|
|
34939
|
-
|
|
34940
|
-
|
|
34941
|
-
|
|
35565
|
+
memory_capture: {
|
|
35566
|
+
attempted: true,
|
|
35567
|
+
stored: memoryStored,
|
|
35568
|
+
error: memoryError,
|
|
35569
|
+
information: memoryInfo.information,
|
|
35570
|
+
metadata: memoryInfo.metadata,
|
|
35571
|
+
note: memoryStored ? "Learning automatically stored in semantic-memory" : `Failed to store: ${memoryError}. Learning lost unless semantic-memory is available.`
|
|
35572
|
+
}
|
|
35573
|
+
};
|
|
35574
|
+
return JSON.stringify(response, null, 2);
|
|
35575
|
+
} catch (error45) {
|
|
35576
|
+
const errorMessage = error45 instanceof Error ? error45.message : String(error45);
|
|
35577
|
+
const errorStack = error45 instanceof Error ? error45.stack : undefined;
|
|
35578
|
+
let failedStep = "unknown";
|
|
35579
|
+
if (errorMessage.includes("verification")) {
|
|
35580
|
+
failedStep = "Verification Gate (UBS/typecheck/tests)";
|
|
35581
|
+
} else if (errorMessage.includes("UBS") || errorMessage.includes("ubs")) {
|
|
35582
|
+
failedStep = "UBS scan";
|
|
35583
|
+
} else if (errorMessage.includes("evaluation")) {
|
|
35584
|
+
failedStep = "Self-evaluation parsing";
|
|
35585
|
+
} else if (errorMessage.includes("bead") || errorMessage.includes("close")) {
|
|
35586
|
+
failedStep = "Bead close";
|
|
35587
|
+
} else if (errorMessage.includes("memory") || errorMessage.includes("semantic")) {
|
|
35588
|
+
failedStep = "Memory storage (non-fatal)";
|
|
35589
|
+
} else if (errorMessage.includes("reservation") || errorMessage.includes("release")) {
|
|
35590
|
+
failedStep = "File reservation release";
|
|
35591
|
+
} else if (errorMessage.includes("message") || errorMessage.includes("mail")) {
|
|
35592
|
+
failedStep = "Swarm mail notification";
|
|
35593
|
+
}
|
|
35594
|
+
const errorBody = [
|
|
35595
|
+
`## ⚠️ SWARM_COMPLETE FAILED`,
|
|
35596
|
+
"",
|
|
35597
|
+
`**Bead**: ${args.bead_id}`,
|
|
35598
|
+
`**Agent**: ${args.agent_name}`,
|
|
35599
|
+
`**Failed Step**: ${failedStep}`,
|
|
35600
|
+
"",
|
|
35601
|
+
`### Error Message`,
|
|
35602
|
+
"```",
|
|
35603
|
+
errorMessage,
|
|
35604
|
+
"```",
|
|
35605
|
+
"",
|
|
35606
|
+
errorStack ? `### Stack Trace
|
|
35607
|
+
\`\`\`
|
|
35608
|
+
${errorStack.slice(0, 1000)}
|
|
35609
|
+
\`\`\`
|
|
35610
|
+
` : "",
|
|
35611
|
+
`### Context`,
|
|
35612
|
+
`- **Summary**: ${args.summary}`,
|
|
35613
|
+
`- **Files touched**: ${args.files_touched?.length ? args.files_touched.join(", ") : "none"}`,
|
|
35614
|
+
`- **Skip UBS**: ${args.skip_ubs_scan ?? false}`,
|
|
35615
|
+
`- **Skip verification**: ${args.skip_verification ?? false}`,
|
|
35616
|
+
"",
|
|
35617
|
+
`### Recovery Actions`,
|
|
35618
|
+
"1. Check error message for specific issue",
|
|
35619
|
+
"2. Review failed step (UBS scan, typecheck, bead close, etc.)",
|
|
35620
|
+
"3. Fix underlying issue or use skip flags if appropriate",
|
|
35621
|
+
"4. Retry swarm_complete after fixing"
|
|
35622
|
+
].filter(Boolean).join(`
|
|
35623
|
+
`);
|
|
35624
|
+
try {
|
|
35625
|
+
await sendSwarmMessage({
|
|
35626
|
+
projectPath: args.project_key,
|
|
35627
|
+
fromAgent: args.agent_name,
|
|
35628
|
+
toAgents: [],
|
|
35629
|
+
subject: `FAILED: swarm_complete for ${args.bead_id}`,
|
|
35630
|
+
body: errorBody,
|
|
35631
|
+
threadId: epicId,
|
|
35632
|
+
importance: "urgent"
|
|
35633
|
+
});
|
|
35634
|
+
} catch (mailError) {
|
|
35635
|
+
console.error(`[swarm_complete] CRITICAL: Failed to notify coordinator of failure for ${args.bead_id}:`, mailError);
|
|
35636
|
+
console.error(`[swarm_complete] Original error:`, error45);
|
|
35637
|
+
}
|
|
35638
|
+
throw error45;
|
|
35639
|
+
}
|
|
34942
35640
|
}
|
|
34943
35641
|
});
|
|
34944
35642
|
var swarm_record_outcome = tool({
|
|
@@ -35166,6 +35864,147 @@ var swarm_check_strikes = tool({
|
|
|
35166
35864
|
}
|
|
35167
35865
|
}
|
|
35168
35866
|
});
|
|
35867
|
+
var swarm_checkpoint = tool({
|
|
35868
|
+
description: "Checkpoint swarm context for recovery. Records current state for crash recovery. Non-fatal errors.",
|
|
35869
|
+
args: {
|
|
35870
|
+
project_key: tool.schema.string().describe("Project path"),
|
|
35871
|
+
agent_name: tool.schema.string().describe("Agent name"),
|
|
35872
|
+
bead_id: tool.schema.string().describe("Subtask bead ID"),
|
|
35873
|
+
epic_id: tool.schema.string().describe("Epic bead ID"),
|
|
35874
|
+
files_modified: tool.schema.array(tool.schema.string()).describe("Files modified so far"),
|
|
35875
|
+
progress_percent: tool.schema.number().min(0).max(100).describe("Current progress"),
|
|
35876
|
+
directives: tool.schema.object({
|
|
35877
|
+
shared_context: tool.schema.string().optional(),
|
|
35878
|
+
skills_to_load: tool.schema.array(tool.schema.string()).optional(),
|
|
35879
|
+
coordinator_notes: tool.schema.string().optional()
|
|
35880
|
+
}).optional().describe("Coordinator directives for this subtask"),
|
|
35881
|
+
error_context: tool.schema.string().optional().describe("Error context if checkpoint is during error handling")
|
|
35882
|
+
},
|
|
35883
|
+
async execute(args) {
|
|
35884
|
+
try {
|
|
35885
|
+
const checkpoint = {
|
|
35886
|
+
epic_id: args.epic_id,
|
|
35887
|
+
bead_id: args.bead_id,
|
|
35888
|
+
strategy: "file-based",
|
|
35889
|
+
files: args.files_modified,
|
|
35890
|
+
dependencies: [],
|
|
35891
|
+
directives: args.directives || {},
|
|
35892
|
+
recovery: {
|
|
35893
|
+
last_checkpoint: Date.now(),
|
|
35894
|
+
files_modified: args.files_modified,
|
|
35895
|
+
progress_percent: args.progress_percent,
|
|
35896
|
+
error_context: args.error_context
|
|
35897
|
+
}
|
|
35898
|
+
};
|
|
35899
|
+
const event = createEvent("swarm_checkpointed", {
|
|
35900
|
+
project_key: args.project_key,
|
|
35901
|
+
epic_id: args.epic_id,
|
|
35902
|
+
bead_id: args.bead_id,
|
|
35903
|
+
strategy: checkpoint.strategy,
|
|
35904
|
+
files: checkpoint.files,
|
|
35905
|
+
dependencies: checkpoint.dependencies,
|
|
35906
|
+
directives: checkpoint.directives,
|
|
35907
|
+
recovery: checkpoint.recovery
|
|
35908
|
+
});
|
|
35909
|
+
await appendEvent(event, args.project_key);
|
|
35910
|
+
const { getDatabase: getDatabase2 } = await Promise.resolve().then(() => (init_streams(), exports_streams));
|
|
35911
|
+
const db = await getDatabase2(args.project_key);
|
|
35912
|
+
const now = Date.now();
|
|
35913
|
+
await db.query(`INSERT INTO swarm_contexts (id, epic_id, bead_id, strategy, files, dependencies, directives, recovery, created_at, updated_at)
|
|
35914
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
|
35915
|
+
ON CONFLICT (id) DO UPDATE SET
|
|
35916
|
+
files = EXCLUDED.files,
|
|
35917
|
+
recovery = EXCLUDED.recovery,
|
|
35918
|
+
updated_at = EXCLUDED.updated_at`, [
|
|
35919
|
+
args.bead_id,
|
|
35920
|
+
args.epic_id,
|
|
35921
|
+
args.bead_id,
|
|
35922
|
+
checkpoint.strategy,
|
|
35923
|
+
JSON.stringify(checkpoint.files),
|
|
35924
|
+
JSON.stringify(checkpoint.dependencies),
|
|
35925
|
+
JSON.stringify(checkpoint.directives),
|
|
35926
|
+
JSON.stringify(checkpoint.recovery),
|
|
35927
|
+
now,
|
|
35928
|
+
now
|
|
35929
|
+
]);
|
|
35930
|
+
return JSON.stringify({
|
|
35931
|
+
success: true,
|
|
35932
|
+
checkpoint_timestamp: now,
|
|
35933
|
+
summary: `Checkpoint saved for ${args.bead_id} at ${args.progress_percent}%`,
|
|
35934
|
+
bead_id: args.bead_id,
|
|
35935
|
+
epic_id: args.epic_id,
|
|
35936
|
+
files_tracked: args.files_modified.length
|
|
35937
|
+
}, null, 2);
|
|
35938
|
+
} catch (error45) {
|
|
35939
|
+
console.warn(`[swarm_checkpoint] Failed to checkpoint ${args.bead_id}:`, error45);
|
|
35940
|
+
return JSON.stringify({
|
|
35941
|
+
success: false,
|
|
35942
|
+
warning: "Checkpoint failed but continuing",
|
|
35943
|
+
error: error45 instanceof Error ? error45.message : String(error45),
|
|
35944
|
+
bead_id: args.bead_id,
|
|
35945
|
+
note: "This is non-fatal. Work can continue without checkpoint."
|
|
35946
|
+
}, null, 2);
|
|
35947
|
+
}
|
|
35948
|
+
}
|
|
35949
|
+
});
|
|
35950
|
+
var swarm_recover = tool({
|
|
35951
|
+
description: "Recover swarm context from last checkpoint. Returns context or null if not found.",
|
|
35952
|
+
args: {
|
|
35953
|
+
project_key: tool.schema.string().describe("Project path"),
|
|
35954
|
+
epic_id: tool.schema.string().describe("Epic bead ID to recover")
|
|
35955
|
+
},
|
|
35956
|
+
async execute(args) {
|
|
35957
|
+
try {
|
|
35958
|
+
const { getDatabase: getDatabase2 } = await Promise.resolve().then(() => (init_streams(), exports_streams));
|
|
35959
|
+
const db = await getDatabase2(args.project_key);
|
|
35960
|
+
const result = await db.query(`SELECT * FROM swarm_contexts
|
|
35961
|
+
WHERE epic_id = $1
|
|
35962
|
+
ORDER BY updated_at DESC
|
|
35963
|
+
LIMIT 1`, [args.epic_id]);
|
|
35964
|
+
if (result.rows.length === 0) {
|
|
35965
|
+
return JSON.stringify({
|
|
35966
|
+
found: false,
|
|
35967
|
+
message: `No checkpoint found for epic ${args.epic_id}`,
|
|
35968
|
+
epic_id: args.epic_id
|
|
35969
|
+
}, null, 2);
|
|
35970
|
+
}
|
|
35971
|
+
const row = result.rows[0];
|
|
35972
|
+
const context = {
|
|
35973
|
+
id: row.id,
|
|
35974
|
+
epic_id: row.epic_id,
|
|
35975
|
+
bead_id: row.bead_id,
|
|
35976
|
+
strategy: row.strategy,
|
|
35977
|
+
files: JSON.parse(row.files),
|
|
35978
|
+
dependencies: JSON.parse(row.dependencies),
|
|
35979
|
+
directives: JSON.parse(row.directives),
|
|
35980
|
+
recovery: JSON.parse(row.recovery),
|
|
35981
|
+
created_at: row.created_at,
|
|
35982
|
+
updated_at: row.updated_at
|
|
35983
|
+
};
|
|
35984
|
+
const event = createEvent("swarm_recovered", {
|
|
35985
|
+
project_key: args.project_key,
|
|
35986
|
+
epic_id: args.epic_id,
|
|
35987
|
+
bead_id: context.bead_id,
|
|
35988
|
+
recovered_from_checkpoint: context.recovery.last_checkpoint
|
|
35989
|
+
});
|
|
35990
|
+
await appendEvent(event, args.project_key);
|
|
35991
|
+
return JSON.stringify({
|
|
35992
|
+
found: true,
|
|
35993
|
+
context,
|
|
35994
|
+
summary: `Recovered checkpoint from ${new Date(context.updated_at).toISOString()}`,
|
|
35995
|
+
age_seconds: Math.round((Date.now() - context.updated_at) / 1000)
|
|
35996
|
+
}, null, 2);
|
|
35997
|
+
} catch (error45) {
|
|
35998
|
+
console.warn(`[swarm_recover] Failed to recover context for ${args.epic_id}:`, error45);
|
|
35999
|
+
return JSON.stringify({
|
|
36000
|
+
found: false,
|
|
36001
|
+
error: error45 instanceof Error ? error45.message : String(error45),
|
|
36002
|
+
message: `Recovery failed for epic ${args.epic_id}`,
|
|
36003
|
+
epic_id: args.epic_id
|
|
36004
|
+
}, null, 2);
|
|
36005
|
+
}
|
|
36006
|
+
}
|
|
36007
|
+
});
|
|
35169
36008
|
var swarm_learn = tool({
|
|
35170
36009
|
description: `Analyze completed work and optionally create a skill from learned patterns.
|
|
35171
36010
|
|
|
@@ -35306,6 +36145,8 @@ var orchestrateTools = {
|
|
|
35306
36145
|
swarm_get_error_context,
|
|
35307
36146
|
swarm_resolve_error,
|
|
35308
36147
|
swarm_check_strikes,
|
|
36148
|
+
swarm_checkpoint,
|
|
36149
|
+
swarm_recover,
|
|
35309
36150
|
swarm_learn
|
|
35310
36151
|
};
|
|
35311
36152
|
|
|
@@ -36354,6 +37195,164 @@ var mandateTools = {
|
|
|
36354
37195
|
mandate_list,
|
|
36355
37196
|
mandate_stats
|
|
36356
37197
|
};
|
|
37198
|
+
|
|
37199
|
+
// src/output-guardrails.ts
|
|
37200
|
+
var DEFAULT_GUARDRAIL_CONFIG = {
|
|
37201
|
+
defaultMaxChars: 32000,
|
|
37202
|
+
toolLimits: {
|
|
37203
|
+
"repo-autopsy_file": 64000,
|
|
37204
|
+
"repo-autopsy_search": 64000,
|
|
37205
|
+
"repo-autopsy_exports_map": 64000,
|
|
37206
|
+
"context7_get-library-docs": 64000,
|
|
37207
|
+
cass_view: 64000,
|
|
37208
|
+
cass_search: 48000,
|
|
37209
|
+
skills_read: 48000,
|
|
37210
|
+
"repo-autopsy_structure": 24000,
|
|
37211
|
+
"repo-autopsy_stats": 16000,
|
|
37212
|
+
cass_stats: 8000
|
|
37213
|
+
},
|
|
37214
|
+
skipTools: [
|
|
37215
|
+
"beads_create",
|
|
37216
|
+
"beads_create_epic",
|
|
37217
|
+
"beads_query",
|
|
37218
|
+
"beads_update",
|
|
37219
|
+
"beads_close",
|
|
37220
|
+
"beads_start",
|
|
37221
|
+
"beads_ready",
|
|
37222
|
+
"beads_sync",
|
|
37223
|
+
"agentmail_init",
|
|
37224
|
+
"agentmail_send",
|
|
37225
|
+
"agentmail_inbox",
|
|
37226
|
+
"agentmail_read_message",
|
|
37227
|
+
"agentmail_summarize_thread",
|
|
37228
|
+
"agentmail_reserve",
|
|
37229
|
+
"agentmail_release",
|
|
37230
|
+
"agentmail_ack",
|
|
37231
|
+
"swarmmail_init",
|
|
37232
|
+
"swarmmail_send",
|
|
37233
|
+
"swarmmail_inbox",
|
|
37234
|
+
"swarmmail_read_message",
|
|
37235
|
+
"swarmmail_reserve",
|
|
37236
|
+
"swarmmail_release",
|
|
37237
|
+
"swarmmail_ack",
|
|
37238
|
+
"structured_extract_json",
|
|
37239
|
+
"structured_validate",
|
|
37240
|
+
"structured_parse_evaluation",
|
|
37241
|
+
"structured_parse_decomposition",
|
|
37242
|
+
"structured_parse_bead_tree",
|
|
37243
|
+
"swarm_select_strategy",
|
|
37244
|
+
"swarm_plan_prompt",
|
|
37245
|
+
"swarm_decompose",
|
|
37246
|
+
"swarm_validate_decomposition",
|
|
37247
|
+
"swarm_status",
|
|
37248
|
+
"swarm_progress",
|
|
37249
|
+
"swarm_complete",
|
|
37250
|
+
"swarm_record_outcome",
|
|
37251
|
+
"swarm_subtask_prompt",
|
|
37252
|
+
"swarm_spawn_subtask",
|
|
37253
|
+
"swarm_complete_subtask",
|
|
37254
|
+
"swarm_evaluation_prompt",
|
|
37255
|
+
"mandate_file",
|
|
37256
|
+
"mandate_vote",
|
|
37257
|
+
"mandate_query",
|
|
37258
|
+
"mandate_list",
|
|
37259
|
+
"mandate_stats"
|
|
37260
|
+
]
|
|
37261
|
+
};
|
|
37262
|
+
function findMatchingBrace(text, startIdx) {
|
|
37263
|
+
const openChar = text[startIdx];
|
|
37264
|
+
const closeChar = openChar === "{" ? "}" : "]";
|
|
37265
|
+
let depth = 1;
|
|
37266
|
+
for (let i = startIdx + 1;i < text.length; i++) {
|
|
37267
|
+
if (text[i] === openChar) {
|
|
37268
|
+
depth++;
|
|
37269
|
+
} else if (text[i] === closeChar) {
|
|
37270
|
+
depth--;
|
|
37271
|
+
if (depth === 0) {
|
|
37272
|
+
return i;
|
|
37273
|
+
}
|
|
37274
|
+
}
|
|
37275
|
+
}
|
|
37276
|
+
return -1;
|
|
37277
|
+
}
|
|
37278
|
+
function truncateWithBoundaries(text, maxChars) {
|
|
37279
|
+
if (text.length <= maxChars) {
|
|
37280
|
+
return text;
|
|
37281
|
+
}
|
|
37282
|
+
let truncateAt = maxChars;
|
|
37283
|
+
const beforeTruncate = text.slice(0, maxChars);
|
|
37284
|
+
const lastOpenBrace = Math.max(beforeTruncate.lastIndexOf("{"), beforeTruncate.lastIndexOf("["));
|
|
37285
|
+
const lastCloseBrace = Math.max(beforeTruncate.lastIndexOf("}"), beforeTruncate.lastIndexOf("]"));
|
|
37286
|
+
if (lastOpenBrace > lastCloseBrace) {
|
|
37287
|
+
const matchingClose = findMatchingBrace(text, lastOpenBrace);
|
|
37288
|
+
if (matchingClose !== -1 && matchingClose < maxChars * 1.2) {
|
|
37289
|
+
truncateAt = matchingClose + 1;
|
|
37290
|
+
} else {
|
|
37291
|
+
truncateAt = lastOpenBrace;
|
|
37292
|
+
}
|
|
37293
|
+
}
|
|
37294
|
+
const codeBlockMarker = "```";
|
|
37295
|
+
const beforeTruncateForCode = text.slice(0, truncateAt);
|
|
37296
|
+
const codeBlockCount = (beforeTruncateForCode.match(/```/g) || []).length;
|
|
37297
|
+
if (codeBlockCount % 2 === 1) {
|
|
37298
|
+
const closeMarkerIdx = text.indexOf(codeBlockMarker, truncateAt);
|
|
37299
|
+
if (closeMarkerIdx !== -1 && closeMarkerIdx < maxChars * 1.2) {
|
|
37300
|
+
truncateAt = closeMarkerIdx + codeBlockMarker.length;
|
|
37301
|
+
} else {
|
|
37302
|
+
const lastOpenMarker = beforeTruncateForCode.lastIndexOf(codeBlockMarker);
|
|
37303
|
+
if (lastOpenMarker !== -1) {
|
|
37304
|
+
truncateAt = lastOpenMarker;
|
|
37305
|
+
}
|
|
37306
|
+
}
|
|
37307
|
+
}
|
|
37308
|
+
const headerMatch = text.slice(0, truncateAt).match(/\n#{1,6}\s/g);
|
|
37309
|
+
if (headerMatch && headerMatch.length > 0) {
|
|
37310
|
+
const lastHeaderIdx = beforeTruncateForCode.lastIndexOf(`
|
|
37311
|
+
##`);
|
|
37312
|
+
if (lastHeaderIdx !== -1 && lastHeaderIdx > maxChars * 0.8) {
|
|
37313
|
+
truncateAt = lastHeaderIdx;
|
|
37314
|
+
}
|
|
37315
|
+
}
|
|
37316
|
+
while (truncateAt > 0 && !/\s/.test(text[truncateAt])) {
|
|
37317
|
+
truncateAt--;
|
|
37318
|
+
}
|
|
37319
|
+
const truncated = text.slice(0, truncateAt).trimEnd();
|
|
37320
|
+
const charsRemoved = text.length - truncated.length;
|
|
37321
|
+
return `${truncated}
|
|
37322
|
+
|
|
37323
|
+
[TRUNCATED - ${charsRemoved.toLocaleString()} chars removed]`;
|
|
37324
|
+
}
|
|
37325
|
+
function getToolLimit(toolName, config2 = DEFAULT_GUARDRAIL_CONFIG) {
|
|
37326
|
+
return config2.toolLimits[toolName] ?? config2.defaultMaxChars;
|
|
37327
|
+
}
|
|
37328
|
+
function guardrailOutput(toolName, output, config2 = DEFAULT_GUARDRAIL_CONFIG) {
|
|
37329
|
+
const originalLength = output.length;
|
|
37330
|
+
if (config2.skipTools.includes(toolName)) {
|
|
37331
|
+
return {
|
|
37332
|
+
output,
|
|
37333
|
+
truncated: false,
|
|
37334
|
+
originalLength,
|
|
37335
|
+
truncatedLength: originalLength
|
|
37336
|
+
};
|
|
37337
|
+
}
|
|
37338
|
+
const limit = getToolLimit(toolName, config2);
|
|
37339
|
+
if (originalLength <= limit) {
|
|
37340
|
+
return {
|
|
37341
|
+
output,
|
|
37342
|
+
truncated: false,
|
|
37343
|
+
originalLength,
|
|
37344
|
+
truncatedLength: originalLength
|
|
37345
|
+
};
|
|
37346
|
+
}
|
|
37347
|
+
const truncatedOutput = truncateWithBoundaries(output, limit);
|
|
37348
|
+
const truncatedLength = truncatedOutput.length;
|
|
37349
|
+
return {
|
|
37350
|
+
output: truncatedOutput,
|
|
37351
|
+
truncated: true,
|
|
37352
|
+
originalLength,
|
|
37353
|
+
truncatedLength
|
|
37354
|
+
};
|
|
37355
|
+
}
|
|
36357
37356
|
// src/storage.ts
|
|
36358
37357
|
init_learning();
|
|
36359
37358
|
|
|
@@ -36448,6 +37447,34 @@ class InMemoryMaturityStorage {
|
|
|
36448
37447
|
}
|
|
36449
37448
|
}
|
|
36450
37449
|
|
|
37450
|
+
// src/storage.ts
|
|
37451
|
+
function getCollectionNames() {
|
|
37452
|
+
const base = {
|
|
37453
|
+
feedback: "swarm-feedback",
|
|
37454
|
+
patterns: "swarm-patterns",
|
|
37455
|
+
maturity: "swarm-maturity"
|
|
37456
|
+
};
|
|
37457
|
+
if (process.env.TEST_MEMORY_COLLECTIONS === "true") {
|
|
37458
|
+
return {
|
|
37459
|
+
feedback: `${base.feedback}-test`,
|
|
37460
|
+
patterns: `${base.patterns}-test`,
|
|
37461
|
+
maturity: `${base.maturity}-test`
|
|
37462
|
+
};
|
|
37463
|
+
}
|
|
37464
|
+
return base;
|
|
37465
|
+
}
|
|
37466
|
+
var DEFAULT_STORAGE_CONFIG = {
|
|
37467
|
+
backend: "semantic-memory",
|
|
37468
|
+
collections: getCollectionNames(),
|
|
37469
|
+
useSemanticSearch: true
|
|
37470
|
+
};
|
|
37471
|
+
var sessionStats = {
|
|
37472
|
+
storesCount: 0,
|
|
37473
|
+
queriesCount: 0,
|
|
37474
|
+
sessionStart: Date.now(),
|
|
37475
|
+
lastAlertCheck: Date.now()
|
|
37476
|
+
};
|
|
37477
|
+
|
|
36451
37478
|
// src/index.ts
|
|
36452
37479
|
init_skills();
|
|
36453
37480
|
var SwarmPlugin = async (input) => {
|
|
@@ -36503,6 +37530,13 @@ var SwarmPlugin = async (input) => {
|
|
|
36503
37530
|
},
|
|
36504
37531
|
"tool.execute.after": async (input2, output) => {
|
|
36505
37532
|
const toolName = input2.tool;
|
|
37533
|
+
if (output.output && typeof output.output === "string") {
|
|
37534
|
+
const guardrailResult = guardrailOutput(toolName, output.output);
|
|
37535
|
+
if (guardrailResult.truncated) {
|
|
37536
|
+
output.output = guardrailResult.output;
|
|
37537
|
+
console.log(`[swarm-plugin] Guardrail truncated ${toolName}: ${guardrailResult.originalLength} → ${guardrailResult.truncatedLength} chars`);
|
|
37538
|
+
}
|
|
37539
|
+
}
|
|
36506
37540
|
if (toolName === "agentmail_init" && output.output) {
|
|
36507
37541
|
try {
|
|
36508
37542
|
const result = JSON.parse(output.output);
|