opencode-swarm 6.39.0 → 6.40.1
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/LICENSE +21 -21
- package/README.md +1 -1
- package/dist/cli/index.js +177 -36
- package/dist/commands/index.d.ts +1 -1
- package/dist/config/plan-schema.d.ts +2 -0
- package/dist/config/schema.d.ts +3 -2
- package/dist/index.js +559 -150
- package/dist/lang/grammars/tree-sitter-bash.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-c-sharp.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-cpp.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-css.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-go.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-ini.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-java.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-javascript.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-php.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-powershell.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-python.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-regex.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-ruby.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-rust.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-tsx.wasm +0 -0
- package/dist/lang/grammars/tree-sitter-typescript.wasm +0 -0
- package/dist/lang/grammars/tree-sitter.wasm +0 -0
- package/dist/services/compaction-service.d.ts +2 -2
- package/dist/session/snapshot-reader.d.ts +8 -0
- package/dist/tools/save-plan.d.ts +1 -0
- package/package.json +4 -1
- /package/dist/commands/{write_retro.d.ts → write-retro.d.ts} +0 -0
package/dist/index.js
CHANGED
|
@@ -4,25 +4,43 @@ var __getProtoOf = Object.getPrototypeOf;
|
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
function __accessProp(key) {
|
|
8
|
+
return this[key];
|
|
9
|
+
}
|
|
10
|
+
var __toESMCache_node;
|
|
11
|
+
var __toESMCache_esm;
|
|
7
12
|
var __toESM = (mod, isNodeMode, target) => {
|
|
13
|
+
var canCache = mod != null && typeof mod === "object";
|
|
14
|
+
if (canCache) {
|
|
15
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
16
|
+
var cached = cache.get(mod);
|
|
17
|
+
if (cached)
|
|
18
|
+
return cached;
|
|
19
|
+
}
|
|
8
20
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
21
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
22
|
for (let key of __getOwnPropNames(mod))
|
|
11
23
|
if (!__hasOwnProp.call(to, key))
|
|
12
24
|
__defProp(to, key, {
|
|
13
|
-
get: (
|
|
25
|
+
get: __accessProp.bind(mod, key),
|
|
14
26
|
enumerable: true
|
|
15
27
|
});
|
|
28
|
+
if (canCache)
|
|
29
|
+
cache.set(mod, to);
|
|
16
30
|
return to;
|
|
17
31
|
};
|
|
18
32
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
33
|
+
var __returnValue = (v) => v;
|
|
34
|
+
function __exportSetter(name2, newValue) {
|
|
35
|
+
this[name2] = __returnValue.bind(null, newValue);
|
|
36
|
+
}
|
|
19
37
|
var __export = (target, all) => {
|
|
20
38
|
for (var name2 in all)
|
|
21
39
|
__defProp(target, name2, {
|
|
22
40
|
get: all[name2],
|
|
23
41
|
enumerable: true,
|
|
24
42
|
configurable: true,
|
|
25
|
-
set: (
|
|
43
|
+
set: __exportSetter.bind(all, name2)
|
|
26
44
|
});
|
|
27
45
|
};
|
|
28
46
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
@@ -14892,8 +14910,8 @@ var init_schema = __esm(() => {
|
|
|
14892
14910
|
});
|
|
14893
14911
|
CheckpointConfigSchema = exports_external.object({
|
|
14894
14912
|
enabled: exports_external.boolean().default(true),
|
|
14895
|
-
auto_checkpoint_threshold: exports_external.number().min(1).max(20).default(3)
|
|
14896
|
-
});
|
|
14913
|
+
auto_checkpoint_threshold: exports_external.number().int().min(1).max(20).default(3)
|
|
14914
|
+
}).strict();
|
|
14897
14915
|
AutomationModeSchema = exports_external.enum(["manual", "hybrid", "auto"]);
|
|
14898
14916
|
AutomationCapabilitiesSchema = exports_external.object({
|
|
14899
14917
|
plan_sync: exports_external.boolean().default(true),
|
|
@@ -15013,7 +15031,8 @@ var init_schema = __esm(() => {
|
|
|
15013
15031
|
block_on_threshold: exports_external.boolean().default(false).describe("If true, block phase completion when threshold exceeded. Default: advisory only.")
|
|
15014
15032
|
}).optional(),
|
|
15015
15033
|
incremental_verify: IncrementalVerifyConfigSchema.optional(),
|
|
15016
|
-
compaction_service: CompactionConfigSchema.optional()
|
|
15034
|
+
compaction_service: CompactionConfigSchema.optional(),
|
|
15035
|
+
turbo_mode: exports_external.boolean().default(false).optional()
|
|
15017
15036
|
});
|
|
15018
15037
|
});
|
|
15019
15038
|
|
|
@@ -15192,7 +15211,8 @@ var init_plan_schema = __esm(() => {
|
|
|
15192
15211
|
id: exports_external.number().int().min(1),
|
|
15193
15212
|
name: exports_external.string().min(1),
|
|
15194
15213
|
status: PhaseStatusSchema.default("pending"),
|
|
15195
|
-
tasks: exports_external.array(TaskSchema).default([])
|
|
15214
|
+
tasks: exports_external.array(TaskSchema).default([]),
|
|
15215
|
+
required_agents: exports_external.array(exports_external.string()).optional()
|
|
15196
15216
|
});
|
|
15197
15217
|
PlanSchema = exports_external.object({
|
|
15198
15218
|
schema_version: exports_external.literal("1.0.0"),
|
|
@@ -16100,7 +16120,7 @@ async function updateTaskStatus(directory, taskId, status) {
|
|
|
16100
16120
|
throw new Error(`Task not found: ${taskId}`);
|
|
16101
16121
|
}
|
|
16102
16122
|
const updatedPlan = { ...plan, phases: updatedPhases };
|
|
16103
|
-
await savePlan(directory, updatedPlan, { preserveCompletedStatuses:
|
|
16123
|
+
await savePlan(directory, updatedPlan, { preserveCompletedStatuses: true });
|
|
16104
16124
|
return updatedPlan;
|
|
16105
16125
|
}
|
|
16106
16126
|
function derivePlanMarkdown(plan) {
|
|
@@ -16456,8 +16476,8 @@ function getTaskBlockers(task, summary, status) {
|
|
|
16456
16476
|
}
|
|
16457
16477
|
return blockers;
|
|
16458
16478
|
}
|
|
16459
|
-
async function buildTaskSummary(task, taskId) {
|
|
16460
|
-
const result = await loadEvidence(
|
|
16479
|
+
async function buildTaskSummary(directory, task, taskId) {
|
|
16480
|
+
const result = await loadEvidence(directory, taskId);
|
|
16461
16481
|
const bundle = result.status === "found" ? result.bundle : null;
|
|
16462
16482
|
const phase = task?.phase ?? 0;
|
|
16463
16483
|
const status = getTaskStatus(task, bundle);
|
|
@@ -16486,18 +16506,18 @@ async function buildTaskSummary(task, taskId) {
|
|
|
16486
16506
|
lastEvidenceTimestamp: lastTimestamp
|
|
16487
16507
|
};
|
|
16488
16508
|
}
|
|
16489
|
-
async function buildPhaseSummary(phase) {
|
|
16490
|
-
const taskIds = await listEvidenceTaskIds(
|
|
16509
|
+
async function buildPhaseSummary(directory, phase) {
|
|
16510
|
+
const taskIds = await listEvidenceTaskIds(directory);
|
|
16491
16511
|
const phaseTaskIds = new Set(phase.tasks.map((t) => t.id));
|
|
16492
16512
|
const taskSummaries = [];
|
|
16493
16513
|
const _taskMap = new Map(phase.tasks.map((t) => [t.id, t]));
|
|
16494
16514
|
for (const task of phase.tasks) {
|
|
16495
|
-
const summary = await buildTaskSummary(task, task.id);
|
|
16515
|
+
const summary = await buildTaskSummary(directory, task, task.id);
|
|
16496
16516
|
taskSummaries.push(summary);
|
|
16497
16517
|
}
|
|
16498
16518
|
const extraTaskIds = taskIds.filter((id) => !phaseTaskIds.has(id));
|
|
16499
16519
|
for (const taskId of extraTaskIds) {
|
|
16500
|
-
const summary = await buildTaskSummary(undefined, taskId);
|
|
16520
|
+
const summary = await buildTaskSummary(directory, undefined, taskId);
|
|
16501
16521
|
if (summary.phase === phase.id) {
|
|
16502
16522
|
taskSummaries.push(summary);
|
|
16503
16523
|
}
|
|
@@ -16598,7 +16618,7 @@ async function buildEvidenceSummary(directory, currentPhase) {
|
|
|
16598
16618
|
let totalTasks = 0;
|
|
16599
16619
|
let completedTasks = 0;
|
|
16600
16620
|
for (const phase of phasesToProcess) {
|
|
16601
|
-
const summary = await buildPhaseSummary(phase);
|
|
16621
|
+
const summary = await buildPhaseSummary(directory, phase);
|
|
16602
16622
|
phaseSummaries.push(summary);
|
|
16603
16623
|
totalTasks += summary.totalTasks;
|
|
16604
16624
|
completedTasks += summary.completedTasks;
|
|
@@ -30176,6 +30196,11 @@ function isGitRepo() {
|
|
|
30176
30196
|
}
|
|
30177
30197
|
function handleSave(label, directory) {
|
|
30178
30198
|
try {
|
|
30199
|
+
let maxCheckpoints = 20;
|
|
30200
|
+
try {
|
|
30201
|
+
const { config: config3 } = loadPluginConfigWithMeta(directory);
|
|
30202
|
+
maxCheckpoints = config3.checkpoint?.auto_checkpoint_threshold ?? maxCheckpoints;
|
|
30203
|
+
} catch {}
|
|
30179
30204
|
const log2 = readCheckpointLog(directory);
|
|
30180
30205
|
const existingCheckpoint = log2.checkpoints.find((c) => c.label === label);
|
|
30181
30206
|
if (existingCheckpoint) {
|
|
@@ -30280,6 +30305,7 @@ function handleDelete(label, directory) {
|
|
|
30280
30305
|
var CHECKPOINT_LOG_PATH = ".swarm/checkpoints.json", MAX_LABEL_LENGTH = 100, GIT_TIMEOUT_MS = 30000, SHELL_METACHARACTERS, SAFE_LABEL_PATTERN, CONTROL_CHAR_PATTERN, NON_ASCII_PATTERN, checkpoint;
|
|
30281
30306
|
var init_checkpoint = __esm(() => {
|
|
30282
30307
|
init_tool();
|
|
30308
|
+
init_config();
|
|
30283
30309
|
init_create_tool();
|
|
30284
30310
|
SHELL_METACHARACTERS = /[;|&$`(){}<>!'"]/;
|
|
30285
30311
|
SAFE_LABEL_PATTERN = /^[a-zA-Z0-9_ -]+$/;
|
|
@@ -30303,7 +30329,7 @@ var init_checkpoint = __esm(() => {
|
|
|
30303
30329
|
let label;
|
|
30304
30330
|
try {
|
|
30305
30331
|
action = String(args2.action);
|
|
30306
|
-
label = args2.label !== undefined ? String(args2.label) : undefined;
|
|
30332
|
+
label = args2.label !== undefined && args2.label !== null ? String(args2.label) : undefined;
|
|
30307
30333
|
} catch {
|
|
30308
30334
|
return JSON.stringify({
|
|
30309
30335
|
action: "unknown",
|
|
@@ -33518,6 +33544,59 @@ var init_profiles = __esm(() => {
|
|
|
33518
33544
|
]
|
|
33519
33545
|
}
|
|
33520
33546
|
});
|
|
33547
|
+
LANGUAGE_REGISTRY.register({
|
|
33548
|
+
id: "php",
|
|
33549
|
+
displayName: "PHP",
|
|
33550
|
+
tier: 3,
|
|
33551
|
+
extensions: [".php", ".phtml"],
|
|
33552
|
+
treeSitter: { grammarId: "php", wasmFile: "tree-sitter-php.wasm" },
|
|
33553
|
+
build: {
|
|
33554
|
+
detectFiles: ["composer.json"],
|
|
33555
|
+
commands: []
|
|
33556
|
+
},
|
|
33557
|
+
test: {
|
|
33558
|
+
detectFiles: ["phpunit.xml", "phpunit.xml.dist"],
|
|
33559
|
+
frameworks: [
|
|
33560
|
+
{
|
|
33561
|
+
name: "PHPUnit",
|
|
33562
|
+
detect: "phpunit.xml",
|
|
33563
|
+
cmd: "vendor/bin/phpunit",
|
|
33564
|
+
priority: 1
|
|
33565
|
+
}
|
|
33566
|
+
]
|
|
33567
|
+
},
|
|
33568
|
+
lint: {
|
|
33569
|
+
detectFiles: [".php-cs-fixer.php", "phpcs.xml"],
|
|
33570
|
+
linters: [
|
|
33571
|
+
{
|
|
33572
|
+
name: "PHP-CS-Fixer",
|
|
33573
|
+
detect: ".php-cs-fixer.php",
|
|
33574
|
+
cmd: "vendor/bin/php-cs-fixer fix --dry-run --diff",
|
|
33575
|
+
priority: 1
|
|
33576
|
+
}
|
|
33577
|
+
]
|
|
33578
|
+
},
|
|
33579
|
+
audit: {
|
|
33580
|
+
detectFiles: ["composer.lock"],
|
|
33581
|
+
command: "composer audit --format=json",
|
|
33582
|
+
outputFormat: "json"
|
|
33583
|
+
},
|
|
33584
|
+
sast: { nativeRuleSet: "php", semgrepSupport: "ga" },
|
|
33585
|
+
prompts: {
|
|
33586
|
+
coderConstraints: [
|
|
33587
|
+
"Follow PSR-12 coding standards",
|
|
33588
|
+
"Use strict types declaration: declare(strict_types=1)",
|
|
33589
|
+
"Prefer type hints and return type declarations on all functions",
|
|
33590
|
+
"Use dependency injection over static methods and singletons"
|
|
33591
|
+
],
|
|
33592
|
+
reviewerChecklist: [
|
|
33593
|
+
"Verify no user input reaches SQL queries without parameterised binding",
|
|
33594
|
+
"Check for XSS \u2014 all output must be escaped with htmlspecialchars()",
|
|
33595
|
+
"Confirm no eval(), exec(), or shell_exec() with user-controlled input",
|
|
33596
|
+
"Validate proper error handling \u2014 no bare catch blocks that swallow errors"
|
|
33597
|
+
]
|
|
33598
|
+
}
|
|
33599
|
+
});
|
|
33521
33600
|
});
|
|
33522
33601
|
|
|
33523
33602
|
// src/lang/detector.ts
|
|
@@ -34135,7 +34214,7 @@ async function detectAvailableLinter(directory) {
|
|
|
34135
34214
|
async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
34136
34215
|
const DETECT_TIMEOUT = 2000;
|
|
34137
34216
|
try {
|
|
34138
|
-
const biomeProc = Bun.spawn([
|
|
34217
|
+
const biomeProc = Bun.spawn([biomeBin, "--version"], {
|
|
34139
34218
|
stdout: "pipe",
|
|
34140
34219
|
stderr: "pipe"
|
|
34141
34220
|
});
|
|
@@ -34149,7 +34228,7 @@ async function _detectAvailableLinter(_projectDir, biomeBin, eslintBin) {
|
|
|
34149
34228
|
}
|
|
34150
34229
|
} catch {}
|
|
34151
34230
|
try {
|
|
34152
|
-
const eslintProc = Bun.spawn([
|
|
34231
|
+
const eslintProc = Bun.spawn([eslintBin, "--version"], {
|
|
34153
34232
|
stdout: "pipe",
|
|
34154
34233
|
stderr: "pipe"
|
|
34155
34234
|
});
|
|
@@ -36161,12 +36240,13 @@ async function runLintCheck(dir, linter, timeoutMs) {
|
|
|
36161
36240
|
const startTime = Date.now();
|
|
36162
36241
|
try {
|
|
36163
36242
|
const lintPromise = runLint(linter, "check", dir);
|
|
36243
|
+
let timeoutId;
|
|
36164
36244
|
const timeoutPromise = new Promise((_, reject) => {
|
|
36165
|
-
setTimeout(() => {
|
|
36245
|
+
timeoutId = setTimeout(() => {
|
|
36166
36246
|
reject(new Error(`Lint check timed out after ${timeoutMs}ms`));
|
|
36167
36247
|
}, timeoutMs);
|
|
36168
36248
|
});
|
|
36169
|
-
const result = await Promise.race([lintPromise, timeoutPromise]);
|
|
36249
|
+
const result = await Promise.race([lintPromise, timeoutPromise]).finally(() => clearTimeout(timeoutId));
|
|
36170
36250
|
if (!result.success) {
|
|
36171
36251
|
return {
|
|
36172
36252
|
type: "lint",
|
|
@@ -36658,7 +36738,7 @@ function deriveRequiredGates(agentType) {
|
|
|
36658
36738
|
}
|
|
36659
36739
|
function expandRequiredGates(existingGates, newAgentType) {
|
|
36660
36740
|
const newGates = deriveRequiredGates(newAgentType);
|
|
36661
|
-
const combined = [...new Set([...existingGates, ...newGates])];
|
|
36741
|
+
const combined = [...new Set([...existingGates ?? [], ...newGates])];
|
|
36662
36742
|
return combined.sort();
|
|
36663
36743
|
}
|
|
36664
36744
|
function getEvidenceDir(directory) {
|
|
@@ -36793,10 +36873,10 @@ function createPreflightIntegration(config3) {
|
|
|
36793
36873
|
});
|
|
36794
36874
|
const report = await runPreflight(directory, request.currentPhase, preflightConfig);
|
|
36795
36875
|
if (statusArtifact) {
|
|
36796
|
-
const
|
|
36797
|
-
statusArtifact.recordOutcome(
|
|
36876
|
+
const state = report.overall === "pass" ? "success" : "failure";
|
|
36877
|
+
statusArtifact.recordOutcome(state, request.currentPhase, report.message);
|
|
36798
36878
|
console.log("[PreflightIntegration] Status artifact updated", {
|
|
36799
|
-
state
|
|
36879
|
+
state,
|
|
36800
36880
|
phase: request.currentPhase,
|
|
36801
36881
|
message: report.message
|
|
36802
36882
|
});
|
|
@@ -37238,7 +37318,7 @@ async function readPriorDriftReports(directory) {
|
|
|
37238
37318
|
continue;
|
|
37239
37319
|
try {
|
|
37240
37320
|
const report = JSON.parse(content);
|
|
37241
|
-
if (typeof report.phase !== "number" || typeof report.alignment !== "string") {
|
|
37321
|
+
if (typeof report.phase !== "number" || typeof report.alignment !== "string" || typeof report.timestamp !== "string" || typeof report.drift_score !== "number" || typeof report.schema_version !== "number" || !Array.isArray(report.compounding_effects)) {
|
|
37242
37322
|
console.warn(`[curator-drift] Skipping corrupt drift report: ${filename}`);
|
|
37243
37323
|
continue;
|
|
37244
37324
|
}
|
|
@@ -40635,8 +40715,8 @@ async function loadGrammar(languageId) {
|
|
|
40635
40715
|
const wasmFileName = getWasmFileName(normalizedId);
|
|
40636
40716
|
const grammarsPath = getGrammarsPath();
|
|
40637
40717
|
const wasmPath = fileURLToPath(new URL(`${grammarsPath}${wasmFileName}`, import.meta.url));
|
|
40638
|
-
const { existsSync:
|
|
40639
|
-
if (!
|
|
40718
|
+
const { existsSync: existsSync28 } = await import("fs");
|
|
40719
|
+
if (!existsSync28(wasmPath)) {
|
|
40640
40720
|
throw new Error(`Grammar file not found for ${languageId}: ${wasmPath}
|
|
40641
40721
|
Make sure to run 'bun run build' to copy grammar files to dist/lang/grammars/`);
|
|
40642
40722
|
}
|
|
@@ -40666,8 +40746,6 @@ var init_runtime = __esm(() => {
|
|
|
40666
40746
|
c: "tree-sitter-cpp.wasm",
|
|
40667
40747
|
csharp: "tree-sitter-c-sharp.wasm",
|
|
40668
40748
|
css: "tree-sitter-css.wasm",
|
|
40669
|
-
html: "tree-sitter-html.wasm",
|
|
40670
|
-
json: "tree-sitter-json.wasm",
|
|
40671
40749
|
bash: "tree-sitter-bash.wasm",
|
|
40672
40750
|
ruby: "tree-sitter-ruby.wasm",
|
|
40673
40751
|
php: "tree-sitter-php.wasm",
|
|
@@ -40760,7 +40838,9 @@ function startAgentSession(sessionId, agentName, staleDurationMs = 7200000, dire
|
|
|
40760
40838
|
applyRehydrationCache(sessionState);
|
|
40761
40839
|
if (directory) {
|
|
40762
40840
|
let rehydrationPromise;
|
|
40763
|
-
rehydrationPromise = rehydrateSessionFromDisk(directory, sessionState).catch(() => {
|
|
40841
|
+
rehydrationPromise = rehydrateSessionFromDisk(directory, sessionState).catch((err2) => {
|
|
40842
|
+
console.warn("[state] Rehydration failed:", err2 instanceof Error ? err2.message : String(err2));
|
|
40843
|
+
}).finally(() => {
|
|
40764
40844
|
swarmState.pendingRehydrations.delete(rehydrationPromise);
|
|
40765
40845
|
});
|
|
40766
40846
|
swarmState.pendingRehydrations.add(rehydrationPromise);
|
|
@@ -45154,7 +45234,24 @@ async function handleBenchmarkCommand(directory, args2) {
|
|
|
45154
45234
|
}
|
|
45155
45235
|
|
|
45156
45236
|
// src/commands/checkpoint.ts
|
|
45237
|
+
init_zod();
|
|
45157
45238
|
init_checkpoint();
|
|
45239
|
+
var CheckpointResultSchema = exports_external.object({
|
|
45240
|
+
action: exports_external.string().optional(),
|
|
45241
|
+
success: exports_external.boolean(),
|
|
45242
|
+
error: exports_external.string().optional(),
|
|
45243
|
+
checkpoints: exports_external.array(exports_external.unknown()).optional()
|
|
45244
|
+
}).passthrough();
|
|
45245
|
+
function safeParseResult(result) {
|
|
45246
|
+
const parsed = CheckpointResultSchema.safeParse(JSON.parse(result));
|
|
45247
|
+
if (!parsed.success) {
|
|
45248
|
+
return {
|
|
45249
|
+
success: false,
|
|
45250
|
+
error: `Invalid response: ${parsed.error.message}`
|
|
45251
|
+
};
|
|
45252
|
+
}
|
|
45253
|
+
return parsed.data;
|
|
45254
|
+
}
|
|
45158
45255
|
async function handleCheckpointCommand(directory, args2) {
|
|
45159
45256
|
const subcommand = args2[0] || "list";
|
|
45160
45257
|
const label = args2[1];
|
|
@@ -45177,7 +45274,7 @@ async function handleSave2(directory, label) {
|
|
|
45177
45274
|
const result = await checkpoint.execute({ action: "save", label }, {
|
|
45178
45275
|
directory
|
|
45179
45276
|
});
|
|
45180
|
-
const parsed =
|
|
45277
|
+
const parsed = safeParseResult(result);
|
|
45181
45278
|
if (parsed.success) {
|
|
45182
45279
|
return `\u2713 Checkpoint saved: "${label}"`;
|
|
45183
45280
|
} else {
|
|
@@ -45196,7 +45293,7 @@ async function handleRestore2(directory, label) {
|
|
|
45196
45293
|
const result = await checkpoint.execute({ action: "restore", label }, {
|
|
45197
45294
|
directory
|
|
45198
45295
|
});
|
|
45199
|
-
const parsed =
|
|
45296
|
+
const parsed = safeParseResult(result);
|
|
45200
45297
|
if (parsed.success) {
|
|
45201
45298
|
return `\u2713 Restored to checkpoint: "${label}"`;
|
|
45202
45299
|
} else {
|
|
@@ -45215,7 +45312,7 @@ async function handleDelete2(directory, label) {
|
|
|
45215
45312
|
const result = await checkpoint.execute({ action: "delete", label }, {
|
|
45216
45313
|
directory
|
|
45217
45314
|
});
|
|
45218
|
-
const parsed =
|
|
45315
|
+
const parsed = safeParseResult(result);
|
|
45219
45316
|
if (parsed.success) {
|
|
45220
45317
|
return `\u2713 Checkpoint deleted: "${label}"`;
|
|
45221
45318
|
} else {
|
|
@@ -45231,7 +45328,7 @@ async function handleList2(directory) {
|
|
|
45231
45328
|
const result = await checkpoint.execute({ action: "list" }, {
|
|
45232
45329
|
directory
|
|
45233
45330
|
});
|
|
45234
|
-
const parsed =
|
|
45331
|
+
const parsed = safeParseResult(result);
|
|
45235
45332
|
if (!parsed.success) {
|
|
45236
45333
|
return `Error: ${parsed.error || "Failed to list checkpoints"}`;
|
|
45237
45334
|
}
|
|
@@ -48287,7 +48384,8 @@ async function extractFromLegacy(directory) {
|
|
|
48287
48384
|
mappedStatus = "complete";
|
|
48288
48385
|
else if (status === "IN PROGRESS")
|
|
48289
48386
|
mappedStatus = "in_progress";
|
|
48290
|
-
const headerLineIndex =
|
|
48387
|
+
const headerLineIndex = planContent.substring(0, match.index).split(`
|
|
48388
|
+
`).length - 1;
|
|
48291
48389
|
let completed = 0;
|
|
48292
48390
|
let total = 0;
|
|
48293
48391
|
if (headerLineIndex !== -1) {
|
|
@@ -48687,9 +48785,9 @@ async function getPlanData(directory, phaseArg) {
|
|
|
48687
48785
|
return {
|
|
48688
48786
|
hasPlan: true,
|
|
48689
48787
|
fullMarkdown,
|
|
48690
|
-
requestedPhase:
|
|
48788
|
+
requestedPhase: null,
|
|
48691
48789
|
phaseMarkdown: null,
|
|
48692
|
-
errorMessage:
|
|
48790
|
+
errorMessage: `Invalid phase number: "${phaseArg}"`,
|
|
48693
48791
|
isLegacy: false
|
|
48694
48792
|
};
|
|
48695
48793
|
}
|
|
@@ -48740,9 +48838,9 @@ async function getPlanData(directory, phaseArg) {
|
|
|
48740
48838
|
return {
|
|
48741
48839
|
hasPlan: true,
|
|
48742
48840
|
fullMarkdown: planContent,
|
|
48743
|
-
requestedPhase:
|
|
48841
|
+
requestedPhase: null,
|
|
48744
48842
|
phaseMarkdown: null,
|
|
48745
|
-
errorMessage:
|
|
48843
|
+
errorMessage: `Invalid phase number: "${phaseArg}"`,
|
|
48746
48844
|
isLegacy: true
|
|
48747
48845
|
};
|
|
48748
48846
|
}
|
|
@@ -48982,9 +49080,16 @@ function sanitizeSummaryId(id) {
|
|
|
48982
49080
|
}
|
|
48983
49081
|
async function storeSummary(directory, id, fullOutput, summaryText, maxStoredBytes) {
|
|
48984
49082
|
const sanitizedId = sanitizeSummaryId(id);
|
|
48985
|
-
const
|
|
48986
|
-
|
|
48987
|
-
|
|
49083
|
+
const preCheckEntry = {
|
|
49084
|
+
id: sanitizedId,
|
|
49085
|
+
summaryText,
|
|
49086
|
+
fullOutput,
|
|
49087
|
+
timestamp: Date.now(),
|
|
49088
|
+
originalBytes: Buffer.byteLength(fullOutput, "utf8")
|
|
49089
|
+
};
|
|
49090
|
+
const serializedSize = Buffer.byteLength(JSON.stringify(preCheckEntry), "utf8");
|
|
49091
|
+
if (serializedSize > maxStoredBytes) {
|
|
49092
|
+
throw new Error(`Summary entry size (${serializedSize} bytes) exceeds maximum (${maxStoredBytes} bytes)`);
|
|
48988
49093
|
}
|
|
48989
49094
|
const relativePath = path27.join("summaries", `${sanitizedId}.json`);
|
|
48990
49095
|
const summaryPath = validateSwarmPath(directory, relativePath);
|
|
@@ -48994,7 +49099,7 @@ async function storeSummary(directory, id, fullOutput, summaryText, maxStoredByt
|
|
|
48994
49099
|
summaryText,
|
|
48995
49100
|
fullOutput,
|
|
48996
49101
|
timestamp: Date.now(),
|
|
48997
|
-
originalBytes:
|
|
49102
|
+
originalBytes: Buffer.byteLength(fullOutput, "utf8")
|
|
48998
49103
|
};
|
|
48999
49104
|
const entryJson = JSON.stringify(entry);
|
|
49000
49105
|
mkdirSync9(summaryDir, { recursive: true });
|
|
@@ -49560,7 +49665,15 @@ function makeInitialState() {
|
|
|
49560
49665
|
lastSnapshotAt: null
|
|
49561
49666
|
};
|
|
49562
49667
|
}
|
|
49563
|
-
var
|
|
49668
|
+
var sessionStates = new Map;
|
|
49669
|
+
function getSessionState(sessionId) {
|
|
49670
|
+
let state = sessionStates.get(sessionId);
|
|
49671
|
+
if (!state) {
|
|
49672
|
+
state = makeInitialState();
|
|
49673
|
+
sessionStates.set(sessionId, state);
|
|
49674
|
+
}
|
|
49675
|
+
return state;
|
|
49676
|
+
}
|
|
49564
49677
|
function appendSnapshot(directory, tier, budgetPct, message) {
|
|
49565
49678
|
try {
|
|
49566
49679
|
const snapshotPath = path29.join(directory, ".swarm", "context-snapshot.md");
|
|
@@ -49599,6 +49712,7 @@ function createCompactionService(config3, directory, injectMessage) {
|
|
|
49599
49712
|
if (budgetPct <= 0)
|
|
49600
49713
|
return;
|
|
49601
49714
|
const sessionId = _input.sessionID;
|
|
49715
|
+
const state = getSessionState(sessionId);
|
|
49602
49716
|
try {
|
|
49603
49717
|
if (budgetPct >= config3.emergencyThreshold && budgetPct > state.lastEmergencyAt + 5) {
|
|
49604
49718
|
state.lastEmergencyAt = budgetPct;
|
|
@@ -49630,11 +49744,23 @@ function createCompactionService(config3, directory, injectMessage) {
|
|
|
49630
49744
|
}
|
|
49631
49745
|
};
|
|
49632
49746
|
}
|
|
49633
|
-
function getCompactionMetrics() {
|
|
49634
|
-
|
|
49635
|
-
|
|
49636
|
-
|
|
49637
|
-
|
|
49747
|
+
function getCompactionMetrics(sessionId) {
|
|
49748
|
+
if (sessionId) {
|
|
49749
|
+
const state = getSessionState(sessionId);
|
|
49750
|
+
return {
|
|
49751
|
+
compactionCount: state.observationCount + state.reflectionCount + state.emergencyCount,
|
|
49752
|
+
lastSnapshotAt: state.lastSnapshotAt
|
|
49753
|
+
};
|
|
49754
|
+
}
|
|
49755
|
+
let total = 0;
|
|
49756
|
+
let lastSnapshot = null;
|
|
49757
|
+
for (const state of sessionStates.values()) {
|
|
49758
|
+
total += state.observationCount + state.reflectionCount + state.emergencyCount;
|
|
49759
|
+
if (state.lastSnapshotAt && (!lastSnapshot || state.lastSnapshotAt > lastSnapshot)) {
|
|
49760
|
+
lastSnapshot = state.lastSnapshotAt;
|
|
49761
|
+
}
|
|
49762
|
+
}
|
|
49763
|
+
return { compactionCount: total, lastSnapshotAt: lastSnapshot };
|
|
49638
49764
|
}
|
|
49639
49765
|
|
|
49640
49766
|
// src/services/context-budget-service.ts
|
|
@@ -49665,10 +49791,14 @@ async function readBudgetState(directory) {
|
|
|
49665
49791
|
return null;
|
|
49666
49792
|
}
|
|
49667
49793
|
}
|
|
49668
|
-
async function writeBudgetState(directory,
|
|
49669
|
-
|
|
49670
|
-
|
|
49671
|
-
|
|
49794
|
+
async function writeBudgetState(directory, state) {
|
|
49795
|
+
try {
|
|
49796
|
+
const resolvedPath = validateSwarmPath(directory, "session/budget-state.json");
|
|
49797
|
+
const content = JSON.stringify(state, null, 2);
|
|
49798
|
+
await Bun.write(resolvedPath, content);
|
|
49799
|
+
} catch (error93) {
|
|
49800
|
+
console.warn("[context-budget] Failed to write budget state:", error93 instanceof Error ? error93.message : String(error93));
|
|
49801
|
+
}
|
|
49672
49802
|
}
|
|
49673
49803
|
async function countEvents(directory) {
|
|
49674
49804
|
const content = await readSwarmFileAsync(directory, "events.jsonl");
|
|
@@ -49756,25 +49886,25 @@ async function formatBudgetWarning(report, directory, config3) {
|
|
|
49756
49886
|
return formatWarningMessage(report);
|
|
49757
49887
|
}
|
|
49758
49888
|
const budgetState = await readBudgetState(directory);
|
|
49759
|
-
const
|
|
49889
|
+
const state = budgetState || {
|
|
49760
49890
|
warningFiredAtTurn: null,
|
|
49761
49891
|
criticalFiredAtTurn: null,
|
|
49762
49892
|
lastInjectedAtTurn: null
|
|
49763
49893
|
};
|
|
49764
49894
|
const currentTurn = report.estimatedTurnCount;
|
|
49765
49895
|
if (report.status === "warning") {
|
|
49766
|
-
if (config3.warningMode === "once" &&
|
|
49896
|
+
if (config3.warningMode === "once" && state.warningFiredAtTurn !== null) {
|
|
49767
49897
|
return null;
|
|
49768
49898
|
}
|
|
49769
|
-
if (config3.warningMode === "interval" &&
|
|
49899
|
+
if (config3.warningMode === "interval" && state.warningFiredAtTurn !== null && currentTurn - state.warningFiredAtTurn < config3.warningIntervalTurns) {
|
|
49770
49900
|
return null;
|
|
49771
49901
|
}
|
|
49772
|
-
|
|
49773
|
-
|
|
49774
|
-
await writeBudgetState(directory,
|
|
49902
|
+
state.warningFiredAtTurn = currentTurn;
|
|
49903
|
+
state.lastInjectedAtTurn = currentTurn;
|
|
49904
|
+
await writeBudgetState(directory, state);
|
|
49775
49905
|
} else if (report.status === "critical") {
|
|
49776
|
-
|
|
49777
|
-
|
|
49906
|
+
state.criticalFiredAtTurn = currentTurn;
|
|
49907
|
+
state.lastInjectedAtTurn = currentTurn;
|
|
49778
49908
|
}
|
|
49779
49909
|
return formatWarningMessage(report);
|
|
49780
49910
|
}
|
|
@@ -49938,6 +50068,7 @@ async function handleTurboCommand(_directory, args2, sessionID) {
|
|
|
49938
50068
|
|
|
49939
50069
|
// src/tools/write-retro.ts
|
|
49940
50070
|
init_tool();
|
|
50071
|
+
init_evidence_schema();
|
|
49941
50072
|
init_manager();
|
|
49942
50073
|
init_create_tool();
|
|
49943
50074
|
async function executeWriteRetro(args2, directory) {
|
|
@@ -50172,8 +50303,9 @@ async function executeWriteRetro(args2, directory) {
|
|
|
50172
50303
|
};
|
|
50173
50304
|
const taxonomy = [];
|
|
50174
50305
|
try {
|
|
50175
|
-
|
|
50176
|
-
|
|
50306
|
+
const allTaskIds = await listEvidenceTaskIds(directory);
|
|
50307
|
+
const phaseTaskIds = allTaskIds.filter((id) => id.startsWith(`${phase}.`));
|
|
50308
|
+
for (const phaseTaskId of phaseTaskIds) {
|
|
50177
50309
|
const result = await loadEvidence(directory, phaseTaskId);
|
|
50178
50310
|
if (result.status !== "found")
|
|
50179
50311
|
continue;
|
|
@@ -50207,6 +50339,13 @@ async function executeWriteRetro(args2, directory) {
|
|
|
50207
50339
|
}
|
|
50208
50340
|
} catch {}
|
|
50209
50341
|
retroEntry.error_taxonomy = [...new Set(taxonomy)];
|
|
50342
|
+
const validationResult = RetrospectiveEvidenceSchema.safeParse(retroEntry);
|
|
50343
|
+
if (!validationResult.success) {
|
|
50344
|
+
return JSON.stringify({
|
|
50345
|
+
success: false,
|
|
50346
|
+
error: `Retrospective entry failed validation: ${validationResult.error.message}`
|
|
50347
|
+
}, null, 2);
|
|
50348
|
+
}
|
|
50210
50349
|
try {
|
|
50211
50350
|
await saveEvidence(directory, taskId, retroEntry);
|
|
50212
50351
|
return JSON.stringify({
|
|
@@ -50271,7 +50410,7 @@ var write_retro = createSwarmTool({
|
|
|
50271
50410
|
}
|
|
50272
50411
|
});
|
|
50273
50412
|
|
|
50274
|
-
// src/commands/
|
|
50413
|
+
// src/commands/write-retro.ts
|
|
50275
50414
|
async function handleWriteRetroCommand(directory, args2) {
|
|
50276
50415
|
if (args2.length === 0 || !args2[0] || args2[0].trim() === "") {
|
|
50277
50416
|
return `## Usage: /swarm write-retro <json>
|
|
@@ -50537,6 +50676,7 @@ init_schema();
|
|
|
50537
50676
|
|
|
50538
50677
|
// src/hooks/agent-activity.ts
|
|
50539
50678
|
import { renameSync as renameSync8, unlinkSync as unlinkSync4 } from "fs";
|
|
50679
|
+
import * as nodePath2 from "path";
|
|
50540
50680
|
init_utils();
|
|
50541
50681
|
init_utils2();
|
|
50542
50682
|
function createAgentActivityHooks(config3, directory) {
|
|
@@ -50561,7 +50701,7 @@ function createAgentActivityHooks(config3, directory) {
|
|
|
50561
50701
|
return;
|
|
50562
50702
|
swarmState.activeToolCalls.delete(input.callID);
|
|
50563
50703
|
const duration5 = Date.now() - entry.startTime;
|
|
50564
|
-
const success3 = output.
|
|
50704
|
+
const success3 = output.success === true;
|
|
50565
50705
|
const key = entry.tool;
|
|
50566
50706
|
const existing = swarmState.toolAggregates.get(key) ?? {
|
|
50567
50707
|
tool: key,
|
|
@@ -50606,7 +50746,7 @@ async function doFlush(directory) {
|
|
|
50606
50746
|
const activitySection = renderActivitySection();
|
|
50607
50747
|
const updated = replaceOrAppendSection(existing, "## Agent Activity", activitySection);
|
|
50608
50748
|
const flushedCount = swarmState.pendingEvents;
|
|
50609
|
-
const path30 =
|
|
50749
|
+
const path30 = nodePath2.join(directory, ".swarm", "context.md");
|
|
50610
50750
|
const tempPath = `${path30}.tmp`;
|
|
50611
50751
|
try {
|
|
50612
50752
|
await Bun.write(tempPath, updated);
|
|
@@ -50660,7 +50800,7 @@ ${content.substring(endIndex + 1)}`;
|
|
|
50660
50800
|
// src/hooks/compaction-customizer.ts
|
|
50661
50801
|
init_manager2();
|
|
50662
50802
|
import * as fs20 from "fs";
|
|
50663
|
-
import { join as
|
|
50803
|
+
import { join as join27 } from "path";
|
|
50664
50804
|
init_utils2();
|
|
50665
50805
|
function createCompactionCustomizerHook(config3, directory) {
|
|
50666
50806
|
const enabled = config3.hooks?.compaction !== false;
|
|
@@ -50706,7 +50846,7 @@ function createCompactionCustomizerHook(config3, directory) {
|
|
|
50706
50846
|
}
|
|
50707
50847
|
}
|
|
50708
50848
|
try {
|
|
50709
|
-
const summariesDir =
|
|
50849
|
+
const summariesDir = join27(directory, ".swarm", "summaries");
|
|
50710
50850
|
const files = await fs20.promises.readdir(summariesDir);
|
|
50711
50851
|
if (files.length > 0) {
|
|
50712
50852
|
const count = files.length;
|
|
@@ -51262,6 +51402,67 @@ init_telemetry();
|
|
|
51262
51402
|
import * as path30 from "path";
|
|
51263
51403
|
init_constants();
|
|
51264
51404
|
init_schema();
|
|
51405
|
+
|
|
51406
|
+
// src/context/zone-classifier.ts
|
|
51407
|
+
function classifyFile(filePath) {
|
|
51408
|
+
const normalized = filePath.toLowerCase().replace(/\\/g, "/");
|
|
51409
|
+
if (normalized.endsWith(".wasm") || normalized.includes("/dist/") || normalized.includes("/build/") || normalized.includes(".swarm/checkpoints/")) {
|
|
51410
|
+
return {
|
|
51411
|
+
filePath,
|
|
51412
|
+
zone: "generated",
|
|
51413
|
+
confidence: "high",
|
|
51414
|
+
reason: "path matches generated file patterns"
|
|
51415
|
+
};
|
|
51416
|
+
}
|
|
51417
|
+
if (normalized.includes("/test/") || normalized.includes("/tests/") || normalized.includes("/__tests__/") || normalized.includes(".test.") || normalized.includes(".spec.") || normalized.includes("/mocks/")) {
|
|
51418
|
+
return {
|
|
51419
|
+
filePath,
|
|
51420
|
+
zone: "test",
|
|
51421
|
+
confidence: "high",
|
|
51422
|
+
reason: "path matches test/**, tests/**, __tests__/**, *.test.*, *.spec.*, or **/mocks/**"
|
|
51423
|
+
};
|
|
51424
|
+
}
|
|
51425
|
+
if (normalized.includes("/scripts/") || normalized.includes("makefile") || normalized.includes("dockerfile") || normalized.includes(".github/")) {
|
|
51426
|
+
return {
|
|
51427
|
+
filePath,
|
|
51428
|
+
zone: "build",
|
|
51429
|
+
confidence: "high",
|
|
51430
|
+
reason: "path matches build scripts or CI configuration"
|
|
51431
|
+
};
|
|
51432
|
+
}
|
|
51433
|
+
if (normalized.endsWith(".json") && !normalized.includes(".github/") || normalized.endsWith(".yaml") && !normalized.includes(".github/") || normalized.endsWith(".yml") && !normalized.includes(".github/") || normalized.endsWith(".toml") || normalized.includes(".env") || normalized.endsWith("biome.json") || normalized.endsWith("tsconfig.json")) {
|
|
51434
|
+
return {
|
|
51435
|
+
filePath,
|
|
51436
|
+
zone: "config",
|
|
51437
|
+
confidence: "high",
|
|
51438
|
+
reason: "extension is config file type"
|
|
51439
|
+
};
|
|
51440
|
+
}
|
|
51441
|
+
if (normalized.includes("/docs/") || normalized.endsWith(".md") && !normalized.includes("/") || normalized.includes("readme") || normalized.includes("changelog") || normalized.includes("license")) {
|
|
51442
|
+
return {
|
|
51443
|
+
filePath,
|
|
51444
|
+
zone: "docs",
|
|
51445
|
+
confidence: "high",
|
|
51446
|
+
reason: "path matches docs/** or documentation files"
|
|
51447
|
+
};
|
|
51448
|
+
}
|
|
51449
|
+
if (normalized.includes("/src/") || normalized.includes("/lib/")) {
|
|
51450
|
+
return {
|
|
51451
|
+
filePath,
|
|
51452
|
+
zone: "production",
|
|
51453
|
+
confidence: "high",
|
|
51454
|
+
reason: "path matches src/** or lib/**"
|
|
51455
|
+
};
|
|
51456
|
+
}
|
|
51457
|
+
return {
|
|
51458
|
+
filePath,
|
|
51459
|
+
zone: "production",
|
|
51460
|
+
confidence: "medium",
|
|
51461
|
+
reason: "default classification"
|
|
51462
|
+
};
|
|
51463
|
+
}
|
|
51464
|
+
|
|
51465
|
+
// src/hooks/guardrails.ts
|
|
51265
51466
|
init_manager2();
|
|
51266
51467
|
init_telemetry();
|
|
51267
51468
|
init_utils();
|
|
@@ -51551,6 +51752,12 @@ function createGuardrailsHooks(directory, directoryOrConfig, config3) {
|
|
|
51551
51752
|
const delegArgs = output.args;
|
|
51552
51753
|
const delegTargetPath = delegArgs?.filePath ?? delegArgs?.path ?? delegArgs?.file ?? delegArgs?.target;
|
|
51553
51754
|
if (typeof delegTargetPath === "string" && delegTargetPath.length > 0) {
|
|
51755
|
+
const agentName2 = swarmState.activeAgent.get(input.sessionID) ?? "unknown";
|
|
51756
|
+
const cwd = directory ?? process.cwd();
|
|
51757
|
+
const authorityCheck = checkFileAuthority(agentName2, delegTargetPath, cwd);
|
|
51758
|
+
if (!authorityCheck.allowed) {
|
|
51759
|
+
throw new Error(`WRITE BLOCKED: Agent "${agentName2}" is not authorised to write "${delegTargetPath}". Reason: ${authorityCheck.reason}`);
|
|
51760
|
+
}
|
|
51554
51761
|
if (!currentSession.modifiedFilesThisCoderTask.includes(delegTargetPath)) {
|
|
51555
51762
|
currentSession.modifiedFilesThisCoderTask.push(delegTargetPath);
|
|
51556
51763
|
}
|
|
@@ -51859,7 +52066,7 @@ function createGuardrailsHooks(directory, directoryOrConfig, config3) {
|
|
|
51859
52066
|
session.partialGateWarningsIssuedForTask?.delete(session.currentTaskId);
|
|
51860
52067
|
if (session.declaredCoderScope !== null) {
|
|
51861
52068
|
const undeclaredFiles = session.modifiedFilesThisCoderTask.map((f) => f.replace(/[\r\n\t]/g, "_")).filter((f) => !isInDeclaredScope(f, session.declaredCoderScope));
|
|
51862
|
-
if (undeclaredFiles.length
|
|
52069
|
+
if (undeclaredFiles.length >= 1) {
|
|
51863
52070
|
const safeTaskId = String(session.currentTaskId ?? "").replace(/[\r\n\t]/g, "_");
|
|
51864
52071
|
session.lastScopeViolation = `Scope violation for task ${safeTaskId}: ` + `${undeclaredFiles.length} undeclared files modified: ` + undeclaredFiles.join(", ");
|
|
51865
52072
|
session.scopeViolationDetected = true;
|
|
@@ -52245,6 +52452,98 @@ function hashArgs(args2) {
|
|
|
52245
52452
|
return 0;
|
|
52246
52453
|
}
|
|
52247
52454
|
}
|
|
52455
|
+
var AGENT_AUTHORITY_RULES = {
|
|
52456
|
+
architect: {
|
|
52457
|
+
blockedExact: [".swarm/plan.md", ".swarm/plan.json"],
|
|
52458
|
+
blockedZones: ["generated"]
|
|
52459
|
+
},
|
|
52460
|
+
coder: {
|
|
52461
|
+
blockedPrefix: [".swarm/"],
|
|
52462
|
+
allowedPrefix: ["src/", "tests/", "docs/", "scripts/"],
|
|
52463
|
+
blockedZones: ["generated", "config"]
|
|
52464
|
+
},
|
|
52465
|
+
reviewer: {
|
|
52466
|
+
blockedExact: [".swarm/plan.md", ".swarm/plan.json"],
|
|
52467
|
+
blockedPrefix: ["src/"],
|
|
52468
|
+
allowedPrefix: [".swarm/evidence/", ".swarm/outputs/"],
|
|
52469
|
+
blockedZones: ["generated"]
|
|
52470
|
+
},
|
|
52471
|
+
explorer: {
|
|
52472
|
+
readOnly: true
|
|
52473
|
+
},
|
|
52474
|
+
sme: {
|
|
52475
|
+
readOnly: true
|
|
52476
|
+
},
|
|
52477
|
+
test_engineer: {
|
|
52478
|
+
blockedExact: [".swarm/plan.md", ".swarm/plan.json"],
|
|
52479
|
+
blockedPrefix: ["src/"],
|
|
52480
|
+
allowedPrefix: ["tests/", ".swarm/evidence/"],
|
|
52481
|
+
blockedZones: ["generated"]
|
|
52482
|
+
},
|
|
52483
|
+
docs: {
|
|
52484
|
+
allowedPrefix: ["docs/", ".swarm/outputs/"],
|
|
52485
|
+
blockedZones: ["generated"]
|
|
52486
|
+
},
|
|
52487
|
+
designer: {
|
|
52488
|
+
allowedPrefix: ["docs/", ".swarm/outputs/"],
|
|
52489
|
+
blockedZones: ["generated"]
|
|
52490
|
+
},
|
|
52491
|
+
critic: {
|
|
52492
|
+
allowedPrefix: [".swarm/evidence/"],
|
|
52493
|
+
blockedZones: ["generated"]
|
|
52494
|
+
}
|
|
52495
|
+
};
|
|
52496
|
+
function checkFileAuthority(agentName, filePath, _cwd) {
|
|
52497
|
+
const normalizedAgent = agentName.toLowerCase();
|
|
52498
|
+
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
52499
|
+
const rules = AGENT_AUTHORITY_RULES[normalizedAgent];
|
|
52500
|
+
if (!rules) {
|
|
52501
|
+
return { allowed: false, reason: `Unknown agent: ${agentName}` };
|
|
52502
|
+
}
|
|
52503
|
+
if (rules.readOnly) {
|
|
52504
|
+
return {
|
|
52505
|
+
allowed: false,
|
|
52506
|
+
reason: `Path blocked: ${normalizedPath} (agent ${normalizedAgent} is read-only)`
|
|
52507
|
+
};
|
|
52508
|
+
}
|
|
52509
|
+
if (rules.blockedExact) {
|
|
52510
|
+
for (const blocked of rules.blockedExact) {
|
|
52511
|
+
if (normalizedPath === blocked) {
|
|
52512
|
+
return { allowed: false, reason: `Path blocked: ${normalizedPath}` };
|
|
52513
|
+
}
|
|
52514
|
+
}
|
|
52515
|
+
}
|
|
52516
|
+
if (rules.blockedPrefix) {
|
|
52517
|
+
for (const prefix of rules.blockedPrefix) {
|
|
52518
|
+
if (normalizedPath.startsWith(prefix)) {
|
|
52519
|
+
return {
|
|
52520
|
+
allowed: false,
|
|
52521
|
+
reason: `Path blocked: ${normalizedPath} is under ${prefix}`
|
|
52522
|
+
};
|
|
52523
|
+
}
|
|
52524
|
+
}
|
|
52525
|
+
}
|
|
52526
|
+
if (rules.allowedPrefix && rules.allowedPrefix.length > 0) {
|
|
52527
|
+
const isAllowed = rules.allowedPrefix.some((prefix) => normalizedPath.startsWith(prefix));
|
|
52528
|
+
if (!isAllowed) {
|
|
52529
|
+
return {
|
|
52530
|
+
allowed: false,
|
|
52531
|
+
reason: `Path ${normalizedPath} not in allowed list for ${normalizedAgent}`
|
|
52532
|
+
};
|
|
52533
|
+
}
|
|
52534
|
+
}
|
|
52535
|
+
if (rules.blockedZones && rules.blockedZones.length > 0) {
|
|
52536
|
+
const { zone } = classifyFile(normalizedPath);
|
|
52537
|
+
if (rules.blockedZones.includes(zone)) {
|
|
52538
|
+
return {
|
|
52539
|
+
allowed: false,
|
|
52540
|
+
reason: `Path blocked: ${normalizedPath} is in ${zone} zone`,
|
|
52541
|
+
zone
|
|
52542
|
+
};
|
|
52543
|
+
}
|
|
52544
|
+
}
|
|
52545
|
+
return { allowed: true };
|
|
52546
|
+
}
|
|
52248
52547
|
|
|
52249
52548
|
// src/hooks/delegation-gate.ts
|
|
52250
52549
|
init_utils2();
|
|
@@ -52349,8 +52648,8 @@ function createDelegationGateHook(config3, directory) {
|
|
|
52349
52648
|
const session = swarmState.agentSessions.get(input.sessionID);
|
|
52350
52649
|
if (!session || !session.taskWorkflowStates)
|
|
52351
52650
|
return;
|
|
52352
|
-
for (const [taskId,
|
|
52353
|
-
if (
|
|
52651
|
+
for (const [taskId, state] of session.taskWorkflowStates) {
|
|
52652
|
+
if (state !== "coder_delegated")
|
|
52354
52653
|
continue;
|
|
52355
52654
|
const turbo = hasActiveTurboMode(input.sessionID);
|
|
52356
52655
|
if (turbo) {
|
|
@@ -52381,23 +52680,23 @@ function createDelegationGateHook(config3, directory) {
|
|
|
52381
52680
|
if (targetAgent === "test_engineer")
|
|
52382
52681
|
hasTestEngineer = true;
|
|
52383
52682
|
if (targetAgent === "reviewer" && session.taskWorkflowStates) {
|
|
52384
|
-
for (const [taskId,
|
|
52385
|
-
if (
|
|
52683
|
+
for (const [taskId, state] of session.taskWorkflowStates) {
|
|
52684
|
+
if (state === "coder_delegated" || state === "pre_check_passed") {
|
|
52386
52685
|
try {
|
|
52387
52686
|
advanceTaskState(session, taskId, "reviewer_run");
|
|
52388
52687
|
} catch (err2) {
|
|
52389
|
-
console.warn(`[delegation-gate] toolAfter: could not advance ${taskId} (${
|
|
52688
|
+
console.warn(`[delegation-gate] toolAfter: could not advance ${taskId} (${state}) \u2192 reviewer_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
52390
52689
|
}
|
|
52391
52690
|
}
|
|
52392
52691
|
}
|
|
52393
52692
|
}
|
|
52394
52693
|
if (targetAgent === "test_engineer" && session.taskWorkflowStates) {
|
|
52395
|
-
for (const [taskId,
|
|
52396
|
-
if (
|
|
52694
|
+
for (const [taskId, state] of session.taskWorkflowStates) {
|
|
52695
|
+
if (state === "reviewer_run") {
|
|
52397
52696
|
try {
|
|
52398
52697
|
advanceTaskState(session, taskId, "tests_run");
|
|
52399
52698
|
} catch (err2) {
|
|
52400
|
-
console.warn(`[delegation-gate] toolAfter: could not advance ${taskId} (${
|
|
52699
|
+
console.warn(`[delegation-gate] toolAfter: could not advance ${taskId} (${state}) \u2192 tests_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
52401
52700
|
}
|
|
52402
52701
|
}
|
|
52403
52702
|
}
|
|
@@ -52413,12 +52712,12 @@ function createDelegationGateHook(config3, directory) {
|
|
|
52413
52712
|
if (seedTaskId && !otherSession.taskWorkflowStates.has(seedTaskId)) {
|
|
52414
52713
|
otherSession.taskWorkflowStates.set(seedTaskId, "coder_delegated");
|
|
52415
52714
|
}
|
|
52416
|
-
for (const [taskId,
|
|
52417
|
-
if (
|
|
52715
|
+
for (const [taskId, state] of otherSession.taskWorkflowStates) {
|
|
52716
|
+
if (state === "coder_delegated" || state === "pre_check_passed") {
|
|
52418
52717
|
try {
|
|
52419
52718
|
advanceTaskState(otherSession, taskId, "reviewer_run");
|
|
52420
52719
|
} catch (err2) {
|
|
52421
|
-
console.warn(`[delegation-gate] toolAfter cross-session: could not advance ${taskId} (${
|
|
52720
|
+
console.warn(`[delegation-gate] toolAfter cross-session: could not advance ${taskId} (${state}) \u2192 reviewer_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
52422
52721
|
}
|
|
52423
52722
|
}
|
|
52424
52723
|
}
|
|
@@ -52428,12 +52727,12 @@ function createDelegationGateHook(config3, directory) {
|
|
|
52428
52727
|
if (seedTaskId && !otherSession.taskWorkflowStates.has(seedTaskId)) {
|
|
52429
52728
|
otherSession.taskWorkflowStates.set(seedTaskId, "reviewer_run");
|
|
52430
52729
|
}
|
|
52431
|
-
for (const [taskId,
|
|
52432
|
-
if (
|
|
52730
|
+
for (const [taskId, state] of otherSession.taskWorkflowStates) {
|
|
52731
|
+
if (state === "reviewer_run") {
|
|
52433
52732
|
try {
|
|
52434
52733
|
advanceTaskState(otherSession, taskId, "tests_run");
|
|
52435
52734
|
} catch (err2) {
|
|
52436
|
-
console.warn(`[delegation-gate] toolAfter cross-session: could not advance ${taskId} (${
|
|
52735
|
+
console.warn(`[delegation-gate] toolAfter cross-session: could not advance ${taskId} (${state}) \u2192 tests_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
52437
52736
|
}
|
|
52438
52737
|
}
|
|
52439
52738
|
}
|
|
@@ -52497,23 +52796,23 @@ function createDelegationGateHook(config3, directory) {
|
|
|
52497
52796
|
session.qaSkipTaskIds = [];
|
|
52498
52797
|
}
|
|
52499
52798
|
if (lastCoderIndex !== -1 && hasReviewer && session.taskWorkflowStates) {
|
|
52500
|
-
for (const [taskId,
|
|
52501
|
-
if (
|
|
52799
|
+
for (const [taskId, state] of session.taskWorkflowStates) {
|
|
52800
|
+
if (state === "coder_delegated" || state === "pre_check_passed") {
|
|
52502
52801
|
try {
|
|
52503
52802
|
advanceTaskState(session, taskId, "reviewer_run");
|
|
52504
52803
|
} catch (err2) {
|
|
52505
|
-
console.warn(`[delegation-gate] fallback: could not advance ${taskId} (${
|
|
52804
|
+
console.warn(`[delegation-gate] fallback: could not advance ${taskId} (${state}) \u2192 reviewer_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
52506
52805
|
}
|
|
52507
52806
|
}
|
|
52508
52807
|
}
|
|
52509
52808
|
}
|
|
52510
52809
|
if (lastCoderIndex !== -1 && hasReviewer && hasTestEngineer && session.taskWorkflowStates) {
|
|
52511
|
-
for (const [taskId,
|
|
52512
|
-
if (
|
|
52810
|
+
for (const [taskId, state] of session.taskWorkflowStates) {
|
|
52811
|
+
if (state === "reviewer_run") {
|
|
52513
52812
|
try {
|
|
52514
52813
|
advanceTaskState(session, taskId, "tests_run");
|
|
52515
52814
|
} catch (err2) {
|
|
52516
|
-
console.warn(`[delegation-gate] fallback: could not advance ${taskId} (${
|
|
52815
|
+
console.warn(`[delegation-gate] fallback: could not advance ${taskId} (${state}) \u2192 tests_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
52517
52816
|
}
|
|
52518
52817
|
}
|
|
52519
52818
|
}
|
|
@@ -52528,12 +52827,12 @@ function createDelegationGateHook(config3, directory) {
|
|
|
52528
52827
|
if (seedTaskId && !otherSession.taskWorkflowStates.has(seedTaskId)) {
|
|
52529
52828
|
otherSession.taskWorkflowStates.set(seedTaskId, "coder_delegated");
|
|
52530
52829
|
}
|
|
52531
|
-
for (const [taskId,
|
|
52532
|
-
if (
|
|
52830
|
+
for (const [taskId, state] of otherSession.taskWorkflowStates) {
|
|
52831
|
+
if (state === "coder_delegated" || state === "pre_check_passed") {
|
|
52533
52832
|
try {
|
|
52534
52833
|
advanceTaskState(otherSession, taskId, "reviewer_run");
|
|
52535
52834
|
} catch (err2) {
|
|
52536
|
-
console.warn(`[delegation-gate] fallback cross-session: could not advance ${taskId} (${
|
|
52835
|
+
console.warn(`[delegation-gate] fallback cross-session: could not advance ${taskId} (${state}) \u2192 reviewer_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
52537
52836
|
}
|
|
52538
52837
|
}
|
|
52539
52838
|
}
|
|
@@ -52549,12 +52848,12 @@ function createDelegationGateHook(config3, directory) {
|
|
|
52549
52848
|
if (seedTaskId && !otherSession.taskWorkflowStates.has(seedTaskId)) {
|
|
52550
52849
|
otherSession.taskWorkflowStates.set(seedTaskId, "reviewer_run");
|
|
52551
52850
|
}
|
|
52552
|
-
for (const [taskId,
|
|
52553
|
-
if (
|
|
52851
|
+
for (const [taskId, state] of otherSession.taskWorkflowStates) {
|
|
52852
|
+
if (state === "reviewer_run") {
|
|
52554
52853
|
try {
|
|
52555
52854
|
advanceTaskState(otherSession, taskId, "tests_run");
|
|
52556
52855
|
} catch (err2) {
|
|
52557
|
-
console.warn(`[delegation-gate] fallback cross-session: could not advance ${taskId} (${
|
|
52856
|
+
console.warn(`[delegation-gate] fallback cross-session: could not advance ${taskId} (${state}) \u2192 tests_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
52558
52857
|
}
|
|
52559
52858
|
}
|
|
52560
52859
|
}
|
|
@@ -55295,7 +55594,7 @@ ${errorSummary}`);
|
|
|
55295
55594
|
|
|
55296
55595
|
// src/hooks/knowledge-reader.ts
|
|
55297
55596
|
init_knowledge_store();
|
|
55298
|
-
import { existsSync as
|
|
55597
|
+
import { existsSync as existsSync23 } from "fs";
|
|
55299
55598
|
import { mkdir as mkdir6, readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
|
|
55300
55599
|
import * as path38 from "path";
|
|
55301
55600
|
var JACCARD_THRESHOLD = 0.6;
|
|
@@ -55348,7 +55647,7 @@ async function recordLessonsShown(directory, lessonIds, currentPhase) {
|
|
|
55348
55647
|
const shownFile = path38.join(directory, ".swarm", ".knowledge-shown.json");
|
|
55349
55648
|
try {
|
|
55350
55649
|
let shownData = {};
|
|
55351
|
-
if (
|
|
55650
|
+
if (existsSync23(shownFile)) {
|
|
55352
55651
|
const content = await readFile6(shownFile, "utf-8");
|
|
55353
55652
|
shownData = JSON.parse(content);
|
|
55354
55653
|
}
|
|
@@ -55452,7 +55751,7 @@ async function readMergedKnowledge(directory, config3, context) {
|
|
|
55452
55751
|
async function updateRetrievalOutcome(directory, phaseInfo, phaseSucceeded) {
|
|
55453
55752
|
const shownFile = path38.join(directory, ".swarm", ".knowledge-shown.json");
|
|
55454
55753
|
try {
|
|
55455
|
-
if (!
|
|
55754
|
+
if (!existsSync23(shownFile)) {
|
|
55456
55755
|
return;
|
|
55457
55756
|
}
|
|
55458
55757
|
const content = await readFile6(shownFile, "utf-8");
|
|
@@ -55917,7 +56216,7 @@ Use this data to avoid repeating known failure patterns.`;
|
|
|
55917
56216
|
const availableContentTokens = MAX_SUMMARY_TOKENS - prefixTokens - suffixTokens;
|
|
55918
56217
|
if (availableContentTokens > 0) {
|
|
55919
56218
|
const maxContentChars = Math.floor(availableContentTokens / 0.33);
|
|
55920
|
-
summaryText = summaryText.slice(
|
|
56219
|
+
summaryText = summaryText.slice(0, maxContentChars);
|
|
55921
56220
|
} else {
|
|
55922
56221
|
summaryText = "";
|
|
55923
56222
|
}
|
|
@@ -56466,6 +56765,19 @@ init_config_doctor();
|
|
|
56466
56765
|
|
|
56467
56766
|
// src/session/snapshot-reader.ts
|
|
56468
56767
|
init_utils2();
|
|
56768
|
+
var TRANSIENT_SESSION_FIELDS = [
|
|
56769
|
+
{ name: "revisionLimitHit", resetValue: false },
|
|
56770
|
+
{ name: "coderRevisions", resetValue: 0 },
|
|
56771
|
+
{ name: "selfFixAttempted", resetValue: false },
|
|
56772
|
+
{ name: "lastGateFailure", resetValue: null },
|
|
56773
|
+
{ name: "architectWriteCount", resetValue: 0 },
|
|
56774
|
+
{ name: "selfCodingWarnedAtCount", resetValue: 0 },
|
|
56775
|
+
{ name: "pendingAdvisoryMessages", resetValue: [] },
|
|
56776
|
+
{ name: "model_fallback_index", resetValue: 0 },
|
|
56777
|
+
{ name: "modelFallbackExhausted", resetValue: false },
|
|
56778
|
+
{ name: "scopeViolationDetected", resetValue: false },
|
|
56779
|
+
{ name: "delegationActive", resetValue: false }
|
|
56780
|
+
];
|
|
56469
56781
|
var VALID_TASK_WORKFLOW_STATES = [
|
|
56470
56782
|
"idle",
|
|
56471
56783
|
"coder_delegated",
|
|
@@ -56612,17 +56924,9 @@ async function rehydrateState(snapshot) {
|
|
|
56612
56924
|
window2.warningReason = "";
|
|
56613
56925
|
}
|
|
56614
56926
|
}
|
|
56615
|
-
|
|
56616
|
-
|
|
56617
|
-
|
|
56618
|
-
session.lastGateFailure = null;
|
|
56619
|
-
session.architectWriteCount = 0;
|
|
56620
|
-
session.selfCodingWarnedAtCount = 0;
|
|
56621
|
-
session.pendingAdvisoryMessages = [];
|
|
56622
|
-
session.model_fallback_index = 0;
|
|
56623
|
-
session.modelFallbackExhausted = false;
|
|
56624
|
-
session.scopeViolationDetected = false;
|
|
56625
|
-
session.delegationActive = false;
|
|
56927
|
+
for (const field of TRANSIENT_SESSION_FIELDS) {
|
|
56928
|
+
session[field.name] = field.resetValue;
|
|
56929
|
+
}
|
|
56626
56930
|
swarmState.agentSessions.set(sessionId, session);
|
|
56627
56931
|
}
|
|
56628
56932
|
}
|
|
@@ -57106,7 +57410,7 @@ async function executeCompletionVerify(args2, directory) {
|
|
|
57106
57410
|
return JSON.stringify(result2, null, 2);
|
|
57107
57411
|
}
|
|
57108
57412
|
let tasksChecked = 0;
|
|
57109
|
-
|
|
57413
|
+
const tasksSkipped = 0;
|
|
57110
57414
|
let tasksBlocked = 0;
|
|
57111
57415
|
const blockedTasks = [];
|
|
57112
57416
|
for (const task of targetPhase.tasks) {
|
|
@@ -57117,17 +57421,30 @@ async function executeCompletionVerify(args2, directory) {
|
|
|
57117
57421
|
const fileTargets = parseFilePaths(task.description, task.files_touched);
|
|
57118
57422
|
const identifiers = parseIdentifiers(task.description);
|
|
57119
57423
|
if (fileTargets.length === 0) {
|
|
57120
|
-
|
|
57424
|
+
blockedTasks.push({
|
|
57425
|
+
task_id: task.id,
|
|
57426
|
+
identifier: "",
|
|
57427
|
+
file_path: "",
|
|
57428
|
+
reason: "No file targets \u2014 cannot verify completion without files_touched"
|
|
57429
|
+
});
|
|
57430
|
+
tasksBlocked++;
|
|
57121
57431
|
continue;
|
|
57122
57432
|
}
|
|
57123
57433
|
if (identifiers.length === 0) {
|
|
57124
|
-
|
|
57434
|
+
blockedTasks.push({
|
|
57435
|
+
task_id: task.id,
|
|
57436
|
+
identifier: "",
|
|
57437
|
+
file_path: "",
|
|
57438
|
+
reason: "No identifiers \u2014 cannot verify completion without identifiers"
|
|
57439
|
+
});
|
|
57440
|
+
tasksBlocked++;
|
|
57125
57441
|
continue;
|
|
57126
57442
|
}
|
|
57127
|
-
|
|
57443
|
+
const foundIdentifiers = new Set;
|
|
57128
57444
|
let hasFileReadFailure = false;
|
|
57129
57445
|
for (const filePath of fileTargets) {
|
|
57130
|
-
const
|
|
57446
|
+
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
57447
|
+
const resolvedPath = path43.resolve(directory, normalizedPath);
|
|
57131
57448
|
let fileContent;
|
|
57132
57449
|
try {
|
|
57133
57450
|
fileContent = fs32.readFileSync(resolvedPath, "utf-8");
|
|
@@ -57143,11 +57460,11 @@ async function executeCompletionVerify(args2, directory) {
|
|
|
57143
57460
|
}
|
|
57144
57461
|
for (const identifier of identifiers) {
|
|
57145
57462
|
if (fileContent.includes(identifier)) {
|
|
57146
|
-
|
|
57147
|
-
break;
|
|
57463
|
+
foundIdentifiers.add(identifier);
|
|
57148
57464
|
}
|
|
57149
57465
|
}
|
|
57150
57466
|
}
|
|
57467
|
+
const foundCount = foundIdentifiers.size;
|
|
57151
57468
|
if (hasFileReadFailure || foundCount === 0) {
|
|
57152
57469
|
if (!hasFileReadFailure) {
|
|
57153
57470
|
blockedTasks.push({
|
|
@@ -57810,6 +58127,11 @@ var languageDefinitions = [
|
|
|
57810
58127
|
id: "rust",
|
|
57811
58128
|
extensions: [".rs"],
|
|
57812
58129
|
commentNodes: ["line_comment", "block_comment"]
|
|
58130
|
+
},
|
|
58131
|
+
{
|
|
58132
|
+
id: "php",
|
|
58133
|
+
extensions: [".php", ".phtml"],
|
|
58134
|
+
commentNodes: ["comment"]
|
|
57813
58135
|
}
|
|
57814
58136
|
];
|
|
57815
58137
|
var extensionMap = new Map;
|
|
@@ -57873,10 +58195,16 @@ async function computeASTDiff(filePath, oldContent, newContent) {
|
|
|
57873
58195
|
};
|
|
57874
58196
|
}
|
|
57875
58197
|
try {
|
|
58198
|
+
let timeoutId;
|
|
57876
58199
|
const parser = await Promise.race([
|
|
57877
58200
|
loadGrammar(language.id),
|
|
57878
|
-
new Promise((_, reject) =>
|
|
57879
|
-
|
|
58201
|
+
new Promise((_, reject) => {
|
|
58202
|
+
timeoutId = setTimeout(() => reject(new Error("AST_TIMEOUT")), AST_TIMEOUT_MS);
|
|
58203
|
+
})
|
|
58204
|
+
]).finally(() => {
|
|
58205
|
+
if (timeoutId)
|
|
58206
|
+
clearTimeout(timeoutId);
|
|
58207
|
+
});
|
|
57880
58208
|
const oldTree = parser.parse(oldContent);
|
|
57881
58209
|
const newTree = parser.parse(newContent);
|
|
57882
58210
|
if (!oldTree || !newTree) {
|
|
@@ -59063,7 +59391,7 @@ function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFile
|
|
|
59063
59391
|
return files;
|
|
59064
59392
|
}
|
|
59065
59393
|
var imports = createSwarmTool({
|
|
59066
|
-
description: "Find all consumers that import from a given file. Returns JSON with file path, line numbers, and import metadata for each consumer.
|
|
59394
|
+
description: "Find all reverse dependencies (consumers) that import from a given file. Returns JSON with file path, line numbers, and import metadata for each consumer. Use this to understand who depends on a module before refactoring.",
|
|
59067
59395
|
args: {
|
|
59068
59396
|
file: tool.schema.string().describe('Source file path to find importers for (e.g., "./src/utils.ts")'),
|
|
59069
59397
|
symbol: tool.schema.string().optional().describe('Optional specific symbol to filter imports (e.g., "MyClass")')
|
|
@@ -59218,7 +59546,9 @@ var imports = createSwarmTool({
|
|
|
59218
59546
|
});
|
|
59219
59547
|
// src/tools/knowledge-add.ts
|
|
59220
59548
|
init_dist();
|
|
59549
|
+
init_config();
|
|
59221
59550
|
init_knowledge_store();
|
|
59551
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
59222
59552
|
init_manager2();
|
|
59223
59553
|
init_create_tool();
|
|
59224
59554
|
var VALID_CATEGORIES2 = [
|
|
@@ -59293,7 +59623,7 @@ var knowledgeAdd = createSwarmTool({
|
|
|
59293
59623
|
project_name = plan?.title ?? "";
|
|
59294
59624
|
} catch {}
|
|
59295
59625
|
const entry = {
|
|
59296
|
-
id:
|
|
59626
|
+
id: randomUUID4(),
|
|
59297
59627
|
tier: "swarm",
|
|
59298
59628
|
lesson,
|
|
59299
59629
|
category,
|
|
@@ -59314,6 +59644,22 @@ var knowledgeAdd = createSwarmTool({
|
|
|
59314
59644
|
auto_generated: true,
|
|
59315
59645
|
hive_eligible: false
|
|
59316
59646
|
};
|
|
59647
|
+
try {
|
|
59648
|
+
const { config: config3 } = loadPluginConfigWithMeta(directory);
|
|
59649
|
+
if (config3.knowledge?.validation_enabled !== false) {
|
|
59650
|
+
const validation = validateLesson(lesson, [], {
|
|
59651
|
+
category,
|
|
59652
|
+
scope,
|
|
59653
|
+
confidence: 0.5
|
|
59654
|
+
});
|
|
59655
|
+
if (!validation.valid) {
|
|
59656
|
+
return JSON.stringify({
|
|
59657
|
+
success: false,
|
|
59658
|
+
error: `Validation failed: ${validation.reason}`
|
|
59659
|
+
});
|
|
59660
|
+
}
|
|
59661
|
+
}
|
|
59662
|
+
} catch {}
|
|
59317
59663
|
try {
|
|
59318
59664
|
await appendKnowledge(resolveSwarmKnowledgePath(directory), entry);
|
|
59319
59665
|
} catch (err2) {
|
|
@@ -59332,9 +59678,10 @@ var knowledgeAdd = createSwarmTool({
|
|
|
59332
59678
|
});
|
|
59333
59679
|
// src/tools/knowledge-query.ts
|
|
59334
59680
|
init_dist();
|
|
59681
|
+
init_config();
|
|
59335
59682
|
init_knowledge_store();
|
|
59336
59683
|
init_create_tool();
|
|
59337
|
-
import { existsSync as
|
|
59684
|
+
import { existsSync as existsSync31 } from "fs";
|
|
59338
59685
|
var DEFAULT_LIMIT = 10;
|
|
59339
59686
|
var MAX_LESSON_LENGTH = 200;
|
|
59340
59687
|
var VALID_CATEGORIES3 = [
|
|
@@ -59403,19 +59750,19 @@ function validateLimit(limit) {
|
|
|
59403
59750
|
}
|
|
59404
59751
|
async function readSwarmKnowledge(directory) {
|
|
59405
59752
|
const swarmPath = resolveSwarmKnowledgePath(directory);
|
|
59406
|
-
if (!
|
|
59753
|
+
if (!existsSync31(swarmPath)) {
|
|
59407
59754
|
return [];
|
|
59408
59755
|
}
|
|
59409
59756
|
return readKnowledge(swarmPath);
|
|
59410
59757
|
}
|
|
59411
59758
|
async function readHiveKnowledge() {
|
|
59412
59759
|
const hivePath = resolveHiveKnowledgePath();
|
|
59413
|
-
if (!
|
|
59760
|
+
if (!existsSync31(hivePath)) {
|
|
59414
59761
|
return [];
|
|
59415
59762
|
}
|
|
59416
59763
|
return readKnowledge(hivePath);
|
|
59417
59764
|
}
|
|
59418
|
-
function filterSwarmEntries(entries, filters) {
|
|
59765
|
+
function filterSwarmEntries(entries, filters, scopeFilter) {
|
|
59419
59766
|
return entries.filter((entry) => {
|
|
59420
59767
|
if (filters.status && entry.status !== filters.status) {
|
|
59421
59768
|
return false;
|
|
@@ -59426,6 +59773,12 @@ function filterSwarmEntries(entries, filters) {
|
|
|
59426
59773
|
if (filters.minScore !== undefined && entry.confidence < filters.minScore) {
|
|
59427
59774
|
return false;
|
|
59428
59775
|
}
|
|
59776
|
+
if (scopeFilter && scopeFilter.length > 0) {
|
|
59777
|
+
const entryScope = entry.scope ?? "global";
|
|
59778
|
+
if (!scopeFilter.some((pattern) => entryScope === pattern)) {
|
|
59779
|
+
return false;
|
|
59780
|
+
}
|
|
59781
|
+
}
|
|
59429
59782
|
return true;
|
|
59430
59783
|
});
|
|
59431
59784
|
}
|
|
@@ -59510,9 +59863,14 @@ var knowledge_query = createSwarmTool({
|
|
|
59510
59863
|
minScore: minScore ?? undefined
|
|
59511
59864
|
};
|
|
59512
59865
|
const results = [];
|
|
59866
|
+
let scopeFilter;
|
|
59867
|
+
try {
|
|
59868
|
+
const { config: config3 } = loadPluginConfigWithMeta(directory);
|
|
59869
|
+
scopeFilter = config3.knowledge?.scope_filter;
|
|
59870
|
+
} catch {}
|
|
59513
59871
|
if (tier === "swarm" || tier === "all") {
|
|
59514
59872
|
const swarmEntries = await readSwarmKnowledge(directory);
|
|
59515
|
-
const filtered = filterSwarmEntries(swarmEntries, filters);
|
|
59873
|
+
const filtered = filterSwarmEntries(swarmEntries, filters, scopeFilter);
|
|
59516
59874
|
for (const entry of filtered) {
|
|
59517
59875
|
results.push({ entry, tier: "swarm" });
|
|
59518
59876
|
}
|
|
@@ -60042,7 +60400,14 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
|
|
|
60042
60400
|
if (retroFound && retroEntry?.lessons_learned && retroEntry.lessons_learned.length > 0) {
|
|
60043
60401
|
try {
|
|
60044
60402
|
const projectName = path49.basename(dir);
|
|
60045
|
-
await curateAndStoreSwarm(retroEntry.lessons_learned, projectName, { phase_number: phase }, dir, knowledgeConfig);
|
|
60403
|
+
const curationResult = await curateAndStoreSwarm(retroEntry.lessons_learned, projectName, { phase_number: phase }, dir, knowledgeConfig);
|
|
60404
|
+
if (curationResult) {
|
|
60405
|
+
const sessionState = swarmState.agentSessions.get(sessionID);
|
|
60406
|
+
if (sessionState) {
|
|
60407
|
+
sessionState.pendingAdvisoryMessages ??= [];
|
|
60408
|
+
sessionState.pendingAdvisoryMessages.push(`[CURATOR] Knowledge curation: ${curationResult.stored} stored, ${curationResult.skipped} skipped, ${curationResult.rejected} rejected.`);
|
|
60409
|
+
}
|
|
60410
|
+
}
|
|
60046
60411
|
} catch (error93) {
|
|
60047
60412
|
safeWarn("[phase_complete] Failed to curate lessons from retrospective:", error93);
|
|
60048
60413
|
}
|
|
@@ -60076,7 +60441,17 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
|
|
|
60076
60441
|
} catch (curatorError) {
|
|
60077
60442
|
safeWarn("[phase_complete] Curator pipeline error (non-blocking):", curatorError);
|
|
60078
60443
|
}
|
|
60079
|
-
|
|
60444
|
+
let phaseRequiredAgents;
|
|
60445
|
+
try {
|
|
60446
|
+
const planPath = validateSwarmPath(dir, "plan.json");
|
|
60447
|
+
const planRaw = fs38.readFileSync(planPath, "utf-8");
|
|
60448
|
+
const plan = JSON.parse(planRaw);
|
|
60449
|
+
const phaseObj = plan.phases.find((p) => p.id === phase);
|
|
60450
|
+
phaseRequiredAgents = phaseObj?.required_agents;
|
|
60451
|
+
} catch {}
|
|
60452
|
+
const effectiveRequired = [
|
|
60453
|
+
...phaseRequiredAgents ?? phaseCompleteConfig.required_agents
|
|
60454
|
+
];
|
|
60080
60455
|
if (phaseCompleteConfig.require_docs && !effectiveRequired.includes("docs")) {
|
|
60081
60456
|
effectiveRequired.push("docs");
|
|
60082
60457
|
}
|
|
@@ -62541,7 +62916,7 @@ var javascriptRules = [
|
|
|
62541
62916
|
languages: ["javascript", "typescript"],
|
|
62542
62917
|
description: "Potential command injection via child_process.exec() with unsanitized input",
|
|
62543
62918
|
remediation: "Never pass user input directly to exec(). Use execFile() with arguments array or sanitize input thoroughly.",
|
|
62544
|
-
pattern: /exec\s*\(\s*[`'"]/,
|
|
62919
|
+
pattern: /exec\s*\(\s*(?:[`'"]|\w)/,
|
|
62545
62920
|
validate: (_match, _context) => {
|
|
62546
62921
|
return true;
|
|
62547
62922
|
}
|
|
@@ -62864,9 +63239,7 @@ function executeRulesSync(filePath, content, language) {
|
|
|
62864
63239
|
}
|
|
62865
63240
|
|
|
62866
63241
|
// src/sast/semgrep.ts
|
|
62867
|
-
import {
|
|
62868
|
-
import { promisify as promisify2 } from "util";
|
|
62869
|
-
var _execFileAsync = promisify2(execFile2);
|
|
63242
|
+
import { execFileSync as execFileSync2, spawn as spawn2 } from "child_process";
|
|
62870
63243
|
var semgrepAvailableCache = null;
|
|
62871
63244
|
var DEFAULT_RULES_DIR = ".swarm/semgrep-rules";
|
|
62872
63245
|
var DEFAULT_TIMEOUT_MS3 = 30000;
|
|
@@ -62891,7 +63264,12 @@ function parseSemgrepResults(semgrepOutput) {
|
|
|
62891
63264
|
try {
|
|
62892
63265
|
const parsed = JSON.parse(semgrepOutput);
|
|
62893
63266
|
const results = parsed.results || parsed;
|
|
63267
|
+
if (!Array.isArray(results)) {
|
|
63268
|
+
return [];
|
|
63269
|
+
}
|
|
62894
63270
|
for (const result of results) {
|
|
63271
|
+
if (!result || typeof result !== "object")
|
|
63272
|
+
continue;
|
|
62895
63273
|
const severity = mapSemgrepSeverity(result.extra?.severity || result.severity);
|
|
62896
63274
|
findings.push({
|
|
62897
63275
|
rule_id: result.check_id || result.rule_id || "unknown",
|
|
@@ -64041,10 +64419,11 @@ import * as path54 from "path";
|
|
|
64041
64419
|
var LOCKS_DIR = ".swarm/locks";
|
|
64042
64420
|
var LOCK_TIMEOUT_MS = 5 * 60 * 1000;
|
|
64043
64421
|
function getLockFilePath(directory, filePath) {
|
|
64044
|
-
|
|
64422
|
+
const normalized = path54.resolve(directory, filePath);
|
|
64423
|
+
if (!normalized.startsWith(path54.resolve(directory))) {
|
|
64045
64424
|
throw new Error("Invalid file path: path traversal not allowed");
|
|
64046
64425
|
}
|
|
64047
|
-
const hash3 = Buffer.from(
|
|
64426
|
+
const hash3 = Buffer.from(normalized).toString("base64").replace(/[/+=]/g, "_");
|
|
64048
64427
|
return path54.join(directory, LOCKS_DIR, `${hash3}.lock`);
|
|
64049
64428
|
}
|
|
64050
64429
|
function tryAcquireLock(directory, filePath, agent, taskId) {
|
|
@@ -64224,12 +64603,24 @@ async function executeSavePlan(args2, fallbackDir) {
|
|
|
64224
64603
|
});
|
|
64225
64604
|
await fs44.promises.writeFile(markerPath, marker, "utf8");
|
|
64226
64605
|
} catch {}
|
|
64606
|
+
const warnings = [];
|
|
64607
|
+
let criticReviewFound = false;
|
|
64608
|
+
for (const [, session] of swarmState.agentSessions) {
|
|
64609
|
+
if (session.phaseAgentsDispatched?.has("critic") || session.lastCompletedPhaseAgentsDispatched?.has("critic")) {
|
|
64610
|
+
criticReviewFound = true;
|
|
64611
|
+
break;
|
|
64612
|
+
}
|
|
64613
|
+
}
|
|
64614
|
+
if (!criticReviewFound) {
|
|
64615
|
+
warnings.push("No critic review detected before plan save. Consider delegating to critic for plan validation.");
|
|
64616
|
+
}
|
|
64227
64617
|
return {
|
|
64228
64618
|
success: true,
|
|
64229
64619
|
message: "Plan saved successfully",
|
|
64230
64620
|
plan_path: path55.join(dir, ".swarm", "plan.json"),
|
|
64231
64621
|
phases_count: plan.phases.length,
|
|
64232
|
-
tasks_count: tasksCount
|
|
64622
|
+
tasks_count: tasksCount,
|
|
64623
|
+
...warnings.length > 0 ? { warnings } : {}
|
|
64233
64624
|
};
|
|
64234
64625
|
} finally {
|
|
64235
64626
|
releaseLock(dir, planFilePath, lockTaskId);
|
|
@@ -64699,7 +65090,7 @@ function parseYarnLock(content) {
|
|
|
64699
65090
|
const components = [];
|
|
64700
65091
|
const blocks = content.split(/^(?=@?[\w-]+@)/m);
|
|
64701
65092
|
for (const block of blocks) {
|
|
64702
|
-
const pkgMatch = block.match(/^(@?[\w-]+)@([\
|
|
65093
|
+
const pkgMatch = block.match(/^(@?[\w-]+)@(\d+\.\d+\.\d+(?:-[\w.]+)?)/m);
|
|
64703
65094
|
if (!pkgMatch)
|
|
64704
65095
|
continue;
|
|
64705
65096
|
const name2 = pkgMatch[1];
|
|
@@ -64963,8 +65354,8 @@ function parsePackageResolved(content) {
|
|
|
64963
65354
|
const pins = resolved.pins || [];
|
|
64964
65355
|
for (const pin of pins) {
|
|
64965
65356
|
const identity = pin.identity || pin.package || "";
|
|
64966
|
-
const
|
|
64967
|
-
const version3 =
|
|
65357
|
+
const state = pin.state || {};
|
|
65358
|
+
const version3 = state.version || state.revision || "";
|
|
64968
65359
|
let org = "";
|
|
64969
65360
|
const location = pin.location || "";
|
|
64970
65361
|
const orgMatch = location.match(/github\.com\/([^/]+)\//);
|
|
@@ -65050,7 +65441,9 @@ function detectComponents(filePath, content) {
|
|
|
65050
65441
|
if (components.length > 0) {
|
|
65051
65442
|
return components;
|
|
65052
65443
|
}
|
|
65053
|
-
} catch {
|
|
65444
|
+
} catch (error93) {
|
|
65445
|
+
console.warn(`[sbom] Detector failed for ${filePath}:`, error93 instanceof Error ? error93.message : String(error93));
|
|
65446
|
+
}
|
|
65054
65447
|
}
|
|
65055
65448
|
return [];
|
|
65056
65449
|
}
|
|
@@ -66439,8 +66832,8 @@ function checkReviewerGate(taskId, workingDirectory) {
|
|
|
66439
66832
|
continue;
|
|
66440
66833
|
}
|
|
66441
66834
|
validSessionCount++;
|
|
66442
|
-
const
|
|
66443
|
-
if (
|
|
66835
|
+
const state = getTaskState(session, taskId);
|
|
66836
|
+
if (state === "tests_run" || state === "complete") {
|
|
66444
66837
|
return { blocked: false, reason: "" };
|
|
66445
66838
|
}
|
|
66446
66839
|
}
|
|
@@ -66451,8 +66844,8 @@ function checkReviewerGate(taskId, workingDirectory) {
|
|
|
66451
66844
|
for (const [sessionId, session] of swarmState.agentSessions) {
|
|
66452
66845
|
if (!(session.taskWorkflowStates instanceof Map))
|
|
66453
66846
|
continue;
|
|
66454
|
-
const
|
|
66455
|
-
stateEntries.push(`${sessionId}: ${
|
|
66847
|
+
const state = getTaskState(session, taskId);
|
|
66848
|
+
stateEntries.push(`${sessionId}: ${state}`);
|
|
66456
66849
|
}
|
|
66457
66850
|
try {
|
|
66458
66851
|
const resolvedDir2 = workingDirectory;
|
|
@@ -66675,19 +67068,35 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
66675
67068
|
}
|
|
66676
67069
|
} else {
|
|
66677
67070
|
if (!fallbackDir) {
|
|
66678
|
-
|
|
67071
|
+
return {
|
|
67072
|
+
success: false,
|
|
67073
|
+
message: "No working_directory provided and fallbackDir is undefined",
|
|
67074
|
+
errors: ["Cannot resolve directory for task status update"]
|
|
67075
|
+
};
|
|
66679
67076
|
}
|
|
66680
67077
|
directory = fallbackDir;
|
|
66681
67078
|
}
|
|
66682
67079
|
if (args2.status === "completed") {
|
|
66683
67080
|
recoverTaskStateFromDelegations(args2.task_id);
|
|
66684
|
-
|
|
66685
|
-
|
|
66686
|
-
|
|
66687
|
-
|
|
66688
|
-
|
|
66689
|
-
|
|
66690
|
-
|
|
67081
|
+
let phaseRequiresReviewer = true;
|
|
67082
|
+
try {
|
|
67083
|
+
const planPath = path61.join(directory, ".swarm", "plan.json");
|
|
67084
|
+
const planRaw = fs50.readFileSync(planPath, "utf-8");
|
|
67085
|
+
const plan = JSON.parse(planRaw);
|
|
67086
|
+
const taskPhase = plan.phases.find((p) => p.tasks.some((t) => t.id === args2.task_id));
|
|
67087
|
+
if (taskPhase?.required_agents && !taskPhase.required_agents.includes("reviewer")) {
|
|
67088
|
+
phaseRequiresReviewer = false;
|
|
67089
|
+
}
|
|
67090
|
+
} catch {}
|
|
67091
|
+
if (phaseRequiresReviewer) {
|
|
67092
|
+
const reviewerCheck = await checkReviewerGateWithScope(args2.task_id, directory);
|
|
67093
|
+
if (reviewerCheck.blocked) {
|
|
67094
|
+
return {
|
|
67095
|
+
success: false,
|
|
67096
|
+
message: "Gate check failed: reviewer delegation required before marking task as completed",
|
|
67097
|
+
errors: [reviewerCheck.reason]
|
|
67098
|
+
};
|
|
67099
|
+
}
|
|
66691
67100
|
}
|
|
66692
67101
|
}
|
|
66693
67102
|
try {
|