opencode-swarm 6.28.1 → 6.29.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/dist/cli/index.js +236 -25
- package/dist/config/constants.d.ts +19 -0
- package/dist/config/evidence-schema.d.ts +6 -0
- package/dist/config/schema.d.ts +41 -0
- package/dist/hooks/diff-scope.d.ts +12 -0
- package/dist/hooks/diff-scope.test.d.ts +1 -0
- package/dist/hooks/incremental-verify.d.ts +18 -0
- package/dist/hooks/incremental-verify.test.d.ts +1 -0
- package/dist/hooks/loop-detector.d.ts +17 -0
- package/dist/hooks/loop-detector.test.d.ts +1 -0
- package/dist/hooks/slop-detector.d.ts +17 -0
- package/dist/hooks/slop-detector.test.d.ts +1 -0
- package/dist/index.js +1316 -547
- package/dist/services/compaction-service.d.ts +28 -0
- package/dist/services/compaction-service.test.d.ts +1 -0
- package/dist/services/status-service.d.ts +6 -0
- package/dist/state.d.ts +17 -0
- package/dist/tools/test-runner.d.ts +1 -1
- package/dist/tools/update-task-status.d.ts +8 -0
- package/dist/tools/write-retro.d.ts +2 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -14217,14 +14217,16 @@ var init_evidence_schema = __esm(() => {
|
|
|
14217
14217
|
});
|
|
14218
14218
|
RetrospectiveEvidenceSchema = BaseEvidenceSchema.extend({
|
|
14219
14219
|
type: exports_external.literal("retrospective"),
|
|
14220
|
-
phase_number: exports_external.number().int().min(0),
|
|
14221
|
-
total_tool_calls: exports_external.number().int().min(0),
|
|
14222
|
-
coder_revisions: exports_external.number().int().min(0),
|
|
14223
|
-
reviewer_rejections: exports_external.number().int().min(0),
|
|
14224
|
-
|
|
14225
|
-
|
|
14226
|
-
|
|
14227
|
-
|
|
14220
|
+
phase_number: exports_external.number().int().min(0).max(99),
|
|
14221
|
+
total_tool_calls: exports_external.number().int().min(0).max(9999),
|
|
14222
|
+
coder_revisions: exports_external.number().int().min(0).max(999),
|
|
14223
|
+
reviewer_rejections: exports_external.number().int().min(0).max(999),
|
|
14224
|
+
loop_detections: exports_external.number().int().min(0).max(9999).optional(),
|
|
14225
|
+
circuit_breaker_trips: exports_external.number().int().min(0).max(9999).optional(),
|
|
14226
|
+
test_failures: exports_external.number().int().min(0).max(9999),
|
|
14227
|
+
security_findings: exports_external.number().int().min(0).max(999),
|
|
14228
|
+
integration_issues: exports_external.number().int().min(0).max(999),
|
|
14229
|
+
task_count: exports_external.number().int().min(1).max(9999),
|
|
14228
14230
|
task_complexity: exports_external.enum(["trivial", "simple", "moderate", "complex"]),
|
|
14229
14231
|
top_rejection_reasons: exports_external.array(exports_external.string()).default([]),
|
|
14230
14232
|
lessons_learned: exports_external.array(exports_external.string()).max(5).default([]),
|
|
@@ -14435,7 +14437,7 @@ function resolveGuardrailsConfig(config2, agentName) {
|
|
|
14435
14437
|
};
|
|
14436
14438
|
return resolved;
|
|
14437
14439
|
}
|
|
14438
|
-
var KNOWN_SWARM_PREFIXES, SEPARATORS, AgentOverrideConfigSchema, SwarmConfigSchema, HooksConfigSchema, ScoringWeightsSchema, DecisionDecaySchema, TokenRatiosSchema, ScoringConfigSchema, ContextBudgetConfigSchema, EvidenceConfigSchema, GateFeatureSchema, PlaceholderScanConfigSchema, QualityBudgetConfigSchema, GateConfigSchema, PipelineConfigSchema, PhaseCompleteConfigSchema, SummaryConfigSchema, ReviewPassesConfigSchema, AdversarialDetectionConfigSchema, AdversarialTestingConfigSchemaBase, AdversarialTestingConfigSchema, IntegrationAnalysisConfigSchema, DocsConfigSchema, UIReviewConfigSchema, CompactionAdvisoryConfigSchema, LintConfigSchema, SecretscanConfigSchema, GuardrailsProfileSchema, DEFAULT_AGENT_PROFILES, DEFAULT_ARCHITECT_PROFILE, GuardrailsConfigSchema, ToolFilterConfigSchema, PlanCursorConfigSchema, CheckpointConfigSchema, AutomationModeSchema, AutomationCapabilitiesSchema, AutomationConfigSchemaBase, AutomationConfigSchema, KnowledgeConfigSchema, CuratorConfigSchema, PluginConfigSchema;
|
|
14440
|
+
var KNOWN_SWARM_PREFIXES, SEPARATORS, AgentOverrideConfigSchema, SwarmConfigSchema, HooksConfigSchema, ScoringWeightsSchema, DecisionDecaySchema, TokenRatiosSchema, ScoringConfigSchema, ContextBudgetConfigSchema, EvidenceConfigSchema, GateFeatureSchema, PlaceholderScanConfigSchema, QualityBudgetConfigSchema, GateConfigSchema, PipelineConfigSchema, PhaseCompleteConfigSchema, SummaryConfigSchema, ReviewPassesConfigSchema, AdversarialDetectionConfigSchema, AdversarialTestingConfigSchemaBase, AdversarialTestingConfigSchema, IntegrationAnalysisConfigSchema, DocsConfigSchema, UIReviewConfigSchema, CompactionAdvisoryConfigSchema, LintConfigSchema, SecretscanConfigSchema, GuardrailsProfileSchema, DEFAULT_AGENT_PROFILES, DEFAULT_ARCHITECT_PROFILE, GuardrailsConfigSchema, ToolFilterConfigSchema, PlanCursorConfigSchema, CheckpointConfigSchema, AutomationModeSchema, AutomationCapabilitiesSchema, AutomationConfigSchemaBase, AutomationConfigSchema, KnowledgeConfigSchema, CuratorConfigSchema, SlopDetectorConfigSchema, IncrementalVerifyConfigSchema, CompactionConfigSchema, PluginConfigSchema;
|
|
14439
14441
|
var init_schema = __esm(() => {
|
|
14440
14442
|
init_zod();
|
|
14441
14443
|
init_constants();
|
|
@@ -14872,6 +14874,25 @@ var init_schema = __esm(() => {
|
|
|
14872
14874
|
suppress_warnings: exports_external.boolean().default(true),
|
|
14873
14875
|
drift_inject_max_chars: exports_external.number().min(100).max(2000).default(500)
|
|
14874
14876
|
});
|
|
14877
|
+
SlopDetectorConfigSchema = exports_external.object({
|
|
14878
|
+
enabled: exports_external.boolean().default(true),
|
|
14879
|
+
classThreshold: exports_external.number().int().min(1).default(3),
|
|
14880
|
+
commentStripThreshold: exports_external.number().int().min(1).default(5),
|
|
14881
|
+
diffLineThreshold: exports_external.number().int().min(10).default(200)
|
|
14882
|
+
});
|
|
14883
|
+
IncrementalVerifyConfigSchema = exports_external.object({
|
|
14884
|
+
enabled: exports_external.boolean().default(true),
|
|
14885
|
+
command: exports_external.string().nullable().default(null),
|
|
14886
|
+
timeoutMs: exports_external.number().int().min(1000).max(300000).default(30000),
|
|
14887
|
+
triggerAgents: exports_external.array(exports_external.string()).default(["coder"])
|
|
14888
|
+
});
|
|
14889
|
+
CompactionConfigSchema = exports_external.object({
|
|
14890
|
+
enabled: exports_external.boolean().default(true),
|
|
14891
|
+
observationThreshold: exports_external.number().min(1).max(99).default(40),
|
|
14892
|
+
reflectionThreshold: exports_external.number().min(1).max(99).default(60),
|
|
14893
|
+
emergencyThreshold: exports_external.number().min(1).max(99).default(80),
|
|
14894
|
+
preserveLastNTurns: exports_external.number().int().min(1).default(5)
|
|
14895
|
+
});
|
|
14875
14896
|
PluginConfigSchema = exports_external.object({
|
|
14876
14897
|
agents: exports_external.record(exports_external.string(), AgentOverrideConfigSchema).optional(),
|
|
14877
14898
|
swarms: exports_external.record(exports_external.string(), SwarmConfigSchema).optional(),
|
|
@@ -14905,7 +14926,10 @@ var init_schema = __esm(() => {
|
|
|
14905
14926
|
truncation_enabled: exports_external.boolean().default(true),
|
|
14906
14927
|
max_lines: exports_external.number().min(10).max(500).default(150),
|
|
14907
14928
|
per_tool: exports_external.record(exports_external.string(), exports_external.number()).optional()
|
|
14908
|
-
}).optional()
|
|
14929
|
+
}).optional(),
|
|
14930
|
+
slop_detector: SlopDetectorConfigSchema.optional(),
|
|
14931
|
+
incremental_verify: IncrementalVerifyConfigSchema.optional(),
|
|
14932
|
+
compaction_service: CompactionConfigSchema.optional()
|
|
14909
14933
|
});
|
|
14910
14934
|
});
|
|
14911
14935
|
|
|
@@ -17112,11 +17136,12 @@ class PreflightTriggerManager {
|
|
|
17112
17136
|
this.unsubscribe = this.eventBus.subscribe("preflight.requested", async (event) => {
|
|
17113
17137
|
if (this.preflightHandler) {
|
|
17114
17138
|
const request = event.payload;
|
|
17139
|
+
let timeoutHandle;
|
|
17115
17140
|
try {
|
|
17116
17141
|
await Promise.race([
|
|
17117
17142
|
this.preflightHandler(request),
|
|
17118
17143
|
new Promise((_, reject) => {
|
|
17119
|
-
setTimeout(() => {
|
|
17144
|
+
timeoutHandle = setTimeout(() => {
|
|
17120
17145
|
reject(new Error(`Preflight handler timed out after ${HANDLER_TIMEOUT_MS}ms`));
|
|
17121
17146
|
}, HANDLER_TIMEOUT_MS);
|
|
17122
17147
|
})
|
|
@@ -17127,6 +17152,8 @@ class PreflightTriggerManager {
|
|
|
17127
17152
|
phase: request.currentPhase,
|
|
17128
17153
|
errorType: error49 instanceof Error ? error49.name : "unknown"
|
|
17129
17154
|
});
|
|
17155
|
+
} finally {
|
|
17156
|
+
clearTimeout(timeoutHandle);
|
|
17130
17157
|
}
|
|
17131
17158
|
}
|
|
17132
17159
|
});
|
|
@@ -34189,10 +34216,7 @@ var init_secretscan = __esm(() => {
|
|
|
34189
34216
|
const excludeExact = new Set(DEFAULT_EXCLUDE_DIRS);
|
|
34190
34217
|
const excludeGlobs = [];
|
|
34191
34218
|
const ignoreFilePatterns = loadSecretScanIgnore(scanDir);
|
|
34192
|
-
const allUserPatterns = [
|
|
34193
|
-
...exclude ?? [],
|
|
34194
|
-
...ignoreFilePatterns
|
|
34195
|
-
];
|
|
34219
|
+
const allUserPatterns = [...exclude ?? [], ...ignoreFilePatterns];
|
|
34196
34220
|
for (const exc of allUserPatterns) {
|
|
34197
34221
|
if (exc.length === 0)
|
|
34198
34222
|
continue;
|
|
@@ -34435,7 +34459,7 @@ function detectMinitest(cwd) {
|
|
|
34435
34459
|
return fs11.existsSync(path23.join(cwd, "test")) && (fs11.existsSync(path23.join(cwd, "Gemfile")) || fs11.existsSync(path23.join(cwd, "Rakefile"))) && isCommandAvailable("ruby");
|
|
34436
34460
|
}
|
|
34437
34461
|
async function detectTestFramework(cwd) {
|
|
34438
|
-
const baseDir = cwd
|
|
34462
|
+
const baseDir = cwd;
|
|
34439
34463
|
try {
|
|
34440
34464
|
const packageJsonPath = path23.join(baseDir, "package.json");
|
|
34441
34465
|
if (fs11.existsSync(packageJsonPath)) {
|
|
@@ -35918,7 +35942,7 @@ __export(exports_gate_evidence, {
|
|
|
35918
35942
|
DEFAULT_REQUIRED_GATES: () => DEFAULT_REQUIRED_GATES
|
|
35919
35943
|
});
|
|
35920
35944
|
import { mkdirSync as mkdirSync8, readFileSync as readFileSync13, renameSync as renameSync7, unlinkSync as unlinkSync4 } from "fs";
|
|
35921
|
-
import * as
|
|
35945
|
+
import * as path29 from "path";
|
|
35922
35946
|
function isValidTaskId2(taskId) {
|
|
35923
35947
|
if (!taskId)
|
|
35924
35948
|
return false;
|
|
@@ -35965,10 +35989,10 @@ function expandRequiredGates(existingGates, newAgentType) {
|
|
|
35965
35989
|
return combined.sort();
|
|
35966
35990
|
}
|
|
35967
35991
|
function getEvidenceDir(directory) {
|
|
35968
|
-
return
|
|
35992
|
+
return path29.join(directory, ".swarm", "evidence");
|
|
35969
35993
|
}
|
|
35970
35994
|
function getEvidencePath(directory, taskId) {
|
|
35971
|
-
return
|
|
35995
|
+
return path29.join(getEvidenceDir(directory), `${taskId}.json`);
|
|
35972
35996
|
}
|
|
35973
35997
|
function readExisting(evidencePath) {
|
|
35974
35998
|
try {
|
|
@@ -36082,10 +36106,10 @@ function createPreflightIntegration(config3) {
|
|
|
36082
36106
|
});
|
|
36083
36107
|
const report = await runPreflight(directory, request.currentPhase, preflightConfig);
|
|
36084
36108
|
if (statusArtifact) {
|
|
36085
|
-
const
|
|
36086
|
-
statusArtifact.recordOutcome(
|
|
36109
|
+
const state2 = report.overall === "pass" ? "success" : "failure";
|
|
36110
|
+
statusArtifact.recordOutcome(state2, request.currentPhase, report.message);
|
|
36087
36111
|
console.log("[PreflightIntegration] Status artifact updated", {
|
|
36088
|
-
state,
|
|
36112
|
+
state: state2,
|
|
36089
36113
|
phase: request.currentPhase,
|
|
36090
36114
|
message: report.message
|
|
36091
36115
|
});
|
|
@@ -37577,11 +37601,11 @@ ${JSON.stringify(symbolNames, null, 2)}`);
|
|
|
37577
37601
|
throw toThrow;
|
|
37578
37602
|
}, "quit_");
|
|
37579
37603
|
var scriptDirectory = "";
|
|
37580
|
-
function locateFile(
|
|
37604
|
+
function locateFile(path45) {
|
|
37581
37605
|
if (Module["locateFile"]) {
|
|
37582
|
-
return Module["locateFile"](
|
|
37606
|
+
return Module["locateFile"](path45, scriptDirectory);
|
|
37583
37607
|
}
|
|
37584
|
-
return scriptDirectory +
|
|
37608
|
+
return scriptDirectory + path45;
|
|
37585
37609
|
}
|
|
37586
37610
|
__name(locateFile, "locateFile");
|
|
37587
37611
|
var readAsync, readBinary;
|
|
@@ -39329,7 +39353,7 @@ var init_runtime = __esm(() => {
|
|
|
39329
39353
|
});
|
|
39330
39354
|
|
|
39331
39355
|
// src/index.ts
|
|
39332
|
-
import * as
|
|
39356
|
+
import * as path55 from "path";
|
|
39333
39357
|
|
|
39334
39358
|
// src/agents/index.ts
|
|
39335
39359
|
init_config();
|
|
@@ -39348,6 +39372,7 @@ var swarmState = {
|
|
|
39348
39372
|
activeAgent: new Map,
|
|
39349
39373
|
delegationChains: new Map,
|
|
39350
39374
|
pendingEvents: 0,
|
|
39375
|
+
lastBudgetPct: 0,
|
|
39351
39376
|
agentSessions: new Map
|
|
39352
39377
|
};
|
|
39353
39378
|
function startAgentSession(sessionId, agentName, staleDurationMs = 7200000, directory) {
|
|
@@ -39392,7 +39417,9 @@ function startAgentSession(sessionId, agentName, staleDurationMs = 7200000, dire
|
|
|
39392
39417
|
lastScopeViolation: null,
|
|
39393
39418
|
scopeViolationDetected: false,
|
|
39394
39419
|
modifiedFilesThisCoderTask: [],
|
|
39395
|
-
turboMode: false
|
|
39420
|
+
turboMode: false,
|
|
39421
|
+
loopDetectionWindow: [],
|
|
39422
|
+
pendingAdvisoryMessages: []
|
|
39396
39423
|
};
|
|
39397
39424
|
swarmState.agentSessions.set(sessionId, sessionState);
|
|
39398
39425
|
swarmState.activeAgent.set(sessionId, agentName);
|
|
@@ -39494,6 +39521,12 @@ function ensureAgentSession(sessionId, agentName, directory) {
|
|
|
39494
39521
|
if (session.turboMode === undefined) {
|
|
39495
39522
|
session.turboMode = false;
|
|
39496
39523
|
}
|
|
39524
|
+
if (session.loopDetectionWindow === undefined) {
|
|
39525
|
+
session.loopDetectionWindow = [];
|
|
39526
|
+
}
|
|
39527
|
+
if (session.pendingAdvisoryMessages === undefined) {
|
|
39528
|
+
session.pendingAdvisoryMessages = [];
|
|
39529
|
+
}
|
|
39497
39530
|
session.lastToolCallTime = now;
|
|
39498
39531
|
return session;
|
|
39499
39532
|
}
|
|
@@ -47578,6 +47611,264 @@ No plan content available. Start by creating a .swarm/plan.md file.
|
|
|
47578
47611
|
// src/services/status-service.ts
|
|
47579
47612
|
init_utils2();
|
|
47580
47613
|
init_manager2();
|
|
47614
|
+
|
|
47615
|
+
// src/services/compaction-service.ts
|
|
47616
|
+
import * as fs15 from "fs";
|
|
47617
|
+
import * as path27 from "path";
|
|
47618
|
+
function makeInitialState() {
|
|
47619
|
+
return {
|
|
47620
|
+
lastObservationAt: 0,
|
|
47621
|
+
lastReflectionAt: 0,
|
|
47622
|
+
lastEmergencyAt: 0,
|
|
47623
|
+
observationCount: 0,
|
|
47624
|
+
reflectionCount: 0,
|
|
47625
|
+
emergencyCount: 0,
|
|
47626
|
+
lastSnapshotAt: null
|
|
47627
|
+
};
|
|
47628
|
+
}
|
|
47629
|
+
var state = makeInitialState();
|
|
47630
|
+
function appendSnapshot(directory, tier, budgetPct, message) {
|
|
47631
|
+
try {
|
|
47632
|
+
const snapshotPath = path27.join(directory, ".swarm", "context-snapshot.md");
|
|
47633
|
+
const timestamp = new Date().toISOString();
|
|
47634
|
+
const entry = `
|
|
47635
|
+
## [${tier.toUpperCase()}] ${timestamp} \u2014 ${budgetPct.toFixed(1)}% used
|
|
47636
|
+
${message}
|
|
47637
|
+
`;
|
|
47638
|
+
fs15.appendFileSync(snapshotPath, entry, "utf-8");
|
|
47639
|
+
} catch {}
|
|
47640
|
+
}
|
|
47641
|
+
function buildObservationMessage(budgetPct) {
|
|
47642
|
+
return `[CONTEXT COMPACTION \u2014 OBSERVATION TIER]
|
|
47643
|
+
` + `Context window is ${budgetPct.toFixed(1)}% used. Initiating observation compaction.
|
|
47644
|
+
` + `INSTRUCTIONS: Summarise the key decisions made so far, files changed, errors resolved, ` + `and the current task state. Discard verbose tool outputs and raw file reads. ` + `Preserve: plan task ID, agent verdicts, file paths touched, unresolved blockers.
|
|
47645
|
+
` + `[/CONTEXT COMPACTION]`;
|
|
47646
|
+
}
|
|
47647
|
+
function buildReflectionMessage(budgetPct) {
|
|
47648
|
+
return `[CONTEXT COMPACTION \u2014 REFLECTION TIER]
|
|
47649
|
+
` + `Context window is ${budgetPct.toFixed(1)}% used. Initiating reflection compaction.
|
|
47650
|
+
` + `INSTRUCTIONS: Re-summarise into a tighter format. Discard completed task details ` + `and resolved errors. Retain ONLY: current phase tasks remaining, open blockers, ` + `last 3 reviewer/test verdicts, and active file scope.
|
|
47651
|
+
` + `[/CONTEXT COMPACTION]`;
|
|
47652
|
+
}
|
|
47653
|
+
function buildEmergencyMessage(budgetPct, preserveLastN) {
|
|
47654
|
+
return `[CONTEXT COMPACTION \u2014 EMERGENCY TIER]
|
|
47655
|
+
` + `Context window is ${budgetPct.toFixed(1)}% used. EMERGENCY compaction required.
|
|
47656
|
+
` + `INSTRUCTIONS: Retain ONLY the system prompt, the current task context, and the ` + `last ${preserveLastN} conversation turns. Discard everything else. ` + `If you cannot complete the current task in the remaining context, escalate to the user.
|
|
47657
|
+
` + `[/CONTEXT COMPACTION]`;
|
|
47658
|
+
}
|
|
47659
|
+
function createCompactionService(config3, directory, injectMessage) {
|
|
47660
|
+
return {
|
|
47661
|
+
toolAfter: async (_input, _output) => {
|
|
47662
|
+
if (!config3.enabled)
|
|
47663
|
+
return;
|
|
47664
|
+
const budgetPct = swarmState.lastBudgetPct ?? 0;
|
|
47665
|
+
if (budgetPct <= 0)
|
|
47666
|
+
return;
|
|
47667
|
+
const sessionId = _input.sessionID;
|
|
47668
|
+
try {
|
|
47669
|
+
if (budgetPct >= config3.emergencyThreshold && budgetPct > state.lastEmergencyAt + 5) {
|
|
47670
|
+
state.lastEmergencyAt = budgetPct;
|
|
47671
|
+
state.emergencyCount++;
|
|
47672
|
+
const msg = buildEmergencyMessage(budgetPct, config3.preserveLastNTurns);
|
|
47673
|
+
appendSnapshot(directory, "emergency", budgetPct, msg);
|
|
47674
|
+
state.lastSnapshotAt = new Date().toISOString();
|
|
47675
|
+
injectMessage(sessionId, msg);
|
|
47676
|
+
return;
|
|
47677
|
+
}
|
|
47678
|
+
if (budgetPct >= config3.reflectionThreshold && budgetPct > state.lastReflectionAt + 5) {
|
|
47679
|
+
state.lastReflectionAt = budgetPct;
|
|
47680
|
+
state.reflectionCount++;
|
|
47681
|
+
const msg = buildReflectionMessage(budgetPct);
|
|
47682
|
+
appendSnapshot(directory, "reflection", budgetPct, msg);
|
|
47683
|
+
state.lastSnapshotAt = new Date().toISOString();
|
|
47684
|
+
injectMessage(sessionId, msg);
|
|
47685
|
+
return;
|
|
47686
|
+
}
|
|
47687
|
+
if (budgetPct >= config3.observationThreshold && budgetPct > state.lastObservationAt + 5) {
|
|
47688
|
+
state.lastObservationAt = budgetPct;
|
|
47689
|
+
state.observationCount++;
|
|
47690
|
+
const msg = buildObservationMessage(budgetPct);
|
|
47691
|
+
appendSnapshot(directory, "observation", budgetPct, msg);
|
|
47692
|
+
state.lastSnapshotAt = new Date().toISOString();
|
|
47693
|
+
injectMessage(sessionId, msg);
|
|
47694
|
+
}
|
|
47695
|
+
} catch {}
|
|
47696
|
+
}
|
|
47697
|
+
};
|
|
47698
|
+
}
|
|
47699
|
+
function getCompactionMetrics() {
|
|
47700
|
+
return {
|
|
47701
|
+
compactionCount: state.observationCount + state.reflectionCount + state.emergencyCount,
|
|
47702
|
+
lastSnapshotAt: state.lastSnapshotAt
|
|
47703
|
+
};
|
|
47704
|
+
}
|
|
47705
|
+
|
|
47706
|
+
// src/services/context-budget-service.ts
|
|
47707
|
+
init_utils2();
|
|
47708
|
+
function validateDirectory(directory) {
|
|
47709
|
+
if (!directory || directory.trim() === "") {
|
|
47710
|
+
throw new Error("Invalid directory: empty");
|
|
47711
|
+
}
|
|
47712
|
+
if (/\.\.[/\\]/.test(directory)) {
|
|
47713
|
+
throw new Error("Invalid directory: path traversal detected");
|
|
47714
|
+
}
|
|
47715
|
+
if (directory.startsWith("/") || directory.startsWith("\\")) {
|
|
47716
|
+
throw new Error("Invalid directory: absolute path");
|
|
47717
|
+
}
|
|
47718
|
+
if (/^[A-Za-z]:[\\/]/.test(directory)) {
|
|
47719
|
+
throw new Error("Invalid directory: Windows absolute path");
|
|
47720
|
+
}
|
|
47721
|
+
}
|
|
47722
|
+
var DEFAULT_CONTEXT_BUDGET_CONFIG = {
|
|
47723
|
+
enabled: true,
|
|
47724
|
+
budgetTokens: 40000,
|
|
47725
|
+
warningPct: 70,
|
|
47726
|
+
criticalPct: 90,
|
|
47727
|
+
warningMode: "once",
|
|
47728
|
+
warningIntervalTurns: 20
|
|
47729
|
+
};
|
|
47730
|
+
var COST_PER_1K_TOKENS = 0.003;
|
|
47731
|
+
function estimateTokens2(text) {
|
|
47732
|
+
if (!text || typeof text !== "string") {
|
|
47733
|
+
return 0;
|
|
47734
|
+
}
|
|
47735
|
+
return Math.ceil(text.length / 3.5);
|
|
47736
|
+
}
|
|
47737
|
+
async function readBudgetState(directory) {
|
|
47738
|
+
const content = await readSwarmFileAsync(directory, "session/budget-state.json");
|
|
47739
|
+
if (!content) {
|
|
47740
|
+
return null;
|
|
47741
|
+
}
|
|
47742
|
+
try {
|
|
47743
|
+
return JSON.parse(content);
|
|
47744
|
+
} catch {
|
|
47745
|
+
return null;
|
|
47746
|
+
}
|
|
47747
|
+
}
|
|
47748
|
+
async function writeBudgetState(directory, state2) {
|
|
47749
|
+
const resolvedPath = validateSwarmPath(directory, "session/budget-state.json");
|
|
47750
|
+
const content = JSON.stringify(state2, null, 2);
|
|
47751
|
+
await Bun.write(resolvedPath, content);
|
|
47752
|
+
}
|
|
47753
|
+
async function countEvents(directory) {
|
|
47754
|
+
const content = await readSwarmFileAsync(directory, "events.jsonl");
|
|
47755
|
+
if (!content) {
|
|
47756
|
+
return 0;
|
|
47757
|
+
}
|
|
47758
|
+
const lines = content.split(`
|
|
47759
|
+
`).filter((line) => line.trim().length > 0);
|
|
47760
|
+
return lines.length;
|
|
47761
|
+
}
|
|
47762
|
+
async function getPlanCursorContent(directory) {
|
|
47763
|
+
const planContent = await readSwarmFileAsync(directory, "plan.md");
|
|
47764
|
+
if (!planContent) {
|
|
47765
|
+
return "";
|
|
47766
|
+
}
|
|
47767
|
+
const lines = planContent.split(`
|
|
47768
|
+
`);
|
|
47769
|
+
const cursorLines = [];
|
|
47770
|
+
let inCurrentSection = false;
|
|
47771
|
+
for (const line of lines) {
|
|
47772
|
+
if (line.includes("in_progress") || line.includes("**Current**")) {
|
|
47773
|
+
inCurrentSection = true;
|
|
47774
|
+
}
|
|
47775
|
+
if (inCurrentSection) {
|
|
47776
|
+
cursorLines.push(line);
|
|
47777
|
+
if (cursorLines.length > 30) {
|
|
47778
|
+
break;
|
|
47779
|
+
}
|
|
47780
|
+
}
|
|
47781
|
+
}
|
|
47782
|
+
return cursorLines.join(`
|
|
47783
|
+
`) || planContent.substring(0, 1000);
|
|
47784
|
+
}
|
|
47785
|
+
async function getContextBudgetReport(directory, assembledSystemPrompt, config3) {
|
|
47786
|
+
validateDirectory(directory);
|
|
47787
|
+
const timestamp = new Date().toISOString();
|
|
47788
|
+
const systemPromptTokens = estimateTokens2(assembledSystemPrompt);
|
|
47789
|
+
const planCursorContent = await getPlanCursorContent(directory);
|
|
47790
|
+
const planCursorTokens = estimateTokens2(planCursorContent);
|
|
47791
|
+
const knowledgeContent = await readSwarmFileAsync(directory, "knowledge.jsonl");
|
|
47792
|
+
const knowledgeTokens = estimateTokens2(knowledgeContent || "");
|
|
47793
|
+
const runMemoryContent = await readSwarmFileAsync(directory, "run-memory.jsonl");
|
|
47794
|
+
const runMemoryTokens = estimateTokens2(runMemoryContent || "");
|
|
47795
|
+
const handoffContent = await readSwarmFileAsync(directory, "handoff.md");
|
|
47796
|
+
const handoffTokens = estimateTokens2(handoffContent || "");
|
|
47797
|
+
const contextMdContent = await readSwarmFileAsync(directory, "context.md");
|
|
47798
|
+
const contextMdTokens = estimateTokens2(contextMdContent || "");
|
|
47799
|
+
const swarmTotalTokens = systemPromptTokens + planCursorTokens + knowledgeTokens + runMemoryTokens + handoffTokens + contextMdTokens;
|
|
47800
|
+
const estimatedTurnCount = await countEvents(directory);
|
|
47801
|
+
const budgetPct = swarmTotalTokens / config3.budgetTokens * 100;
|
|
47802
|
+
let status;
|
|
47803
|
+
let recommendation = null;
|
|
47804
|
+
if (budgetPct < config3.warningPct) {
|
|
47805
|
+
status = "ok";
|
|
47806
|
+
} else if (budgetPct < config3.criticalPct) {
|
|
47807
|
+
status = "warning";
|
|
47808
|
+
recommendation = "Consider wrapping up current phase and running /swarm handoff before starting new work.";
|
|
47809
|
+
} else {
|
|
47810
|
+
status = "critical";
|
|
47811
|
+
recommendation = "Run /swarm handoff and start a new session to avoid cost escalation.";
|
|
47812
|
+
}
|
|
47813
|
+
const estimatedSessionTokens = swarmTotalTokens * Math.max(1, estimatedTurnCount);
|
|
47814
|
+
return {
|
|
47815
|
+
timestamp,
|
|
47816
|
+
systemPromptTokens,
|
|
47817
|
+
planCursorTokens,
|
|
47818
|
+
knowledgeTokens,
|
|
47819
|
+
runMemoryTokens,
|
|
47820
|
+
handoffTokens,
|
|
47821
|
+
contextMdTokens,
|
|
47822
|
+
swarmTotalTokens,
|
|
47823
|
+
estimatedTurnCount,
|
|
47824
|
+
estimatedSessionTokens,
|
|
47825
|
+
budgetPct,
|
|
47826
|
+
status,
|
|
47827
|
+
recommendation
|
|
47828
|
+
};
|
|
47829
|
+
}
|
|
47830
|
+
async function formatBudgetWarning(report, directory, config3) {
|
|
47831
|
+
validateDirectory(directory);
|
|
47832
|
+
if (report.status === "ok") {
|
|
47833
|
+
return null;
|
|
47834
|
+
}
|
|
47835
|
+
if (!directory || directory.trim() === "") {
|
|
47836
|
+
return formatWarningMessage(report);
|
|
47837
|
+
}
|
|
47838
|
+
const budgetState = await readBudgetState(directory);
|
|
47839
|
+
const state2 = budgetState || {
|
|
47840
|
+
warningFiredAtTurn: null,
|
|
47841
|
+
criticalFiredAtTurn: null,
|
|
47842
|
+
lastInjectedAtTurn: null
|
|
47843
|
+
};
|
|
47844
|
+
const currentTurn = report.estimatedTurnCount;
|
|
47845
|
+
if (report.status === "warning") {
|
|
47846
|
+
if (config3.warningMode === "once" && state2.warningFiredAtTurn !== null) {
|
|
47847
|
+
return null;
|
|
47848
|
+
}
|
|
47849
|
+
if (config3.warningMode === "interval" && state2.warningFiredAtTurn !== null && currentTurn - state2.warningFiredAtTurn < config3.warningIntervalTurns) {
|
|
47850
|
+
return null;
|
|
47851
|
+
}
|
|
47852
|
+
state2.warningFiredAtTurn = currentTurn;
|
|
47853
|
+
state2.lastInjectedAtTurn = currentTurn;
|
|
47854
|
+
await writeBudgetState(directory, state2);
|
|
47855
|
+
} else if (report.status === "critical") {
|
|
47856
|
+
state2.criticalFiredAtTurn = currentTurn;
|
|
47857
|
+
state2.lastInjectedAtTurn = currentTurn;
|
|
47858
|
+
}
|
|
47859
|
+
return formatWarningMessage(report);
|
|
47860
|
+
}
|
|
47861
|
+
function formatWarningMessage(report) {
|
|
47862
|
+
const budgetPctStr = report.budgetPct.toFixed(1);
|
|
47863
|
+
const tokensPerTurn = report.swarmTotalTokens.toLocaleString();
|
|
47864
|
+
if (report.status === "warning") {
|
|
47865
|
+
return `[CONTEXT BUDGET: ${budgetPctStr}% \u2014 swarm injecting ~${tokensPerTurn} tokens/turn. Consider wrapping current phase and running /swarm handoff before starting new work.]`;
|
|
47866
|
+
}
|
|
47867
|
+
const costPerTurn = (report.swarmTotalTokens / 1000 * COST_PER_1K_TOKENS).toFixed(3);
|
|
47868
|
+
return `[CONTEXT BUDGET: ${budgetPctStr}% CRITICAL \u2014 swarm injecting ~${tokensPerTurn} tokens/turn. Run /swarm handoff and start a new session to avoid cost escalation. Estimated session cost scaling: ~$${costPerTurn}/turn at current context size.]`;
|
|
47869
|
+
}
|
|
47870
|
+
|
|
47871
|
+
// src/services/status-service.ts
|
|
47581
47872
|
async function getStatusData(directory, agents) {
|
|
47582
47873
|
const plan = await loadPlan(directory);
|
|
47583
47874
|
if (plan && plan.migration_status !== "migration_failed") {
|
|
@@ -47592,6 +47883,7 @@ async function getStatusData(directory, agents) {
|
|
|
47592
47883
|
}
|
|
47593
47884
|
}
|
|
47594
47885
|
const agentCount2 = Object.keys(agents).length;
|
|
47886
|
+
const metrics2 = getCompactionMetrics();
|
|
47595
47887
|
return {
|
|
47596
47888
|
hasPlan: true,
|
|
47597
47889
|
currentPhase: currentPhase2,
|
|
@@ -47599,11 +47891,15 @@ async function getStatusData(directory, agents) {
|
|
|
47599
47891
|
totalTasks: totalTasks2,
|
|
47600
47892
|
agentCount: agentCount2,
|
|
47601
47893
|
isLegacy: false,
|
|
47602
|
-
turboMode: hasActiveTurboMode()
|
|
47894
|
+
turboMode: hasActiveTurboMode(),
|
|
47895
|
+
contextBudgetPct: swarmState.lastBudgetPct > 0 ? swarmState.lastBudgetPct : null,
|
|
47896
|
+
compactionCount: metrics2.compactionCount,
|
|
47897
|
+
lastSnapshotAt: metrics2.lastSnapshotAt
|
|
47603
47898
|
};
|
|
47604
47899
|
}
|
|
47605
47900
|
const planContent = await readSwarmFileAsync(directory, "plan.md");
|
|
47606
47901
|
if (!planContent) {
|
|
47902
|
+
const metrics2 = getCompactionMetrics();
|
|
47607
47903
|
return {
|
|
47608
47904
|
hasPlan: false,
|
|
47609
47905
|
currentPhase: "Unknown",
|
|
@@ -47611,7 +47907,10 @@ async function getStatusData(directory, agents) {
|
|
|
47611
47907
|
totalTasks: 0,
|
|
47612
47908
|
agentCount: Object.keys(agents).length,
|
|
47613
47909
|
isLegacy: true,
|
|
47614
|
-
turboMode: hasActiveTurboMode()
|
|
47910
|
+
turboMode: hasActiveTurboMode(),
|
|
47911
|
+
contextBudgetPct: swarmState.lastBudgetPct > 0 ? swarmState.lastBudgetPct : null,
|
|
47912
|
+
compactionCount: metrics2.compactionCount,
|
|
47913
|
+
lastSnapshotAt: metrics2.lastSnapshotAt
|
|
47615
47914
|
};
|
|
47616
47915
|
}
|
|
47617
47916
|
const currentPhase = extractCurrentPhase(planContent) || "Unknown";
|
|
@@ -47619,6 +47918,7 @@ async function getStatusData(directory, agents) {
|
|
|
47619
47918
|
const incompleteTasks = (planContent.match(/^- \[ \]/gm) || []).length;
|
|
47620
47919
|
const totalTasks = completedTasks + incompleteTasks;
|
|
47621
47920
|
const agentCount = Object.keys(agents).length;
|
|
47921
|
+
const metrics = getCompactionMetrics();
|
|
47622
47922
|
return {
|
|
47623
47923
|
hasPlan: true,
|
|
47624
47924
|
currentPhase,
|
|
@@ -47626,7 +47926,10 @@ async function getStatusData(directory, agents) {
|
|
|
47626
47926
|
totalTasks,
|
|
47627
47927
|
agentCount,
|
|
47628
47928
|
isLegacy: true,
|
|
47629
|
-
turboMode: hasActiveTurboMode()
|
|
47929
|
+
turboMode: hasActiveTurboMode(),
|
|
47930
|
+
contextBudgetPct: swarmState.lastBudgetPct > 0 ? swarmState.lastBudgetPct : null,
|
|
47931
|
+
compactionCount: metrics.compactionCount,
|
|
47932
|
+
lastSnapshotAt: metrics.lastSnapshotAt
|
|
47630
47933
|
};
|
|
47631
47934
|
}
|
|
47632
47935
|
function formatStatusMarkdown(status) {
|
|
@@ -47640,6 +47943,18 @@ function formatStatusMarkdown(status) {
|
|
|
47640
47943
|
if (status.turboMode) {
|
|
47641
47944
|
lines.push("", `**TURBO MODE**: active`);
|
|
47642
47945
|
}
|
|
47946
|
+
if (status.contextBudgetPct !== null && status.contextBudgetPct > 0) {
|
|
47947
|
+
const pct = status.contextBudgetPct.toFixed(1);
|
|
47948
|
+
const budgetTokens = DEFAULT_CONTEXT_BUDGET_CONFIG.budgetTokens;
|
|
47949
|
+
const est = Math.round(status.contextBudgetPct / 100 * budgetTokens);
|
|
47950
|
+
lines.push("", `**Context**: ${pct}% used (est. ${est.toLocaleString()} / ${budgetTokens.toLocaleString()} tokens)`);
|
|
47951
|
+
if (status.compactionCount > 0) {
|
|
47952
|
+
lines.push(`**Compaction events**: ${status.compactionCount} triggered`);
|
|
47953
|
+
}
|
|
47954
|
+
if (status.lastSnapshotAt) {
|
|
47955
|
+
lines.push(`**Last snapshot**: ${status.lastSnapshotAt}`);
|
|
47956
|
+
}
|
|
47957
|
+
}
|
|
47643
47958
|
return lines.join(`
|
|
47644
47959
|
`);
|
|
47645
47960
|
}
|
|
@@ -47734,6 +48049,132 @@ async function executeWriteRetro(args2, directory) {
|
|
|
47734
48049
|
message: "Invalid task_count: must be a positive integer >= 1"
|
|
47735
48050
|
}, null, 2);
|
|
47736
48051
|
}
|
|
48052
|
+
if (!Number.isInteger(args2.total_tool_calls) || args2.total_tool_calls < 0) {
|
|
48053
|
+
return JSON.stringify({
|
|
48054
|
+
success: false,
|
|
48055
|
+
phase,
|
|
48056
|
+
message: "Invalid total_tool_calls: must be a non-negative integer"
|
|
48057
|
+
}, null, 2);
|
|
48058
|
+
}
|
|
48059
|
+
if (!Number.isInteger(args2.coder_revisions) || args2.coder_revisions < 0) {
|
|
48060
|
+
return JSON.stringify({
|
|
48061
|
+
success: false,
|
|
48062
|
+
phase,
|
|
48063
|
+
message: "Invalid coder_revisions: must be a non-negative integer"
|
|
48064
|
+
}, null, 2);
|
|
48065
|
+
}
|
|
48066
|
+
if (!Number.isInteger(args2.reviewer_rejections) || args2.reviewer_rejections < 0) {
|
|
48067
|
+
return JSON.stringify({
|
|
48068
|
+
success: false,
|
|
48069
|
+
phase,
|
|
48070
|
+
message: "Invalid reviewer_rejections: must be a non-negative integer"
|
|
48071
|
+
}, null, 2);
|
|
48072
|
+
}
|
|
48073
|
+
if (!Number.isInteger(args2.test_failures) || args2.test_failures < 0) {
|
|
48074
|
+
return JSON.stringify({
|
|
48075
|
+
success: false,
|
|
48076
|
+
phase,
|
|
48077
|
+
message: "Invalid test_failures: must be a non-negative integer"
|
|
48078
|
+
}, null, 2);
|
|
48079
|
+
}
|
|
48080
|
+
if (!Number.isInteger(args2.security_findings) || args2.security_findings < 0) {
|
|
48081
|
+
return JSON.stringify({
|
|
48082
|
+
success: false,
|
|
48083
|
+
phase,
|
|
48084
|
+
message: "Invalid security_findings: must be a non-negative integer"
|
|
48085
|
+
}, null, 2);
|
|
48086
|
+
}
|
|
48087
|
+
if (!Number.isInteger(args2.integration_issues) || args2.integration_issues < 0) {
|
|
48088
|
+
return JSON.stringify({
|
|
48089
|
+
success: false,
|
|
48090
|
+
phase,
|
|
48091
|
+
message: "Invalid integration_issues: must be a non-negative integer"
|
|
48092
|
+
}, null, 2);
|
|
48093
|
+
}
|
|
48094
|
+
if (args2.loop_detections !== undefined && (!Number.isInteger(args2.loop_detections) || args2.loop_detections < 0)) {
|
|
48095
|
+
return JSON.stringify({
|
|
48096
|
+
success: false,
|
|
48097
|
+
phase,
|
|
48098
|
+
message: "Invalid loop_detections: must be a non-negative integer"
|
|
48099
|
+
}, null, 2);
|
|
48100
|
+
}
|
|
48101
|
+
if (args2.circuit_breaker_trips !== undefined && (!Number.isInteger(args2.circuit_breaker_trips) || args2.circuit_breaker_trips < 0)) {
|
|
48102
|
+
return JSON.stringify({
|
|
48103
|
+
success: false,
|
|
48104
|
+
phase,
|
|
48105
|
+
message: "Invalid circuit_breaker_trips: must be a non-negative integer"
|
|
48106
|
+
}, null, 2);
|
|
48107
|
+
}
|
|
48108
|
+
if (args2.phase > 99) {
|
|
48109
|
+
return JSON.stringify({
|
|
48110
|
+
success: false,
|
|
48111
|
+
phase,
|
|
48112
|
+
message: "Invalid phase: must be <= 99"
|
|
48113
|
+
}, null, 2);
|
|
48114
|
+
}
|
|
48115
|
+
if (args2.task_count > 9999) {
|
|
48116
|
+
return JSON.stringify({
|
|
48117
|
+
success: false,
|
|
48118
|
+
phase,
|
|
48119
|
+
message: "Invalid task_count: must be <= 9999"
|
|
48120
|
+
}, null, 2);
|
|
48121
|
+
}
|
|
48122
|
+
if (args2.total_tool_calls > 9999) {
|
|
48123
|
+
return JSON.stringify({
|
|
48124
|
+
success: false,
|
|
48125
|
+
phase,
|
|
48126
|
+
message: "Invalid total_tool_calls: must be <= 9999"
|
|
48127
|
+
}, null, 2);
|
|
48128
|
+
}
|
|
48129
|
+
if (args2.coder_revisions > 999) {
|
|
48130
|
+
return JSON.stringify({
|
|
48131
|
+
success: false,
|
|
48132
|
+
phase,
|
|
48133
|
+
message: "Invalid coder_revisions: must be <= 999"
|
|
48134
|
+
}, null, 2);
|
|
48135
|
+
}
|
|
48136
|
+
if (args2.reviewer_rejections > 999) {
|
|
48137
|
+
return JSON.stringify({
|
|
48138
|
+
success: false,
|
|
48139
|
+
phase,
|
|
48140
|
+
message: "Invalid reviewer_rejections: must be <= 999"
|
|
48141
|
+
}, null, 2);
|
|
48142
|
+
}
|
|
48143
|
+
if (args2.loop_detections !== undefined && args2.loop_detections > 9999) {
|
|
48144
|
+
return JSON.stringify({
|
|
48145
|
+
success: false,
|
|
48146
|
+
phase,
|
|
48147
|
+
message: "Invalid loop_detections: must be <= 9999"
|
|
48148
|
+
}, null, 2);
|
|
48149
|
+
}
|
|
48150
|
+
if (args2.circuit_breaker_trips !== undefined && args2.circuit_breaker_trips > 9999) {
|
|
48151
|
+
return JSON.stringify({
|
|
48152
|
+
success: false,
|
|
48153
|
+
phase,
|
|
48154
|
+
message: "Invalid circuit_breaker_trips: must be <= 9999"
|
|
48155
|
+
}, null, 2);
|
|
48156
|
+
}
|
|
48157
|
+
if (args2.test_failures > 9999) {
|
|
48158
|
+
return JSON.stringify({
|
|
48159
|
+
success: false,
|
|
48160
|
+
phase,
|
|
48161
|
+
message: "Invalid test_failures: must be <= 9999"
|
|
48162
|
+
}, null, 2);
|
|
48163
|
+
}
|
|
48164
|
+
if (args2.security_findings > 999) {
|
|
48165
|
+
return JSON.stringify({
|
|
48166
|
+
success: false,
|
|
48167
|
+
phase,
|
|
48168
|
+
message: "Invalid security_findings: must be <= 999"
|
|
48169
|
+
}, null, 2);
|
|
48170
|
+
}
|
|
48171
|
+
if (args2.integration_issues > 999) {
|
|
48172
|
+
return JSON.stringify({
|
|
48173
|
+
success: false,
|
|
48174
|
+
phase,
|
|
48175
|
+
message: "Invalid integration_issues: must be <= 999"
|
|
48176
|
+
}, null, 2);
|
|
48177
|
+
}
|
|
47737
48178
|
const summary = args2.summary;
|
|
47738
48179
|
if (typeof summary !== "string" || summary.trim().length === 0) {
|
|
47739
48180
|
return JSON.stringify({
|
|
@@ -47755,6 +48196,8 @@ async function executeWriteRetro(args2, directory) {
|
|
|
47755
48196
|
total_tool_calls: args2.total_tool_calls,
|
|
47756
48197
|
coder_revisions: args2.coder_revisions,
|
|
47757
48198
|
reviewer_rejections: args2.reviewer_rejections,
|
|
48199
|
+
loop_detections: args2.loop_detections,
|
|
48200
|
+
circuit_breaker_trips: args2.circuit_breaker_trips,
|
|
47758
48201
|
test_failures: args2.test_failures,
|
|
47759
48202
|
security_findings: args2.security_findings,
|
|
47760
48203
|
integration_issues: args2.integration_issues,
|
|
@@ -47784,16 +48227,18 @@ async function executeWriteRetro(args2, directory) {
|
|
|
47784
48227
|
var write_retro = createSwarmTool({
|
|
47785
48228
|
description: "Write a retrospective evidence bundle for a completed phase. " + "Accepts flat retro fields and writes a correctly-wrapped EvidenceBundle to " + ".swarm/evidence/retro-{phase}/evidence.json. " + "Use this instead of manually writing retro JSON to avoid schema validation failures in phase_complete.",
|
|
47786
48229
|
args: {
|
|
47787
|
-
phase: tool.schema.number().int().positive().describe("The phase number being completed (e.g., 1, 2, 3)"),
|
|
48230
|
+
phase: tool.schema.number().int().positive().max(99).describe("The phase number being completed (e.g., 1, 2, 3)"),
|
|
47788
48231
|
summary: tool.schema.string().describe("Human-readable summary of the phase"),
|
|
47789
|
-
task_count: tool.schema.number().int().min(1).describe("Count of tasks completed in this phase"),
|
|
48232
|
+
task_count: tool.schema.number().int().min(1).max(9999).describe("Count of tasks completed in this phase"),
|
|
47790
48233
|
task_complexity: tool.schema.enum(["trivial", "simple", "moderate", "complex"]).describe("Complexity level of the completed tasks"),
|
|
47791
|
-
total_tool_calls: tool.schema.number().int().min(0).describe("Total number of tool calls in this phase"),
|
|
47792
|
-
coder_revisions: tool.schema.number().int().min(0).describe("Number of coder revisions made"),
|
|
47793
|
-
reviewer_rejections: tool.schema.number().int().min(0).describe("Number of reviewer rejections received"),
|
|
47794
|
-
|
|
47795
|
-
|
|
47796
|
-
|
|
48234
|
+
total_tool_calls: tool.schema.number().int().min(0).max(9999).describe("Total number of tool calls in this phase"),
|
|
48235
|
+
coder_revisions: tool.schema.number().int().min(0).max(999).describe("Number of coder revisions made"),
|
|
48236
|
+
reviewer_rejections: tool.schema.number().int().min(0).max(999).describe("Number of reviewer rejections received"),
|
|
48237
|
+
loop_detections: tool.schema.number().int().min(0).max(9999).optional().describe("Number of loop detection events in this phase"),
|
|
48238
|
+
circuit_breaker_trips: tool.schema.number().int().min(0).max(9999).optional().describe("Number of circuit breaker trips in this phase"),
|
|
48239
|
+
test_failures: tool.schema.number().int().min(0).max(9999).describe("Number of test failures encountered"),
|
|
48240
|
+
security_findings: tool.schema.number().int().min(0).max(999).describe("Number of security findings"),
|
|
48241
|
+
integration_issues: tool.schema.number().int().min(0).max(999).describe("Number of integration issues"),
|
|
47797
48242
|
lessons_learned: tool.schema.array(tool.schema.string()).max(5).optional().describe("Key lessons learned from this phase (max 5)"),
|
|
47798
48243
|
top_rejection_reasons: tool.schema.array(tool.schema.string()).optional().describe("Top reasons for reviewer rejections"),
|
|
47799
48244
|
task_id: tool.schema.string().optional().describe("Optional custom task ID (defaults to retro-{phase})"),
|
|
@@ -47810,6 +48255,8 @@ var write_retro = createSwarmTool({
|
|
|
47810
48255
|
total_tool_calls: Number(args2.total_tool_calls),
|
|
47811
48256
|
coder_revisions: Number(args2.coder_revisions),
|
|
47812
48257
|
reviewer_rejections: Number(args2.reviewer_rejections),
|
|
48258
|
+
loop_detections: args2.loop_detections != null ? Number(args2.loop_detections) : undefined,
|
|
48259
|
+
circuit_breaker_trips: args2.circuit_breaker_trips != null ? Number(args2.circuit_breaker_trips) : undefined,
|
|
47813
48260
|
test_failures: Number(args2.test_failures),
|
|
47814
48261
|
security_findings: Number(args2.security_findings),
|
|
47815
48262
|
integration_issues: Number(args2.integration_issues),
|
|
@@ -48112,11 +48559,11 @@ async function doFlush(directory) {
|
|
|
48112
48559
|
const activitySection = renderActivitySection();
|
|
48113
48560
|
const updated = replaceOrAppendSection(existing, "## Agent Activity", activitySection);
|
|
48114
48561
|
const flushedCount = swarmState.pendingEvents;
|
|
48115
|
-
const
|
|
48116
|
-
const tempPath = `${
|
|
48562
|
+
const path28 = `${directory}/.swarm/context.md`;
|
|
48563
|
+
const tempPath = `${path28}.tmp`;
|
|
48117
48564
|
try {
|
|
48118
48565
|
await Bun.write(tempPath, updated);
|
|
48119
|
-
renameSync6(tempPath,
|
|
48566
|
+
renameSync6(tempPath, path28);
|
|
48120
48567
|
} catch (writeError) {
|
|
48121
48568
|
try {
|
|
48122
48569
|
unlinkSync3(tempPath);
|
|
@@ -48686,15 +49133,57 @@ function maskToolOutput(msg, _threshold) {
|
|
|
48686
49133
|
}
|
|
48687
49134
|
// src/hooks/delegation-gate.ts
|
|
48688
49135
|
init_schema();
|
|
48689
|
-
import * as
|
|
48690
|
-
import * as
|
|
49136
|
+
import * as fs16 from "fs";
|
|
49137
|
+
import * as path30 from "path";
|
|
48691
49138
|
|
|
48692
49139
|
// src/hooks/guardrails.ts
|
|
48693
49140
|
init_constants();
|
|
48694
49141
|
init_schema();
|
|
48695
49142
|
init_manager2();
|
|
48696
|
-
import * as
|
|
49143
|
+
import * as path28 from "path";
|
|
48697
49144
|
init_utils();
|
|
49145
|
+
|
|
49146
|
+
// src/hooks/loop-detector.ts
|
|
49147
|
+
function hashDelegation(toolName, args2) {
|
|
49148
|
+
const targetAgent = typeof args2?.subagent_type === "string" ? args2.subagent_type : "unknown";
|
|
49149
|
+
const firstArgKey = args2 != null ? Object.keys(args2)[0] ?? "noargs" : "noargs";
|
|
49150
|
+
return `${toolName}:${targetAgent}:${firstArgKey}`;
|
|
49151
|
+
}
|
|
49152
|
+
function detectLoop(sessionId, toolName, args2) {
|
|
49153
|
+
if (toolName !== "Task") {
|
|
49154
|
+
return { looping: false, count: 0, pattern: "" };
|
|
49155
|
+
}
|
|
49156
|
+
const session = swarmState.agentSessions.get(sessionId);
|
|
49157
|
+
if (!session) {
|
|
49158
|
+
return { looping: false, count: 0, pattern: "" };
|
|
49159
|
+
}
|
|
49160
|
+
if (!session.loopDetectionWindow) {
|
|
49161
|
+
session.loopDetectionWindow = [];
|
|
49162
|
+
}
|
|
49163
|
+
const argsRecord = args2 != null && typeof args2 === "object" && !Array.isArray(args2) ? args2 : undefined;
|
|
49164
|
+
const hash3 = hashDelegation(toolName, argsRecord);
|
|
49165
|
+
const now = Date.now();
|
|
49166
|
+
session.loopDetectionWindow.push({ hash: hash3, timestamp: now });
|
|
49167
|
+
if (session.loopDetectionWindow.length > 10) {
|
|
49168
|
+
session.loopDetectionWindow.shift();
|
|
49169
|
+
}
|
|
49170
|
+
const window2 = session.loopDetectionWindow;
|
|
49171
|
+
let consecutiveCount = 0;
|
|
49172
|
+
for (let i2 = window2.length - 1;i2 >= 0; i2--) {
|
|
49173
|
+
if (window2[i2].hash === hash3) {
|
|
49174
|
+
consecutiveCount++;
|
|
49175
|
+
} else {
|
|
49176
|
+
break;
|
|
49177
|
+
}
|
|
49178
|
+
}
|
|
49179
|
+
return {
|
|
49180
|
+
looping: consecutiveCount >= 3,
|
|
49181
|
+
count: consecutiveCount,
|
|
49182
|
+
pattern: hash3
|
|
49183
|
+
};
|
|
49184
|
+
}
|
|
49185
|
+
|
|
49186
|
+
// src/hooks/guardrails.ts
|
|
48698
49187
|
var storedInputArgs = new Map;
|
|
48699
49188
|
function getStoredInputArgs(callID) {
|
|
48700
49189
|
return storedInputArgs.get(callID);
|
|
@@ -48744,10 +49233,10 @@ function isArchitect(sessionId) {
|
|
|
48744
49233
|
function isOutsideSwarmDir(filePath, directory) {
|
|
48745
49234
|
if (!filePath)
|
|
48746
49235
|
return false;
|
|
48747
|
-
const swarmDir =
|
|
48748
|
-
const resolved =
|
|
48749
|
-
const relative4 =
|
|
48750
|
-
return relative4.startsWith("..") ||
|
|
49236
|
+
const swarmDir = path28.resolve(directory, ".swarm");
|
|
49237
|
+
const resolved = path28.resolve(directory, filePath);
|
|
49238
|
+
const relative4 = path28.relative(swarmDir, resolved);
|
|
49239
|
+
return relative4.startsWith("..") || path28.isAbsolute(relative4);
|
|
48751
49240
|
}
|
|
48752
49241
|
function isSourceCodePath(filePath) {
|
|
48753
49242
|
if (!filePath)
|
|
@@ -48802,7 +49291,10 @@ function isAgentDelegation(toolName, args2) {
|
|
|
48802
49291
|
}
|
|
48803
49292
|
const subagentType = argsObj.subagent_type;
|
|
48804
49293
|
if (typeof subagentType === "string") {
|
|
48805
|
-
return {
|
|
49294
|
+
return {
|
|
49295
|
+
isDelegation: true,
|
|
49296
|
+
targetAgent: stripKnownSwarmPrefix(subagentType)
|
|
49297
|
+
};
|
|
48806
49298
|
}
|
|
48807
49299
|
return { isDelegation: false, targetAgent: null };
|
|
48808
49300
|
}
|
|
@@ -48811,13 +49303,13 @@ function getCurrentTaskId(sessionId) {
|
|
|
48811
49303
|
return session?.currentTaskId ?? `${sessionId}:unknown`;
|
|
48812
49304
|
}
|
|
48813
49305
|
function isInDeclaredScope(filePath, scopeEntries) {
|
|
48814
|
-
const resolvedFile =
|
|
49306
|
+
const resolvedFile = path28.resolve(filePath);
|
|
48815
49307
|
return scopeEntries.some((scope) => {
|
|
48816
|
-
const resolvedScope =
|
|
49308
|
+
const resolvedScope = path28.resolve(scope);
|
|
48817
49309
|
if (resolvedFile === resolvedScope)
|
|
48818
49310
|
return true;
|
|
48819
|
-
const rel =
|
|
48820
|
-
return rel.length > 0 && !rel.startsWith("..") && !
|
|
49311
|
+
const rel = path28.relative(resolvedScope, resolvedFile);
|
|
49312
|
+
return rel.length > 0 && !rel.startsWith("..") && !path28.isAbsolute(rel);
|
|
48821
49313
|
});
|
|
48822
49314
|
}
|
|
48823
49315
|
function createGuardrailsHooks(directoryOrConfig, config3) {
|
|
@@ -48869,13 +49361,44 @@ function createGuardrailsHooks(directoryOrConfig, config3) {
|
|
|
48869
49361
|
}
|
|
48870
49362
|
}
|
|
48871
49363
|
}
|
|
49364
|
+
if (input.tool === "Task") {
|
|
49365
|
+
const loopArgs = output.args;
|
|
49366
|
+
const loopResult = detectLoop(input.sessionID, input.tool, loopArgs);
|
|
49367
|
+
if (loopResult.count >= 5) {
|
|
49368
|
+
throw new Error(`CIRCUIT BREAKER: Delegation loop detected (${loopResult.count} identical patterns). Session paused. Ask the user for guidance.`);
|
|
49369
|
+
} else if (loopResult.count === 3) {
|
|
49370
|
+
const agentName2 = typeof loopArgs?.subagent_type === "string" ? loopArgs.subagent_type : "agent";
|
|
49371
|
+
const loopSession = swarmState.agentSessions.get(input.sessionID);
|
|
49372
|
+
if (loopSession) {
|
|
49373
|
+
loopSession.loopWarningPending = {
|
|
49374
|
+
agent: agentName2,
|
|
49375
|
+
message: `LOOP DETECTED: You have delegated to ${agentName2} with the same pattern 3 times. Change your approach \u2014 try a different agent, different instructions, or escalate to the user.`,
|
|
49376
|
+
timestamp: Date.now()
|
|
49377
|
+
};
|
|
49378
|
+
}
|
|
49379
|
+
}
|
|
49380
|
+
}
|
|
49381
|
+
if (input.tool === "bash" || input.tool === "shell") {
|
|
49382
|
+
const bashArgs = output.args;
|
|
49383
|
+
const cmd = (typeof bashArgs?.command === "string" ? bashArgs.command : "").trim();
|
|
49384
|
+
const testRunnerPrefixPattern = /^(bun\s+test|npm\s+test|npx\s+vitest|bunx\s+vitest)\b/;
|
|
49385
|
+
if (testRunnerPrefixPattern.test(cmd)) {
|
|
49386
|
+
const tokens = cmd.split(/\s+/);
|
|
49387
|
+
const runnerTokenCount = tokens[0] === "npx" || tokens[0] === "bunx" ? 3 : 2;
|
|
49388
|
+
const remainingTokens = tokens.slice(runnerTokenCount);
|
|
49389
|
+
const hasFileArg = remainingTokens.some((token) => token.length > 0 && !token.startsWith("-") && (token.includes("/") || token.includes("\\") || token.endsWith(".ts") || token.endsWith(".js") || token.endsWith(".tsx") || token.endsWith(".jsx") || token.endsWith(".mts") || token.endsWith(".mjs")));
|
|
49390
|
+
if (!hasFileArg) {
|
|
49391
|
+
throw new Error("BLOCKED: Full test suite execution is not allowed in-session. Run a specific test file instead: bun test path/to/file.test.ts");
|
|
49392
|
+
}
|
|
49393
|
+
}
|
|
49394
|
+
}
|
|
48872
49395
|
if (isArchitect(input.sessionID) && isWriteTool(input.tool)) {
|
|
48873
49396
|
const args2 = output.args;
|
|
48874
49397
|
const targetPath = args2?.filePath ?? args2?.path ?? args2?.file ?? args2?.target;
|
|
48875
49398
|
if (typeof targetPath === "string" && targetPath.length > 0) {
|
|
48876
|
-
const resolvedTarget =
|
|
48877
|
-
const planMdPath =
|
|
48878
|
-
const planJsonPath =
|
|
49399
|
+
const resolvedTarget = path28.resolve(directory, targetPath).toLowerCase();
|
|
49400
|
+
const planMdPath = path28.resolve(directory, ".swarm", "plan.md").toLowerCase();
|
|
49401
|
+
const planJsonPath = path28.resolve(directory, ".swarm", "plan.json").toLowerCase();
|
|
48879
49402
|
if (resolvedTarget === planMdPath || resolvedTarget === planJsonPath) {
|
|
48880
49403
|
throw new Error("PLAN STATE VIOLATION: Direct writes to .swarm/plan.md and .swarm/plan.json are blocked. " + "plan.md is auto-regenerated from plan.json by PlanSyncWorker. " + "Use update_task_status() to mark tasks complete, " + "phase_complete() for phase transitions, or " + "save_plan to create/restructure plans.");
|
|
48881
49404
|
}
|
|
@@ -48924,9 +49447,9 @@ function createGuardrailsHooks(directoryOrConfig, config3) {
|
|
|
48924
49447
|
}
|
|
48925
49448
|
}
|
|
48926
49449
|
for (const p of paths) {
|
|
48927
|
-
const resolvedP =
|
|
48928
|
-
const planMdPath =
|
|
48929
|
-
const planJsonPath =
|
|
49450
|
+
const resolvedP = path28.resolve(directory, p);
|
|
49451
|
+
const planMdPath = path28.resolve(directory, ".swarm", "plan.md").toLowerCase();
|
|
49452
|
+
const planJsonPath = path28.resolve(directory, ".swarm", "plan.json").toLowerCase();
|
|
48930
49453
|
if (resolvedP.toLowerCase() === planMdPath || resolvedP.toLowerCase() === planJsonPath) {
|
|
48931
49454
|
throw new Error("PLAN STATE VIOLATION: Direct writes to .swarm/plan.md and .swarm/plan.json are blocked. " + "plan.md is auto-regenerated from plan.json by PlanSyncWorker. " + "Use update_task_status() to mark tasks complete, " + "phase_complete() for phase transitions, or " + "save_plan to create/restructure plans.");
|
|
48932
49455
|
}
|
|
@@ -48946,7 +49469,7 @@ function createGuardrailsHooks(directoryOrConfig, config3) {
|
|
|
48946
49469
|
}
|
|
48947
49470
|
}
|
|
48948
49471
|
}
|
|
48949
|
-
if (typeof targetPath === "string" && targetPath.length > 0 && isOutsideSwarmDir(targetPath, directory) && isSourceCodePath(
|
|
49472
|
+
if (typeof targetPath === "string" && targetPath.length > 0 && isOutsideSwarmDir(targetPath, directory) && isSourceCodePath(path28.relative(directory, path28.resolve(directory, targetPath)))) {
|
|
48950
49473
|
const session2 = swarmState.agentSessions.get(input.sessionID);
|
|
48951
49474
|
if (session2) {
|
|
48952
49475
|
session2.architectWriteCount++;
|
|
@@ -49213,6 +49736,45 @@ function createGuardrailsHooks(directoryOrConfig, config3) {
|
|
|
49213
49736
|
const activeAgent = swarmState.activeAgent.get(sessionId);
|
|
49214
49737
|
const isArchitectSession = activeAgent ? stripKnownSwarmPrefix(activeAgent) === ORCHESTRATOR_NAME : session ? stripKnownSwarmPrefix(session.agentName) === ORCHESTRATOR_NAME : false;
|
|
49215
49738
|
const systemMessages = messages.filter((msg) => msg.info?.role === "system");
|
|
49739
|
+
if (isArchitectSession && session?.loopWarningPending) {
|
|
49740
|
+
const pending = session.loopWarningPending;
|
|
49741
|
+
session.loopWarningPending = undefined;
|
|
49742
|
+
const loopSystemMsg = systemMessages[0];
|
|
49743
|
+
if (loopSystemMsg) {
|
|
49744
|
+
const loopTextPart = (loopSystemMsg.parts ?? []).find((part) => part.type === "text" && typeof part.text === "string");
|
|
49745
|
+
if (loopTextPart && !loopTextPart.text.includes("LOOP DETECTED")) {
|
|
49746
|
+
loopTextPart.text = `[LOOP WARNING]
|
|
49747
|
+
${pending.message}
|
|
49748
|
+
[/LOOP WARNING]
|
|
49749
|
+
|
|
49750
|
+
` + loopTextPart.text;
|
|
49751
|
+
}
|
|
49752
|
+
}
|
|
49753
|
+
}
|
|
49754
|
+
if (isArchitectSession && (session?.pendingAdvisoryMessages?.length ?? 0) > 0) {
|
|
49755
|
+
const advisories = session.pendingAdvisoryMessages;
|
|
49756
|
+
let targetMsg = systemMessages[0];
|
|
49757
|
+
if (!targetMsg) {
|
|
49758
|
+
const newMsg = {
|
|
49759
|
+
info: { role: "system" },
|
|
49760
|
+
parts: [{ type: "text", text: "" }]
|
|
49761
|
+
};
|
|
49762
|
+
messages.unshift(newMsg);
|
|
49763
|
+
targetMsg = newMsg;
|
|
49764
|
+
}
|
|
49765
|
+
const textPart2 = (targetMsg.parts ?? []).find((part) => part.type === "text" && typeof part.text === "string");
|
|
49766
|
+
if (textPart2) {
|
|
49767
|
+
const joined = advisories.join(`
|
|
49768
|
+
---
|
|
49769
|
+
`);
|
|
49770
|
+
textPart2.text = `[ADVISORIES]
|
|
49771
|
+
${joined}
|
|
49772
|
+
[/ADVISORIES]
|
|
49773
|
+
|
|
49774
|
+
` + textPart2.text;
|
|
49775
|
+
}
|
|
49776
|
+
session.pendingAdvisoryMessages = [];
|
|
49777
|
+
}
|
|
49216
49778
|
if (isArchitectSession && session && session.architectWriteCount > session.selfCodingWarnedAtCount) {
|
|
49217
49779
|
let targetSystemMessage = systemMessages[0];
|
|
49218
49780
|
if (!targetSystemMessage) {
|
|
@@ -49440,13 +50002,13 @@ function getEvidenceTaskId(session, directory) {
|
|
|
49440
50002
|
if (typeof directory !== "string" || directory.length === 0) {
|
|
49441
50003
|
return null;
|
|
49442
50004
|
}
|
|
49443
|
-
const resolvedDirectory =
|
|
49444
|
-
const planPath =
|
|
49445
|
-
const resolvedPlanPath =
|
|
49446
|
-
if (!resolvedPlanPath.startsWith(resolvedDirectory +
|
|
50005
|
+
const resolvedDirectory = path30.resolve(directory);
|
|
50006
|
+
const planPath = path30.join(resolvedDirectory, ".swarm", "plan.json");
|
|
50007
|
+
const resolvedPlanPath = path30.resolve(planPath);
|
|
50008
|
+
if (!resolvedPlanPath.startsWith(resolvedDirectory + path30.sep) && resolvedPlanPath !== resolvedDirectory) {
|
|
49447
50009
|
return null;
|
|
49448
50010
|
}
|
|
49449
|
-
const planContent =
|
|
50011
|
+
const planContent = fs16.readFileSync(resolvedPlanPath, "utf-8");
|
|
49450
50012
|
const plan = JSON.parse(planContent);
|
|
49451
50013
|
if (!plan || !Array.isArray(plan.phases)) {
|
|
49452
50014
|
return null;
|
|
@@ -49504,23 +50066,23 @@ function createDelegationGateHook(config3, directory) {
|
|
|
49504
50066
|
if (targetAgent === "test_engineer")
|
|
49505
50067
|
hasTestEngineer = true;
|
|
49506
50068
|
if (targetAgent === "reviewer" && session.taskWorkflowStates) {
|
|
49507
|
-
for (const [taskId,
|
|
49508
|
-
if (
|
|
50069
|
+
for (const [taskId, state2] of session.taskWorkflowStates) {
|
|
50070
|
+
if (state2 === "coder_delegated" || state2 === "pre_check_passed") {
|
|
49509
50071
|
try {
|
|
49510
50072
|
advanceTaskState(session, taskId, "reviewer_run");
|
|
49511
50073
|
} catch (err2) {
|
|
49512
|
-
console.warn(`[delegation-gate] toolAfter: could not advance ${taskId} (${
|
|
50074
|
+
console.warn(`[delegation-gate] toolAfter: could not advance ${taskId} (${state2}) \u2192 reviewer_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
49513
50075
|
}
|
|
49514
50076
|
}
|
|
49515
50077
|
}
|
|
49516
50078
|
}
|
|
49517
50079
|
if (targetAgent === "test_engineer" && session.taskWorkflowStates) {
|
|
49518
|
-
for (const [taskId,
|
|
49519
|
-
if (
|
|
50080
|
+
for (const [taskId, state2] of session.taskWorkflowStates) {
|
|
50081
|
+
if (state2 === "reviewer_run") {
|
|
49520
50082
|
try {
|
|
49521
50083
|
advanceTaskState(session, taskId, "tests_run");
|
|
49522
50084
|
} catch (err2) {
|
|
49523
|
-
console.warn(`[delegation-gate] toolAfter: could not advance ${taskId} (${
|
|
50085
|
+
console.warn(`[delegation-gate] toolAfter: could not advance ${taskId} (${state2}) \u2192 tests_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
49524
50086
|
}
|
|
49525
50087
|
}
|
|
49526
50088
|
}
|
|
@@ -49536,12 +50098,12 @@ function createDelegationGateHook(config3, directory) {
|
|
|
49536
50098
|
if (seedTaskId && !otherSession.taskWorkflowStates.has(seedTaskId)) {
|
|
49537
50099
|
otherSession.taskWorkflowStates.set(seedTaskId, "coder_delegated");
|
|
49538
50100
|
}
|
|
49539
|
-
for (const [taskId,
|
|
49540
|
-
if (
|
|
50101
|
+
for (const [taskId, state2] of otherSession.taskWorkflowStates) {
|
|
50102
|
+
if (state2 === "coder_delegated" || state2 === "pre_check_passed") {
|
|
49541
50103
|
try {
|
|
49542
50104
|
advanceTaskState(otherSession, taskId, "reviewer_run");
|
|
49543
50105
|
} catch (err2) {
|
|
49544
|
-
console.warn(`[delegation-gate] toolAfter cross-session: could not advance ${taskId} (${
|
|
50106
|
+
console.warn(`[delegation-gate] toolAfter cross-session: could not advance ${taskId} (${state2}) \u2192 reviewer_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
49545
50107
|
}
|
|
49546
50108
|
}
|
|
49547
50109
|
}
|
|
@@ -49551,12 +50113,12 @@ function createDelegationGateHook(config3, directory) {
|
|
|
49551
50113
|
if (seedTaskId && !otherSession.taskWorkflowStates.has(seedTaskId)) {
|
|
49552
50114
|
otherSession.taskWorkflowStates.set(seedTaskId, "reviewer_run");
|
|
49553
50115
|
}
|
|
49554
|
-
for (const [taskId,
|
|
49555
|
-
if (
|
|
50116
|
+
for (const [taskId, state2] of otherSession.taskWorkflowStates) {
|
|
50117
|
+
if (state2 === "reviewer_run") {
|
|
49556
50118
|
try {
|
|
49557
50119
|
advanceTaskState(otherSession, taskId, "tests_run");
|
|
49558
50120
|
} catch (err2) {
|
|
49559
|
-
console.warn(`[delegation-gate] toolAfter cross-session: could not advance ${taskId} (${
|
|
50121
|
+
console.warn(`[delegation-gate] toolAfter cross-session: could not advance ${taskId} (${state2}) \u2192 tests_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
49560
50122
|
}
|
|
49561
50123
|
}
|
|
49562
50124
|
}
|
|
@@ -49620,23 +50182,23 @@ function createDelegationGateHook(config3, directory) {
|
|
|
49620
50182
|
session.qaSkipTaskIds = [];
|
|
49621
50183
|
}
|
|
49622
50184
|
if (hasReviewer && session.taskWorkflowStates) {
|
|
49623
|
-
for (const [taskId,
|
|
49624
|
-
if (
|
|
50185
|
+
for (const [taskId, state2] of session.taskWorkflowStates) {
|
|
50186
|
+
if (state2 === "coder_delegated" || state2 === "pre_check_passed") {
|
|
49625
50187
|
try {
|
|
49626
50188
|
advanceTaskState(session, taskId, "reviewer_run");
|
|
49627
50189
|
} catch (err2) {
|
|
49628
|
-
console.warn(`[delegation-gate] fallback: could not advance ${taskId} (${
|
|
50190
|
+
console.warn(`[delegation-gate] fallback: could not advance ${taskId} (${state2}) \u2192 reviewer_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
49629
50191
|
}
|
|
49630
50192
|
}
|
|
49631
50193
|
}
|
|
49632
50194
|
}
|
|
49633
50195
|
if (hasReviewer && hasTestEngineer && session.taskWorkflowStates) {
|
|
49634
|
-
for (const [taskId,
|
|
49635
|
-
if (
|
|
50196
|
+
for (const [taskId, state2] of session.taskWorkflowStates) {
|
|
50197
|
+
if (state2 === "reviewer_run") {
|
|
49636
50198
|
try {
|
|
49637
50199
|
advanceTaskState(session, taskId, "tests_run");
|
|
49638
50200
|
} catch (err2) {
|
|
49639
|
-
console.warn(`[delegation-gate] fallback: could not advance ${taskId} (${
|
|
50201
|
+
console.warn(`[delegation-gate] fallback: could not advance ${taskId} (${state2}) \u2192 tests_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
49640
50202
|
}
|
|
49641
50203
|
}
|
|
49642
50204
|
}
|
|
@@ -49651,12 +50213,12 @@ function createDelegationGateHook(config3, directory) {
|
|
|
49651
50213
|
if (seedTaskId && !otherSession.taskWorkflowStates.has(seedTaskId)) {
|
|
49652
50214
|
otherSession.taskWorkflowStates.set(seedTaskId, "coder_delegated");
|
|
49653
50215
|
}
|
|
49654
|
-
for (const [taskId,
|
|
49655
|
-
if (
|
|
50216
|
+
for (const [taskId, state2] of otherSession.taskWorkflowStates) {
|
|
50217
|
+
if (state2 === "coder_delegated" || state2 === "pre_check_passed") {
|
|
49656
50218
|
try {
|
|
49657
50219
|
advanceTaskState(otherSession, taskId, "reviewer_run");
|
|
49658
50220
|
} catch (err2) {
|
|
49659
|
-
console.warn(`[delegation-gate] fallback cross-session: could not advance ${taskId} (${
|
|
50221
|
+
console.warn(`[delegation-gate] fallback cross-session: could not advance ${taskId} (${state2}) \u2192 reviewer_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
49660
50222
|
}
|
|
49661
50223
|
}
|
|
49662
50224
|
}
|
|
@@ -49672,12 +50234,12 @@ function createDelegationGateHook(config3, directory) {
|
|
|
49672
50234
|
if (seedTaskId && !otherSession.taskWorkflowStates.has(seedTaskId)) {
|
|
49673
50235
|
otherSession.taskWorkflowStates.set(seedTaskId, "reviewer_run");
|
|
49674
50236
|
}
|
|
49675
|
-
for (const [taskId,
|
|
49676
|
-
if (
|
|
50237
|
+
for (const [taskId, state2] of otherSession.taskWorkflowStates) {
|
|
50238
|
+
if (state2 === "reviewer_run") {
|
|
49677
50239
|
try {
|
|
49678
50240
|
advanceTaskState(otherSession, taskId, "tests_run");
|
|
49679
50241
|
} catch (err2) {
|
|
49680
|
-
console.warn(`[delegation-gate] fallback cross-session: could not advance ${taskId} (${
|
|
50242
|
+
console.warn(`[delegation-gate] fallback cross-session: could not advance ${taskId} (${state2}) \u2192 tests_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
49681
50243
|
}
|
|
49682
50244
|
}
|
|
49683
50245
|
}
|
|
@@ -49923,7 +50485,7 @@ ${warningLines.join(`
|
|
|
49923
50485
|
}
|
|
49924
50486
|
// src/hooks/delegation-sanitizer.ts
|
|
49925
50487
|
init_utils2();
|
|
49926
|
-
import * as
|
|
50488
|
+
import * as fs17 from "fs";
|
|
49927
50489
|
var SANITIZATION_PATTERNS = [
|
|
49928
50490
|
/\b\d+(st|nd|rd|th)\s+(attempt|try|time)\b/gi,
|
|
49929
50491
|
/\b(5th|fifth|final|last)\s+attempt\b/gi,
|
|
@@ -49994,7 +50556,7 @@ function createDelegationSanitizerHook(directory) {
|
|
|
49994
50556
|
stripped_patterns: result.stripped,
|
|
49995
50557
|
timestamp: new Date().toISOString()
|
|
49996
50558
|
};
|
|
49997
|
-
|
|
50559
|
+
fs17.appendFileSync(eventsPath, `${JSON.stringify(event)}
|
|
49998
50560
|
`, "utf-8");
|
|
49999
50561
|
} catch {}
|
|
50000
50562
|
}
|
|
@@ -50244,13 +50806,13 @@ init_schema();
|
|
|
50244
50806
|
init_manager();
|
|
50245
50807
|
init_detector();
|
|
50246
50808
|
init_manager2();
|
|
50247
|
-
import * as
|
|
50809
|
+
import * as fs19 from "fs";
|
|
50248
50810
|
|
|
50249
50811
|
// src/services/decision-drift-analyzer.ts
|
|
50250
50812
|
init_utils2();
|
|
50251
50813
|
init_manager2();
|
|
50252
|
-
import * as
|
|
50253
|
-
import * as
|
|
50814
|
+
import * as fs18 from "fs";
|
|
50815
|
+
import * as path31 from "path";
|
|
50254
50816
|
var DEFAULT_DRIFT_CONFIG = {
|
|
50255
50817
|
staleThresholdPhases: 1,
|
|
50256
50818
|
detectContradictions: true,
|
|
@@ -50404,11 +50966,11 @@ async function analyzeDecisionDrift(directory, config3 = {}) {
|
|
|
50404
50966
|
currentPhase = legacyPhase;
|
|
50405
50967
|
}
|
|
50406
50968
|
}
|
|
50407
|
-
const contextPath =
|
|
50969
|
+
const contextPath = path31.join(directory, ".swarm", "context.md");
|
|
50408
50970
|
let contextContent = "";
|
|
50409
50971
|
try {
|
|
50410
|
-
if (
|
|
50411
|
-
contextContent =
|
|
50972
|
+
if (fs18.existsSync(contextPath)) {
|
|
50973
|
+
contextContent = fs18.readFileSync(contextPath, "utf-8");
|
|
50412
50974
|
}
|
|
50413
50975
|
} catch {
|
|
50414
50976
|
return {
|
|
@@ -50516,165 +51078,6 @@ function formatDriftForContext(result) {
|
|
|
50516
51078
|
|
|
50517
51079
|
// src/services/index.ts
|
|
50518
51080
|
init_config_doctor();
|
|
50519
|
-
|
|
50520
|
-
// src/services/context-budget-service.ts
|
|
50521
|
-
init_utils2();
|
|
50522
|
-
function validateDirectory(directory) {
|
|
50523
|
-
if (!directory || directory.trim() === "") {
|
|
50524
|
-
throw new Error("Invalid directory: empty");
|
|
50525
|
-
}
|
|
50526
|
-
if (/\.\.[/\\]/.test(directory)) {
|
|
50527
|
-
throw new Error("Invalid directory: path traversal detected");
|
|
50528
|
-
}
|
|
50529
|
-
if (directory.startsWith("/") || directory.startsWith("\\")) {
|
|
50530
|
-
throw new Error("Invalid directory: absolute path");
|
|
50531
|
-
}
|
|
50532
|
-
if (/^[A-Za-z]:[\\/]/.test(directory)) {
|
|
50533
|
-
throw new Error("Invalid directory: Windows absolute path");
|
|
50534
|
-
}
|
|
50535
|
-
}
|
|
50536
|
-
var COST_PER_1K_TOKENS = 0.003;
|
|
50537
|
-
function estimateTokens2(text) {
|
|
50538
|
-
if (!text || typeof text !== "string") {
|
|
50539
|
-
return 0;
|
|
50540
|
-
}
|
|
50541
|
-
return Math.ceil(text.length / 3.5);
|
|
50542
|
-
}
|
|
50543
|
-
async function readBudgetState(directory) {
|
|
50544
|
-
const content = await readSwarmFileAsync(directory, "session/budget-state.json");
|
|
50545
|
-
if (!content) {
|
|
50546
|
-
return null;
|
|
50547
|
-
}
|
|
50548
|
-
try {
|
|
50549
|
-
return JSON.parse(content);
|
|
50550
|
-
} catch {
|
|
50551
|
-
return null;
|
|
50552
|
-
}
|
|
50553
|
-
}
|
|
50554
|
-
async function writeBudgetState(directory, state) {
|
|
50555
|
-
const resolvedPath = validateSwarmPath(directory, "session/budget-state.json");
|
|
50556
|
-
const content = JSON.stringify(state, null, 2);
|
|
50557
|
-
await Bun.write(resolvedPath, content);
|
|
50558
|
-
}
|
|
50559
|
-
async function countEvents(directory) {
|
|
50560
|
-
const content = await readSwarmFileAsync(directory, "events.jsonl");
|
|
50561
|
-
if (!content) {
|
|
50562
|
-
return 0;
|
|
50563
|
-
}
|
|
50564
|
-
const lines = content.split(`
|
|
50565
|
-
`).filter((line) => line.trim().length > 0);
|
|
50566
|
-
return lines.length;
|
|
50567
|
-
}
|
|
50568
|
-
async function getPlanCursorContent(directory) {
|
|
50569
|
-
const planContent = await readSwarmFileAsync(directory, "plan.md");
|
|
50570
|
-
if (!planContent) {
|
|
50571
|
-
return "";
|
|
50572
|
-
}
|
|
50573
|
-
const lines = planContent.split(`
|
|
50574
|
-
`);
|
|
50575
|
-
const cursorLines = [];
|
|
50576
|
-
let inCurrentSection = false;
|
|
50577
|
-
for (const line of lines) {
|
|
50578
|
-
if (line.includes("in_progress") || line.includes("**Current**")) {
|
|
50579
|
-
inCurrentSection = true;
|
|
50580
|
-
}
|
|
50581
|
-
if (inCurrentSection) {
|
|
50582
|
-
cursorLines.push(line);
|
|
50583
|
-
if (cursorLines.length > 30) {
|
|
50584
|
-
break;
|
|
50585
|
-
}
|
|
50586
|
-
}
|
|
50587
|
-
}
|
|
50588
|
-
return cursorLines.join(`
|
|
50589
|
-
`) || planContent.substring(0, 1000);
|
|
50590
|
-
}
|
|
50591
|
-
async function getContextBudgetReport(directory, assembledSystemPrompt, config3) {
|
|
50592
|
-
validateDirectory(directory);
|
|
50593
|
-
const timestamp = new Date().toISOString();
|
|
50594
|
-
const systemPromptTokens = estimateTokens2(assembledSystemPrompt);
|
|
50595
|
-
const planCursorContent = await getPlanCursorContent(directory);
|
|
50596
|
-
const planCursorTokens = estimateTokens2(planCursorContent);
|
|
50597
|
-
const knowledgeContent = await readSwarmFileAsync(directory, "knowledge.jsonl");
|
|
50598
|
-
const knowledgeTokens = estimateTokens2(knowledgeContent || "");
|
|
50599
|
-
const runMemoryContent = await readSwarmFileAsync(directory, "run-memory.jsonl");
|
|
50600
|
-
const runMemoryTokens = estimateTokens2(runMemoryContent || "");
|
|
50601
|
-
const handoffContent = await readSwarmFileAsync(directory, "handoff.md");
|
|
50602
|
-
const handoffTokens = estimateTokens2(handoffContent || "");
|
|
50603
|
-
const contextMdContent = await readSwarmFileAsync(directory, "context.md");
|
|
50604
|
-
const contextMdTokens = estimateTokens2(contextMdContent || "");
|
|
50605
|
-
const swarmTotalTokens = systemPromptTokens + planCursorTokens + knowledgeTokens + runMemoryTokens + handoffTokens + contextMdTokens;
|
|
50606
|
-
const estimatedTurnCount = await countEvents(directory);
|
|
50607
|
-
const budgetPct = swarmTotalTokens / config3.budgetTokens * 100;
|
|
50608
|
-
let status;
|
|
50609
|
-
let recommendation = null;
|
|
50610
|
-
if (budgetPct < config3.warningPct) {
|
|
50611
|
-
status = "ok";
|
|
50612
|
-
} else if (budgetPct < config3.criticalPct) {
|
|
50613
|
-
status = "warning";
|
|
50614
|
-
recommendation = "Consider wrapping up current phase and running /swarm handoff before starting new work.";
|
|
50615
|
-
} else {
|
|
50616
|
-
status = "critical";
|
|
50617
|
-
recommendation = "Run /swarm handoff and start a new session to avoid cost escalation.";
|
|
50618
|
-
}
|
|
50619
|
-
const estimatedSessionTokens = swarmTotalTokens * Math.max(1, estimatedTurnCount);
|
|
50620
|
-
return {
|
|
50621
|
-
timestamp,
|
|
50622
|
-
systemPromptTokens,
|
|
50623
|
-
planCursorTokens,
|
|
50624
|
-
knowledgeTokens,
|
|
50625
|
-
runMemoryTokens,
|
|
50626
|
-
handoffTokens,
|
|
50627
|
-
contextMdTokens,
|
|
50628
|
-
swarmTotalTokens,
|
|
50629
|
-
estimatedTurnCount,
|
|
50630
|
-
estimatedSessionTokens,
|
|
50631
|
-
budgetPct,
|
|
50632
|
-
status,
|
|
50633
|
-
recommendation
|
|
50634
|
-
};
|
|
50635
|
-
}
|
|
50636
|
-
async function formatBudgetWarning(report, directory, config3) {
|
|
50637
|
-
validateDirectory(directory);
|
|
50638
|
-
if (report.status === "ok") {
|
|
50639
|
-
return null;
|
|
50640
|
-
}
|
|
50641
|
-
if (!directory || directory.trim() === "") {
|
|
50642
|
-
return formatWarningMessage(report);
|
|
50643
|
-
}
|
|
50644
|
-
const budgetState = await readBudgetState(directory);
|
|
50645
|
-
const state = budgetState || {
|
|
50646
|
-
warningFiredAtTurn: null,
|
|
50647
|
-
criticalFiredAtTurn: null,
|
|
50648
|
-
lastInjectedAtTurn: null
|
|
50649
|
-
};
|
|
50650
|
-
const currentTurn = report.estimatedTurnCount;
|
|
50651
|
-
if (report.status === "warning") {
|
|
50652
|
-
if (config3.warningMode === "once" && state.warningFiredAtTurn !== null) {
|
|
50653
|
-
return null;
|
|
50654
|
-
}
|
|
50655
|
-
if (config3.warningMode === "interval" && state.warningFiredAtTurn !== null && currentTurn - state.warningFiredAtTurn < config3.warningIntervalTurns) {
|
|
50656
|
-
return null;
|
|
50657
|
-
}
|
|
50658
|
-
state.warningFiredAtTurn = currentTurn;
|
|
50659
|
-
state.lastInjectedAtTurn = currentTurn;
|
|
50660
|
-
await writeBudgetState(directory, state);
|
|
50661
|
-
} else if (report.status === "critical") {
|
|
50662
|
-
state.criticalFiredAtTurn = currentTurn;
|
|
50663
|
-
state.lastInjectedAtTurn = currentTurn;
|
|
50664
|
-
}
|
|
50665
|
-
return formatWarningMessage(report);
|
|
50666
|
-
}
|
|
50667
|
-
function formatWarningMessage(report) {
|
|
50668
|
-
const budgetPctStr = report.budgetPct.toFixed(1);
|
|
50669
|
-
const tokensPerTurn = report.swarmTotalTokens.toLocaleString();
|
|
50670
|
-
if (report.status === "warning") {
|
|
50671
|
-
return `[CONTEXT BUDGET: ${budgetPctStr}% \u2014 swarm injecting ~${tokensPerTurn} tokens/turn. Consider wrapping current phase and running /swarm handoff before starting new work.]`;
|
|
50672
|
-
}
|
|
50673
|
-
const costPerTurn = (report.swarmTotalTokens / 1000 * COST_PER_1K_TOKENS).toFixed(3);
|
|
50674
|
-
return `[CONTEXT BUDGET: ${budgetPctStr}% CRITICAL \u2014 swarm injecting ~${tokensPerTurn} tokens/turn. Run /swarm handoff and start a new session to avoid cost escalation. Estimated session cost scaling: ~$${costPerTurn}/turn at current context size.]`;
|
|
50675
|
-
}
|
|
50676
|
-
|
|
50677
|
-
// src/services/index.ts
|
|
50678
51081
|
init_evidence_summary_service();
|
|
50679
51082
|
init_preflight_integration();
|
|
50680
51083
|
init_preflight_service();
|
|
@@ -51061,11 +51464,11 @@ function createSystemEnhancerHook(config3, directory) {
|
|
|
51061
51464
|
if (handoffContent) {
|
|
51062
51465
|
const handoffPath = validateSwarmPath(directory, "handoff.md");
|
|
51063
51466
|
const consumedPath = validateSwarmPath(directory, "handoff-consumed.md");
|
|
51064
|
-
if (
|
|
51467
|
+
if (fs19.existsSync(consumedPath)) {
|
|
51065
51468
|
warn("Duplicate handoff detected: handoff-consumed.md already exists");
|
|
51066
|
-
|
|
51469
|
+
fs19.unlinkSync(consumedPath);
|
|
51067
51470
|
}
|
|
51068
|
-
|
|
51471
|
+
fs19.renameSync(handoffPath, consumedPath);
|
|
51069
51472
|
const handoffBlock = `## HANDOFF \u2014 Resuming from model switch
|
|
51070
51473
|
The previous model's session ended. Here is your starting context:
|
|
51071
51474
|
|
|
@@ -51261,6 +51664,7 @@ ${handoffBlock}`);
|
|
|
51261
51664
|
const assembledSystemPrompt = output.system.join(`
|
|
51262
51665
|
`);
|
|
51263
51666
|
const budgetReport = await getContextBudgetReport(directory, assembledSystemPrompt, contextBudgetConfig);
|
|
51667
|
+
swarmState.lastBudgetPct = budgetReport.budgetPct;
|
|
51264
51668
|
const budgetWarning = await formatBudgetWarning(budgetReport, directory, contextBudgetConfig);
|
|
51265
51669
|
if (budgetWarning) {
|
|
51266
51670
|
const sessionId_cb = _input.sessionID;
|
|
@@ -51344,11 +51748,11 @@ ${budgetWarning}`);
|
|
|
51344
51748
|
if (handoffContent) {
|
|
51345
51749
|
const handoffPath = validateSwarmPath(directory, "handoff.md");
|
|
51346
51750
|
const consumedPath = validateSwarmPath(directory, "handoff-consumed.md");
|
|
51347
|
-
if (
|
|
51751
|
+
if (fs19.existsSync(consumedPath)) {
|
|
51348
51752
|
warn("Duplicate handoff detected: handoff-consumed.md already exists");
|
|
51349
|
-
|
|
51753
|
+
fs19.unlinkSync(consumedPath);
|
|
51350
51754
|
}
|
|
51351
|
-
|
|
51755
|
+
fs19.renameSync(handoffPath, consumedPath);
|
|
51352
51756
|
const handoffBlock = `## HANDOFF \u2014 Resuming from model switch
|
|
51353
51757
|
The previous model's session ended. Here is your starting context:
|
|
51354
51758
|
|
|
@@ -51662,6 +52066,7 @@ ${handoffBlock}`;
|
|
|
51662
52066
|
const assembledSystemPrompt_b = output.system.join(`
|
|
51663
52067
|
`);
|
|
51664
52068
|
const budgetReport_b = await getContextBudgetReport(directory, assembledSystemPrompt_b, contextBudgetConfig_b);
|
|
52069
|
+
swarmState.lastBudgetPct = budgetReport_b.budgetPct;
|
|
51665
52070
|
const budgetWarning_b = await formatBudgetWarning(budgetReport_b, directory, contextBudgetConfig_b);
|
|
51666
52071
|
if (budgetWarning_b) {
|
|
51667
52072
|
const sessionId_cb_b = _input.sessionID;
|
|
@@ -52024,10 +52429,92 @@ function createDarkMatterDetectorHook(directory) {
|
|
|
52024
52429
|
return safeHook(hook);
|
|
52025
52430
|
}
|
|
52026
52431
|
|
|
52432
|
+
// src/hooks/incremental-verify.ts
|
|
52433
|
+
import * as fs20 from "fs";
|
|
52434
|
+
import * as path32 from "path";
|
|
52435
|
+
function detectTypecheckCommand(projectDir) {
|
|
52436
|
+
const pkgPath = path32.join(projectDir, "package.json");
|
|
52437
|
+
if (!fs20.existsSync(pkgPath))
|
|
52438
|
+
return null;
|
|
52439
|
+
try {
|
|
52440
|
+
const pkg = JSON.parse(fs20.readFileSync(pkgPath, "utf8"));
|
|
52441
|
+
const scripts = pkg.scripts;
|
|
52442
|
+
if (scripts?.typecheck)
|
|
52443
|
+
return ["bun", "run", "typecheck"];
|
|
52444
|
+
if (scripts?.["type-check"])
|
|
52445
|
+
return ["bun", "run", "type-check"];
|
|
52446
|
+
const deps = {
|
|
52447
|
+
...pkg.dependencies,
|
|
52448
|
+
...pkg.devDependencies
|
|
52449
|
+
};
|
|
52450
|
+
if (!deps?.typescript && !fs20.existsSync(path32.join(projectDir, "tsconfig.json"))) {
|
|
52451
|
+
return null;
|
|
52452
|
+
}
|
|
52453
|
+
return ["npx", "tsc", "--noEmit"];
|
|
52454
|
+
} catch {
|
|
52455
|
+
return null;
|
|
52456
|
+
}
|
|
52457
|
+
}
|
|
52458
|
+
async function runWithTimeout(command, cwd, timeoutMs) {
|
|
52459
|
+
try {
|
|
52460
|
+
const proc = Bun.spawn(command, {
|
|
52461
|
+
cwd,
|
|
52462
|
+
stdout: "pipe",
|
|
52463
|
+
stderr: "pipe"
|
|
52464
|
+
});
|
|
52465
|
+
const timeoutHandle = setTimeout(() => {
|
|
52466
|
+
try {
|
|
52467
|
+
proc.kill();
|
|
52468
|
+
} catch {}
|
|
52469
|
+
}, timeoutMs);
|
|
52470
|
+
try {
|
|
52471
|
+
const [exitCode, stderr] = await Promise.all([
|
|
52472
|
+
proc.exited,
|
|
52473
|
+
new Response(proc.stderr).text()
|
|
52474
|
+
]);
|
|
52475
|
+
return { exitCode, stderr };
|
|
52476
|
+
} finally {
|
|
52477
|
+
clearTimeout(timeoutHandle);
|
|
52478
|
+
}
|
|
52479
|
+
} catch {
|
|
52480
|
+
return null;
|
|
52481
|
+
}
|
|
52482
|
+
}
|
|
52483
|
+
function createIncrementalVerifyHook(config3, projectDir, injectMessage) {
|
|
52484
|
+
return {
|
|
52485
|
+
toolAfter: async (input, output) => {
|
|
52486
|
+
if (!config3.enabled)
|
|
52487
|
+
return;
|
|
52488
|
+
if (input.tool !== "Task")
|
|
52489
|
+
return;
|
|
52490
|
+
const args2 = input.args ?? output.args;
|
|
52491
|
+
const subagentType = typeof args2?.subagent_type === "string" ? args2.subagent_type : "";
|
|
52492
|
+
const agentName = subagentType.replace(/^[^_]+_/, "");
|
|
52493
|
+
if (!config3.triggerAgents.includes(agentName) && !config3.triggerAgents.includes(subagentType)) {
|
|
52494
|
+
return;
|
|
52495
|
+
}
|
|
52496
|
+
const command = config3.command != null ? config3.command.split(" ") : detectTypecheckCommand(projectDir);
|
|
52497
|
+
if (!command)
|
|
52498
|
+
return;
|
|
52499
|
+
const result = await runWithTimeout(command, projectDir, config3.timeoutMs);
|
|
52500
|
+
if (result === null) {
|
|
52501
|
+
return;
|
|
52502
|
+
}
|
|
52503
|
+
if (result.exitCode === 0) {
|
|
52504
|
+
injectMessage(input.sessionID, "POST-CODER CHECK PASSED: No type errors.");
|
|
52505
|
+
} else {
|
|
52506
|
+
const errorSummary = result.stderr.slice(0, 800);
|
|
52507
|
+
injectMessage(input.sessionID, `POST-CODER CHECK FAILED: Type errors detected after coder delegation. Address these before proceeding.
|
|
52508
|
+
${errorSummary}`);
|
|
52509
|
+
}
|
|
52510
|
+
}
|
|
52511
|
+
};
|
|
52512
|
+
}
|
|
52513
|
+
|
|
52027
52514
|
// src/hooks/knowledge-reader.ts
|
|
52028
|
-
import { existsSync as
|
|
52515
|
+
import { existsSync as existsSync19 } from "fs";
|
|
52029
52516
|
import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
|
|
52030
|
-
import * as
|
|
52517
|
+
import * as path33 from "path";
|
|
52031
52518
|
var JACCARD_THRESHOLD = 0.6;
|
|
52032
52519
|
var HIVE_TIER_BOOST = 0.05;
|
|
52033
52520
|
var SAME_PROJECT_PENALTY = -0.05;
|
|
@@ -52075,15 +52562,15 @@ function inferCategoriesFromPhase(phaseDescription) {
|
|
|
52075
52562
|
return ["process", "tooling"];
|
|
52076
52563
|
}
|
|
52077
52564
|
async function recordLessonsShown(directory, lessonIds, currentPhase) {
|
|
52078
|
-
const shownFile =
|
|
52565
|
+
const shownFile = path33.join(directory, ".swarm", ".knowledge-shown.json");
|
|
52079
52566
|
try {
|
|
52080
52567
|
let shownData = {};
|
|
52081
|
-
if (
|
|
52568
|
+
if (existsSync19(shownFile)) {
|
|
52082
52569
|
const content = await readFile5(shownFile, "utf-8");
|
|
52083
52570
|
shownData = JSON.parse(content);
|
|
52084
52571
|
}
|
|
52085
52572
|
shownData[currentPhase] = lessonIds;
|
|
52086
|
-
await mkdir4(
|
|
52573
|
+
await mkdir4(path33.dirname(shownFile), { recursive: true });
|
|
52087
52574
|
await writeFile4(shownFile, JSON.stringify(shownData, null, 2), "utf-8");
|
|
52088
52575
|
} catch {
|
|
52089
52576
|
console.warn("[swarm] Knowledge: failed to record shown lessons");
|
|
@@ -52178,9 +52665,9 @@ async function readMergedKnowledge(directory, config3, context) {
|
|
|
52178
52665
|
return topN;
|
|
52179
52666
|
}
|
|
52180
52667
|
async function updateRetrievalOutcome(directory, phaseInfo, phaseSucceeded) {
|
|
52181
|
-
const shownFile =
|
|
52668
|
+
const shownFile = path33.join(directory, ".swarm", ".knowledge-shown.json");
|
|
52182
52669
|
try {
|
|
52183
|
-
if (!
|
|
52670
|
+
if (!existsSync19(shownFile)) {
|
|
52184
52671
|
return;
|
|
52185
52672
|
}
|
|
52186
52673
|
const content = await readFile5(shownFile, "utf-8");
|
|
@@ -52650,12 +53137,12 @@ Use this data to avoid repeating known failure patterns.`;
|
|
|
52650
53137
|
// src/hooks/curator-drift.ts
|
|
52651
53138
|
init_event_bus();
|
|
52652
53139
|
init_utils2();
|
|
52653
|
-
import * as
|
|
52654
|
-
import * as
|
|
53140
|
+
import * as fs21 from "fs";
|
|
53141
|
+
import * as path34 from "path";
|
|
52655
53142
|
var DRIFT_REPORT_PREFIX = "drift-report-phase-";
|
|
52656
53143
|
async function readPriorDriftReports(directory) {
|
|
52657
|
-
const swarmDir =
|
|
52658
|
-
const entries = await
|
|
53144
|
+
const swarmDir = path34.join(directory, ".swarm");
|
|
53145
|
+
const entries = await fs21.promises.readdir(swarmDir).catch(() => null);
|
|
52659
53146
|
if (entries === null)
|
|
52660
53147
|
return [];
|
|
52661
53148
|
const reportFiles = entries.filter((name2) => name2.startsWith(DRIFT_REPORT_PREFIX) && name2.endsWith(".json")).sort();
|
|
@@ -52681,10 +53168,10 @@ async function readPriorDriftReports(directory) {
|
|
|
52681
53168
|
async function writeDriftReport(directory, report) {
|
|
52682
53169
|
const filename = `${DRIFT_REPORT_PREFIX}${report.phase}.json`;
|
|
52683
53170
|
const filePath = validateSwarmPath(directory, filename);
|
|
52684
|
-
const swarmDir =
|
|
52685
|
-
await
|
|
53171
|
+
const swarmDir = path34.dirname(filePath);
|
|
53172
|
+
await fs21.promises.mkdir(swarmDir, { recursive: true });
|
|
52686
53173
|
try {
|
|
52687
|
-
await
|
|
53174
|
+
await fs21.promises.writeFile(filePath, JSON.stringify(report, null, 2), "utf-8");
|
|
52688
53175
|
} catch (err2) {
|
|
52689
53176
|
throw new Error(`[curator-drift] Failed to write drift report to ${filePath}: ${String(err2)}`);
|
|
52690
53177
|
}
|
|
@@ -52934,9 +53421,143 @@ ${cachedInjectionText}`;
|
|
|
52934
53421
|
});
|
|
52935
53422
|
}
|
|
52936
53423
|
|
|
53424
|
+
// src/hooks/slop-detector.ts
|
|
53425
|
+
var WRITE_EDIT_TOOLS = new Set([
|
|
53426
|
+
"write",
|
|
53427
|
+
"edit",
|
|
53428
|
+
"apply_patch",
|
|
53429
|
+
"create_file"
|
|
53430
|
+
]);
|
|
53431
|
+
function countMatches(text, pattern) {
|
|
53432
|
+
return (text.match(pattern) ?? []).length;
|
|
53433
|
+
}
|
|
53434
|
+
function checkAbstractionBloat(content, threshold) {
|
|
53435
|
+
const newClasses = countMatches(content, /^\+.*\bclass\s+\w+/gm);
|
|
53436
|
+
if (newClasses >= threshold) {
|
|
53437
|
+
return {
|
|
53438
|
+
type: "abstraction_bloat",
|
|
53439
|
+
detail: `${newClasses} new class declarations added (threshold: ${threshold}). Consider whether all abstractions are necessary.`
|
|
53440
|
+
};
|
|
53441
|
+
}
|
|
53442
|
+
return null;
|
|
53443
|
+
}
|
|
53444
|
+
function checkCommentStrip(content, threshold) {
|
|
53445
|
+
const removedComments = countMatches(content, /^-\s*\/[/*]/gm);
|
|
53446
|
+
const addedComments = countMatches(content, /^\+\s*\/[/*]/gm);
|
|
53447
|
+
if (removedComments >= threshold && addedComments === 0) {
|
|
53448
|
+
return {
|
|
53449
|
+
type: "comment_strip",
|
|
53450
|
+
detail: `${removedComments} comment lines removed and 0 added. Verify comments were not documenting important behaviour.`
|
|
53451
|
+
};
|
|
53452
|
+
}
|
|
53453
|
+
return null;
|
|
53454
|
+
}
|
|
53455
|
+
function checkBoilerplateExplosion(content, taskDescription, threshold) {
|
|
53456
|
+
const addedLines = countMatches(content, /^\+[^+]/gm);
|
|
53457
|
+
const isSmallTask = /\b(fix|patch|update|tweak|adjust|correct|remove|rename|change)\b/i.test(taskDescription);
|
|
53458
|
+
if (isSmallTask && addedLines >= threshold) {
|
|
53459
|
+
return {
|
|
53460
|
+
type: "boilerplate_explosion",
|
|
53461
|
+
detail: `${addedLines} lines added for a "${taskDescription.slice(0, 40)}" task (threshold: ${threshold}). Review for scope creep.`
|
|
53462
|
+
};
|
|
53463
|
+
}
|
|
53464
|
+
return null;
|
|
53465
|
+
}
|
|
53466
|
+
async function checkDeadExports(content, projectDir, startTime) {
|
|
53467
|
+
const exportMatches = content.matchAll(/^(?:export)\s+(?:function|class|const|type|interface)\s+(\w{3,})/gm);
|
|
53468
|
+
const newExports = [];
|
|
53469
|
+
for (const match of exportMatches) {
|
|
53470
|
+
if (match[1])
|
|
53471
|
+
newExports.push(match[1]);
|
|
53472
|
+
}
|
|
53473
|
+
if (newExports.length === 0)
|
|
53474
|
+
return null;
|
|
53475
|
+
const deadExports = [];
|
|
53476
|
+
for (const name2 of newExports) {
|
|
53477
|
+
if (Date.now() - startTime > 480)
|
|
53478
|
+
break;
|
|
53479
|
+
try {
|
|
53480
|
+
const importPattern = new RegExp(`\\bimport\\b[^;]*\\b${name2}\\b`, "g");
|
|
53481
|
+
const glob = new Bun.Glob(`src/**/*.ts`);
|
|
53482
|
+
let found = false;
|
|
53483
|
+
for await (const file3 of glob.scan(projectDir)) {
|
|
53484
|
+
if (found || Date.now() - startTime > 480)
|
|
53485
|
+
break;
|
|
53486
|
+
try {
|
|
53487
|
+
const text = await Bun.file(`${projectDir}/${file3}`).text();
|
|
53488
|
+
if (importPattern.test(text))
|
|
53489
|
+
found = true;
|
|
53490
|
+
importPattern.lastIndex = 0;
|
|
53491
|
+
} catch {}
|
|
53492
|
+
}
|
|
53493
|
+
if (!found)
|
|
53494
|
+
deadExports.push(name2);
|
|
53495
|
+
} catch {}
|
|
53496
|
+
}
|
|
53497
|
+
if (deadExports.length === 0)
|
|
53498
|
+
return null;
|
|
53499
|
+
return {
|
|
53500
|
+
type: "dead_export",
|
|
53501
|
+
detail: `New exports not found in any import: ${deadExports.slice(0, 3).join(", ")}. Verify these are intentionally exported.`
|
|
53502
|
+
};
|
|
53503
|
+
}
|
|
53504
|
+
function createSlopDetectorHook(config3, projectDir, injectSystemMessage) {
|
|
53505
|
+
return {
|
|
53506
|
+
toolAfter: async (input, output) => {
|
|
53507
|
+
if (!config3.enabled)
|
|
53508
|
+
return;
|
|
53509
|
+
if (!WRITE_EDIT_TOOLS.has(input.tool.toLowerCase()))
|
|
53510
|
+
return;
|
|
53511
|
+
const args2 = output.args;
|
|
53512
|
+
const content = (() => {
|
|
53513
|
+
if (typeof args2?.content === "string")
|
|
53514
|
+
return args2.content;
|
|
53515
|
+
if (typeof args2?.newString === "string")
|
|
53516
|
+
return args2.newString;
|
|
53517
|
+
if (typeof args2?.patch === "string")
|
|
53518
|
+
return args2.patch;
|
|
53519
|
+
if (typeof args2?.file_text === "string")
|
|
53520
|
+
return args2.file_text;
|
|
53521
|
+
return "";
|
|
53522
|
+
})();
|
|
53523
|
+
if (!content || content.length < 10)
|
|
53524
|
+
return;
|
|
53525
|
+
const taskDescription = typeof args2?.description === "string" ? args2.description : typeof args2?.task === "string" ? args2.task : "";
|
|
53526
|
+
const startTime = Date.now();
|
|
53527
|
+
const findings = [];
|
|
53528
|
+
try {
|
|
53529
|
+
const bloat = checkAbstractionBloat(content, config3.classThreshold);
|
|
53530
|
+
if (bloat)
|
|
53531
|
+
findings.push(bloat);
|
|
53532
|
+
const strip = checkCommentStrip(content, config3.commentStripThreshold);
|
|
53533
|
+
if (strip)
|
|
53534
|
+
findings.push(strip);
|
|
53535
|
+
const explosion = checkBoilerplateExplosion(content, taskDescription, config3.diffLineThreshold);
|
|
53536
|
+
if (explosion)
|
|
53537
|
+
findings.push(explosion);
|
|
53538
|
+
} catch {}
|
|
53539
|
+
if (Date.now() - startTime < 400) {
|
|
53540
|
+
try {
|
|
53541
|
+
const dead = await checkDeadExports(content, projectDir, startTime);
|
|
53542
|
+
if (dead)
|
|
53543
|
+
findings.push(dead);
|
|
53544
|
+
} catch {}
|
|
53545
|
+
}
|
|
53546
|
+
if (findings.length === 0)
|
|
53547
|
+
return;
|
|
53548
|
+
const findingText = findings.map((f) => ` \u2022 ${f.type}: ${f.detail}`).join(`
|
|
53549
|
+
`);
|
|
53550
|
+
const message = `SLOP CHECK: ${findings.length} potential issue(s) detected after ${input.tool}:
|
|
53551
|
+
${findingText}
|
|
53552
|
+
Review before proceeding.`;
|
|
53553
|
+
injectSystemMessage(input.sessionID, message);
|
|
53554
|
+
}
|
|
53555
|
+
};
|
|
53556
|
+
}
|
|
53557
|
+
|
|
52937
53558
|
// src/hooks/steering-consumed.ts
|
|
52938
53559
|
init_utils2();
|
|
52939
|
-
import * as
|
|
53560
|
+
import * as fs22 from "fs";
|
|
52940
53561
|
function recordSteeringConsumed(directory, directiveId) {
|
|
52941
53562
|
try {
|
|
52942
53563
|
const eventsPath = validateSwarmPath(directory, "events.jsonl");
|
|
@@ -52945,7 +53566,7 @@ function recordSteeringConsumed(directory, directiveId) {
|
|
|
52945
53566
|
directiveId,
|
|
52946
53567
|
timestamp: new Date().toISOString()
|
|
52947
53568
|
};
|
|
52948
|
-
|
|
53569
|
+
fs22.appendFileSync(eventsPath, `${JSON.stringify(event)}
|
|
52949
53570
|
`, "utf-8");
|
|
52950
53571
|
} catch {}
|
|
52951
53572
|
}
|
|
@@ -52990,7 +53611,7 @@ init_config_doctor();
|
|
|
52990
53611
|
|
|
52991
53612
|
// src/session/snapshot-reader.ts
|
|
52992
53613
|
init_utils2();
|
|
52993
|
-
import
|
|
53614
|
+
import path35 from "path";
|
|
52994
53615
|
var VALID_TASK_WORKFLOW_STATES = [
|
|
52995
53616
|
"idle",
|
|
52996
53617
|
"coder_delegated",
|
|
@@ -53115,7 +53736,7 @@ function rehydrateState(snapshot) {
|
|
|
53115
53736
|
async function reconcileTaskStatesFromPlan(directory) {
|
|
53116
53737
|
let raw;
|
|
53117
53738
|
try {
|
|
53118
|
-
raw = await Bun.file(
|
|
53739
|
+
raw = await Bun.file(path35.join(directory, ".swarm/plan.json")).text();
|
|
53119
53740
|
} catch {
|
|
53120
53741
|
return;
|
|
53121
53742
|
}
|
|
@@ -53337,8 +53958,8 @@ var build_check = createSwarmTool({
|
|
|
53337
53958
|
// src/tools/check-gate-status.ts
|
|
53338
53959
|
init_dist();
|
|
53339
53960
|
init_create_tool();
|
|
53340
|
-
import * as
|
|
53341
|
-
import * as
|
|
53961
|
+
import * as fs23 from "fs";
|
|
53962
|
+
import * as path36 from "path";
|
|
53342
53963
|
var EVIDENCE_DIR = ".swarm/evidence";
|
|
53343
53964
|
var TASK_ID_PATTERN2 = /^\d+\.\d+(\.\d+)*$/;
|
|
53344
53965
|
function isValidTaskId3(taskId) {
|
|
@@ -53355,18 +53976,18 @@ function isValidTaskId3(taskId) {
|
|
|
53355
53976
|
return TASK_ID_PATTERN2.test(taskId);
|
|
53356
53977
|
}
|
|
53357
53978
|
function isPathWithinSwarm(filePath, workspaceRoot) {
|
|
53358
|
-
const normalizedWorkspace =
|
|
53359
|
-
const swarmPath =
|
|
53360
|
-
const normalizedPath =
|
|
53979
|
+
const normalizedWorkspace = path36.resolve(workspaceRoot);
|
|
53980
|
+
const swarmPath = path36.join(normalizedWorkspace, ".swarm", "evidence");
|
|
53981
|
+
const normalizedPath = path36.resolve(filePath);
|
|
53361
53982
|
return normalizedPath.startsWith(swarmPath);
|
|
53362
53983
|
}
|
|
53363
53984
|
function readEvidenceFile(evidencePath) {
|
|
53364
|
-
if (!
|
|
53985
|
+
if (!fs23.existsSync(evidencePath)) {
|
|
53365
53986
|
return null;
|
|
53366
53987
|
}
|
|
53367
53988
|
let content;
|
|
53368
53989
|
try {
|
|
53369
|
-
content =
|
|
53990
|
+
content = fs23.readFileSync(evidencePath, "utf-8");
|
|
53370
53991
|
} catch {
|
|
53371
53992
|
return null;
|
|
53372
53993
|
}
|
|
@@ -53418,7 +54039,7 @@ var check_gate_status = createSwarmTool({
|
|
|
53418
54039
|
};
|
|
53419
54040
|
return JSON.stringify(errorResult, null, 2);
|
|
53420
54041
|
}
|
|
53421
|
-
const evidencePath =
|
|
54042
|
+
const evidencePath = path36.join(directory, EVIDENCE_DIR, `${taskIdInput}.json`);
|
|
53422
54043
|
if (!isPathWithinSwarm(evidencePath, directory)) {
|
|
53423
54044
|
const errorResult = {
|
|
53424
54045
|
taskId: taskIdInput,
|
|
@@ -53478,8 +54099,8 @@ var check_gate_status = createSwarmTool({
|
|
|
53478
54099
|
init_tool();
|
|
53479
54100
|
init_create_tool();
|
|
53480
54101
|
import { spawnSync } from "child_process";
|
|
53481
|
-
import * as
|
|
53482
|
-
import * as
|
|
54102
|
+
import * as fs24 from "fs";
|
|
54103
|
+
import * as path37 from "path";
|
|
53483
54104
|
var CHECKPOINT_LOG_PATH = ".swarm/checkpoints.json";
|
|
53484
54105
|
var MAX_LABEL_LENGTH = 100;
|
|
53485
54106
|
var GIT_TIMEOUT_MS = 30000;
|
|
@@ -53530,13 +54151,13 @@ function validateLabel(label) {
|
|
|
53530
54151
|
return null;
|
|
53531
54152
|
}
|
|
53532
54153
|
function getCheckpointLogPath(directory) {
|
|
53533
|
-
return
|
|
54154
|
+
return path37.join(directory, CHECKPOINT_LOG_PATH);
|
|
53534
54155
|
}
|
|
53535
54156
|
function readCheckpointLog(directory) {
|
|
53536
54157
|
const logPath = getCheckpointLogPath(directory);
|
|
53537
54158
|
try {
|
|
53538
|
-
if (
|
|
53539
|
-
const content =
|
|
54159
|
+
if (fs24.existsSync(logPath)) {
|
|
54160
|
+
const content = fs24.readFileSync(logPath, "utf-8");
|
|
53540
54161
|
const parsed = JSON.parse(content);
|
|
53541
54162
|
if (!parsed.checkpoints || !Array.isArray(parsed.checkpoints)) {
|
|
53542
54163
|
return { version: 1, checkpoints: [] };
|
|
@@ -53548,13 +54169,13 @@ function readCheckpointLog(directory) {
|
|
|
53548
54169
|
}
|
|
53549
54170
|
function writeCheckpointLog(log2, directory) {
|
|
53550
54171
|
const logPath = getCheckpointLogPath(directory);
|
|
53551
|
-
const dir =
|
|
53552
|
-
if (!
|
|
53553
|
-
|
|
54172
|
+
const dir = path37.dirname(logPath);
|
|
54173
|
+
if (!fs24.existsSync(dir)) {
|
|
54174
|
+
fs24.mkdirSync(dir, { recursive: true });
|
|
53554
54175
|
}
|
|
53555
54176
|
const tempPath = `${logPath}.tmp`;
|
|
53556
|
-
|
|
53557
|
-
|
|
54177
|
+
fs24.writeFileSync(tempPath, JSON.stringify(log2, null, 2), "utf-8");
|
|
54178
|
+
fs24.renameSync(tempPath, logPath);
|
|
53558
54179
|
}
|
|
53559
54180
|
function gitExec(args2) {
|
|
53560
54181
|
const result = spawnSync("git", args2, {
|
|
@@ -53755,8 +54376,8 @@ var checkpoint = createSwarmTool({
|
|
|
53755
54376
|
// src/tools/complexity-hotspots.ts
|
|
53756
54377
|
init_dist();
|
|
53757
54378
|
init_create_tool();
|
|
53758
|
-
import * as
|
|
53759
|
-
import * as
|
|
54379
|
+
import * as fs25 from "fs";
|
|
54380
|
+
import * as path38 from "path";
|
|
53760
54381
|
var MAX_FILE_SIZE_BYTES2 = 256 * 1024;
|
|
53761
54382
|
var DEFAULT_DAYS = 90;
|
|
53762
54383
|
var DEFAULT_TOP_N = 20;
|
|
@@ -53885,11 +54506,11 @@ function estimateComplexity(content) {
|
|
|
53885
54506
|
}
|
|
53886
54507
|
function getComplexityForFile(filePath) {
|
|
53887
54508
|
try {
|
|
53888
|
-
const stat2 =
|
|
54509
|
+
const stat2 = fs25.statSync(filePath);
|
|
53889
54510
|
if (stat2.size > MAX_FILE_SIZE_BYTES2) {
|
|
53890
54511
|
return null;
|
|
53891
54512
|
}
|
|
53892
|
-
const content =
|
|
54513
|
+
const content = fs25.readFileSync(filePath, "utf-8");
|
|
53893
54514
|
return estimateComplexity(content);
|
|
53894
54515
|
} catch {
|
|
53895
54516
|
return null;
|
|
@@ -53900,7 +54521,7 @@ async function analyzeHotspots(days, topN, extensions, directory) {
|
|
|
53900
54521
|
const extSet = new Set(extensions.map((e) => e.startsWith(".") ? e : `.${e}`));
|
|
53901
54522
|
const filteredChurn = new Map;
|
|
53902
54523
|
for (const [file3, count] of churnMap) {
|
|
53903
|
-
const ext =
|
|
54524
|
+
const ext = path38.extname(file3).toLowerCase();
|
|
53904
54525
|
if (extSet.has(ext)) {
|
|
53905
54526
|
filteredChurn.set(file3, count);
|
|
53906
54527
|
}
|
|
@@ -53910,8 +54531,8 @@ async function analyzeHotspots(days, topN, extensions, directory) {
|
|
|
53910
54531
|
let analyzedFiles = 0;
|
|
53911
54532
|
for (const [file3, churnCount] of filteredChurn) {
|
|
53912
54533
|
let fullPath = file3;
|
|
53913
|
-
if (!
|
|
53914
|
-
fullPath =
|
|
54534
|
+
if (!fs25.existsSync(fullPath)) {
|
|
54535
|
+
fullPath = path38.join(cwd, file3);
|
|
53915
54536
|
}
|
|
53916
54537
|
const complexity = getComplexityForFile(fullPath);
|
|
53917
54538
|
if (complexity !== null) {
|
|
@@ -54058,8 +54679,8 @@ var complexity_hotspots = createSwarmTool({
|
|
|
54058
54679
|
});
|
|
54059
54680
|
// src/tools/declare-scope.ts
|
|
54060
54681
|
init_tool();
|
|
54061
|
-
import * as
|
|
54062
|
-
import * as
|
|
54682
|
+
import * as fs26 from "fs";
|
|
54683
|
+
import * as path39 from "path";
|
|
54063
54684
|
init_create_tool();
|
|
54064
54685
|
function validateTaskIdFormat(taskId) {
|
|
54065
54686
|
const taskIdPattern = /^\d+\.\d+(\.\d+)*$/;
|
|
@@ -54138,8 +54759,8 @@ async function executeDeclareScope(args2, fallbackDir) {
|
|
|
54138
54759
|
};
|
|
54139
54760
|
}
|
|
54140
54761
|
}
|
|
54141
|
-
normalizedDir =
|
|
54142
|
-
const pathParts = normalizedDir.split(
|
|
54762
|
+
normalizedDir = path39.normalize(args2.working_directory);
|
|
54763
|
+
const pathParts = normalizedDir.split(path39.sep);
|
|
54143
54764
|
if (pathParts.includes("..")) {
|
|
54144
54765
|
return {
|
|
54145
54766
|
success: false,
|
|
@@ -54149,11 +54770,11 @@ async function executeDeclareScope(args2, fallbackDir) {
|
|
|
54149
54770
|
]
|
|
54150
54771
|
};
|
|
54151
54772
|
}
|
|
54152
|
-
const resolvedDir =
|
|
54773
|
+
const resolvedDir = path39.resolve(normalizedDir);
|
|
54153
54774
|
try {
|
|
54154
|
-
const realPath =
|
|
54155
|
-
const planPath2 =
|
|
54156
|
-
if (!
|
|
54775
|
+
const realPath = fs26.realpathSync(resolvedDir);
|
|
54776
|
+
const planPath2 = path39.join(realPath, ".swarm", "plan.json");
|
|
54777
|
+
if (!fs26.existsSync(planPath2)) {
|
|
54157
54778
|
return {
|
|
54158
54779
|
success: false,
|
|
54159
54780
|
message: `Invalid working_directory: plan not found in "${realPath}"`,
|
|
@@ -54173,8 +54794,8 @@ async function executeDeclareScope(args2, fallbackDir) {
|
|
|
54173
54794
|
}
|
|
54174
54795
|
}
|
|
54175
54796
|
const directory = normalizedDir ?? fallbackDir ?? process.cwd();
|
|
54176
|
-
const planPath =
|
|
54177
|
-
if (!
|
|
54797
|
+
const planPath = path39.resolve(directory, ".swarm", "plan.json");
|
|
54798
|
+
if (!fs26.existsSync(planPath)) {
|
|
54178
54799
|
return {
|
|
54179
54800
|
success: false,
|
|
54180
54801
|
message: "No plan found",
|
|
@@ -54183,7 +54804,7 @@ async function executeDeclareScope(args2, fallbackDir) {
|
|
|
54183
54804
|
}
|
|
54184
54805
|
let planContent;
|
|
54185
54806
|
try {
|
|
54186
|
-
planContent = JSON.parse(
|
|
54807
|
+
planContent = JSON.parse(fs26.readFileSync(planPath, "utf-8"));
|
|
54187
54808
|
} catch {
|
|
54188
54809
|
return {
|
|
54189
54810
|
success: false,
|
|
@@ -54263,20 +54884,20 @@ function validateBase(base) {
|
|
|
54263
54884
|
function validatePaths(paths) {
|
|
54264
54885
|
if (!paths)
|
|
54265
54886
|
return null;
|
|
54266
|
-
for (const
|
|
54267
|
-
if (!
|
|
54887
|
+
for (const path40 of paths) {
|
|
54888
|
+
if (!path40 || path40.length === 0) {
|
|
54268
54889
|
return "empty path not allowed";
|
|
54269
54890
|
}
|
|
54270
|
-
if (
|
|
54891
|
+
if (path40.length > MAX_PATH_LENGTH) {
|
|
54271
54892
|
return `path exceeds maximum length of ${MAX_PATH_LENGTH}`;
|
|
54272
54893
|
}
|
|
54273
|
-
if (SHELL_METACHARACTERS2.test(
|
|
54894
|
+
if (SHELL_METACHARACTERS2.test(path40)) {
|
|
54274
54895
|
return "path contains shell metacharacters";
|
|
54275
54896
|
}
|
|
54276
|
-
if (
|
|
54897
|
+
if (path40.startsWith("-")) {
|
|
54277
54898
|
return 'path cannot start with "-" (option-like arguments not allowed)';
|
|
54278
54899
|
}
|
|
54279
|
-
if (CONTROL_CHAR_PATTERN2.test(
|
|
54900
|
+
if (CONTROL_CHAR_PATTERN2.test(path40)) {
|
|
54280
54901
|
return "path contains control characters";
|
|
54281
54902
|
}
|
|
54282
54903
|
}
|
|
@@ -54356,8 +54977,8 @@ var diff = tool({
|
|
|
54356
54977
|
if (parts2.length >= 3) {
|
|
54357
54978
|
const additions = parseInt(parts2[0], 10) || 0;
|
|
54358
54979
|
const deletions = parseInt(parts2[1], 10) || 0;
|
|
54359
|
-
const
|
|
54360
|
-
files.push({ path:
|
|
54980
|
+
const path40 = parts2[2];
|
|
54981
|
+
files.push({ path: path40, additions, deletions });
|
|
54361
54982
|
}
|
|
54362
54983
|
}
|
|
54363
54984
|
const contractChanges = [];
|
|
@@ -54586,8 +55207,8 @@ Use these as DOMAIN values when delegating to @sme.`;
|
|
|
54586
55207
|
// src/tools/evidence-check.ts
|
|
54587
55208
|
init_dist();
|
|
54588
55209
|
init_create_tool();
|
|
54589
|
-
import * as
|
|
54590
|
-
import * as
|
|
55210
|
+
import * as fs27 from "fs";
|
|
55211
|
+
import * as path40 from "path";
|
|
54591
55212
|
var MAX_FILE_SIZE_BYTES3 = 1024 * 1024;
|
|
54592
55213
|
var MAX_EVIDENCE_FILES = 1000;
|
|
54593
55214
|
var EVIDENCE_DIR2 = ".swarm/evidence";
|
|
@@ -54617,9 +55238,9 @@ function validateRequiredTypes(input) {
|
|
|
54617
55238
|
return null;
|
|
54618
55239
|
}
|
|
54619
55240
|
function isPathWithinSwarm2(filePath, cwd) {
|
|
54620
|
-
const normalizedCwd =
|
|
54621
|
-
const swarmPath =
|
|
54622
|
-
const normalizedPath =
|
|
55241
|
+
const normalizedCwd = path40.resolve(cwd);
|
|
55242
|
+
const swarmPath = path40.join(normalizedCwd, ".swarm");
|
|
55243
|
+
const normalizedPath = path40.resolve(filePath);
|
|
54623
55244
|
return normalizedPath.startsWith(swarmPath);
|
|
54624
55245
|
}
|
|
54625
55246
|
function parseCompletedTasks(planContent) {
|
|
@@ -54635,12 +55256,12 @@ function parseCompletedTasks(planContent) {
|
|
|
54635
55256
|
}
|
|
54636
55257
|
function readEvidenceFiles(evidenceDir, _cwd) {
|
|
54637
55258
|
const evidence = [];
|
|
54638
|
-
if (!
|
|
55259
|
+
if (!fs27.existsSync(evidenceDir) || !fs27.statSync(evidenceDir).isDirectory()) {
|
|
54639
55260
|
return evidence;
|
|
54640
55261
|
}
|
|
54641
55262
|
let files;
|
|
54642
55263
|
try {
|
|
54643
|
-
files =
|
|
55264
|
+
files = fs27.readdirSync(evidenceDir);
|
|
54644
55265
|
} catch {
|
|
54645
55266
|
return evidence;
|
|
54646
55267
|
}
|
|
@@ -54649,14 +55270,14 @@ function readEvidenceFiles(evidenceDir, _cwd) {
|
|
|
54649
55270
|
if (!VALID_EVIDENCE_FILENAME_REGEX.test(filename)) {
|
|
54650
55271
|
continue;
|
|
54651
55272
|
}
|
|
54652
|
-
const filePath =
|
|
55273
|
+
const filePath = path40.join(evidenceDir, filename);
|
|
54653
55274
|
try {
|
|
54654
|
-
const resolvedPath =
|
|
54655
|
-
const evidenceDirResolved =
|
|
55275
|
+
const resolvedPath = path40.resolve(filePath);
|
|
55276
|
+
const evidenceDirResolved = path40.resolve(evidenceDir);
|
|
54656
55277
|
if (!resolvedPath.startsWith(evidenceDirResolved)) {
|
|
54657
55278
|
continue;
|
|
54658
55279
|
}
|
|
54659
|
-
const stat2 =
|
|
55280
|
+
const stat2 = fs27.lstatSync(filePath);
|
|
54660
55281
|
if (!stat2.isFile()) {
|
|
54661
55282
|
continue;
|
|
54662
55283
|
}
|
|
@@ -54665,7 +55286,7 @@ function readEvidenceFiles(evidenceDir, _cwd) {
|
|
|
54665
55286
|
}
|
|
54666
55287
|
let fileStat;
|
|
54667
55288
|
try {
|
|
54668
|
-
fileStat =
|
|
55289
|
+
fileStat = fs27.statSync(filePath);
|
|
54669
55290
|
if (fileStat.size > MAX_FILE_SIZE_BYTES3) {
|
|
54670
55291
|
continue;
|
|
54671
55292
|
}
|
|
@@ -54674,7 +55295,7 @@ function readEvidenceFiles(evidenceDir, _cwd) {
|
|
|
54674
55295
|
}
|
|
54675
55296
|
let content;
|
|
54676
55297
|
try {
|
|
54677
|
-
content =
|
|
55298
|
+
content = fs27.readFileSync(filePath, "utf-8");
|
|
54678
55299
|
} catch {
|
|
54679
55300
|
continue;
|
|
54680
55301
|
}
|
|
@@ -54770,7 +55391,7 @@ var evidence_check = createSwarmTool({
|
|
|
54770
55391
|
return JSON.stringify(errorResult, null, 2);
|
|
54771
55392
|
}
|
|
54772
55393
|
const requiredTypes = requiredTypesValue.split(",").map((t) => t.trim()).filter((t) => t.length > 0).map(normalizeEvidenceType);
|
|
54773
|
-
const planPath =
|
|
55394
|
+
const planPath = path40.join(cwd, PLAN_FILE);
|
|
54774
55395
|
if (!isPathWithinSwarm2(planPath, cwd)) {
|
|
54775
55396
|
const errorResult = {
|
|
54776
55397
|
error: "plan file path validation failed",
|
|
@@ -54784,7 +55405,7 @@ var evidence_check = createSwarmTool({
|
|
|
54784
55405
|
}
|
|
54785
55406
|
let planContent;
|
|
54786
55407
|
try {
|
|
54787
|
-
planContent =
|
|
55408
|
+
planContent = fs27.readFileSync(planPath, "utf-8");
|
|
54788
55409
|
} catch {
|
|
54789
55410
|
const result2 = {
|
|
54790
55411
|
message: "No completed tasks found in plan.",
|
|
@@ -54802,7 +55423,7 @@ var evidence_check = createSwarmTool({
|
|
|
54802
55423
|
};
|
|
54803
55424
|
return JSON.stringify(result2, null, 2);
|
|
54804
55425
|
}
|
|
54805
|
-
const evidenceDir =
|
|
55426
|
+
const evidenceDir = path40.join(cwd, EVIDENCE_DIR2);
|
|
54806
55427
|
const evidence = readEvidenceFiles(evidenceDir, cwd);
|
|
54807
55428
|
const { tasksWithFullEvidence, gaps } = analyzeGaps(completedTasks, evidence, requiredTypes);
|
|
54808
55429
|
const completeness = completedTasks.length > 0 ? Math.round(tasksWithFullEvidence.length / completedTasks.length * 100) / 100 : 1;
|
|
@@ -54819,8 +55440,8 @@ var evidence_check = createSwarmTool({
|
|
|
54819
55440
|
// src/tools/file-extractor.ts
|
|
54820
55441
|
init_tool();
|
|
54821
55442
|
init_create_tool();
|
|
54822
|
-
import * as
|
|
54823
|
-
import * as
|
|
55443
|
+
import * as fs28 from "fs";
|
|
55444
|
+
import * as path41 from "path";
|
|
54824
55445
|
var EXT_MAP = {
|
|
54825
55446
|
python: ".py",
|
|
54826
55447
|
py: ".py",
|
|
@@ -54882,8 +55503,8 @@ var extract_code_blocks = createSwarmTool({
|
|
|
54882
55503
|
execute: async (args2, directory) => {
|
|
54883
55504
|
const { content, output_dir, prefix } = args2;
|
|
54884
55505
|
const targetDir = output_dir || directory;
|
|
54885
|
-
if (!
|
|
54886
|
-
|
|
55506
|
+
if (!fs28.existsSync(targetDir)) {
|
|
55507
|
+
fs28.mkdirSync(targetDir, { recursive: true });
|
|
54887
55508
|
}
|
|
54888
55509
|
if (!content) {
|
|
54889
55510
|
return "Error: content is required";
|
|
@@ -54901,16 +55522,16 @@ var extract_code_blocks = createSwarmTool({
|
|
|
54901
55522
|
if (prefix) {
|
|
54902
55523
|
filename = `${prefix}_${filename}`;
|
|
54903
55524
|
}
|
|
54904
|
-
let filepath =
|
|
54905
|
-
const base =
|
|
54906
|
-
const ext =
|
|
55525
|
+
let filepath = path41.join(targetDir, filename);
|
|
55526
|
+
const base = path41.basename(filepath, path41.extname(filepath));
|
|
55527
|
+
const ext = path41.extname(filepath);
|
|
54907
55528
|
let counter = 1;
|
|
54908
|
-
while (
|
|
54909
|
-
filepath =
|
|
55529
|
+
while (fs28.existsSync(filepath)) {
|
|
55530
|
+
filepath = path41.join(targetDir, `${base}_${counter}${ext}`);
|
|
54910
55531
|
counter++;
|
|
54911
55532
|
}
|
|
54912
55533
|
try {
|
|
54913
|
-
|
|
55534
|
+
fs28.writeFileSync(filepath, code.trim(), "utf-8");
|
|
54914
55535
|
savedFiles.push(filepath);
|
|
54915
55536
|
} catch (error93) {
|
|
54916
55537
|
errors5.push(`Failed to save ${filename}: ${error93 instanceof Error ? error93.message : String(error93)}`);
|
|
@@ -55023,8 +55644,8 @@ var gitingest = tool({
|
|
|
55023
55644
|
});
|
|
55024
55645
|
// src/tools/imports.ts
|
|
55025
55646
|
init_dist();
|
|
55026
|
-
import * as
|
|
55027
|
-
import * as
|
|
55647
|
+
import * as fs29 from "fs";
|
|
55648
|
+
import * as path42 from "path";
|
|
55028
55649
|
var MAX_FILE_PATH_LENGTH2 = 500;
|
|
55029
55650
|
var MAX_SYMBOL_LENGTH = 256;
|
|
55030
55651
|
var MAX_FILE_SIZE_BYTES4 = 1024 * 1024;
|
|
@@ -55078,7 +55699,7 @@ function validateSymbolInput(symbol3) {
|
|
|
55078
55699
|
return null;
|
|
55079
55700
|
}
|
|
55080
55701
|
function isBinaryFile2(filePath, buffer) {
|
|
55081
|
-
const ext =
|
|
55702
|
+
const ext = path42.extname(filePath).toLowerCase();
|
|
55082
55703
|
if (ext === ".json" || ext === ".md" || ext === ".txt") {
|
|
55083
55704
|
return false;
|
|
55084
55705
|
}
|
|
@@ -55102,15 +55723,15 @@ function parseImports(content, targetFile, targetSymbol) {
|
|
|
55102
55723
|
const imports = [];
|
|
55103
55724
|
let _resolvedTarget;
|
|
55104
55725
|
try {
|
|
55105
|
-
_resolvedTarget =
|
|
55726
|
+
_resolvedTarget = path42.resolve(targetFile);
|
|
55106
55727
|
} catch {
|
|
55107
55728
|
_resolvedTarget = targetFile;
|
|
55108
55729
|
}
|
|
55109
|
-
const targetBasename =
|
|
55730
|
+
const targetBasename = path42.basename(targetFile, path42.extname(targetFile));
|
|
55110
55731
|
const targetWithExt = targetFile;
|
|
55111
55732
|
const targetWithoutExt = targetFile.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
|
|
55112
|
-
const normalizedTargetWithExt =
|
|
55113
|
-
const normalizedTargetWithoutExt =
|
|
55733
|
+
const normalizedTargetWithExt = path42.normalize(targetWithExt).replace(/\\/g, "/");
|
|
55734
|
+
const normalizedTargetWithoutExt = path42.normalize(targetWithoutExt).replace(/\\/g, "/");
|
|
55114
55735
|
const importRegex = /import\s+(?:\{[\s\S]*?\}|(?:\*\s+as\s+\w+)|\w+)\s+from\s+['"`]([^'"`]+)['"`]|import\s+['"`]([^'"`]+)['"`]|require\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
|
|
55115
55736
|
for (let match = importRegex.exec(content);match !== null; match = importRegex.exec(content)) {
|
|
55116
55737
|
const modulePath = match[1] || match[2] || match[3];
|
|
@@ -55133,9 +55754,9 @@ function parseImports(content, targetFile, targetSymbol) {
|
|
|
55133
55754
|
}
|
|
55134
55755
|
const _normalizedModule = modulePath.replace(/^\.\//, "").replace(/^\.\.\\/, "../");
|
|
55135
55756
|
let isMatch = false;
|
|
55136
|
-
const _targetDir =
|
|
55137
|
-
const targetExt =
|
|
55138
|
-
const targetBasenameNoExt =
|
|
55757
|
+
const _targetDir = path42.dirname(targetFile);
|
|
55758
|
+
const targetExt = path42.extname(targetFile);
|
|
55759
|
+
const targetBasenameNoExt = path42.basename(targetFile, targetExt);
|
|
55139
55760
|
const moduleNormalized = modulePath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
55140
55761
|
const moduleName = modulePath.split(/[/\\]/).pop() || "";
|
|
55141
55762
|
const moduleNameNoExt = moduleName.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
|
|
@@ -55192,7 +55813,7 @@ var SKIP_DIRECTORIES2 = new Set([
|
|
|
55192
55813
|
function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFiles: 0, fileErrors: [] }) {
|
|
55193
55814
|
let entries;
|
|
55194
55815
|
try {
|
|
55195
|
-
entries =
|
|
55816
|
+
entries = fs29.readdirSync(dir);
|
|
55196
55817
|
} catch (e) {
|
|
55197
55818
|
stats.fileErrors.push({
|
|
55198
55819
|
path: dir,
|
|
@@ -55203,13 +55824,13 @@ function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFile
|
|
|
55203
55824
|
entries.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
|
|
55204
55825
|
for (const entry of entries) {
|
|
55205
55826
|
if (SKIP_DIRECTORIES2.has(entry)) {
|
|
55206
|
-
stats.skippedDirs.push(
|
|
55827
|
+
stats.skippedDirs.push(path42.join(dir, entry));
|
|
55207
55828
|
continue;
|
|
55208
55829
|
}
|
|
55209
|
-
const fullPath =
|
|
55830
|
+
const fullPath = path42.join(dir, entry);
|
|
55210
55831
|
let stat2;
|
|
55211
55832
|
try {
|
|
55212
|
-
stat2 =
|
|
55833
|
+
stat2 = fs29.statSync(fullPath);
|
|
55213
55834
|
} catch (e) {
|
|
55214
55835
|
stats.fileErrors.push({
|
|
55215
55836
|
path: fullPath,
|
|
@@ -55220,7 +55841,7 @@ function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFile
|
|
|
55220
55841
|
if (stat2.isDirectory()) {
|
|
55221
55842
|
findSourceFiles(fullPath, files, stats);
|
|
55222
55843
|
} else if (stat2.isFile()) {
|
|
55223
|
-
const ext =
|
|
55844
|
+
const ext = path42.extname(fullPath).toLowerCase();
|
|
55224
55845
|
if (SUPPORTED_EXTENSIONS.includes(ext)) {
|
|
55225
55846
|
files.push(fullPath);
|
|
55226
55847
|
}
|
|
@@ -55276,8 +55897,8 @@ var imports = tool({
|
|
|
55276
55897
|
return JSON.stringify(errorResult, null, 2);
|
|
55277
55898
|
}
|
|
55278
55899
|
try {
|
|
55279
|
-
const targetFile =
|
|
55280
|
-
if (!
|
|
55900
|
+
const targetFile = path42.resolve(file3);
|
|
55901
|
+
if (!fs29.existsSync(targetFile)) {
|
|
55281
55902
|
const errorResult = {
|
|
55282
55903
|
error: `target file not found: ${file3}`,
|
|
55283
55904
|
target: file3,
|
|
@@ -55287,7 +55908,7 @@ var imports = tool({
|
|
|
55287
55908
|
};
|
|
55288
55909
|
return JSON.stringify(errorResult, null, 2);
|
|
55289
55910
|
}
|
|
55290
|
-
const targetStat =
|
|
55911
|
+
const targetStat = fs29.statSync(targetFile);
|
|
55291
55912
|
if (!targetStat.isFile()) {
|
|
55292
55913
|
const errorResult = {
|
|
55293
55914
|
error: "target must be a file, not a directory",
|
|
@@ -55298,7 +55919,7 @@ var imports = tool({
|
|
|
55298
55919
|
};
|
|
55299
55920
|
return JSON.stringify(errorResult, null, 2);
|
|
55300
55921
|
}
|
|
55301
|
-
const baseDir =
|
|
55922
|
+
const baseDir = path42.dirname(targetFile);
|
|
55302
55923
|
const scanStats = {
|
|
55303
55924
|
skippedDirs: [],
|
|
55304
55925
|
skippedFiles: 0,
|
|
@@ -55313,12 +55934,12 @@ var imports = tool({
|
|
|
55313
55934
|
if (consumers.length >= MAX_CONSUMERS)
|
|
55314
55935
|
break;
|
|
55315
55936
|
try {
|
|
55316
|
-
const stat2 =
|
|
55937
|
+
const stat2 = fs29.statSync(filePath);
|
|
55317
55938
|
if (stat2.size > MAX_FILE_SIZE_BYTES4) {
|
|
55318
55939
|
skippedFileCount++;
|
|
55319
55940
|
continue;
|
|
55320
55941
|
}
|
|
55321
|
-
const buffer =
|
|
55942
|
+
const buffer = fs29.readFileSync(filePath);
|
|
55322
55943
|
if (isBinaryFile2(filePath, buffer)) {
|
|
55323
55944
|
skippedFileCount++;
|
|
55324
55945
|
continue;
|
|
@@ -55383,7 +56004,7 @@ var imports = tool({
|
|
|
55383
56004
|
});
|
|
55384
56005
|
// src/tools/knowledge-query.ts
|
|
55385
56006
|
init_dist();
|
|
55386
|
-
import { existsSync as
|
|
56007
|
+
import { existsSync as existsSync27 } from "fs";
|
|
55387
56008
|
init_create_tool();
|
|
55388
56009
|
var DEFAULT_LIMIT = 10;
|
|
55389
56010
|
var MAX_LESSON_LENGTH = 200;
|
|
@@ -55453,14 +56074,14 @@ function validateLimit(limit) {
|
|
|
55453
56074
|
}
|
|
55454
56075
|
async function readSwarmKnowledge(directory) {
|
|
55455
56076
|
const swarmPath = resolveSwarmKnowledgePath(directory);
|
|
55456
|
-
if (!
|
|
56077
|
+
if (!existsSync27(swarmPath)) {
|
|
55457
56078
|
return [];
|
|
55458
56079
|
}
|
|
55459
56080
|
return readKnowledge(swarmPath);
|
|
55460
56081
|
}
|
|
55461
56082
|
async function readHiveKnowledge() {
|
|
55462
56083
|
const hivePath = resolveHiveKnowledgePath();
|
|
55463
|
-
if (!
|
|
56084
|
+
if (!existsSync27(hivePath)) {
|
|
55464
56085
|
return [];
|
|
55465
56086
|
}
|
|
55466
56087
|
return readKnowledge(hivePath);
|
|
@@ -55619,8 +56240,8 @@ init_dist();
|
|
|
55619
56240
|
init_config();
|
|
55620
56241
|
init_schema();
|
|
55621
56242
|
init_manager();
|
|
55622
|
-
import * as
|
|
55623
|
-
import * as
|
|
56243
|
+
import * as fs30 from "fs";
|
|
56244
|
+
import * as path43 from "path";
|
|
55624
56245
|
init_utils2();
|
|
55625
56246
|
init_create_tool();
|
|
55626
56247
|
function safeWarn(message, error93) {
|
|
@@ -55815,7 +56436,7 @@ async function executePhaseComplete(args2, workingDirectory) {
|
|
|
55815
56436
|
}
|
|
55816
56437
|
if (retroFound && retroEntry?.lessons_learned && retroEntry.lessons_learned.length > 0) {
|
|
55817
56438
|
try {
|
|
55818
|
-
const projectName =
|
|
56439
|
+
const projectName = path43.basename(dir);
|
|
55819
56440
|
const knowledgeConfig = {
|
|
55820
56441
|
enabled: true,
|
|
55821
56442
|
swarm_max_entries: 100,
|
|
@@ -55863,7 +56484,7 @@ async function executePhaseComplete(args2, workingDirectory) {
|
|
|
55863
56484
|
if (agentsMissing.length > 0) {
|
|
55864
56485
|
try {
|
|
55865
56486
|
const planPath = validateSwarmPath(dir, "plan.json");
|
|
55866
|
-
const planRaw =
|
|
56487
|
+
const planRaw = fs30.readFileSync(planPath, "utf-8");
|
|
55867
56488
|
const plan = JSON.parse(planRaw);
|
|
55868
56489
|
const targetPhase = plan.phases.find((p) => p.id === phase);
|
|
55869
56490
|
if (targetPhase && targetPhase.tasks.length > 0 && targetPhase.tasks.every((t) => t.status === "completed")) {
|
|
@@ -55904,7 +56525,7 @@ async function executePhaseComplete(args2, workingDirectory) {
|
|
|
55904
56525
|
};
|
|
55905
56526
|
try {
|
|
55906
56527
|
const eventsPath = validateSwarmPath(dir, "events.jsonl");
|
|
55907
|
-
|
|
56528
|
+
fs30.appendFileSync(eventsPath, `${JSON.stringify(event)}
|
|
55908
56529
|
`, "utf-8");
|
|
55909
56530
|
} catch (writeError) {
|
|
55910
56531
|
warnings.push(`Warning: failed to write phase complete event: ${writeError instanceof Error ? writeError.message : String(writeError)}`);
|
|
@@ -55923,12 +56544,12 @@ async function executePhaseComplete(args2, workingDirectory) {
|
|
|
55923
56544
|
}
|
|
55924
56545
|
try {
|
|
55925
56546
|
const planPath = validateSwarmPath(dir, "plan.json");
|
|
55926
|
-
const planJson =
|
|
56547
|
+
const planJson = fs30.readFileSync(planPath, "utf-8");
|
|
55927
56548
|
const plan = JSON.parse(planJson);
|
|
55928
56549
|
const phaseObj = plan.phases.find((p) => p.id === phase);
|
|
55929
56550
|
if (phaseObj) {
|
|
55930
56551
|
phaseObj.status = "completed";
|
|
55931
|
-
|
|
56552
|
+
fs30.writeFileSync(planPath, `${JSON.stringify(plan, null, 2)}
|
|
55932
56553
|
`, "utf-8");
|
|
55933
56554
|
}
|
|
55934
56555
|
} catch (error93) {
|
|
@@ -55978,8 +56599,8 @@ init_dist();
|
|
|
55978
56599
|
init_discovery();
|
|
55979
56600
|
init_utils();
|
|
55980
56601
|
init_create_tool();
|
|
55981
|
-
import * as
|
|
55982
|
-
import * as
|
|
56602
|
+
import * as fs31 from "fs";
|
|
56603
|
+
import * as path44 from "path";
|
|
55983
56604
|
var MAX_OUTPUT_BYTES5 = 52428800;
|
|
55984
56605
|
var AUDIT_TIMEOUT_MS = 120000;
|
|
55985
56606
|
function isValidEcosystem(value) {
|
|
@@ -55997,28 +56618,28 @@ function validateArgs3(args2) {
|
|
|
55997
56618
|
function detectEcosystems(directory) {
|
|
55998
56619
|
const ecosystems = [];
|
|
55999
56620
|
const cwd = directory;
|
|
56000
|
-
if (
|
|
56621
|
+
if (fs31.existsSync(path44.join(cwd, "package.json"))) {
|
|
56001
56622
|
ecosystems.push("npm");
|
|
56002
56623
|
}
|
|
56003
|
-
if (
|
|
56624
|
+
if (fs31.existsSync(path44.join(cwd, "pyproject.toml")) || fs31.existsSync(path44.join(cwd, "requirements.txt"))) {
|
|
56004
56625
|
ecosystems.push("pip");
|
|
56005
56626
|
}
|
|
56006
|
-
if (
|
|
56627
|
+
if (fs31.existsSync(path44.join(cwd, "Cargo.toml"))) {
|
|
56007
56628
|
ecosystems.push("cargo");
|
|
56008
56629
|
}
|
|
56009
|
-
if (
|
|
56630
|
+
if (fs31.existsSync(path44.join(cwd, "go.mod"))) {
|
|
56010
56631
|
ecosystems.push("go");
|
|
56011
56632
|
}
|
|
56012
56633
|
try {
|
|
56013
|
-
const files =
|
|
56634
|
+
const files = fs31.readdirSync(cwd);
|
|
56014
56635
|
if (files.some((f) => f.endsWith(".csproj") || f.endsWith(".sln"))) {
|
|
56015
56636
|
ecosystems.push("dotnet");
|
|
56016
56637
|
}
|
|
56017
56638
|
} catch {}
|
|
56018
|
-
if (
|
|
56639
|
+
if (fs31.existsSync(path44.join(cwd, "Gemfile")) || fs31.existsSync(path44.join(cwd, "Gemfile.lock"))) {
|
|
56019
56640
|
ecosystems.push("ruby");
|
|
56020
56641
|
}
|
|
56021
|
-
if (
|
|
56642
|
+
if (fs31.existsSync(path44.join(cwd, "pubspec.yaml"))) {
|
|
56022
56643
|
ecosystems.push("dart");
|
|
56023
56644
|
}
|
|
56024
56645
|
return ecosystems;
|
|
@@ -57080,8 +57701,8 @@ var SUPPORTED_PARSER_EXTENSIONS = new Set([
|
|
|
57080
57701
|
]);
|
|
57081
57702
|
// src/tools/pre-check-batch.ts
|
|
57082
57703
|
init_dist();
|
|
57083
|
-
import * as
|
|
57084
|
-
import * as
|
|
57704
|
+
import * as fs34 from "fs";
|
|
57705
|
+
import * as path47 from "path";
|
|
57085
57706
|
|
|
57086
57707
|
// node_modules/yocto-queue/index.js
|
|
57087
57708
|
class Node2 {
|
|
@@ -57248,8 +57869,8 @@ init_lint();
|
|
|
57248
57869
|
init_manager();
|
|
57249
57870
|
|
|
57250
57871
|
// src/quality/metrics.ts
|
|
57251
|
-
import * as
|
|
57252
|
-
import * as
|
|
57872
|
+
import * as fs32 from "fs";
|
|
57873
|
+
import * as path45 from "path";
|
|
57253
57874
|
var MAX_FILE_SIZE_BYTES5 = 256 * 1024;
|
|
57254
57875
|
var MIN_DUPLICATION_LINES = 10;
|
|
57255
57876
|
function estimateCyclomaticComplexity(content) {
|
|
@@ -57287,11 +57908,11 @@ function estimateCyclomaticComplexity(content) {
|
|
|
57287
57908
|
}
|
|
57288
57909
|
function getComplexityForFile2(filePath) {
|
|
57289
57910
|
try {
|
|
57290
|
-
const stat2 =
|
|
57911
|
+
const stat2 = fs32.statSync(filePath);
|
|
57291
57912
|
if (stat2.size > MAX_FILE_SIZE_BYTES5) {
|
|
57292
57913
|
return null;
|
|
57293
57914
|
}
|
|
57294
|
-
const content =
|
|
57915
|
+
const content = fs32.readFileSync(filePath, "utf-8");
|
|
57295
57916
|
return estimateCyclomaticComplexity(content);
|
|
57296
57917
|
} catch {
|
|
57297
57918
|
return null;
|
|
@@ -57301,8 +57922,8 @@ async function computeComplexityDelta(files, workingDir) {
|
|
|
57301
57922
|
let totalComplexity = 0;
|
|
57302
57923
|
const analyzedFiles = [];
|
|
57303
57924
|
for (const file3 of files) {
|
|
57304
|
-
const fullPath =
|
|
57305
|
-
if (!
|
|
57925
|
+
const fullPath = path45.isAbsolute(file3) ? file3 : path45.join(workingDir, file3);
|
|
57926
|
+
if (!fs32.existsSync(fullPath)) {
|
|
57306
57927
|
continue;
|
|
57307
57928
|
}
|
|
57308
57929
|
const complexity = getComplexityForFile2(fullPath);
|
|
@@ -57423,8 +58044,8 @@ function countGoExports(content) {
|
|
|
57423
58044
|
}
|
|
57424
58045
|
function getExportCountForFile(filePath) {
|
|
57425
58046
|
try {
|
|
57426
|
-
const content =
|
|
57427
|
-
const ext =
|
|
58047
|
+
const content = fs32.readFileSync(filePath, "utf-8");
|
|
58048
|
+
const ext = path45.extname(filePath).toLowerCase();
|
|
57428
58049
|
switch (ext) {
|
|
57429
58050
|
case ".ts":
|
|
57430
58051
|
case ".tsx":
|
|
@@ -57450,8 +58071,8 @@ async function computePublicApiDelta(files, workingDir) {
|
|
|
57450
58071
|
let totalExports = 0;
|
|
57451
58072
|
const analyzedFiles = [];
|
|
57452
58073
|
for (const file3 of files) {
|
|
57453
|
-
const fullPath =
|
|
57454
|
-
if (!
|
|
58074
|
+
const fullPath = path45.isAbsolute(file3) ? file3 : path45.join(workingDir, file3);
|
|
58075
|
+
if (!fs32.existsSync(fullPath)) {
|
|
57455
58076
|
continue;
|
|
57456
58077
|
}
|
|
57457
58078
|
const exports = getExportCountForFile(fullPath);
|
|
@@ -57484,16 +58105,16 @@ async function computeDuplicationRatio(files, workingDir) {
|
|
|
57484
58105
|
let duplicateLines = 0;
|
|
57485
58106
|
const analyzedFiles = [];
|
|
57486
58107
|
for (const file3 of files) {
|
|
57487
|
-
const fullPath =
|
|
57488
|
-
if (!
|
|
58108
|
+
const fullPath = path45.isAbsolute(file3) ? file3 : path45.join(workingDir, file3);
|
|
58109
|
+
if (!fs32.existsSync(fullPath)) {
|
|
57489
58110
|
continue;
|
|
57490
58111
|
}
|
|
57491
58112
|
try {
|
|
57492
|
-
const stat2 =
|
|
58113
|
+
const stat2 = fs32.statSync(fullPath);
|
|
57493
58114
|
if (stat2.size > MAX_FILE_SIZE_BYTES5) {
|
|
57494
58115
|
continue;
|
|
57495
58116
|
}
|
|
57496
|
-
const content =
|
|
58117
|
+
const content = fs32.readFileSync(fullPath, "utf-8");
|
|
57497
58118
|
const lines = content.split(`
|
|
57498
58119
|
`).filter((line) => line.trim().length > 0);
|
|
57499
58120
|
if (lines.length < MIN_DUPLICATION_LINES) {
|
|
@@ -57517,8 +58138,8 @@ function countCodeLines(content) {
|
|
|
57517
58138
|
return lines.length;
|
|
57518
58139
|
}
|
|
57519
58140
|
function isTestFile(filePath) {
|
|
57520
|
-
const basename8 =
|
|
57521
|
-
const _ext =
|
|
58141
|
+
const basename8 = path45.basename(filePath);
|
|
58142
|
+
const _ext = path45.extname(filePath).toLowerCase();
|
|
57522
58143
|
const testPatterns = [
|
|
57523
58144
|
".test.",
|
|
57524
58145
|
".spec.",
|
|
@@ -57599,8 +58220,8 @@ function matchGlobSegment(globSegments, pathSegments) {
|
|
|
57599
58220
|
}
|
|
57600
58221
|
return gIndex === globSegments.length && pIndex === pathSegments.length;
|
|
57601
58222
|
}
|
|
57602
|
-
function matchesGlobSegment(
|
|
57603
|
-
const normalizedPath =
|
|
58223
|
+
function matchesGlobSegment(path46, glob) {
|
|
58224
|
+
const normalizedPath = path46.replace(/\\/g, "/");
|
|
57604
58225
|
const normalizedGlob = glob.replace(/\\/g, "/");
|
|
57605
58226
|
if (normalizedPath.includes("//")) {
|
|
57606
58227
|
return false;
|
|
@@ -57631,8 +58252,8 @@ function simpleGlobToRegex2(glob) {
|
|
|
57631
58252
|
function hasGlobstar(glob) {
|
|
57632
58253
|
return glob.includes("**");
|
|
57633
58254
|
}
|
|
57634
|
-
function globMatches(
|
|
57635
|
-
const normalizedPath =
|
|
58255
|
+
function globMatches(path46, glob) {
|
|
58256
|
+
const normalizedPath = path46.replace(/\\/g, "/");
|
|
57636
58257
|
if (!glob || glob === "") {
|
|
57637
58258
|
if (normalizedPath.includes("//")) {
|
|
57638
58259
|
return false;
|
|
@@ -57668,31 +58289,31 @@ function shouldExcludeFile(filePath, excludeGlobs) {
|
|
|
57668
58289
|
async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
|
|
57669
58290
|
let testLines = 0;
|
|
57670
58291
|
let codeLines = 0;
|
|
57671
|
-
const srcDir =
|
|
57672
|
-
if (
|
|
58292
|
+
const srcDir = path45.join(workingDir, "src");
|
|
58293
|
+
if (fs32.existsSync(srcDir)) {
|
|
57673
58294
|
await scanDirectoryForLines(srcDir, enforceGlobs, excludeGlobs, false, (lines) => {
|
|
57674
58295
|
codeLines += lines;
|
|
57675
58296
|
});
|
|
57676
58297
|
}
|
|
57677
58298
|
const possibleSrcDirs = ["lib", "app", "source", "core"];
|
|
57678
58299
|
for (const dir of possibleSrcDirs) {
|
|
57679
|
-
const dirPath =
|
|
57680
|
-
if (
|
|
58300
|
+
const dirPath = path45.join(workingDir, dir);
|
|
58301
|
+
if (fs32.existsSync(dirPath)) {
|
|
57681
58302
|
await scanDirectoryForLines(dirPath, enforceGlobs, excludeGlobs, false, (lines) => {
|
|
57682
58303
|
codeLines += lines;
|
|
57683
58304
|
});
|
|
57684
58305
|
}
|
|
57685
58306
|
}
|
|
57686
|
-
const testsDir =
|
|
57687
|
-
if (
|
|
58307
|
+
const testsDir = path45.join(workingDir, "tests");
|
|
58308
|
+
if (fs32.existsSync(testsDir)) {
|
|
57688
58309
|
await scanDirectoryForLines(testsDir, ["**"], ["node_modules", "dist"], true, (lines) => {
|
|
57689
58310
|
testLines += lines;
|
|
57690
58311
|
});
|
|
57691
58312
|
}
|
|
57692
58313
|
const possibleTestDirs = ["test", "__tests__", "specs"];
|
|
57693
58314
|
for (const dir of possibleTestDirs) {
|
|
57694
|
-
const dirPath =
|
|
57695
|
-
if (
|
|
58315
|
+
const dirPath = path45.join(workingDir, dir);
|
|
58316
|
+
if (fs32.existsSync(dirPath) && dirPath !== testsDir) {
|
|
57696
58317
|
await scanDirectoryForLines(dirPath, ["**"], ["node_modules", "dist"], true, (lines) => {
|
|
57697
58318
|
testLines += lines;
|
|
57698
58319
|
});
|
|
@@ -57704,9 +58325,9 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
|
|
|
57704
58325
|
}
|
|
57705
58326
|
async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTestScan, callback) {
|
|
57706
58327
|
try {
|
|
57707
|
-
const entries =
|
|
58328
|
+
const entries = fs32.readdirSync(dirPath, { withFileTypes: true });
|
|
57708
58329
|
for (const entry of entries) {
|
|
57709
|
-
const fullPath =
|
|
58330
|
+
const fullPath = path45.join(dirPath, entry.name);
|
|
57710
58331
|
if (entry.isDirectory()) {
|
|
57711
58332
|
if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === ".git") {
|
|
57712
58333
|
continue;
|
|
@@ -57714,7 +58335,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
|
|
|
57714
58335
|
await scanDirectoryForLines(fullPath, includeGlobs, excludeGlobs, isTestScan, callback);
|
|
57715
58336
|
} else if (entry.isFile()) {
|
|
57716
58337
|
const relativePath = fullPath.replace(`${process.cwd()}/`, "");
|
|
57717
|
-
const ext =
|
|
58338
|
+
const ext = path45.extname(entry.name).toLowerCase();
|
|
57718
58339
|
const validExts = [
|
|
57719
58340
|
".ts",
|
|
57720
58341
|
".tsx",
|
|
@@ -57750,7 +58371,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
|
|
|
57750
58371
|
continue;
|
|
57751
58372
|
}
|
|
57752
58373
|
try {
|
|
57753
|
-
const content =
|
|
58374
|
+
const content = fs32.readFileSync(fullPath, "utf-8");
|
|
57754
58375
|
const lines = countCodeLines(content);
|
|
57755
58376
|
callback(lines);
|
|
57756
58377
|
} catch {}
|
|
@@ -57964,8 +58585,8 @@ async function qualityBudget(input, directory) {
|
|
|
57964
58585
|
init_dist();
|
|
57965
58586
|
init_manager();
|
|
57966
58587
|
init_detector();
|
|
57967
|
-
import * as
|
|
57968
|
-
import * as
|
|
58588
|
+
import * as fs33 from "fs";
|
|
58589
|
+
import * as path46 from "path";
|
|
57969
58590
|
import { extname as extname9 } from "path";
|
|
57970
58591
|
|
|
57971
58592
|
// src/sast/rules/c.ts
|
|
@@ -58832,17 +59453,17 @@ var SEVERITY_ORDER = {
|
|
|
58832
59453
|
};
|
|
58833
59454
|
function shouldSkipFile(filePath) {
|
|
58834
59455
|
try {
|
|
58835
|
-
const stats =
|
|
59456
|
+
const stats = fs33.statSync(filePath);
|
|
58836
59457
|
if (stats.size > MAX_FILE_SIZE_BYTES6) {
|
|
58837
59458
|
return { skip: true, reason: "file too large" };
|
|
58838
59459
|
}
|
|
58839
59460
|
if (stats.size === 0) {
|
|
58840
59461
|
return { skip: true, reason: "empty file" };
|
|
58841
59462
|
}
|
|
58842
|
-
const fd =
|
|
59463
|
+
const fd = fs33.openSync(filePath, "r");
|
|
58843
59464
|
const buffer = Buffer.alloc(8192);
|
|
58844
|
-
const bytesRead =
|
|
58845
|
-
|
|
59465
|
+
const bytesRead = fs33.readSync(fd, buffer, 0, 8192, 0);
|
|
59466
|
+
fs33.closeSync(fd);
|
|
58846
59467
|
if (bytesRead > 0) {
|
|
58847
59468
|
let nullCount = 0;
|
|
58848
59469
|
for (let i2 = 0;i2 < bytesRead; i2++) {
|
|
@@ -58881,7 +59502,7 @@ function countBySeverity(findings) {
|
|
|
58881
59502
|
}
|
|
58882
59503
|
function scanFileWithTierA(filePath, language) {
|
|
58883
59504
|
try {
|
|
58884
|
-
const content =
|
|
59505
|
+
const content = fs33.readFileSync(filePath, "utf-8");
|
|
58885
59506
|
const findings = executeRulesSync(filePath, content, language);
|
|
58886
59507
|
return findings.map((f) => ({
|
|
58887
59508
|
rule_id: f.rule_id,
|
|
@@ -58928,8 +59549,8 @@ async function sastScan(input, directory, config3) {
|
|
|
58928
59549
|
_filesSkipped++;
|
|
58929
59550
|
continue;
|
|
58930
59551
|
}
|
|
58931
|
-
const resolvedPath =
|
|
58932
|
-
if (!
|
|
59552
|
+
const resolvedPath = path46.isAbsolute(filePath) ? filePath : path46.resolve(directory, filePath);
|
|
59553
|
+
if (!fs33.existsSync(resolvedPath)) {
|
|
58933
59554
|
_filesSkipped++;
|
|
58934
59555
|
continue;
|
|
58935
59556
|
}
|
|
@@ -59127,18 +59748,18 @@ function validatePath(inputPath, baseDir, workspaceDir) {
|
|
|
59127
59748
|
let resolved;
|
|
59128
59749
|
const isWinAbs = isWindowsAbsolutePath(inputPath);
|
|
59129
59750
|
if (isWinAbs) {
|
|
59130
|
-
resolved =
|
|
59131
|
-
} else if (
|
|
59132
|
-
resolved =
|
|
59751
|
+
resolved = path47.win32.resolve(inputPath);
|
|
59752
|
+
} else if (path47.isAbsolute(inputPath)) {
|
|
59753
|
+
resolved = path47.resolve(inputPath);
|
|
59133
59754
|
} else {
|
|
59134
|
-
resolved =
|
|
59755
|
+
resolved = path47.resolve(baseDir, inputPath);
|
|
59135
59756
|
}
|
|
59136
|
-
const workspaceResolved =
|
|
59757
|
+
const workspaceResolved = path47.resolve(workspaceDir);
|
|
59137
59758
|
let relative5;
|
|
59138
59759
|
if (isWinAbs) {
|
|
59139
|
-
relative5 =
|
|
59760
|
+
relative5 = path47.win32.relative(workspaceResolved, resolved);
|
|
59140
59761
|
} else {
|
|
59141
|
-
relative5 =
|
|
59762
|
+
relative5 = path47.relative(workspaceResolved, resolved);
|
|
59142
59763
|
}
|
|
59143
59764
|
if (relative5.startsWith("..")) {
|
|
59144
59765
|
return "path traversal detected";
|
|
@@ -59158,7 +59779,7 @@ function validateDirectory3(dir, workspaceDir) {
|
|
|
59158
59779
|
}
|
|
59159
59780
|
return null;
|
|
59160
59781
|
}
|
|
59161
|
-
async function
|
|
59782
|
+
async function runWithTimeout2(promise3, timeoutMs) {
|
|
59162
59783
|
const timeoutPromise = new Promise((_, reject) => {
|
|
59163
59784
|
setTimeout(() => reject(new Error(`Timeout after ${timeoutMs}ms`)), timeoutMs);
|
|
59164
59785
|
});
|
|
@@ -59183,7 +59804,7 @@ async function runLintWrapped(files, directory, _config) {
|
|
|
59183
59804
|
duration_ms: Number(process.hrtime.bigint() - start2) / 1e6
|
|
59184
59805
|
};
|
|
59185
59806
|
}
|
|
59186
|
-
const result = await
|
|
59807
|
+
const result = await runWithTimeout2(runLint(linter, "check", directory), TOOL_TIMEOUT_MS);
|
|
59187
59808
|
return {
|
|
59188
59809
|
ran: true,
|
|
59189
59810
|
result,
|
|
@@ -59199,13 +59820,13 @@ async function runLintWrapped(files, directory, _config) {
|
|
|
59199
59820
|
}
|
|
59200
59821
|
async function runLintOnFiles(linter, files, workspaceDir) {
|
|
59201
59822
|
const isWindows = process.platform === "win32";
|
|
59202
|
-
const binDir =
|
|
59823
|
+
const binDir = path47.join(workspaceDir, "node_modules", ".bin");
|
|
59203
59824
|
const validatedFiles = [];
|
|
59204
59825
|
for (const file3 of files) {
|
|
59205
59826
|
if (typeof file3 !== "string") {
|
|
59206
59827
|
continue;
|
|
59207
59828
|
}
|
|
59208
|
-
const resolvedPath =
|
|
59829
|
+
const resolvedPath = path47.resolve(file3);
|
|
59209
59830
|
const validationError = validatePath(resolvedPath, workspaceDir, workspaceDir);
|
|
59210
59831
|
if (validationError) {
|
|
59211
59832
|
continue;
|
|
@@ -59223,10 +59844,10 @@ async function runLintOnFiles(linter, files, workspaceDir) {
|
|
|
59223
59844
|
}
|
|
59224
59845
|
let command;
|
|
59225
59846
|
if (linter === "biome") {
|
|
59226
|
-
const biomeBin = isWindows ?
|
|
59847
|
+
const biomeBin = isWindows ? path47.join(binDir, "biome.EXE") : path47.join(binDir, "biome");
|
|
59227
59848
|
command = [biomeBin, "check", ...validatedFiles];
|
|
59228
59849
|
} else {
|
|
59229
|
-
const eslintBin = isWindows ?
|
|
59850
|
+
const eslintBin = isWindows ? path47.join(binDir, "eslint.cmd") : path47.join(binDir, "eslint");
|
|
59230
59851
|
command = [eslintBin, ...validatedFiles];
|
|
59231
59852
|
}
|
|
59232
59853
|
try {
|
|
@@ -59277,14 +59898,14 @@ async function runSecretscanWrapped(files, directory, _config) {
|
|
|
59277
59898
|
const start2 = process.hrtime.bigint();
|
|
59278
59899
|
try {
|
|
59279
59900
|
if (files && files.length > 0) {
|
|
59280
|
-
const result2 = await
|
|
59901
|
+
const result2 = await runWithTimeout2(runSecretscanWithFiles(files, directory), TOOL_TIMEOUT_MS);
|
|
59281
59902
|
return {
|
|
59282
59903
|
ran: true,
|
|
59283
59904
|
result: result2,
|
|
59284
59905
|
duration_ms: Number(process.hrtime.bigint() - start2) / 1e6
|
|
59285
59906
|
};
|
|
59286
59907
|
}
|
|
59287
|
-
const result = await
|
|
59908
|
+
const result = await runWithTimeout2(runSecretscan(directory), TOOL_TIMEOUT_MS);
|
|
59288
59909
|
return {
|
|
59289
59910
|
ran: true,
|
|
59290
59911
|
result,
|
|
@@ -59363,7 +59984,7 @@ async function runSecretscanWithFiles(files, directory) {
|
|
|
59363
59984
|
skippedFiles++;
|
|
59364
59985
|
continue;
|
|
59365
59986
|
}
|
|
59366
|
-
const resolvedPath =
|
|
59987
|
+
const resolvedPath = path47.resolve(file3);
|
|
59367
59988
|
const validationError = validatePath(resolvedPath, directory, directory);
|
|
59368
59989
|
if (validationError) {
|
|
59369
59990
|
skippedFiles++;
|
|
@@ -59381,14 +60002,14 @@ async function runSecretscanWithFiles(files, directory) {
|
|
|
59381
60002
|
};
|
|
59382
60003
|
}
|
|
59383
60004
|
for (const file3 of validatedFiles) {
|
|
59384
|
-
const ext =
|
|
60005
|
+
const ext = path47.extname(file3).toLowerCase();
|
|
59385
60006
|
if (DEFAULT_EXCLUDE_EXTENSIONS2.has(ext)) {
|
|
59386
60007
|
skippedFiles++;
|
|
59387
60008
|
continue;
|
|
59388
60009
|
}
|
|
59389
60010
|
let stat2;
|
|
59390
60011
|
try {
|
|
59391
|
-
stat2 =
|
|
60012
|
+
stat2 = fs34.statSync(file3);
|
|
59392
60013
|
} catch {
|
|
59393
60014
|
skippedFiles++;
|
|
59394
60015
|
continue;
|
|
@@ -59399,7 +60020,7 @@ async function runSecretscanWithFiles(files, directory) {
|
|
|
59399
60020
|
}
|
|
59400
60021
|
let content;
|
|
59401
60022
|
try {
|
|
59402
|
-
const buffer =
|
|
60023
|
+
const buffer = fs34.readFileSync(file3);
|
|
59403
60024
|
if (buffer.includes(0)) {
|
|
59404
60025
|
skippedFiles++;
|
|
59405
60026
|
continue;
|
|
@@ -59468,7 +60089,7 @@ async function runSecretscanWithFiles(files, directory) {
|
|
|
59468
60089
|
async function runSastScanWrapped(changedFiles, directory, severityThreshold, config3) {
|
|
59469
60090
|
const start2 = process.hrtime.bigint();
|
|
59470
60091
|
try {
|
|
59471
|
-
const result = await
|
|
60092
|
+
const result = await runWithTimeout2(sastScan({ changed_files: changedFiles, severity_threshold: severityThreshold }, directory, config3), TOOL_TIMEOUT_MS);
|
|
59472
60093
|
return {
|
|
59473
60094
|
ran: true,
|
|
59474
60095
|
result,
|
|
@@ -59485,7 +60106,7 @@ async function runSastScanWrapped(changedFiles, directory, severityThreshold, co
|
|
|
59485
60106
|
async function runQualityBudgetWrapped(changedFiles, directory, _config) {
|
|
59486
60107
|
const start2 = process.hrtime.bigint();
|
|
59487
60108
|
try {
|
|
59488
|
-
const result = await
|
|
60109
|
+
const result = await runWithTimeout2(qualityBudget({ changed_files: changedFiles }, directory), TOOL_TIMEOUT_MS);
|
|
59489
60110
|
return {
|
|
59490
60111
|
ran: true,
|
|
59491
60112
|
result,
|
|
@@ -59540,7 +60161,7 @@ async function runPreCheckBatch(input, workspaceDir) {
|
|
|
59540
60161
|
warn(`pre_check_batch: Invalid file path: ${file3}`);
|
|
59541
60162
|
continue;
|
|
59542
60163
|
}
|
|
59543
|
-
changedFiles.push(
|
|
60164
|
+
changedFiles.push(path47.resolve(directory, file3));
|
|
59544
60165
|
}
|
|
59545
60166
|
if (changedFiles.length === 0) {
|
|
59546
60167
|
warn("pre_check_batch: No valid files after validation, skipping all tools (fail-closed)");
|
|
@@ -59691,7 +60312,7 @@ var pre_check_batch = createSwarmTool({
|
|
|
59691
60312
|
};
|
|
59692
60313
|
return JSON.stringify(errorResult, null, 2);
|
|
59693
60314
|
}
|
|
59694
|
-
const resolvedDirectory =
|
|
60315
|
+
const resolvedDirectory = path47.resolve(typedArgs.directory);
|
|
59695
60316
|
const workspaceAnchor = resolvedDirectory;
|
|
59696
60317
|
const dirError = validateDirectory3(resolvedDirectory, workspaceAnchor);
|
|
59697
60318
|
if (dirError) {
|
|
@@ -59798,8 +60419,8 @@ ${paginatedContent}`;
|
|
|
59798
60419
|
init_tool();
|
|
59799
60420
|
init_manager2();
|
|
59800
60421
|
init_create_tool();
|
|
59801
|
-
import * as
|
|
59802
|
-
import * as
|
|
60422
|
+
import * as fs35 from "fs";
|
|
60423
|
+
import * as path48 from "path";
|
|
59803
60424
|
function detectPlaceholderContent(args2) {
|
|
59804
60425
|
const issues = [];
|
|
59805
60426
|
const placeholderPattern = /^\[\w[\w\s]*\]$/;
|
|
@@ -59903,19 +60524,19 @@ async function executeSavePlan(args2, fallbackDir) {
|
|
|
59903
60524
|
try {
|
|
59904
60525
|
await savePlan(dir, plan);
|
|
59905
60526
|
try {
|
|
59906
|
-
const markerPath =
|
|
60527
|
+
const markerPath = path48.join(dir, ".swarm", ".plan-write-marker");
|
|
59907
60528
|
const marker = JSON.stringify({
|
|
59908
60529
|
source: "save_plan",
|
|
59909
60530
|
timestamp: new Date().toISOString(),
|
|
59910
60531
|
phases_count: plan.phases.length,
|
|
59911
60532
|
tasks_count: tasksCount
|
|
59912
60533
|
});
|
|
59913
|
-
await
|
|
60534
|
+
await fs35.promises.writeFile(markerPath, marker, "utf8");
|
|
59914
60535
|
} catch {}
|
|
59915
60536
|
return {
|
|
59916
60537
|
success: true,
|
|
59917
60538
|
message: "Plan saved successfully",
|
|
59918
|
-
plan_path:
|
|
60539
|
+
plan_path: path48.join(dir, ".swarm", "plan.json"),
|
|
59919
60540
|
phases_count: plan.phases.length,
|
|
59920
60541
|
tasks_count: tasksCount
|
|
59921
60542
|
};
|
|
@@ -59953,8 +60574,8 @@ var save_plan = createSwarmTool({
|
|
|
59953
60574
|
// src/tools/sbom-generate.ts
|
|
59954
60575
|
init_dist();
|
|
59955
60576
|
init_manager();
|
|
59956
|
-
import * as
|
|
59957
|
-
import * as
|
|
60577
|
+
import * as fs36 from "fs";
|
|
60578
|
+
import * as path49 from "path";
|
|
59958
60579
|
|
|
59959
60580
|
// src/sbom/detectors/index.ts
|
|
59960
60581
|
init_utils();
|
|
@@ -60648,8 +61269,8 @@ function parsePackageResolved(content) {
|
|
|
60648
61269
|
const pins = resolved.pins || [];
|
|
60649
61270
|
for (const pin of pins) {
|
|
60650
61271
|
const identity = pin.identity || pin.package || "";
|
|
60651
|
-
const
|
|
60652
|
-
const version3 =
|
|
61272
|
+
const state2 = pin.state || {};
|
|
61273
|
+
const version3 = state2.version || state2.revision || "";
|
|
60653
61274
|
let org = "";
|
|
60654
61275
|
const location = pin.location || "";
|
|
60655
61276
|
const orgMatch = location.match(/github\.com\/([^/]+)\//);
|
|
@@ -60800,9 +61421,9 @@ function findManifestFiles(rootDir) {
|
|
|
60800
61421
|
const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
|
|
60801
61422
|
function searchDir(dir) {
|
|
60802
61423
|
try {
|
|
60803
|
-
const entries =
|
|
61424
|
+
const entries = fs36.readdirSync(dir, { withFileTypes: true });
|
|
60804
61425
|
for (const entry of entries) {
|
|
60805
|
-
const fullPath =
|
|
61426
|
+
const fullPath = path49.join(dir, entry.name);
|
|
60806
61427
|
if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target") {
|
|
60807
61428
|
continue;
|
|
60808
61429
|
}
|
|
@@ -60811,7 +61432,7 @@ function findManifestFiles(rootDir) {
|
|
|
60811
61432
|
} else if (entry.isFile()) {
|
|
60812
61433
|
for (const pattern of patterns) {
|
|
60813
61434
|
if (simpleGlobToRegex(pattern).test(entry.name)) {
|
|
60814
|
-
manifestFiles.push(
|
|
61435
|
+
manifestFiles.push(path49.relative(rootDir, fullPath));
|
|
60815
61436
|
break;
|
|
60816
61437
|
}
|
|
60817
61438
|
}
|
|
@@ -60827,13 +61448,13 @@ function findManifestFilesInDirs(directories, workingDir) {
|
|
|
60827
61448
|
const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
|
|
60828
61449
|
for (const dir of directories) {
|
|
60829
61450
|
try {
|
|
60830
|
-
const entries =
|
|
61451
|
+
const entries = fs36.readdirSync(dir, { withFileTypes: true });
|
|
60831
61452
|
for (const entry of entries) {
|
|
60832
|
-
const fullPath =
|
|
61453
|
+
const fullPath = path49.join(dir, entry.name);
|
|
60833
61454
|
if (entry.isFile()) {
|
|
60834
61455
|
for (const pattern of patterns) {
|
|
60835
61456
|
if (simpleGlobToRegex(pattern).test(entry.name)) {
|
|
60836
|
-
found.push(
|
|
61457
|
+
found.push(path49.relative(workingDir, fullPath));
|
|
60837
61458
|
break;
|
|
60838
61459
|
}
|
|
60839
61460
|
}
|
|
@@ -60846,11 +61467,11 @@ function findManifestFilesInDirs(directories, workingDir) {
|
|
|
60846
61467
|
function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
|
|
60847
61468
|
const dirs = new Set;
|
|
60848
61469
|
for (const file3 of changedFiles) {
|
|
60849
|
-
let currentDir =
|
|
61470
|
+
let currentDir = path49.dirname(file3);
|
|
60850
61471
|
while (true) {
|
|
60851
|
-
if (currentDir && currentDir !== "." && currentDir !==
|
|
60852
|
-
dirs.add(
|
|
60853
|
-
const parent =
|
|
61472
|
+
if (currentDir && currentDir !== "." && currentDir !== path49.sep) {
|
|
61473
|
+
dirs.add(path49.join(workingDir, currentDir));
|
|
61474
|
+
const parent = path49.dirname(currentDir);
|
|
60854
61475
|
if (parent === currentDir)
|
|
60855
61476
|
break;
|
|
60856
61477
|
currentDir = parent;
|
|
@@ -60864,7 +61485,7 @@ function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
|
|
|
60864
61485
|
}
|
|
60865
61486
|
function ensureOutputDir(outputDir) {
|
|
60866
61487
|
try {
|
|
60867
|
-
|
|
61488
|
+
fs36.mkdirSync(outputDir, { recursive: true });
|
|
60868
61489
|
} catch (error93) {
|
|
60869
61490
|
if (!error93 || error93.code !== "EEXIST") {
|
|
60870
61491
|
throw error93;
|
|
@@ -60934,7 +61555,7 @@ var sbom_generate = createSwarmTool({
|
|
|
60934
61555
|
const changedFiles = obj.changed_files;
|
|
60935
61556
|
const relativeOutputDir = obj.output_dir || DEFAULT_OUTPUT_DIR;
|
|
60936
61557
|
const workingDir = directory;
|
|
60937
|
-
const outputDir =
|
|
61558
|
+
const outputDir = path49.isAbsolute(relativeOutputDir) ? relativeOutputDir : path49.join(workingDir, relativeOutputDir);
|
|
60938
61559
|
let manifestFiles = [];
|
|
60939
61560
|
if (scope === "all") {
|
|
60940
61561
|
manifestFiles = findManifestFiles(workingDir);
|
|
@@ -60957,11 +61578,11 @@ var sbom_generate = createSwarmTool({
|
|
|
60957
61578
|
const processedFiles = [];
|
|
60958
61579
|
for (const manifestFile of manifestFiles) {
|
|
60959
61580
|
try {
|
|
60960
|
-
const fullPath =
|
|
60961
|
-
if (!
|
|
61581
|
+
const fullPath = path49.isAbsolute(manifestFile) ? manifestFile : path49.join(workingDir, manifestFile);
|
|
61582
|
+
if (!fs36.existsSync(fullPath)) {
|
|
60962
61583
|
continue;
|
|
60963
61584
|
}
|
|
60964
|
-
const content =
|
|
61585
|
+
const content = fs36.readFileSync(fullPath, "utf-8");
|
|
60965
61586
|
const components = detectComponents(manifestFile, content);
|
|
60966
61587
|
processedFiles.push(manifestFile);
|
|
60967
61588
|
if (components.length > 0) {
|
|
@@ -60974,8 +61595,8 @@ var sbom_generate = createSwarmTool({
|
|
|
60974
61595
|
const bom = generateCycloneDX(allComponents);
|
|
60975
61596
|
const bomJson = serializeCycloneDX(bom);
|
|
60976
61597
|
const filename = generateSbomFilename();
|
|
60977
|
-
const outputPath =
|
|
60978
|
-
|
|
61598
|
+
const outputPath = path49.join(outputDir, filename);
|
|
61599
|
+
fs36.writeFileSync(outputPath, bomJson, "utf-8");
|
|
60979
61600
|
const verdict = processedFiles.length > 0 ? "pass" : "pass";
|
|
60980
61601
|
try {
|
|
60981
61602
|
const timestamp = new Date().toISOString();
|
|
@@ -61017,8 +61638,8 @@ var sbom_generate = createSwarmTool({
|
|
|
61017
61638
|
// src/tools/schema-drift.ts
|
|
61018
61639
|
init_dist();
|
|
61019
61640
|
init_create_tool();
|
|
61020
|
-
import * as
|
|
61021
|
-
import * as
|
|
61641
|
+
import * as fs37 from "fs";
|
|
61642
|
+
import * as path50 from "path";
|
|
61022
61643
|
var SPEC_CANDIDATES = [
|
|
61023
61644
|
"openapi.json",
|
|
61024
61645
|
"openapi.yaml",
|
|
@@ -61050,28 +61671,28 @@ function normalizePath2(p) {
|
|
|
61050
61671
|
}
|
|
61051
61672
|
function discoverSpecFile(cwd, specFileArg) {
|
|
61052
61673
|
if (specFileArg) {
|
|
61053
|
-
const resolvedPath =
|
|
61054
|
-
const normalizedCwd = cwd.endsWith(
|
|
61674
|
+
const resolvedPath = path50.resolve(cwd, specFileArg);
|
|
61675
|
+
const normalizedCwd = cwd.endsWith(path50.sep) ? cwd : cwd + path50.sep;
|
|
61055
61676
|
if (!resolvedPath.startsWith(normalizedCwd) && resolvedPath !== cwd) {
|
|
61056
61677
|
throw new Error("Invalid spec_file: path traversal detected");
|
|
61057
61678
|
}
|
|
61058
|
-
const ext =
|
|
61679
|
+
const ext = path50.extname(resolvedPath).toLowerCase();
|
|
61059
61680
|
if (!ALLOWED_EXTENSIONS.includes(ext)) {
|
|
61060
61681
|
throw new Error(`Invalid spec_file: must end in .json, .yaml, or .yml, got ${ext}`);
|
|
61061
61682
|
}
|
|
61062
|
-
const stats =
|
|
61683
|
+
const stats = fs37.statSync(resolvedPath);
|
|
61063
61684
|
if (stats.size > MAX_SPEC_SIZE) {
|
|
61064
61685
|
throw new Error(`Invalid spec_file: file exceeds ${MAX_SPEC_SIZE / 1024 / 1024}MB limit`);
|
|
61065
61686
|
}
|
|
61066
|
-
if (!
|
|
61687
|
+
if (!fs37.existsSync(resolvedPath)) {
|
|
61067
61688
|
throw new Error(`Spec file not found: ${resolvedPath}`);
|
|
61068
61689
|
}
|
|
61069
61690
|
return resolvedPath;
|
|
61070
61691
|
}
|
|
61071
61692
|
for (const candidate of SPEC_CANDIDATES) {
|
|
61072
|
-
const candidatePath =
|
|
61073
|
-
if (
|
|
61074
|
-
const stats =
|
|
61693
|
+
const candidatePath = path50.resolve(cwd, candidate);
|
|
61694
|
+
if (fs37.existsSync(candidatePath)) {
|
|
61695
|
+
const stats = fs37.statSync(candidatePath);
|
|
61075
61696
|
if (stats.size <= MAX_SPEC_SIZE) {
|
|
61076
61697
|
return candidatePath;
|
|
61077
61698
|
}
|
|
@@ -61080,8 +61701,8 @@ function discoverSpecFile(cwd, specFileArg) {
|
|
|
61080
61701
|
return null;
|
|
61081
61702
|
}
|
|
61082
61703
|
function parseSpec(specFile) {
|
|
61083
|
-
const content =
|
|
61084
|
-
const ext =
|
|
61704
|
+
const content = fs37.readFileSync(specFile, "utf-8");
|
|
61705
|
+
const ext = path50.extname(specFile).toLowerCase();
|
|
61085
61706
|
if (ext === ".json") {
|
|
61086
61707
|
return parseJsonSpec(content);
|
|
61087
61708
|
}
|
|
@@ -61152,12 +61773,12 @@ function extractRoutes(cwd) {
|
|
|
61152
61773
|
function walkDir(dir) {
|
|
61153
61774
|
let entries;
|
|
61154
61775
|
try {
|
|
61155
|
-
entries =
|
|
61776
|
+
entries = fs37.readdirSync(dir, { withFileTypes: true });
|
|
61156
61777
|
} catch {
|
|
61157
61778
|
return;
|
|
61158
61779
|
}
|
|
61159
61780
|
for (const entry of entries) {
|
|
61160
|
-
const fullPath =
|
|
61781
|
+
const fullPath = path50.join(dir, entry.name);
|
|
61161
61782
|
if (entry.isSymbolicLink()) {
|
|
61162
61783
|
continue;
|
|
61163
61784
|
}
|
|
@@ -61167,7 +61788,7 @@ function extractRoutes(cwd) {
|
|
|
61167
61788
|
}
|
|
61168
61789
|
walkDir(fullPath);
|
|
61169
61790
|
} else if (entry.isFile()) {
|
|
61170
|
-
const ext =
|
|
61791
|
+
const ext = path50.extname(entry.name).toLowerCase();
|
|
61171
61792
|
const baseName = entry.name.toLowerCase();
|
|
61172
61793
|
if (![".ts", ".js", ".mjs"].includes(ext)) {
|
|
61173
61794
|
continue;
|
|
@@ -61185,7 +61806,7 @@ function extractRoutes(cwd) {
|
|
|
61185
61806
|
}
|
|
61186
61807
|
function extractRoutesFromFile(filePath) {
|
|
61187
61808
|
const routes = [];
|
|
61188
|
-
const content =
|
|
61809
|
+
const content = fs37.readFileSync(filePath, "utf-8");
|
|
61189
61810
|
const lines = content.split(/\r?\n/);
|
|
61190
61811
|
const expressRegex = /(?:app|router|server|express)\.(get|post|put|patch|delete|options|head)\s*\(\s*['"`]([^'"`]+)['"`]/g;
|
|
61191
61812
|
const flaskRegex = /@(?:app|blueprint|bp)\.route\s*\(\s*['"]([^'"]+)['"]/g;
|
|
@@ -61336,8 +61957,8 @@ init_secretscan();
|
|
|
61336
61957
|
// src/tools/symbols.ts
|
|
61337
61958
|
init_tool();
|
|
61338
61959
|
init_create_tool();
|
|
61339
|
-
import * as
|
|
61340
|
-
import * as
|
|
61960
|
+
import * as fs38 from "fs";
|
|
61961
|
+
import * as path51 from "path";
|
|
61341
61962
|
var MAX_FILE_SIZE_BYTES7 = 1024 * 1024;
|
|
61342
61963
|
var WINDOWS_RESERVED_NAMES = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
|
|
61343
61964
|
function containsControlCharacters(str) {
|
|
@@ -61366,11 +61987,11 @@ function containsWindowsAttacks(str) {
|
|
|
61366
61987
|
}
|
|
61367
61988
|
function isPathInWorkspace(filePath, workspace) {
|
|
61368
61989
|
try {
|
|
61369
|
-
const resolvedPath =
|
|
61370
|
-
const realWorkspace =
|
|
61371
|
-
const realResolvedPath =
|
|
61372
|
-
const relativePath =
|
|
61373
|
-
if (relativePath.startsWith("..") ||
|
|
61990
|
+
const resolvedPath = path51.resolve(workspace, filePath);
|
|
61991
|
+
const realWorkspace = fs38.realpathSync(workspace);
|
|
61992
|
+
const realResolvedPath = fs38.realpathSync(resolvedPath);
|
|
61993
|
+
const relativePath = path51.relative(realWorkspace, realResolvedPath);
|
|
61994
|
+
if (relativePath.startsWith("..") || path51.isAbsolute(relativePath)) {
|
|
61374
61995
|
return false;
|
|
61375
61996
|
}
|
|
61376
61997
|
return true;
|
|
@@ -61382,17 +62003,17 @@ function validatePathForRead(filePath, workspace) {
|
|
|
61382
62003
|
return isPathInWorkspace(filePath, workspace);
|
|
61383
62004
|
}
|
|
61384
62005
|
function extractTSSymbols(filePath, cwd) {
|
|
61385
|
-
const fullPath =
|
|
62006
|
+
const fullPath = path51.join(cwd, filePath);
|
|
61386
62007
|
if (!validatePathForRead(fullPath, cwd)) {
|
|
61387
62008
|
return [];
|
|
61388
62009
|
}
|
|
61389
62010
|
let content;
|
|
61390
62011
|
try {
|
|
61391
|
-
const stats =
|
|
62012
|
+
const stats = fs38.statSync(fullPath);
|
|
61392
62013
|
if (stats.size > MAX_FILE_SIZE_BYTES7) {
|
|
61393
62014
|
throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES7})`);
|
|
61394
62015
|
}
|
|
61395
|
-
content =
|
|
62016
|
+
content = fs38.readFileSync(fullPath, "utf-8");
|
|
61396
62017
|
} catch {
|
|
61397
62018
|
return [];
|
|
61398
62019
|
}
|
|
@@ -61534,17 +62155,17 @@ function extractTSSymbols(filePath, cwd) {
|
|
|
61534
62155
|
});
|
|
61535
62156
|
}
|
|
61536
62157
|
function extractPythonSymbols(filePath, cwd) {
|
|
61537
|
-
const fullPath =
|
|
62158
|
+
const fullPath = path51.join(cwd, filePath);
|
|
61538
62159
|
if (!validatePathForRead(fullPath, cwd)) {
|
|
61539
62160
|
return [];
|
|
61540
62161
|
}
|
|
61541
62162
|
let content;
|
|
61542
62163
|
try {
|
|
61543
|
-
const stats =
|
|
62164
|
+
const stats = fs38.statSync(fullPath);
|
|
61544
62165
|
if (stats.size > MAX_FILE_SIZE_BYTES7) {
|
|
61545
62166
|
throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES7})`);
|
|
61546
62167
|
}
|
|
61547
|
-
content =
|
|
62168
|
+
content = fs38.readFileSync(fullPath, "utf-8");
|
|
61548
62169
|
} catch {
|
|
61549
62170
|
return [];
|
|
61550
62171
|
}
|
|
@@ -61617,7 +62238,7 @@ var symbols = createSwarmTool({
|
|
|
61617
62238
|
}, null, 2);
|
|
61618
62239
|
}
|
|
61619
62240
|
const cwd = directory;
|
|
61620
|
-
const ext =
|
|
62241
|
+
const ext = path51.extname(file3);
|
|
61621
62242
|
if (containsControlCharacters(file3)) {
|
|
61622
62243
|
return JSON.stringify({
|
|
61623
62244
|
file: file3,
|
|
@@ -61688,8 +62309,8 @@ init_test_runner();
|
|
|
61688
62309
|
init_dist();
|
|
61689
62310
|
init_utils();
|
|
61690
62311
|
init_create_tool();
|
|
61691
|
-
import * as
|
|
61692
|
-
import * as
|
|
62312
|
+
import * as fs39 from "fs";
|
|
62313
|
+
import * as path52 from "path";
|
|
61693
62314
|
var MAX_TEXT_LENGTH = 200;
|
|
61694
62315
|
var MAX_FILE_SIZE_BYTES8 = 1024 * 1024;
|
|
61695
62316
|
var SUPPORTED_EXTENSIONS2 = new Set([
|
|
@@ -61760,9 +62381,9 @@ function validatePathsInput(paths, cwd) {
|
|
|
61760
62381
|
return { error: "paths contains path traversal", resolvedPath: null };
|
|
61761
62382
|
}
|
|
61762
62383
|
try {
|
|
61763
|
-
const resolvedPath =
|
|
61764
|
-
const normalizedCwd =
|
|
61765
|
-
const normalizedResolved =
|
|
62384
|
+
const resolvedPath = path52.resolve(paths);
|
|
62385
|
+
const normalizedCwd = path52.resolve(cwd);
|
|
62386
|
+
const normalizedResolved = path52.resolve(resolvedPath);
|
|
61766
62387
|
if (!normalizedResolved.startsWith(normalizedCwd)) {
|
|
61767
62388
|
return {
|
|
61768
62389
|
error: "paths must be within the current working directory",
|
|
@@ -61778,13 +62399,13 @@ function validatePathsInput(paths, cwd) {
|
|
|
61778
62399
|
}
|
|
61779
62400
|
}
|
|
61780
62401
|
function isSupportedExtension(filePath) {
|
|
61781
|
-
const ext =
|
|
62402
|
+
const ext = path52.extname(filePath).toLowerCase();
|
|
61782
62403
|
return SUPPORTED_EXTENSIONS2.has(ext);
|
|
61783
62404
|
}
|
|
61784
62405
|
function findSourceFiles2(dir, files = []) {
|
|
61785
62406
|
let entries;
|
|
61786
62407
|
try {
|
|
61787
|
-
entries =
|
|
62408
|
+
entries = fs39.readdirSync(dir);
|
|
61788
62409
|
} catch {
|
|
61789
62410
|
return files;
|
|
61790
62411
|
}
|
|
@@ -61793,10 +62414,10 @@ function findSourceFiles2(dir, files = []) {
|
|
|
61793
62414
|
if (SKIP_DIRECTORIES3.has(entry)) {
|
|
61794
62415
|
continue;
|
|
61795
62416
|
}
|
|
61796
|
-
const fullPath =
|
|
62417
|
+
const fullPath = path52.join(dir, entry);
|
|
61797
62418
|
let stat2;
|
|
61798
62419
|
try {
|
|
61799
|
-
stat2 =
|
|
62420
|
+
stat2 = fs39.statSync(fullPath);
|
|
61800
62421
|
} catch {
|
|
61801
62422
|
continue;
|
|
61802
62423
|
}
|
|
@@ -61889,7 +62510,7 @@ var todo_extract = createSwarmTool({
|
|
|
61889
62510
|
return JSON.stringify(errorResult, null, 2);
|
|
61890
62511
|
}
|
|
61891
62512
|
const scanPath = resolvedPath;
|
|
61892
|
-
if (!
|
|
62513
|
+
if (!fs39.existsSync(scanPath)) {
|
|
61893
62514
|
const errorResult = {
|
|
61894
62515
|
error: `path not found: ${pathsInput}`,
|
|
61895
62516
|
total: 0,
|
|
@@ -61899,13 +62520,13 @@ var todo_extract = createSwarmTool({
|
|
|
61899
62520
|
return JSON.stringify(errorResult, null, 2);
|
|
61900
62521
|
}
|
|
61901
62522
|
const filesToScan = [];
|
|
61902
|
-
const stat2 =
|
|
62523
|
+
const stat2 = fs39.statSync(scanPath);
|
|
61903
62524
|
if (stat2.isFile()) {
|
|
61904
62525
|
if (isSupportedExtension(scanPath)) {
|
|
61905
62526
|
filesToScan.push(scanPath);
|
|
61906
62527
|
} else {
|
|
61907
62528
|
const errorResult = {
|
|
61908
|
-
error: `unsupported file extension: ${
|
|
62529
|
+
error: `unsupported file extension: ${path52.extname(scanPath)}`,
|
|
61909
62530
|
total: 0,
|
|
61910
62531
|
byPriority: { high: 0, medium: 0, low: 0 },
|
|
61911
62532
|
entries: []
|
|
@@ -61918,11 +62539,11 @@ var todo_extract = createSwarmTool({
|
|
|
61918
62539
|
const allEntries = [];
|
|
61919
62540
|
for (const filePath of filesToScan) {
|
|
61920
62541
|
try {
|
|
61921
|
-
const fileStat =
|
|
62542
|
+
const fileStat = fs39.statSync(filePath);
|
|
61922
62543
|
if (fileStat.size > MAX_FILE_SIZE_BYTES8) {
|
|
61923
62544
|
continue;
|
|
61924
62545
|
}
|
|
61925
|
-
const content =
|
|
62546
|
+
const content = fs39.readFileSync(filePath, "utf-8");
|
|
61926
62547
|
const entries = parseTodoComments(content, filePath, tagsSet);
|
|
61927
62548
|
allEntries.push(...entries);
|
|
61928
62549
|
} catch {}
|
|
@@ -61950,9 +62571,95 @@ var todo_extract = createSwarmTool({
|
|
|
61950
62571
|
// src/tools/update-task-status.ts
|
|
61951
62572
|
init_tool();
|
|
61952
62573
|
init_schema();
|
|
62574
|
+
import * as fs41 from "fs";
|
|
62575
|
+
import * as path54 from "path";
|
|
62576
|
+
|
|
62577
|
+
// src/hooks/diff-scope.ts
|
|
62578
|
+
import * as fs40 from "fs";
|
|
62579
|
+
import * as path53 from "path";
|
|
62580
|
+
function getDeclaredScope(taskId, directory) {
|
|
62581
|
+
try {
|
|
62582
|
+
const planPath = path53.join(directory, ".swarm", "plan.json");
|
|
62583
|
+
if (!fs40.existsSync(planPath))
|
|
62584
|
+
return null;
|
|
62585
|
+
const raw = fs40.readFileSync(planPath, "utf-8");
|
|
62586
|
+
const plan = JSON.parse(raw);
|
|
62587
|
+
for (const phase of plan.phases ?? []) {
|
|
62588
|
+
for (const task of phase.tasks ?? []) {
|
|
62589
|
+
if (task.id !== taskId)
|
|
62590
|
+
continue;
|
|
62591
|
+
const ft = task.files_touched;
|
|
62592
|
+
if (Array.isArray(ft) && ft.length > 0) {
|
|
62593
|
+
return ft;
|
|
62594
|
+
}
|
|
62595
|
+
if (typeof ft === "string" && ft.length > 0) {
|
|
62596
|
+
return [ft];
|
|
62597
|
+
}
|
|
62598
|
+
return null;
|
|
62599
|
+
}
|
|
62600
|
+
}
|
|
62601
|
+
return null;
|
|
62602
|
+
} catch {
|
|
62603
|
+
return null;
|
|
62604
|
+
}
|
|
62605
|
+
}
|
|
62606
|
+
async function getChangedFiles(directory) {
|
|
62607
|
+
try {
|
|
62608
|
+
const proc = Bun.spawn(["git", "diff", "--name-only", "HEAD~1"], {
|
|
62609
|
+
cwd: directory,
|
|
62610
|
+
stdout: "pipe",
|
|
62611
|
+
stderr: "pipe"
|
|
62612
|
+
});
|
|
62613
|
+
const [exitCode, stdout] = await Promise.all([
|
|
62614
|
+
proc.exited,
|
|
62615
|
+
new Response(proc.stdout).text()
|
|
62616
|
+
]);
|
|
62617
|
+
if (exitCode === 0) {
|
|
62618
|
+
return stdout.trim().split(`
|
|
62619
|
+
`).map((f) => f.trim()).filter((f) => f.length > 0);
|
|
62620
|
+
}
|
|
62621
|
+
const proc2 = Bun.spawn(["git", "diff", "--name-only", "HEAD"], {
|
|
62622
|
+
cwd: directory,
|
|
62623
|
+
stdout: "pipe",
|
|
62624
|
+
stderr: "pipe"
|
|
62625
|
+
});
|
|
62626
|
+
const [exitCode2, stdout2] = await Promise.all([
|
|
62627
|
+
proc2.exited,
|
|
62628
|
+
new Response(proc2.stdout).text()
|
|
62629
|
+
]);
|
|
62630
|
+
if (exitCode2 === 0) {
|
|
62631
|
+
return stdout2.trim().split(`
|
|
62632
|
+
`).map((f) => f.trim()).filter((f) => f.length > 0);
|
|
62633
|
+
}
|
|
62634
|
+
return null;
|
|
62635
|
+
} catch {
|
|
62636
|
+
return null;
|
|
62637
|
+
}
|
|
62638
|
+
}
|
|
62639
|
+
async function validateDiffScope(taskId, directory) {
|
|
62640
|
+
try {
|
|
62641
|
+
const declaredScope = getDeclaredScope(taskId, directory);
|
|
62642
|
+
if (!declaredScope)
|
|
62643
|
+
return null;
|
|
62644
|
+
const changedFiles = await getChangedFiles(directory);
|
|
62645
|
+
if (!changedFiles)
|
|
62646
|
+
return null;
|
|
62647
|
+
const normalise = (p) => p.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
62648
|
+
const normScope = new Set(declaredScope.map(normalise));
|
|
62649
|
+
const undeclared = changedFiles.map(normalise).filter((f) => !normScope.has(f));
|
|
62650
|
+
if (undeclared.length === 0)
|
|
62651
|
+
return null;
|
|
62652
|
+
const scopeStr = declaredScope.join(", ");
|
|
62653
|
+
const undeclaredStr = undeclared.slice(0, 5).join(", ");
|
|
62654
|
+
const extra = undeclared.length > 5 ? ` (+${undeclared.length - 5} more)` : "";
|
|
62655
|
+
return `SCOPE WARNING: Task ${taskId} declared scope [${scopeStr}] but also modified [${undeclaredStr}${extra}]. Reviewer should verify these changes are intentional.`;
|
|
62656
|
+
} catch {
|
|
62657
|
+
return null;
|
|
62658
|
+
}
|
|
62659
|
+
}
|
|
62660
|
+
|
|
62661
|
+
// src/tools/update-task-status.ts
|
|
61953
62662
|
init_manager2();
|
|
61954
|
-
import * as fs38 from "fs";
|
|
61955
|
-
import * as path51 from "path";
|
|
61956
62663
|
init_create_tool();
|
|
61957
62664
|
var VALID_STATUSES2 = [
|
|
61958
62665
|
"pending",
|
|
@@ -61987,7 +62694,7 @@ var TIER_3_PATTERNS = [
|
|
|
61987
62694
|
];
|
|
61988
62695
|
function matchesTier3Pattern(files) {
|
|
61989
62696
|
for (const file3 of files) {
|
|
61990
|
-
const fileName =
|
|
62697
|
+
const fileName = path54.basename(file3);
|
|
61991
62698
|
for (const pattern of TIER_3_PATTERNS) {
|
|
61992
62699
|
if (pattern.test(fileName)) {
|
|
61993
62700
|
return true;
|
|
@@ -62009,8 +62716,8 @@ function checkReviewerGate(taskId, workingDirectory) {
|
|
|
62009
62716
|
if (hasActiveTurboMode2()) {
|
|
62010
62717
|
const resolvedDir2 = workingDirectory ?? process.cwd();
|
|
62011
62718
|
try {
|
|
62012
|
-
const planPath =
|
|
62013
|
-
const planRaw =
|
|
62719
|
+
const planPath = path54.join(resolvedDir2, ".swarm", "plan.json");
|
|
62720
|
+
const planRaw = fs41.readFileSync(planPath, "utf-8");
|
|
62014
62721
|
const plan = JSON.parse(planRaw);
|
|
62015
62722
|
for (const planPhase of plan.phases ?? []) {
|
|
62016
62723
|
for (const task of planPhase.tasks ?? []) {
|
|
@@ -62029,8 +62736,8 @@ function checkReviewerGate(taskId, workingDirectory) {
|
|
|
62029
62736
|
}
|
|
62030
62737
|
const resolvedDir = workingDirectory ?? process.cwd();
|
|
62031
62738
|
try {
|
|
62032
|
-
const evidencePath =
|
|
62033
|
-
const raw =
|
|
62739
|
+
const evidencePath = path54.join(resolvedDir, ".swarm", "evidence", `${taskId}.json`);
|
|
62740
|
+
const raw = fs41.readFileSync(evidencePath, "utf-8");
|
|
62034
62741
|
const evidence = JSON.parse(raw);
|
|
62035
62742
|
if (evidence?.required_gates && Array.isArray(evidence.required_gates) && evidence?.gates) {
|
|
62036
62743
|
const allGatesMet = evidence.required_gates.every((gate) => evidence.gates[gate] != null);
|
|
@@ -62053,8 +62760,8 @@ function checkReviewerGate(taskId, workingDirectory) {
|
|
|
62053
62760
|
continue;
|
|
62054
62761
|
}
|
|
62055
62762
|
validSessionCount++;
|
|
62056
|
-
const
|
|
62057
|
-
if (
|
|
62763
|
+
const state2 = getTaskState(session, taskId);
|
|
62764
|
+
if (state2 === "tests_run" || state2 === "complete") {
|
|
62058
62765
|
return { blocked: false, reason: "" };
|
|
62059
62766
|
}
|
|
62060
62767
|
}
|
|
@@ -62065,13 +62772,13 @@ function checkReviewerGate(taskId, workingDirectory) {
|
|
|
62065
62772
|
for (const [sessionId, session] of swarmState.agentSessions) {
|
|
62066
62773
|
if (!(session.taskWorkflowStates instanceof Map))
|
|
62067
62774
|
continue;
|
|
62068
|
-
const
|
|
62069
|
-
stateEntries.push(`${sessionId}: ${
|
|
62775
|
+
const state2 = getTaskState(session, taskId);
|
|
62776
|
+
stateEntries.push(`${sessionId}: ${state2}`);
|
|
62070
62777
|
}
|
|
62071
62778
|
try {
|
|
62072
62779
|
const resolvedDir2 = workingDirectory ?? process.cwd();
|
|
62073
|
-
const planPath =
|
|
62074
|
-
const planRaw =
|
|
62780
|
+
const planPath = path54.join(resolvedDir2, ".swarm", "plan.json");
|
|
62781
|
+
const planRaw = fs41.readFileSync(planPath, "utf-8");
|
|
62075
62782
|
const plan = JSON.parse(planRaw);
|
|
62076
62783
|
for (const planPhase of plan.phases ?? []) {
|
|
62077
62784
|
for (const task of planPhase.tasks ?? []) {
|
|
@@ -62129,6 +62836,17 @@ function checkReviewerGate(taskId, workingDirectory) {
|
|
|
62129
62836
|
return { blocked: false, reason: "" };
|
|
62130
62837
|
}
|
|
62131
62838
|
}
|
|
62839
|
+
async function checkReviewerGateWithScope(taskId, workingDirectory) {
|
|
62840
|
+
const result = checkReviewerGate(taskId, workingDirectory);
|
|
62841
|
+
const scopeWarning = await validateDiffScope(taskId, workingDirectory ?? process.cwd()).catch(() => null);
|
|
62842
|
+
if (!scopeWarning)
|
|
62843
|
+
return result;
|
|
62844
|
+
return {
|
|
62845
|
+
...result,
|
|
62846
|
+
reason: result.reason ? `${result.reason}
|
|
62847
|
+
${scopeWarning}` : scopeWarning
|
|
62848
|
+
};
|
|
62849
|
+
}
|
|
62132
62850
|
function recoverTaskStateFromDelegations(taskId) {
|
|
62133
62851
|
let hasReviewer = false;
|
|
62134
62852
|
let hasTestEngineer = false;
|
|
@@ -62241,8 +62959,8 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
62241
62959
|
};
|
|
62242
62960
|
}
|
|
62243
62961
|
}
|
|
62244
|
-
normalizedDir =
|
|
62245
|
-
const pathParts = normalizedDir.split(
|
|
62962
|
+
normalizedDir = path54.normalize(args2.working_directory);
|
|
62963
|
+
const pathParts = normalizedDir.split(path54.sep);
|
|
62246
62964
|
if (pathParts.includes("..")) {
|
|
62247
62965
|
return {
|
|
62248
62966
|
success: false,
|
|
@@ -62252,11 +62970,11 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
62252
62970
|
]
|
|
62253
62971
|
};
|
|
62254
62972
|
}
|
|
62255
|
-
const resolvedDir =
|
|
62973
|
+
const resolvedDir = path54.resolve(normalizedDir);
|
|
62256
62974
|
try {
|
|
62257
|
-
const realPath =
|
|
62258
|
-
const planPath =
|
|
62259
|
-
if (!
|
|
62975
|
+
const realPath = fs41.realpathSync(resolvedDir);
|
|
62976
|
+
const planPath = path54.join(realPath, ".swarm", "plan.json");
|
|
62977
|
+
if (!fs41.existsSync(planPath)) {
|
|
62260
62978
|
return {
|
|
62261
62979
|
success: false,
|
|
62262
62980
|
message: `Invalid working_directory: plan not found in "${realPath}"`,
|
|
@@ -62280,7 +62998,7 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
62280
62998
|
}
|
|
62281
62999
|
if (args2.status === "completed") {
|
|
62282
63000
|
recoverTaskStateFromDelegations(args2.task_id);
|
|
62283
|
-
const reviewerCheck =
|
|
63001
|
+
const reviewerCheck = await checkReviewerGateWithScope(args2.task_id, directory);
|
|
62284
63002
|
if (reviewerCheck.blocked) {
|
|
62285
63003
|
return {
|
|
62286
63004
|
success: false,
|
|
@@ -62403,6 +63121,43 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
62403
63121
|
const steeringConsumedHook = createSteeringConsumedHook(ctx.directory);
|
|
62404
63122
|
const coChangeSuggesterHook = createCoChangeSuggesterHook(ctx.directory);
|
|
62405
63123
|
const darkMatterDetectorHook = createDarkMatterDetectorHook(ctx.directory);
|
|
63124
|
+
const slopDetectorHook = config3.slop_detector?.enabled !== false ? createSlopDetectorHook(config3.slop_detector ?? {
|
|
63125
|
+
enabled: true,
|
|
63126
|
+
classThreshold: 3,
|
|
63127
|
+
commentStripThreshold: 5,
|
|
63128
|
+
diffLineThreshold: 200
|
|
63129
|
+
}, ctx.directory, (sessionId, message) => {
|
|
63130
|
+
const s = swarmState.agentSessions.get(sessionId);
|
|
63131
|
+
if (s) {
|
|
63132
|
+
s.pendingAdvisoryMessages ??= [];
|
|
63133
|
+
s.pendingAdvisoryMessages.push(message);
|
|
63134
|
+
}
|
|
63135
|
+
}) : null;
|
|
63136
|
+
const incrementalVerifyHook = config3.incremental_verify?.enabled !== false ? createIncrementalVerifyHook(config3.incremental_verify ?? {
|
|
63137
|
+
enabled: true,
|
|
63138
|
+
command: null,
|
|
63139
|
+
timeoutMs: 30000,
|
|
63140
|
+
triggerAgents: ["coder"]
|
|
63141
|
+
}, ctx.directory, (sessionId, message) => {
|
|
63142
|
+
const s = swarmState.agentSessions.get(sessionId);
|
|
63143
|
+
if (s) {
|
|
63144
|
+
s.pendingAdvisoryMessages ??= [];
|
|
63145
|
+
s.pendingAdvisoryMessages.push(message);
|
|
63146
|
+
}
|
|
63147
|
+
}) : null;
|
|
63148
|
+
const compactionServiceHook = config3.compaction_service?.enabled !== false ? createCompactionService(config3.compaction_service ?? {
|
|
63149
|
+
enabled: true,
|
|
63150
|
+
observationThreshold: 40,
|
|
63151
|
+
reflectionThreshold: 60,
|
|
63152
|
+
emergencyThreshold: 80,
|
|
63153
|
+
preserveLastNTurns: 5
|
|
63154
|
+
}, ctx.directory, (sessionId, message) => {
|
|
63155
|
+
const s = swarmState.agentSessions.get(sessionId);
|
|
63156
|
+
if (s) {
|
|
63157
|
+
s.pendingAdvisoryMessages ??= [];
|
|
63158
|
+
s.pendingAdvisoryMessages.push(message);
|
|
63159
|
+
}
|
|
63160
|
+
}) : null;
|
|
62406
63161
|
const snapshotWriterHook = createSnapshotWriterHook(ctx.directory);
|
|
62407
63162
|
const automationConfig = AutomationConfigSchema.parse(config3.automation ?? {});
|
|
62408
63163
|
let automationManager;
|
|
@@ -62413,7 +63168,7 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
62413
63168
|
const { PreflightTriggerManager: PTM } = await Promise.resolve().then(() => (init_trigger(), exports_trigger));
|
|
62414
63169
|
preflightTriggerManager = new PTM(automationConfig);
|
|
62415
63170
|
const { AutomationStatusArtifact: ASA } = await Promise.resolve().then(() => (init_status_artifact(), exports_status_artifact));
|
|
62416
|
-
const swarmDir =
|
|
63171
|
+
const swarmDir = path55.resolve(ctx.directory, ".swarm");
|
|
62417
63172
|
statusArtifact = new ASA(swarmDir);
|
|
62418
63173
|
statusArtifact.updateConfig(automationConfig.mode, automationConfig.capabilities);
|
|
62419
63174
|
if (automationConfig.capabilities?.evidence_auto_summaries === true) {
|
|
@@ -62675,6 +63430,14 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
62675
63430
|
}
|
|
62676
63431
|
}
|
|
62677
63432
|
await guardrailsHooks.toolBefore(input, output);
|
|
63433
|
+
if (swarmState.lastBudgetPct >= 50) {
|
|
63434
|
+
const pressureSession = ensureAgentSession(input.sessionID, swarmState.activeAgent.get(input.sessionID) ?? ORCHESTRATOR_NAME);
|
|
63435
|
+
if (!pressureSession.contextPressureWarningSent) {
|
|
63436
|
+
pressureSession.contextPressureWarningSent = true;
|
|
63437
|
+
pressureSession.pendingAdvisoryMessages ??= [];
|
|
63438
|
+
pressureSession.pendingAdvisoryMessages.push(`CONTEXT PRESSURE: ${swarmState.lastBudgetPct.toFixed(1)}% of context window used. Prioritize completing the current task before starting new work.`);
|
|
63439
|
+
}
|
|
63440
|
+
}
|
|
62678
63441
|
await safeHook(activityHooks.toolBefore)(input, output);
|
|
62679
63442
|
},
|
|
62680
63443
|
"tool.execute.after": async (input, output) => {
|
|
@@ -62690,6 +63453,12 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
62690
63453
|
await safeHook(darkMatterDetectorHook)(input, output);
|
|
62691
63454
|
await snapshotWriterHook(input, output);
|
|
62692
63455
|
await toolSummarizerHook?.(input, output);
|
|
63456
|
+
if (slopDetectorHook)
|
|
63457
|
+
await slopDetectorHook.toolAfter(input, output);
|
|
63458
|
+
if (incrementalVerifyHook)
|
|
63459
|
+
await incrementalVerifyHook.toolAfter(input, output);
|
|
63460
|
+
if (compactionServiceHook)
|
|
63461
|
+
await compactionServiceHook.toolAfter(input, output);
|
|
62693
63462
|
const toolOutputConfig = config3.tool_output;
|
|
62694
63463
|
if (toolOutputConfig && toolOutputConfig.truncation_enabled !== false && typeof output.output === "string") {
|
|
62695
63464
|
const skipTools = [
|