opencode-swarm 7.29.2 → 7.29.4
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/dist/cli/index.js +50 -25
- package/dist/evidence/manager.d.ts +8 -2
- package/dist/index.js +105 -108
- package/dist/telemetry.d.ts +2 -1
- package/dist/tools/test-runner.d.ts +1 -0
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -34,7 +34,7 @@ var package_default;
|
|
|
34
34
|
var init_package = __esm(() => {
|
|
35
35
|
package_default = {
|
|
36
36
|
name: "opencode-swarm",
|
|
37
|
-
version: "7.29.
|
|
37
|
+
version: "7.29.4",
|
|
38
38
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
39
39
|
main: "dist/index.js",
|
|
40
40
|
types: "dist/index.d.ts",
|
|
@@ -505,9 +505,7 @@ function bunSpawn(cmd, options) {
|
|
|
505
505
|
return proc.exitCode;
|
|
506
506
|
},
|
|
507
507
|
kill(signal) {
|
|
508
|
-
|
|
509
|
-
killChild(signal);
|
|
510
|
-
} catch {}
|
|
508
|
+
killChild(signal);
|
|
511
509
|
}
|
|
512
510
|
};
|
|
513
511
|
}
|
|
@@ -19859,15 +19857,36 @@ function validateProjectRoot(directory) {
|
|
|
19859
19857
|
throw new Error(`Cannot verify project root for "${directory}" \u2014 directory may not exist or is inaccessible`);
|
|
19860
19858
|
}
|
|
19861
19859
|
let current = resolved;
|
|
19860
|
+
let depth = 0;
|
|
19862
19861
|
while (true) {
|
|
19862
|
+
if (depth >= MAX_DEPTH)
|
|
19863
|
+
break;
|
|
19864
|
+
depth++;
|
|
19863
19865
|
const parent = path7.dirname(current);
|
|
19864
19866
|
if (parent === current)
|
|
19865
19867
|
break;
|
|
19866
19868
|
const parentSwarm = path7.join(parent, ".swarm");
|
|
19867
19869
|
try {
|
|
19868
19870
|
if (statSync4(parentSwarm).isDirectory()) {
|
|
19869
|
-
|
|
19870
|
-
|
|
19871
|
+
let hasProjectIndicator = false;
|
|
19872
|
+
for (const indicator of PROJECT_INDICATORS) {
|
|
19873
|
+
try {
|
|
19874
|
+
const indicatorStat = statSync4(path7.join(parent, indicator));
|
|
19875
|
+
if (indicatorStat.isFile() || indicatorStat.isDirectory()) {
|
|
19876
|
+
hasProjectIndicator = true;
|
|
19877
|
+
break;
|
|
19878
|
+
}
|
|
19879
|
+
} catch (error49) {
|
|
19880
|
+
if (error49 instanceof Error && "code" in error49 && error49.code === "ENOENT") {} else {
|
|
19881
|
+
hasProjectIndicator = true;
|
|
19882
|
+
break;
|
|
19883
|
+
}
|
|
19884
|
+
}
|
|
19885
|
+
}
|
|
19886
|
+
if (hasProjectIndicator) {
|
|
19887
|
+
warn(`[evidence] Rejecting write to subdirectory "${resolved}" \u2014 parent "${parent}" already contains .swarm/`);
|
|
19888
|
+
throw new Error(`Cannot write evidence in "${resolved}" \u2014 parent directory "${parent}" already contains a .swarm/ folder. Evidence must be written to the project root.`);
|
|
19889
|
+
}
|
|
19871
19890
|
}
|
|
19872
19891
|
} catch (error49) {
|
|
19873
19892
|
if (error49 instanceof Error && error49.message.startsWith("Cannot write evidence")) {
|
|
@@ -20110,7 +20129,7 @@ async function archiveEvidence(directory, maxAgeDays, maxBundles) {
|
|
|
20110
20129
|
}
|
|
20111
20130
|
return archived;
|
|
20112
20131
|
}
|
|
20113
|
-
var VALID_EVIDENCE_TYPES, sanitizeTaskId2, LEGACY_TASK_COMPLEXITY_MAP, _internals5;
|
|
20132
|
+
var VALID_EVIDENCE_TYPES, sanitizeTaskId2, MAX_DEPTH = 20, PROJECT_INDICATORS, LEGACY_TASK_COMPLEXITY_MAP, _internals5;
|
|
20114
20133
|
var init_manager2 = __esm(() => {
|
|
20115
20134
|
init_zod();
|
|
20116
20135
|
init_evidence_schema();
|
|
@@ -20135,6 +20154,19 @@ var init_manager2 = __esm(() => {
|
|
|
20135
20154
|
"secretscan"
|
|
20136
20155
|
];
|
|
20137
20156
|
sanitizeTaskId2 = sanitizeTaskId;
|
|
20157
|
+
PROJECT_INDICATORS = [
|
|
20158
|
+
"package.json",
|
|
20159
|
+
".git",
|
|
20160
|
+
".opencode",
|
|
20161
|
+
"Cargo.toml",
|
|
20162
|
+
"go.mod",
|
|
20163
|
+
"pyproject.toml",
|
|
20164
|
+
"Gemfile",
|
|
20165
|
+
"composer.json",
|
|
20166
|
+
"pom.xml",
|
|
20167
|
+
"build.gradle",
|
|
20168
|
+
"CMakeLists.txt"
|
|
20169
|
+
];
|
|
20138
20170
|
LEGACY_TASK_COMPLEXITY_MAP = {
|
|
20139
20171
|
low: "simple",
|
|
20140
20172
|
medium: "moderate",
|
|
@@ -40695,10 +40727,10 @@ function detectStraySwarmDirs(projectRoot) {
|
|
|
40695
40727
|
"__pycache__",
|
|
40696
40728
|
".tox"
|
|
40697
40729
|
]);
|
|
40698
|
-
const
|
|
40730
|
+
const MAX_DEPTH2 = 10;
|
|
40699
40731
|
const MAX_CONTENTS_ENTRIES = 20;
|
|
40700
40732
|
function walk(dir, depth) {
|
|
40701
|
-
if (depth >
|
|
40733
|
+
if (depth > MAX_DEPTH2)
|
|
40702
40734
|
return;
|
|
40703
40735
|
let entries;
|
|
40704
40736
|
try {
|
|
@@ -46282,7 +46314,7 @@ function defaultBuildTestCommand(profile, framework, files, dir = ".", opts = {}
|
|
|
46282
46314
|
const coverage = opts.coverage ?? false;
|
|
46283
46315
|
switch (framework) {
|
|
46284
46316
|
case "bun": {
|
|
46285
|
-
const args = ["bun", "
|
|
46317
|
+
const args = ["bun", "test"];
|
|
46286
46318
|
if (coverage)
|
|
46287
46319
|
args.push("--coverage");
|
|
46288
46320
|
if (scope !== "all" && files.length > 0)
|
|
@@ -48715,7 +48747,7 @@ function getTargetedExecutionUnsupportedReason(framework) {
|
|
|
48715
48747
|
function buildTestCommand2(framework, scope, files, coverage, baseDir) {
|
|
48716
48748
|
switch (framework) {
|
|
48717
48749
|
case "bun": {
|
|
48718
|
-
const args = ["bun", "
|
|
48750
|
+
const args = ["bun", "test"];
|
|
48719
48751
|
if (coverage)
|
|
48720
48752
|
args.push("--coverage");
|
|
48721
48753
|
if (scope !== "all" && files.length > 0) {
|
|
@@ -49252,24 +49284,17 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
49252
49284
|
const proc = bunSpawn(command, {
|
|
49253
49285
|
stdout: "pipe",
|
|
49254
49286
|
stderr: "pipe",
|
|
49255
|
-
|
|
49256
|
-
cwd,
|
|
49257
|
-
killProcessTree: true
|
|
49258
|
-
});
|
|
49259
|
-
let timeoutHandle;
|
|
49260
|
-
const timeoutPromise = new Promise((resolve14) => {
|
|
49261
|
-
timeoutHandle = setTimeout(() => {
|
|
49262
|
-
proc.kill();
|
|
49263
|
-
resolve14(-1);
|
|
49264
|
-
}, timeout_ms);
|
|
49287
|
+
cwd
|
|
49265
49288
|
});
|
|
49289
|
+
const timeoutPromise = new Promise((resolve14) => setTimeout(() => {
|
|
49290
|
+
proc.kill();
|
|
49291
|
+
resolve14(-1);
|
|
49292
|
+
}, timeout_ms));
|
|
49266
49293
|
const [exitCode, stdoutResult, stderrResult] = await Promise.all([
|
|
49267
49294
|
Promise.race([proc.exited, timeoutPromise]),
|
|
49268
49295
|
readBoundedStream(proc.stdout, MAX_OUTPUT_BYTES3),
|
|
49269
49296
|
readBoundedStream(proc.stderr, MAX_OUTPUT_BYTES3)
|
|
49270
49297
|
]);
|
|
49271
|
-
if (timeoutHandle !== undefined)
|
|
49272
|
-
clearTimeout(timeoutHandle);
|
|
49273
49298
|
const duration_ms = Date.now() - startTime;
|
|
49274
49299
|
let output = stdoutResult.text;
|
|
49275
49300
|
if (stderrResult.text) {
|
|
@@ -49572,6 +49597,7 @@ var init_test_runner = __esm(() => {
|
|
|
49572
49597
|
files: exports_external.array(exports_external.string()).optional().describe('Specific files to test. For "convention", pass source files or direct test files. For "graph" and "impact", pass source files only.'),
|
|
49573
49598
|
coverage: exports_external.boolean().optional().describe("Enable coverage reporting if supported"),
|
|
49574
49599
|
timeout_ms: exports_external.number().optional().describe("Timeout in milliseconds (default 60000, max 300000)"),
|
|
49600
|
+
allow_full_suite: exports_external.boolean().optional().describe('Explicit opt-in for scope "all". Required because full-suite output can destabilize SSE streaming.'),
|
|
49575
49601
|
working_directory: exports_external.string().optional().describe("Explicit project root directory. When provided, tests run relative to this path instead of the plugin context directory. Use this when CWD differs from the actual project root.")
|
|
49576
49602
|
},
|
|
49577
49603
|
async execute(args, directory) {
|
|
@@ -49645,8 +49671,7 @@ var init_test_runner = __esm(() => {
|
|
|
49645
49671
|
}
|
|
49646
49672
|
const scope = args.scope || "all";
|
|
49647
49673
|
if (scope === "all") {
|
|
49648
|
-
|
|
49649
|
-
if (!fullSuiteAllowed) {
|
|
49674
|
+
if (!process.env.SWARM_ALLOW_FULL_SUITE) {
|
|
49650
49675
|
const errorResult = {
|
|
49651
49676
|
success: false,
|
|
49652
49677
|
framework: "none",
|
|
@@ -38,11 +38,17 @@ export declare function isQualityBudgetEvidence(evidence: Evidence): evidence is
|
|
|
38
38
|
export declare function isSecretscanEvidence(evidence: Evidence): evidence is SecretscanEvidence;
|
|
39
39
|
import { sanitizeTaskId as _sanitizeTaskId } from '../validation/task-id';
|
|
40
40
|
export declare const sanitizeTaskId: typeof _sanitizeTaskId;
|
|
41
|
+
/** Maximum depth to walk up the directory tree before stopping (fail-open). */
|
|
42
|
+
export declare const MAX_DEPTH = 20;
|
|
43
|
+
/** File/directory names that indicate a real project root. */
|
|
44
|
+
export declare const PROJECT_INDICATORS: readonly ["package.json", ".git", ".opencode", "Cargo.toml", "go.mod", "pyproject.toml", "Gemfile", "composer.json", "pom.xml", "build.gradle", "CMakeLists.txt"];
|
|
41
45
|
/**
|
|
42
46
|
* Defense-in-depth: verify that `directory` is the project root and not a subdirectory
|
|
43
47
|
* of a project that already has a .swarm/ at its root.
|
|
44
|
-
* Walks up the directory tree
|
|
45
|
-
*
|
|
48
|
+
* Walks up the directory tree (bounded by MAX_DEPTH) looking for a parent .swarm/ directory.
|
|
49
|
+
* When .swarm/ is found, checks for at least one PROJECT_INDICATORS entry to distinguish
|
|
50
|
+
* real projects from stray artifacts (e.g. `C:\.swarm`).
|
|
51
|
+
* @throws Error if a parent directory contains both .swarm/ and a project indicator
|
|
46
52
|
*/
|
|
47
53
|
export declare function validateProjectRoot(directory: string): void;
|
|
48
54
|
/**
|
package/dist/index.js
CHANGED
|
@@ -48,7 +48,7 @@ var package_default;
|
|
|
48
48
|
var init_package = __esm(() => {
|
|
49
49
|
package_default = {
|
|
50
50
|
name: "opencode-swarm",
|
|
51
|
-
version: "7.29.
|
|
51
|
+
version: "7.29.4",
|
|
52
52
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
53
53
|
main: "dist/index.js",
|
|
54
54
|
types: "dist/index.d.ts",
|
|
@@ -16715,9 +16715,7 @@ function bunSpawn(cmd, options) {
|
|
|
16715
16715
|
return proc.exitCode;
|
|
16716
16716
|
},
|
|
16717
16717
|
kill(signal) {
|
|
16718
|
-
|
|
16719
|
-
killChild(signal);
|
|
16720
|
-
} catch {}
|
|
16718
|
+
killChild(signal);
|
|
16721
16719
|
}
|
|
16722
16720
|
};
|
|
16723
16721
|
}
|
|
@@ -16969,6 +16967,13 @@ var init_telemetry = __esm(() => {
|
|
|
16969
16967
|
gatePassed(sessionId, gate, taskId) {
|
|
16970
16968
|
_internals4.emit("gate_passed", { sessionId, gate, taskId });
|
|
16971
16969
|
},
|
|
16970
|
+
gateParseError(taskId, error49) {
|
|
16971
|
+
_internals4.emit("gate_parse_error", {
|
|
16972
|
+
taskId,
|
|
16973
|
+
errorName: error49.name,
|
|
16974
|
+
errorMessage: error49.message.slice(0, 200)
|
|
16975
|
+
});
|
|
16976
|
+
},
|
|
16972
16977
|
gateFailed(sessionId, gate, taskId, reason) {
|
|
16973
16978
|
_internals4.emit("gate_failed", { sessionId, gate, taskId, reason });
|
|
16974
16979
|
},
|
|
@@ -20687,15 +20692,36 @@ function validateProjectRoot(directory) {
|
|
|
20687
20692
|
throw new Error(`Cannot verify project root for "${directory}" — directory may not exist or is inaccessible`);
|
|
20688
20693
|
}
|
|
20689
20694
|
let current = resolved;
|
|
20695
|
+
let depth = 0;
|
|
20690
20696
|
while (true) {
|
|
20697
|
+
if (depth >= MAX_DEPTH)
|
|
20698
|
+
break;
|
|
20699
|
+
depth++;
|
|
20691
20700
|
const parent = path8.dirname(current);
|
|
20692
20701
|
if (parent === current)
|
|
20693
20702
|
break;
|
|
20694
20703
|
const parentSwarm = path8.join(parent, ".swarm");
|
|
20695
20704
|
try {
|
|
20696
20705
|
if (statSync5(parentSwarm).isDirectory()) {
|
|
20697
|
-
|
|
20698
|
-
|
|
20706
|
+
let hasProjectIndicator = false;
|
|
20707
|
+
for (const indicator of PROJECT_INDICATORS) {
|
|
20708
|
+
try {
|
|
20709
|
+
const indicatorStat = statSync5(path8.join(parent, indicator));
|
|
20710
|
+
if (indicatorStat.isFile() || indicatorStat.isDirectory()) {
|
|
20711
|
+
hasProjectIndicator = true;
|
|
20712
|
+
break;
|
|
20713
|
+
}
|
|
20714
|
+
} catch (error49) {
|
|
20715
|
+
if (error49 instanceof Error && "code" in error49 && error49.code === "ENOENT") {} else {
|
|
20716
|
+
hasProjectIndicator = true;
|
|
20717
|
+
break;
|
|
20718
|
+
}
|
|
20719
|
+
}
|
|
20720
|
+
}
|
|
20721
|
+
if (hasProjectIndicator) {
|
|
20722
|
+
warn(`[evidence] Rejecting write to subdirectory "${resolved}" — parent "${parent}" already contains .swarm/`);
|
|
20723
|
+
throw new Error(`Cannot write evidence in "${resolved}" — parent directory "${parent}" already contains a .swarm/ folder. Evidence must be written to the project root.`);
|
|
20724
|
+
}
|
|
20699
20725
|
}
|
|
20700
20726
|
} catch (error49) {
|
|
20701
20727
|
if (error49 instanceof Error && error49.message.startsWith("Cannot write evidence")) {
|
|
@@ -20938,7 +20964,7 @@ async function archiveEvidence(directory, maxAgeDays, maxBundles) {
|
|
|
20938
20964
|
}
|
|
20939
20965
|
return archived;
|
|
20940
20966
|
}
|
|
20941
|
-
var VALID_EVIDENCE_TYPES, sanitizeTaskId2, LEGACY_TASK_COMPLEXITY_MAP, _internals8;
|
|
20967
|
+
var VALID_EVIDENCE_TYPES, sanitizeTaskId2, MAX_DEPTH = 20, PROJECT_INDICATORS, LEGACY_TASK_COMPLEXITY_MAP, _internals8;
|
|
20942
20968
|
var init_manager2 = __esm(() => {
|
|
20943
20969
|
init_zod();
|
|
20944
20970
|
init_evidence_schema();
|
|
@@ -20963,6 +20989,19 @@ var init_manager2 = __esm(() => {
|
|
|
20963
20989
|
"secretscan"
|
|
20964
20990
|
];
|
|
20965
20991
|
sanitizeTaskId2 = sanitizeTaskId;
|
|
20992
|
+
PROJECT_INDICATORS = [
|
|
20993
|
+
"package.json",
|
|
20994
|
+
".git",
|
|
20995
|
+
".opencode",
|
|
20996
|
+
"Cargo.toml",
|
|
20997
|
+
"go.mod",
|
|
20998
|
+
"pyproject.toml",
|
|
20999
|
+
"Gemfile",
|
|
21000
|
+
"composer.json",
|
|
21001
|
+
"pom.xml",
|
|
21002
|
+
"build.gradle",
|
|
21003
|
+
"CMakeLists.txt"
|
|
21004
|
+
];
|
|
20966
21005
|
LEGACY_TASK_COMPLEXITY_MAP = {
|
|
20967
21006
|
low: "simple",
|
|
20968
21007
|
medium: "moderate",
|
|
@@ -38741,12 +38780,15 @@ function getEvidencePath(directory, taskId) {
|
|
|
38741
38780
|
assertValidTaskId(taskId);
|
|
38742
38781
|
return path12.join(getEvidenceDir(directory), `${taskId}.json`);
|
|
38743
38782
|
}
|
|
38744
|
-
function readExisting(evidencePath) {
|
|
38783
|
+
function readExisting(evidencePath, taskId) {
|
|
38745
38784
|
try {
|
|
38746
38785
|
const raw = readFileSync5(evidencePath, "utf-8");
|
|
38747
38786
|
return TaskEvidenceSchema.parse(JSON.parse(raw));
|
|
38748
|
-
} catch {
|
|
38749
|
-
|
|
38787
|
+
} catch (error49) {
|
|
38788
|
+
if (error49.code === "ENOENT")
|
|
38789
|
+
return null;
|
|
38790
|
+
telemetry.gateParseError(taskId, error49);
|
|
38791
|
+
throw error49;
|
|
38750
38792
|
}
|
|
38751
38793
|
}
|
|
38752
38794
|
async function atomicWrite2(targetPath, content) {
|
|
@@ -38767,7 +38809,13 @@ async function recordGateEvidence(directory, taskId, gate, sessionId, turbo) {
|
|
|
38767
38809
|
const lockRelPath = path12.join("evidence", `${taskId}.json`);
|
|
38768
38810
|
await withEvidenceLock(directory, lockRelPath, gate, taskId, async () => {
|
|
38769
38811
|
const evidencePath = getEvidencePath(directory, taskId);
|
|
38770
|
-
|
|
38812
|
+
let existing = null;
|
|
38813
|
+
try {
|
|
38814
|
+
existing = readExisting(evidencePath, taskId);
|
|
38815
|
+
} catch (error49) {
|
|
38816
|
+
telemetry.gateParseError(taskId, error49);
|
|
38817
|
+
throw error49;
|
|
38818
|
+
}
|
|
38771
38819
|
const requiredGates = existing ? expandRequiredGates(existing.required_gates, gate) : deriveRequiredGates(gate);
|
|
38772
38820
|
const updated = {
|
|
38773
38821
|
taskId,
|
|
@@ -38793,7 +38841,13 @@ async function recordAgentDispatch(directory, taskId, agentType, turbo) {
|
|
|
38793
38841
|
const lockRelPath = path12.join("evidence", `${taskId}.json`);
|
|
38794
38842
|
await withEvidenceLock(directory, lockRelPath, agentType, taskId, async () => {
|
|
38795
38843
|
const evidencePath = getEvidencePath(directory, taskId);
|
|
38796
|
-
|
|
38844
|
+
let existing = null;
|
|
38845
|
+
try {
|
|
38846
|
+
existing = readExisting(evidencePath, taskId);
|
|
38847
|
+
} catch (error49) {
|
|
38848
|
+
telemetry.gateParseError(taskId, error49);
|
|
38849
|
+
throw error49;
|
|
38850
|
+
}
|
|
38797
38851
|
const requiredGates = existing ? expandRequiredGates(existing.required_gates, agentType) : deriveRequiredGates(agentType);
|
|
38798
38852
|
const updated = {
|
|
38799
38853
|
taskId,
|
|
@@ -38807,7 +38861,7 @@ async function recordAgentDispatch(directory, taskId, agentType, turbo) {
|
|
|
38807
38861
|
async function readTaskEvidence(directory, taskId) {
|
|
38808
38862
|
try {
|
|
38809
38863
|
assertValidTaskId(taskId);
|
|
38810
|
-
return readExisting(getEvidencePath(directory, taskId));
|
|
38864
|
+
return readExisting(getEvidencePath(directory, taskId), taskId);
|
|
38811
38865
|
} catch {
|
|
38812
38866
|
return null;
|
|
38813
38867
|
}
|
|
@@ -39003,46 +39057,7 @@ async function buildParallelExecutionGuidance(directory, sessionID, session) {
|
|
|
39003
39057
|
if (eligible.length === 0) {
|
|
39004
39058
|
return `[PARALLEL EXECUTION PROFILE] parallelization_enabled=true max_concurrent_tasks=${maxConcurrent}; no dependency-ready pending tasks are available for a new coder slot. Continue the current task/gate.`;
|
|
39005
39059
|
}
|
|
39006
|
-
return `[PARALLEL EXECUTION PROFILE] parallelization_enabled=true max_concurrent_tasks=${maxConcurrent}; ${occupied.size} slot(s) occupied. Eligible now: ${eligible.join(", ")}. [NEXT] dispatch up to ${availableSlots} eligible coder task(s) before waiting;
|
|
39007
|
-
}
|
|
39008
|
-
async function buildPlanContinuationGuidance(directory) {
|
|
39009
|
-
if (!directory)
|
|
39010
|
-
return null;
|
|
39011
|
-
const plan = await loadPlanJsonOnly(directory);
|
|
39012
|
-
const currentTaskId = getPlanContinuationTaskId(plan);
|
|
39013
|
-
if (!currentTaskId)
|
|
39014
|
-
return null;
|
|
39015
|
-
const sanitizedTaskId = sanitizeGuidanceValue(currentTaskId, 32);
|
|
39016
|
-
return `[NEXT] Continue plan task ${sanitizedTaskId}: if it is not already in progress, call update_task_status with task_id="${sanitizedTaskId}" and status="in_progress"; ` + `then call declare_scope for the task files and dispatch coder Task call(s) according to the execution profile. ` + `Preserve ONE atomic task per coder Task call; when parallel execution is enabled, use available coder slots instead of forcing a single coder.`;
|
|
39017
|
-
}
|
|
39018
|
-
function sanitizeGuidanceValue(value, maxLength) {
|
|
39019
|
-
return value.replace(/</g, "<").replace(/>/g, ">").replace(/\[ \]/g, "()").replace(/\[/g, "(").replace(/\]/g, ")").replace(/[\r\n]/g, " ").slice(0, maxLength);
|
|
39020
|
-
}
|
|
39021
|
-
function getPlanContinuationTaskId(plan) {
|
|
39022
|
-
if (!plan)
|
|
39023
|
-
return;
|
|
39024
|
-
const currentPhase = plan.current_phase ?? 1;
|
|
39025
|
-
const phase = plan.phases.find((p) => p.id === currentPhase);
|
|
39026
|
-
if (!phase)
|
|
39027
|
-
return;
|
|
39028
|
-
const sortedTasks = [...phase.tasks].sort((a, b) => comparePlanTaskIds(a.id, b.id));
|
|
39029
|
-
const inProgress = sortedTasks.find((task) => task.status === "in_progress");
|
|
39030
|
-
if (inProgress)
|
|
39031
|
-
return inProgress.id;
|
|
39032
|
-
const incomplete = sortedTasks.find((task) => task.status !== "completed" && task.status !== "closed");
|
|
39033
|
-
return incomplete?.id;
|
|
39034
|
-
}
|
|
39035
|
-
function comparePlanTaskIds(a, b) {
|
|
39036
|
-
const partsA = a.split(".").map((part) => Number.parseInt(part, 10));
|
|
39037
|
-
const partsB = b.split(".").map((part) => Number.parseInt(part, 10));
|
|
39038
|
-
const maxLength = Math.max(partsA.length, partsB.length);
|
|
39039
|
-
for (let i2 = 0;i2 < maxLength; i2++) {
|
|
39040
|
-
const numA = partsA[i2] ?? 0;
|
|
39041
|
-
const numB = partsB[i2] ?? 0;
|
|
39042
|
-
if (numA !== numB)
|
|
39043
|
-
return numA - numB;
|
|
39044
|
-
}
|
|
39045
|
-
return 0;
|
|
39060
|
+
return `[PARALLEL EXECUTION PROFILE] parallelization_enabled=true max_concurrent_tasks=${maxConcurrent}; ${occupied.size} slot(s) occupied. Eligible now: ${eligible.join(", ")}. [NEXT] dispatch up to ${availableSlots} eligible coder task(s) before waiting; preserve ONE task per coder call and call declare_scope for each task.`;
|
|
39046
39061
|
}
|
|
39047
39062
|
function isParallelGuidancePhaseComplete(phase) {
|
|
39048
39063
|
return phase.status === "complete" || phase.status === "completed" || phase.status === "closed";
|
|
@@ -39620,7 +39635,6 @@ ${trimComment}${after}`;
|
|
|
39620
39635
|
const deliberationSession = ensureAgentSession(deliberationSessionID);
|
|
39621
39636
|
const lastGate = deliberationSession.lastGateOutcome;
|
|
39622
39637
|
const parallelGuidance = await buildParallelExecutionGuidance(directory, deliberationSessionID, deliberationSession);
|
|
39623
|
-
const planContinuationGuidance = parallelGuidance === null ? await buildPlanContinuationGuidance(directory) : null;
|
|
39624
39638
|
const taskAwaitingCompletion = await findTaskAwaitingCompletion(directory, deliberationSession);
|
|
39625
39639
|
let guidance;
|
|
39626
39640
|
if (taskAwaitingCompletion) {
|
|
@@ -39628,12 +39642,12 @@ ${trimComment}${after}`;
|
|
|
39628
39642
|
[NEXT] Print the task completion checklist, then call update_task_status with task_id="${taskAwaitingCompletion}" and status="completed" before declare_scope or starting another task.`;
|
|
39629
39643
|
} else if (lastGate?.taskId) {
|
|
39630
39644
|
const gateResult = lastGate.passed ? "PASSED" : "FAILED";
|
|
39631
|
-
const sanitizedGate =
|
|
39632
|
-
const sanitizedTaskId =
|
|
39645
|
+
const sanitizedGate = lastGate.gate.replace(/</g, "<").replace(/>/g, ">").replace(/\[ \]/g, "()").replace(/\[/g, "(").replace(/\]/g, ")").replace(/[\r\n]/g, " ").slice(0, 64);
|
|
39646
|
+
const sanitizedTaskId = lastGate.taskId.replace(/</g, "<").replace(/>/g, ">").replace(/\[/g, "(").replace(/\]/g, ")").replace(/[\r\n]/g, " ").slice(0, 32);
|
|
39633
39647
|
guidance = `[Last gate: ${sanitizedGate} ${gateResult} for task ${sanitizedTaskId}]
|
|
39634
39648
|
${parallelGuidance ?? "[NEXT] Execute the next gate for the current task."}`;
|
|
39635
39649
|
} else {
|
|
39636
|
-
guidance = parallelGuidance ??
|
|
39650
|
+
guidance = parallelGuidance ?? "[NEXT] Begin the first plan task and run gates sequentially.";
|
|
39637
39651
|
}
|
|
39638
39652
|
const systemMsgIdx = messages.findIndex((m) => m && m.info?.role === "system");
|
|
39639
39653
|
const insertIdx = systemMsgIdx >= 0 ? systemMsgIdx + 1 : 0;
|
|
@@ -61639,10 +61653,10 @@ function detectStraySwarmDirs(projectRoot) {
|
|
|
61639
61653
|
"__pycache__",
|
|
61640
61654
|
".tox"
|
|
61641
61655
|
]);
|
|
61642
|
-
const
|
|
61656
|
+
const MAX_DEPTH2 = 10;
|
|
61643
61657
|
const MAX_CONTENTS_ENTRIES = 20;
|
|
61644
61658
|
function walk(dir, depth) {
|
|
61645
|
-
if (depth >
|
|
61659
|
+
if (depth > MAX_DEPTH2)
|
|
61646
61660
|
return;
|
|
61647
61661
|
let entries;
|
|
61648
61662
|
try {
|
|
@@ -67415,7 +67429,7 @@ function defaultBuildTestCommand(profile, framework, files, dir = ".", opts = {}
|
|
|
67415
67429
|
const coverage = opts.coverage ?? false;
|
|
67416
67430
|
switch (framework) {
|
|
67417
67431
|
case "bun": {
|
|
67418
|
-
const args2 = ["bun", "
|
|
67432
|
+
const args2 = ["bun", "test"];
|
|
67419
67433
|
if (coverage)
|
|
67420
67434
|
args2.push("--coverage");
|
|
67421
67435
|
if (scope !== "all" && files.length > 0)
|
|
@@ -69848,7 +69862,7 @@ function getTargetedExecutionUnsupportedReason(framework) {
|
|
|
69848
69862
|
function buildTestCommand2(framework, scope, files, coverage, baseDir) {
|
|
69849
69863
|
switch (framework) {
|
|
69850
69864
|
case "bun": {
|
|
69851
|
-
const args2 = ["bun", "
|
|
69865
|
+
const args2 = ["bun", "test"];
|
|
69852
69866
|
if (coverage)
|
|
69853
69867
|
args2.push("--coverage");
|
|
69854
69868
|
if (scope !== "all" && files.length > 0) {
|
|
@@ -70385,24 +70399,17 @@ async function runTests(framework, scope, files, coverage, timeout_ms, cwd) {
|
|
|
70385
70399
|
const proc = bunSpawn(command, {
|
|
70386
70400
|
stdout: "pipe",
|
|
70387
70401
|
stderr: "pipe",
|
|
70388
|
-
|
|
70389
|
-
cwd,
|
|
70390
|
-
killProcessTree: true
|
|
70391
|
-
});
|
|
70392
|
-
let timeoutHandle;
|
|
70393
|
-
const timeoutPromise = new Promise((resolve16) => {
|
|
70394
|
-
timeoutHandle = setTimeout(() => {
|
|
70395
|
-
proc.kill();
|
|
70396
|
-
resolve16(-1);
|
|
70397
|
-
}, timeout_ms);
|
|
70402
|
+
cwd
|
|
70398
70403
|
});
|
|
70404
|
+
const timeoutPromise = new Promise((resolve16) => setTimeout(() => {
|
|
70405
|
+
proc.kill();
|
|
70406
|
+
resolve16(-1);
|
|
70407
|
+
}, timeout_ms));
|
|
70399
70408
|
const [exitCode, stdoutResult, stderrResult] = await Promise.all([
|
|
70400
70409
|
Promise.race([proc.exited, timeoutPromise]),
|
|
70401
70410
|
readBoundedStream(proc.stdout, MAX_OUTPUT_BYTES3),
|
|
70402
70411
|
readBoundedStream(proc.stderr, MAX_OUTPUT_BYTES3)
|
|
70403
70412
|
]);
|
|
70404
|
-
if (timeoutHandle !== undefined)
|
|
70405
|
-
clearTimeout(timeoutHandle);
|
|
70406
70413
|
const duration_ms = Date.now() - startTime;
|
|
70407
70414
|
let output = stdoutResult.text;
|
|
70408
70415
|
if (stderrResult.text) {
|
|
@@ -70705,6 +70712,7 @@ var init_test_runner = __esm(() => {
|
|
|
70705
70712
|
files: exports_external.array(exports_external.string()).optional().describe('Specific files to test. For "convention", pass source files or direct test files. For "graph" and "impact", pass source files only.'),
|
|
70706
70713
|
coverage: exports_external.boolean().optional().describe("Enable coverage reporting if supported"),
|
|
70707
70714
|
timeout_ms: exports_external.number().optional().describe("Timeout in milliseconds (default 60000, max 300000)"),
|
|
70715
|
+
allow_full_suite: exports_external.boolean().optional().describe('Explicit opt-in for scope "all". Required because full-suite output can destabilize SSE streaming.'),
|
|
70708
70716
|
working_directory: exports_external.string().optional().describe("Explicit project root directory. When provided, tests run relative to this path instead of the plugin context directory. Use this when CWD differs from the actual project root.")
|
|
70709
70717
|
},
|
|
70710
70718
|
async execute(args2, directory) {
|
|
@@ -70778,8 +70786,7 @@ var init_test_runner = __esm(() => {
|
|
|
70778
70786
|
}
|
|
70779
70787
|
const scope = args2.scope || "all";
|
|
70780
70788
|
if (scope === "all") {
|
|
70781
|
-
|
|
70782
|
-
if (!fullSuiteAllowed) {
|
|
70789
|
+
if (!process.env.SWARM_ALLOW_FULL_SUITE) {
|
|
70783
70790
|
const errorResult = {
|
|
70784
70791
|
success: false,
|
|
70785
70792
|
framework: "none",
|
|
@@ -75971,8 +75978,6 @@ SECURITY_KEYWORDS: password, secret, token, credential, auth, login, encryption,
|
|
|
75971
75978
|
{{AGENT_PREFIX}}test_engineer - Test generation AND execution (writes tests, runs them, reports PASS/FAIL)
|
|
75972
75979
|
{{AGENT_PREFIX}}critic - Plan review gate (reviews plan BEFORE implementation)
|
|
75973
75980
|
{{AGENT_PREFIX}}critic_sounding_board - Pre-escalation pushback (honest engineer review before user contact)
|
|
75974
|
-
{{AGENT_PREFIX}}skill_improver - Low-frequency skill / knowledge / prompt improvement adviser
|
|
75975
|
-
{{AGENT_PREFIX}}spec_writer - .swarm/spec.md authoring via spec_write
|
|
75976
75981
|
{{AGENT_PREFIX}}docs - Documentation updates (README, API docs, guides — NOT .swarm/ files)
|
|
75977
75982
|
{{AGENT_PREFIX}}designer - UI/UX design specs (scaffold generation for UI components — runs BEFORE coder on UI tasks)
|
|
75978
75983
|
|
|
@@ -76023,20 +76028,30 @@ For every applicable directive in the block:
|
|
|
76023
76028
|
|
|
76024
76029
|
You may also call the \`knowledge_ack\` tool to record an outcome explicitly when chat-text markers would be ambiguous (e.g. inside structured tool args).
|
|
76025
76030
|
|
|
76026
|
-
## SKILL IMPROVER
|
|
76031
|
+
## SKILL IMPROVER (low-frequency, expensive-model adviser)
|
|
76027
76032
|
|
|
76028
|
-
\`
|
|
76029
|
-
|
|
76030
|
-
|
|
76033
|
+
The \`skill_improver\` agent and the \`skill_improve\` tool exist for rare, deep
|
|
76034
|
+
review of accumulated knowledge / skills / spec / architect prompt. They are
|
|
76035
|
+
quota-bounded (default 10 calls/day) and disabled by default. Suggest running
|
|
76036
|
+
\`skill_improve\` only after one of:
|
|
76037
|
+
- repeated reviewer rejections in a row,
|
|
76038
|
+
- many \`KNOWLEDGE_IGNORED\` outcomes for the same cluster,
|
|
76039
|
+
- stale skills (no updates while their target area changed),
|
|
76040
|
+
- a fresh spec mismatch with shipped behaviour.
|
|
76031
76041
|
|
|
76032
76042
|
When \`skill_improver.require_user_approval\` is true (default), ASK the user
|
|
76033
76043
|
before running. Default outputs are proposals only — they never modify source.
|
|
76034
76044
|
|
|
76035
76045
|
## SPEC WRITER
|
|
76036
76046
|
|
|
76037
|
-
For substantial spec authoring
|
|
76038
|
-
|
|
76039
|
-
|
|
76047
|
+
For substantial spec authoring or revision, prefer delegating to the
|
|
76048
|
+
\`spec_writer\` agent (independent model from architect). It writes only via
|
|
76049
|
+
the safe \`spec_write\` tool. Use it when:
|
|
76050
|
+
- the user requests a new spec or major spec revision,
|
|
76051
|
+
- requirements decomposition is non-trivial,
|
|
76052
|
+
- you would otherwise inline-author \`.swarm/spec.md\` yourself.
|
|
76053
|
+
|
|
76054
|
+
Continue handling small touch-ups (typos, cross-references) inline.
|
|
76040
76055
|
|
|
76041
76056
|
### ANTI-RATIONALIZATION
|
|
76042
76057
|
- ✗ "The coder already knows these conventions" → Skills contain project-specific rules the model cannot know from training. Always pass.
|
|
@@ -76217,12 +76232,11 @@ MODE: BRAINSTORM runs seven phases in strict order. Do not skip phases. Do not c
|
|
|
76217
76232
|
- Exit with a design outline the user can skim in under two minutes.
|
|
76218
76233
|
|
|
76219
76234
|
**Phase 5: SPEC WRITE + SELF-REVIEW (architect + reviewer).**
|
|
76220
|
-
-
|
|
76221
|
-
- The spec must follow the same SPEC CONTENT RULES that MODE: SPECIFY uses: WHAT/WHY only, no tech stack, no implementation details, FR-### / SC-### numbering, Given/When/Then scenarios, \`[NEEDS CLARIFICATION]\` markers (max 3).
|
|
76235
|
+
- Generate \`.swarm/spec.md\` following the same SPEC CONTENT RULES that MODE: SPECIFY uses: WHAT/WHY only, no tech stack, no implementation details, FR-### / SC-### numbering, Given/When/Then scenarios, \`[NEEDS CLARIFICATION]\` markers (max 3).
|
|
76222
76236
|
- Cross-reference design sections by name where relevant context helps (but keep HOW out of the spec).
|
|
76223
76237
|
- Delegate to \`{{AGENT_PREFIX}}reviewer\` for an independent review of the draft spec. Reviewer must flag: requirements that encode HOW, untestable requirements, missing edge cases, silent assumptions.
|
|
76224
76238
|
- Apply reviewer feedback. If reviewer rejects, iterate once and re-review. After two rounds, surface remaining disagreements to the user.
|
|
76225
|
-
-
|
|
76239
|
+
- Write the final spec to \`.swarm/spec.md\`.
|
|
76226
76240
|
- Exit when reviewer signs off (or user explicitly accepts remaining disagreements).
|
|
76227
76241
|
|
|
76228
76242
|
**Phase 6: QA GATE SELECTION (architect, dialogue only).**
|
|
@@ -76294,7 +76308,7 @@ Activates when: user asks to "specify", "define requirements", "write a spec", o
|
|
|
76294
76308
|
1b. Run CODEBASE REALITY CHECK for any codebase references mentioned by the user or implied by the feature. Skip if work is purely greenfield (no existing codebase to check). Report discrepancies before proceeding to explorer.
|
|
76295
76309
|
2. Delegate to \`{{AGENT_PREFIX}}explorer\` to scan the codebase for relevant context (existing patterns, related code, affected areas).
|
|
76296
76310
|
3. Delegate to \`{{AGENT_PREFIX}}sme\` for domain research on the feature area to surface known constraints, best practices, and integration concerns.
|
|
76297
|
-
4.
|
|
76311
|
+
4. Generate \`.swarm/spec.md\` capturing:
|
|
76298
76312
|
- First line must be: \`# Specification: <feature-name>\`
|
|
76299
76313
|
- Feature description: WHAT users need and WHY — never HOW to implement
|
|
76300
76314
|
- User scenarios with acceptance criteria (Given/When/Then format)
|
|
@@ -76303,7 +76317,7 @@ Activates when: user asks to "specify", "define requirements", "write a spec", o
|
|
|
76303
76317
|
- Key entities if data is involved (no schema or field definitions — entity names only)
|
|
76304
76318
|
- Edge cases and known failure modes
|
|
76305
76319
|
- \`[NEEDS CLARIFICATION]\` markers (max 3) for items where uncertainty could change scope, security, or core behavior; prefer informed defaults over asking
|
|
76306
|
-
5.
|
|
76320
|
+
5. Write the spec to \`.swarm/spec.md\`.
|
|
76307
76321
|
5b. **QA GATE SELECTION (dialogue only).**
|
|
76308
76322
|
{{QA_GATE_DIALOGUE_SPECIFY}}
|
|
76309
76323
|
|
|
@@ -76458,7 +76472,6 @@ If .swarm/plan.md exists:
|
|
|
76458
76472
|
- Update context.md Swarm field to "{{SWARM_ID}}"
|
|
76459
76473
|
- Inform user: "Resuming project from [other] swarm. Cleared stale context. Ready to continue."
|
|
76460
76474
|
- Resume at current task
|
|
76461
|
-
Resume execution rule: after identifying the current task, do not restart broad discovery. Move into EXECUTE: call \`update_task_status\` if the task is not already in progress, call \`declare_scope\` for the task files, then dispatch coder Task call(s) according to \`execution_profile\`. Preserve ONE atomic task per coder Task call; parallel profiles may dispatch up to the available coder slots.
|
|
76462
76475
|
If .swarm/plan.md does not exist → New project, proceed to MODE: CLARIFY
|
|
76463
76476
|
If new project: Run \`complexity_hotspots\` tool (90 days) to generate a risk map. Note modules with recommendation "security_review" or "full_gates" in context.md for stricter QA gates during Phase 5. Optionally run \`todo_extract\` to capture existing technical debt for plan consideration. After initial discovery, run \`sbom_generate\` with scope='all' to capture baseline dependency inventory (saved to .swarm/evidence/sbom/).
|
|
76464
76477
|
|
|
@@ -79507,24 +79520,8 @@ function createSwarmAgents(swarmId, swarmConfig, isDefault, pluginConfig, projec
|
|
|
79507
79520
|
const swarmIdentity = isDefault ? "default" : swarmId;
|
|
79508
79521
|
const agentPrefix = prefix;
|
|
79509
79522
|
architect.config.prompt = architect.config.prompt?.replace(/\{\{SWARM_ID\}\}/g, swarmIdentity).replace(/\{\{AGENT_PREFIX\}\}/g, agentPrefix).replace(/\{\{QA_RETRY_LIMIT\}\}/g, String(qaRetryLimit)).replace(/\{\{PROJECT_LANGUAGE\}\}/g, projectContext.PROJECT_LANGUAGE).replace(/\{\{PROJECT_FRAMEWORK\}\}/g, projectContext.PROJECT_FRAMEWORK).replace(/\{\{BUILD_CMD\}\}/g, projectContext.BUILD_CMD).replace(/\{\{TEST_CMD\}\}/g, projectContext.TEST_CMD).replace(/\{\{LINT_CMD\}\}/g, projectContext.LINT_CMD).replace(/\{\{ENTRY_POINTS\}\}/g, projectContext.ENTRY_POINTS).replace(/\{\{CODER_CONSTRAINTS\}\}/g, projectContext.CODER_CONSTRAINTS).replace(/\{\{TEST_CONSTRAINTS\}\}/g, projectContext.TEST_CONSTRAINTS).replace(/\{\{REVIEWER_CHECKLIST\}\}/g, projectContext.REVIEWER_CHECKLIST).replace(/\{\{PROJECT_CONTEXT_SECONDARY_LANGUAGES\}\}/g, projectContext.PROJECT_CONTEXT_SECONDARY_LANGUAGES);
|
|
79510
|
-
const skillImproverEnabled = !isAgentDisabled("skill_improver", swarmAgents, swarmPrefix);
|
|
79511
|
-
const specWriterEnabled = !isAgentDisabled("spec_writer", swarmAgents, swarmPrefix);
|
|
79512
|
-
if (!skillImproverEnabled) {
|
|
79513
|
-
architect.config.prompt = architect.config.prompt?.replace(`, ${agentPrefix}skill_improver`, "").replace(`
|
|
79514
|
-
${agentPrefix}skill_improver - Low-frequency skill / knowledge / prompt improvement adviser`, "").replace(/\n## SKILL IMPROVER[\s\S]*?(?=\n\n## SPEC WRITER)/, "");
|
|
79515
|
-
}
|
|
79516
|
-
if (!specWriterEnabled) {
|
|
79517
|
-
const escapedAgentPrefix = agentPrefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
79518
|
-
architect.config.prompt = architect.config.prompt?.replace(`, ${agentPrefix}spec_writer`, "").replace(`
|
|
79519
|
-
${agentPrefix}spec_writer - .swarm/spec.md authoring via spec_write`, "").replace(/\n## SPEC WRITER[\s\S]*?(?=\n\n### ANTI-RATIONALIZATION)/, "").replace(new RegExp(`- Delegate substantial spec drafting to \`${escapedAgentPrefix}spec_writer\`[^\\n]*\\n`, "g"), "- spec_writer is disabled. Ask the user to enable the spec_writer agent before creating or revising `.swarm/spec.md`.\n").replace(new RegExp(`4\\. Delegate substantial spec drafting to \`${escapedAgentPrefix}spec_writer\`[^\\n]*`), "4. spec_writer is disabled. Ask the user to enable the spec_writer agent before creating or revising `.swarm/spec.md`.").replace(new RegExp(`5\\. Require \`${escapedAgentPrefix}spec_writer\` to write the spec via \`spec_write\`, then read back and lint \`\\.swarm/spec\\.md\`\\.`), "5. Do not continue SPECIFY until spec_writer is available.").replace(new RegExp(`- Read back and lint the final spec after \`${escapedAgentPrefix}spec_writer\` writes it\\.`), "- Do not continue BRAINSTORM spec writing until spec_writer is available.");
|
|
79520
|
-
}
|
|
79521
79523
|
if (!isDefault) {
|
|
79522
79524
|
architect.description = `[${swarmName}] ${architect.description}`;
|
|
79523
|
-
const optionalAgentLines = [
|
|
79524
|
-
skillImproverEnabled ? `- @${swarmId}_skill_improver (not @skill_improver)` : undefined,
|
|
79525
|
-
specWriterEnabled ? `- @${swarmId}_spec_writer (not @spec_writer)` : undefined
|
|
79526
|
-
].filter((line) => Boolean(line)).join(`
|
|
79527
|
-
`);
|
|
79528
79525
|
const swarmHeader = `## ⚠️ YOU ARE THE ${swarmName.toUpperCase()} SWARM ARCHITECT
|
|
79529
79526
|
|
|
79530
79527
|
Your swarm ID is "${swarmId}". ALL your agents have the "${swarmId}_" prefix:
|
|
@@ -79532,8 +79529,8 @@ Your swarm ID is "${swarmId}". ALL your agents have the "${swarmId}_" prefix:
|
|
|
79532
79529
|
- @${swarmId}_coder (not @coder)
|
|
79533
79530
|
- @${swarmId}_sme (not @sme)
|
|
79534
79531
|
- @${swarmId}_reviewer (not @reviewer)
|
|
79535
|
-
|
|
79536
|
-
|
|
79532
|
+
- @${swarmId}_spec_writer (not @spec_writer)
|
|
79533
|
+
- etc.
|
|
79537
79534
|
|
|
79538
79535
|
CRITICAL: Agents without the "${swarmId}_" prefix DO NOT EXIST or belong to a DIFFERENT swarm.
|
|
79539
79536
|
If you call @coder instead of @${swarmId}_coder, the call will FAIL or go to the wrong swarm.
|
package/dist/telemetry.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type TelemetryEvent = 'session_started' | 'session_ended' | 'agent_activated' | 'delegation_begin' | 'delegation_end' | 'task_state_changed' | 'gate_passed' | 'gate_failed' | 'phase_changed' | 'budget_updated' | 'model_fallback' | 'hard_limit_hit' | 'revision_limit_hit' | 'loop_detected' | 'scope_violation' | 'qa_skip_violation' | 'heartbeat' | 'turbo_mode_changed' | 'auto_oversight_escalation' | 'environment_detected' | 'evidence_lock_acquired' | 'evidence_lock_contended' | 'evidence_lock_stale_recovered' | 'plan_ledger_cas_retry' | 'plan_md_write_failed' | 'prm_pattern_detected' | 'prm_course_correction_injected' | 'prm_escalation_triggered' | 'prm_hard_stop';
|
|
1
|
+
export type TelemetryEvent = 'session_started' | 'session_ended' | 'agent_activated' | 'delegation_begin' | 'delegation_end' | 'task_state_changed' | 'gate_passed' | 'gate_failed' | 'gate_parse_error' | 'phase_changed' | 'budget_updated' | 'model_fallback' | 'hard_limit_hit' | 'revision_limit_hit' | 'loop_detected' | 'scope_violation' | 'qa_skip_violation' | 'heartbeat' | 'turbo_mode_changed' | 'auto_oversight_escalation' | 'environment_detected' | 'evidence_lock_acquired' | 'evidence_lock_contended' | 'evidence_lock_stale_recovered' | 'plan_ledger_cas_retry' | 'plan_md_write_failed' | 'prm_pattern_detected' | 'prm_course_correction_injected' | 'prm_escalation_triggered' | 'prm_hard_stop';
|
|
2
2
|
export type TelemetryListener = (event: TelemetryEvent, data: Record<string, unknown>) => void;
|
|
3
3
|
/** @internal - For testing only */
|
|
4
4
|
export declare function resetTelemetryForTesting(): void;
|
|
@@ -39,6 +39,7 @@ export declare const telemetry: {
|
|
|
39
39
|
delegationEnd(sessionId: string, agentName: string, taskId: string, result: string): void;
|
|
40
40
|
taskStateChanged(sessionId: string, taskId: string, newState: string, oldState?: string): void;
|
|
41
41
|
gatePassed(sessionId: string, gate: string, taskId: string): void;
|
|
42
|
+
gateParseError(taskId: string, error: Error): void;
|
|
42
43
|
gateFailed(sessionId: string, gate: string, taskId: string, reason: string): void;
|
|
43
44
|
phaseChanged(sessionId: string, oldPhase: number, newPhase: number): void;
|
|
44
45
|
budgetUpdated(sessionId: string, budgetPct: number, agentName: string): void;
|
|
@@ -23,6 +23,7 @@ export interface TestRunnerArgs {
|
|
|
23
23
|
files?: string[];
|
|
24
24
|
coverage?: boolean;
|
|
25
25
|
timeout_ms?: number;
|
|
26
|
+
allow_full_suite?: boolean;
|
|
26
27
|
}
|
|
27
28
|
export type RegressionOutcome = 'pass' | 'skip' | 'regression' | 'scope_exceeded' | 'error';
|
|
28
29
|
export interface TestTotals {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "7.29.
|
|
3
|
+
"version": "7.29.4",
|
|
4
4
|
"description": "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|