opencode-swarm 6.29.0 → 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 +29 -6
- package/dist/index.js +254 -200
- package/dist/services/compaction-service.d.ts +5 -0
- package/dist/state.d.ts +2 -0
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -35663,6 +35663,26 @@ function extractCurrentPhaseFromPlan2(plan) {
|
|
|
35663
35663
|
init_utils2();
|
|
35664
35664
|
init_manager2();
|
|
35665
35665
|
|
|
35666
|
+
// src/services/compaction-service.ts
|
|
35667
|
+
function makeInitialState() {
|
|
35668
|
+
return {
|
|
35669
|
+
lastObservationAt: 0,
|
|
35670
|
+
lastReflectionAt: 0,
|
|
35671
|
+
lastEmergencyAt: 0,
|
|
35672
|
+
observationCount: 0,
|
|
35673
|
+
reflectionCount: 0,
|
|
35674
|
+
emergencyCount: 0,
|
|
35675
|
+
lastSnapshotAt: null
|
|
35676
|
+
};
|
|
35677
|
+
}
|
|
35678
|
+
var state = makeInitialState();
|
|
35679
|
+
function getCompactionMetrics() {
|
|
35680
|
+
return {
|
|
35681
|
+
compactionCount: state.observationCount + state.reflectionCount + state.emergencyCount,
|
|
35682
|
+
lastSnapshotAt: state.lastSnapshotAt
|
|
35683
|
+
};
|
|
35684
|
+
}
|
|
35685
|
+
|
|
35666
35686
|
// src/services/context-budget-service.ts
|
|
35667
35687
|
init_utils2();
|
|
35668
35688
|
var DEFAULT_CONTEXT_BUDGET_CONFIG = {
|
|
@@ -35689,6 +35709,7 @@ async function getStatusData(directory, agents) {
|
|
|
35689
35709
|
}
|
|
35690
35710
|
}
|
|
35691
35711
|
const agentCount2 = Object.keys(agents).length;
|
|
35712
|
+
const metrics2 = getCompactionMetrics();
|
|
35692
35713
|
return {
|
|
35693
35714
|
hasPlan: true,
|
|
35694
35715
|
currentPhase: currentPhase2,
|
|
@@ -35698,12 +35719,13 @@ async function getStatusData(directory, agents) {
|
|
|
35698
35719
|
isLegacy: false,
|
|
35699
35720
|
turboMode: hasActiveTurboMode(),
|
|
35700
35721
|
contextBudgetPct: swarmState.lastBudgetPct > 0 ? swarmState.lastBudgetPct : null,
|
|
35701
|
-
compactionCount:
|
|
35702
|
-
lastSnapshotAt:
|
|
35722
|
+
compactionCount: metrics2.compactionCount,
|
|
35723
|
+
lastSnapshotAt: metrics2.lastSnapshotAt
|
|
35703
35724
|
};
|
|
35704
35725
|
}
|
|
35705
35726
|
const planContent = await readSwarmFileAsync(directory, "plan.md");
|
|
35706
35727
|
if (!planContent) {
|
|
35728
|
+
const metrics2 = getCompactionMetrics();
|
|
35707
35729
|
return {
|
|
35708
35730
|
hasPlan: false,
|
|
35709
35731
|
currentPhase: "Unknown",
|
|
@@ -35713,8 +35735,8 @@ async function getStatusData(directory, agents) {
|
|
|
35713
35735
|
isLegacy: true,
|
|
35714
35736
|
turboMode: hasActiveTurboMode(),
|
|
35715
35737
|
contextBudgetPct: swarmState.lastBudgetPct > 0 ? swarmState.lastBudgetPct : null,
|
|
35716
|
-
compactionCount:
|
|
35717
|
-
lastSnapshotAt:
|
|
35738
|
+
compactionCount: metrics2.compactionCount,
|
|
35739
|
+
lastSnapshotAt: metrics2.lastSnapshotAt
|
|
35718
35740
|
};
|
|
35719
35741
|
}
|
|
35720
35742
|
const currentPhase = extractCurrentPhase(planContent) || "Unknown";
|
|
@@ -35722,6 +35744,7 @@ async function getStatusData(directory, agents) {
|
|
|
35722
35744
|
const incompleteTasks = (planContent.match(/^- \[ \]/gm) || []).length;
|
|
35723
35745
|
const totalTasks = completedTasks + incompleteTasks;
|
|
35724
35746
|
const agentCount = Object.keys(agents).length;
|
|
35747
|
+
const metrics = getCompactionMetrics();
|
|
35725
35748
|
return {
|
|
35726
35749
|
hasPlan: true,
|
|
35727
35750
|
currentPhase,
|
|
@@ -35731,8 +35754,8 @@ async function getStatusData(directory, agents) {
|
|
|
35731
35754
|
isLegacy: true,
|
|
35732
35755
|
turboMode: hasActiveTurboMode(),
|
|
35733
35756
|
contextBudgetPct: swarmState.lastBudgetPct > 0 ? swarmState.lastBudgetPct : null,
|
|
35734
|
-
compactionCount:
|
|
35735
|
-
lastSnapshotAt:
|
|
35757
|
+
compactionCount: metrics.compactionCount,
|
|
35758
|
+
lastSnapshotAt: metrics.lastSnapshotAt
|
|
35736
35759
|
};
|
|
35737
35760
|
}
|
|
35738
35761
|
function formatStatusMarkdown(status) {
|
package/dist/index.js
CHANGED
|
@@ -35942,7 +35942,7 @@ __export(exports_gate_evidence, {
|
|
|
35942
35942
|
DEFAULT_REQUIRED_GATES: () => DEFAULT_REQUIRED_GATES
|
|
35943
35943
|
});
|
|
35944
35944
|
import { mkdirSync as mkdirSync8, readFileSync as readFileSync13, renameSync as renameSync7, unlinkSync as unlinkSync4 } from "fs";
|
|
35945
|
-
import * as
|
|
35945
|
+
import * as path29 from "path";
|
|
35946
35946
|
function isValidTaskId2(taskId) {
|
|
35947
35947
|
if (!taskId)
|
|
35948
35948
|
return false;
|
|
@@ -35989,10 +35989,10 @@ function expandRequiredGates(existingGates, newAgentType) {
|
|
|
35989
35989
|
return combined.sort();
|
|
35990
35990
|
}
|
|
35991
35991
|
function getEvidenceDir(directory) {
|
|
35992
|
-
return
|
|
35992
|
+
return path29.join(directory, ".swarm", "evidence");
|
|
35993
35993
|
}
|
|
35994
35994
|
function getEvidencePath(directory, taskId) {
|
|
35995
|
-
return
|
|
35995
|
+
return path29.join(getEvidenceDir(directory), `${taskId}.json`);
|
|
35996
35996
|
}
|
|
35997
35997
|
function readExisting(evidencePath) {
|
|
35998
35998
|
try {
|
|
@@ -36106,10 +36106,10 @@ function createPreflightIntegration(config3) {
|
|
|
36106
36106
|
});
|
|
36107
36107
|
const report = await runPreflight(directory, request.currentPhase, preflightConfig);
|
|
36108
36108
|
if (statusArtifact) {
|
|
36109
|
-
const
|
|
36110
|
-
statusArtifact.recordOutcome(
|
|
36109
|
+
const state2 = report.overall === "pass" ? "success" : "failure";
|
|
36110
|
+
statusArtifact.recordOutcome(state2, request.currentPhase, report.message);
|
|
36111
36111
|
console.log("[PreflightIntegration] Status artifact updated", {
|
|
36112
|
-
state,
|
|
36112
|
+
state: state2,
|
|
36113
36113
|
phase: request.currentPhase,
|
|
36114
36114
|
message: report.message
|
|
36115
36115
|
});
|
|
@@ -39418,7 +39418,8 @@ function startAgentSession(sessionId, agentName, staleDurationMs = 7200000, dire
|
|
|
39418
39418
|
scopeViolationDetected: false,
|
|
39419
39419
|
modifiedFilesThisCoderTask: [],
|
|
39420
39420
|
turboMode: false,
|
|
39421
|
-
loopDetectionWindow: []
|
|
39421
|
+
loopDetectionWindow: [],
|
|
39422
|
+
pendingAdvisoryMessages: []
|
|
39422
39423
|
};
|
|
39423
39424
|
swarmState.agentSessions.set(sessionId, sessionState);
|
|
39424
39425
|
swarmState.activeAgent.set(sessionId, agentName);
|
|
@@ -39523,6 +39524,9 @@ function ensureAgentSession(sessionId, agentName, directory) {
|
|
|
39523
39524
|
if (session.loopDetectionWindow === undefined) {
|
|
39524
39525
|
session.loopDetectionWindow = [];
|
|
39525
39526
|
}
|
|
39527
|
+
if (session.pendingAdvisoryMessages === undefined) {
|
|
39528
|
+
session.pendingAdvisoryMessages = [];
|
|
39529
|
+
}
|
|
39526
39530
|
session.lastToolCallTime = now;
|
|
39527
39531
|
return session;
|
|
39528
39532
|
}
|
|
@@ -47608,6 +47612,97 @@ No plan content available. Start by creating a .swarm/plan.md file.
|
|
|
47608
47612
|
init_utils2();
|
|
47609
47613
|
init_manager2();
|
|
47610
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
|
+
|
|
47611
47706
|
// src/services/context-budget-service.ts
|
|
47612
47707
|
init_utils2();
|
|
47613
47708
|
function validateDirectory(directory) {
|
|
@@ -47650,9 +47745,9 @@ async function readBudgetState(directory) {
|
|
|
47650
47745
|
return null;
|
|
47651
47746
|
}
|
|
47652
47747
|
}
|
|
47653
|
-
async function writeBudgetState(directory,
|
|
47748
|
+
async function writeBudgetState(directory, state2) {
|
|
47654
47749
|
const resolvedPath = validateSwarmPath(directory, "session/budget-state.json");
|
|
47655
|
-
const content = JSON.stringify(
|
|
47750
|
+
const content = JSON.stringify(state2, null, 2);
|
|
47656
47751
|
await Bun.write(resolvedPath, content);
|
|
47657
47752
|
}
|
|
47658
47753
|
async function countEvents(directory) {
|
|
@@ -47741,25 +47836,25 @@ async function formatBudgetWarning(report, directory, config3) {
|
|
|
47741
47836
|
return formatWarningMessage(report);
|
|
47742
47837
|
}
|
|
47743
47838
|
const budgetState = await readBudgetState(directory);
|
|
47744
|
-
const
|
|
47839
|
+
const state2 = budgetState || {
|
|
47745
47840
|
warningFiredAtTurn: null,
|
|
47746
47841
|
criticalFiredAtTurn: null,
|
|
47747
47842
|
lastInjectedAtTurn: null
|
|
47748
47843
|
};
|
|
47749
47844
|
const currentTurn = report.estimatedTurnCount;
|
|
47750
47845
|
if (report.status === "warning") {
|
|
47751
|
-
if (config3.warningMode === "once" &&
|
|
47846
|
+
if (config3.warningMode === "once" && state2.warningFiredAtTurn !== null) {
|
|
47752
47847
|
return null;
|
|
47753
47848
|
}
|
|
47754
|
-
if (config3.warningMode === "interval" &&
|
|
47849
|
+
if (config3.warningMode === "interval" && state2.warningFiredAtTurn !== null && currentTurn - state2.warningFiredAtTurn < config3.warningIntervalTurns) {
|
|
47755
47850
|
return null;
|
|
47756
47851
|
}
|
|
47757
|
-
|
|
47758
|
-
|
|
47759
|
-
await writeBudgetState(directory,
|
|
47852
|
+
state2.warningFiredAtTurn = currentTurn;
|
|
47853
|
+
state2.lastInjectedAtTurn = currentTurn;
|
|
47854
|
+
await writeBudgetState(directory, state2);
|
|
47760
47855
|
} else if (report.status === "critical") {
|
|
47761
|
-
|
|
47762
|
-
|
|
47856
|
+
state2.criticalFiredAtTurn = currentTurn;
|
|
47857
|
+
state2.lastInjectedAtTurn = currentTurn;
|
|
47763
47858
|
}
|
|
47764
47859
|
return formatWarningMessage(report);
|
|
47765
47860
|
}
|
|
@@ -47788,6 +47883,7 @@ async function getStatusData(directory, agents) {
|
|
|
47788
47883
|
}
|
|
47789
47884
|
}
|
|
47790
47885
|
const agentCount2 = Object.keys(agents).length;
|
|
47886
|
+
const metrics2 = getCompactionMetrics();
|
|
47791
47887
|
return {
|
|
47792
47888
|
hasPlan: true,
|
|
47793
47889
|
currentPhase: currentPhase2,
|
|
@@ -47797,12 +47893,13 @@ async function getStatusData(directory, agents) {
|
|
|
47797
47893
|
isLegacy: false,
|
|
47798
47894
|
turboMode: hasActiveTurboMode(),
|
|
47799
47895
|
contextBudgetPct: swarmState.lastBudgetPct > 0 ? swarmState.lastBudgetPct : null,
|
|
47800
|
-
compactionCount:
|
|
47801
|
-
lastSnapshotAt:
|
|
47896
|
+
compactionCount: metrics2.compactionCount,
|
|
47897
|
+
lastSnapshotAt: metrics2.lastSnapshotAt
|
|
47802
47898
|
};
|
|
47803
47899
|
}
|
|
47804
47900
|
const planContent = await readSwarmFileAsync(directory, "plan.md");
|
|
47805
47901
|
if (!planContent) {
|
|
47902
|
+
const metrics2 = getCompactionMetrics();
|
|
47806
47903
|
return {
|
|
47807
47904
|
hasPlan: false,
|
|
47808
47905
|
currentPhase: "Unknown",
|
|
@@ -47812,8 +47909,8 @@ async function getStatusData(directory, agents) {
|
|
|
47812
47909
|
isLegacy: true,
|
|
47813
47910
|
turboMode: hasActiveTurboMode(),
|
|
47814
47911
|
contextBudgetPct: swarmState.lastBudgetPct > 0 ? swarmState.lastBudgetPct : null,
|
|
47815
|
-
compactionCount:
|
|
47816
|
-
lastSnapshotAt:
|
|
47912
|
+
compactionCount: metrics2.compactionCount,
|
|
47913
|
+
lastSnapshotAt: metrics2.lastSnapshotAt
|
|
47817
47914
|
};
|
|
47818
47915
|
}
|
|
47819
47916
|
const currentPhase = extractCurrentPhase(planContent) || "Unknown";
|
|
@@ -47821,6 +47918,7 @@ async function getStatusData(directory, agents) {
|
|
|
47821
47918
|
const incompleteTasks = (planContent.match(/^- \[ \]/gm) || []).length;
|
|
47822
47919
|
const totalTasks = completedTasks + incompleteTasks;
|
|
47823
47920
|
const agentCount = Object.keys(agents).length;
|
|
47921
|
+
const metrics = getCompactionMetrics();
|
|
47824
47922
|
return {
|
|
47825
47923
|
hasPlan: true,
|
|
47826
47924
|
currentPhase,
|
|
@@ -47830,8 +47928,8 @@ async function getStatusData(directory, agents) {
|
|
|
47830
47928
|
isLegacy: true,
|
|
47831
47929
|
turboMode: hasActiveTurboMode(),
|
|
47832
47930
|
contextBudgetPct: swarmState.lastBudgetPct > 0 ? swarmState.lastBudgetPct : null,
|
|
47833
|
-
compactionCount:
|
|
47834
|
-
lastSnapshotAt:
|
|
47931
|
+
compactionCount: metrics.compactionCount,
|
|
47932
|
+
lastSnapshotAt: metrics.lastSnapshotAt
|
|
47835
47933
|
};
|
|
47836
47934
|
}
|
|
47837
47935
|
function formatStatusMarkdown(status) {
|
|
@@ -48461,11 +48559,11 @@ async function doFlush(directory) {
|
|
|
48461
48559
|
const activitySection = renderActivitySection();
|
|
48462
48560
|
const updated = replaceOrAppendSection(existing, "## Agent Activity", activitySection);
|
|
48463
48561
|
const flushedCount = swarmState.pendingEvents;
|
|
48464
|
-
const
|
|
48465
|
-
const tempPath = `${
|
|
48562
|
+
const path28 = `${directory}/.swarm/context.md`;
|
|
48563
|
+
const tempPath = `${path28}.tmp`;
|
|
48466
48564
|
try {
|
|
48467
48565
|
await Bun.write(tempPath, updated);
|
|
48468
|
-
renameSync6(tempPath,
|
|
48566
|
+
renameSync6(tempPath, path28);
|
|
48469
48567
|
} catch (writeError) {
|
|
48470
48568
|
try {
|
|
48471
48569
|
unlinkSync3(tempPath);
|
|
@@ -49035,14 +49133,14 @@ function maskToolOutput(msg, _threshold) {
|
|
|
49035
49133
|
}
|
|
49036
49134
|
// src/hooks/delegation-gate.ts
|
|
49037
49135
|
init_schema();
|
|
49038
|
-
import * as
|
|
49039
|
-
import * as
|
|
49136
|
+
import * as fs16 from "fs";
|
|
49137
|
+
import * as path30 from "path";
|
|
49040
49138
|
|
|
49041
49139
|
// src/hooks/guardrails.ts
|
|
49042
49140
|
init_constants();
|
|
49043
49141
|
init_schema();
|
|
49044
49142
|
init_manager2();
|
|
49045
|
-
import * as
|
|
49143
|
+
import * as path28 from "path";
|
|
49046
49144
|
init_utils();
|
|
49047
49145
|
|
|
49048
49146
|
// src/hooks/loop-detector.ts
|
|
@@ -49135,10 +49233,10 @@ function isArchitect(sessionId) {
|
|
|
49135
49233
|
function isOutsideSwarmDir(filePath, directory) {
|
|
49136
49234
|
if (!filePath)
|
|
49137
49235
|
return false;
|
|
49138
|
-
const swarmDir =
|
|
49139
|
-
const resolved =
|
|
49140
|
-
const relative4 =
|
|
49141
|
-
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);
|
|
49142
49240
|
}
|
|
49143
49241
|
function isSourceCodePath(filePath) {
|
|
49144
49242
|
if (!filePath)
|
|
@@ -49205,13 +49303,13 @@ function getCurrentTaskId(sessionId) {
|
|
|
49205
49303
|
return session?.currentTaskId ?? `${sessionId}:unknown`;
|
|
49206
49304
|
}
|
|
49207
49305
|
function isInDeclaredScope(filePath, scopeEntries) {
|
|
49208
|
-
const resolvedFile =
|
|
49306
|
+
const resolvedFile = path28.resolve(filePath);
|
|
49209
49307
|
return scopeEntries.some((scope) => {
|
|
49210
|
-
const resolvedScope =
|
|
49308
|
+
const resolvedScope = path28.resolve(scope);
|
|
49211
49309
|
if (resolvedFile === resolvedScope)
|
|
49212
49310
|
return true;
|
|
49213
|
-
const rel =
|
|
49214
|
-
return rel.length > 0 && !rel.startsWith("..") && !
|
|
49311
|
+
const rel = path28.relative(resolvedScope, resolvedFile);
|
|
49312
|
+
return rel.length > 0 && !rel.startsWith("..") && !path28.isAbsolute(rel);
|
|
49215
49313
|
});
|
|
49216
49314
|
}
|
|
49217
49315
|
function createGuardrailsHooks(directoryOrConfig, config3) {
|
|
@@ -49298,9 +49396,9 @@ function createGuardrailsHooks(directoryOrConfig, config3) {
|
|
|
49298
49396
|
const args2 = output.args;
|
|
49299
49397
|
const targetPath = args2?.filePath ?? args2?.path ?? args2?.file ?? args2?.target;
|
|
49300
49398
|
if (typeof targetPath === "string" && targetPath.length > 0) {
|
|
49301
|
-
const resolvedTarget =
|
|
49302
|
-
const planMdPath =
|
|
49303
|
-
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();
|
|
49304
49402
|
if (resolvedTarget === planMdPath || resolvedTarget === planJsonPath) {
|
|
49305
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.");
|
|
49306
49404
|
}
|
|
@@ -49349,9 +49447,9 @@ function createGuardrailsHooks(directoryOrConfig, config3) {
|
|
|
49349
49447
|
}
|
|
49350
49448
|
}
|
|
49351
49449
|
for (const p of paths) {
|
|
49352
|
-
const resolvedP =
|
|
49353
|
-
const planMdPath =
|
|
49354
|
-
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();
|
|
49355
49453
|
if (resolvedP.toLowerCase() === planMdPath || resolvedP.toLowerCase() === planJsonPath) {
|
|
49356
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.");
|
|
49357
49455
|
}
|
|
@@ -49371,7 +49469,7 @@ function createGuardrailsHooks(directoryOrConfig, config3) {
|
|
|
49371
49469
|
}
|
|
49372
49470
|
}
|
|
49373
49471
|
}
|
|
49374
|
-
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)))) {
|
|
49375
49473
|
const session2 = swarmState.agentSessions.get(input.sessionID);
|
|
49376
49474
|
if (session2) {
|
|
49377
49475
|
session2.architectWriteCount++;
|
|
@@ -49653,6 +49751,30 @@ ${pending.message}
|
|
|
49653
49751
|
}
|
|
49654
49752
|
}
|
|
49655
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
|
+
}
|
|
49656
49778
|
if (isArchitectSession && session && session.architectWriteCount > session.selfCodingWarnedAtCount) {
|
|
49657
49779
|
let targetSystemMessage = systemMessages[0];
|
|
49658
49780
|
if (!targetSystemMessage) {
|
|
@@ -49880,13 +50002,13 @@ function getEvidenceTaskId(session, directory) {
|
|
|
49880
50002
|
if (typeof directory !== "string" || directory.length === 0) {
|
|
49881
50003
|
return null;
|
|
49882
50004
|
}
|
|
49883
|
-
const resolvedDirectory =
|
|
49884
|
-
const planPath =
|
|
49885
|
-
const resolvedPlanPath =
|
|
49886
|
-
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) {
|
|
49887
50009
|
return null;
|
|
49888
50010
|
}
|
|
49889
|
-
const planContent =
|
|
50011
|
+
const planContent = fs16.readFileSync(resolvedPlanPath, "utf-8");
|
|
49890
50012
|
const plan = JSON.parse(planContent);
|
|
49891
50013
|
if (!plan || !Array.isArray(plan.phases)) {
|
|
49892
50014
|
return null;
|
|
@@ -49944,23 +50066,23 @@ function createDelegationGateHook(config3, directory) {
|
|
|
49944
50066
|
if (targetAgent === "test_engineer")
|
|
49945
50067
|
hasTestEngineer = true;
|
|
49946
50068
|
if (targetAgent === "reviewer" && session.taskWorkflowStates) {
|
|
49947
|
-
for (const [taskId,
|
|
49948
|
-
if (
|
|
50069
|
+
for (const [taskId, state2] of session.taskWorkflowStates) {
|
|
50070
|
+
if (state2 === "coder_delegated" || state2 === "pre_check_passed") {
|
|
49949
50071
|
try {
|
|
49950
50072
|
advanceTaskState(session, taskId, "reviewer_run");
|
|
49951
50073
|
} catch (err2) {
|
|
49952
|
-
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)}`);
|
|
49953
50075
|
}
|
|
49954
50076
|
}
|
|
49955
50077
|
}
|
|
49956
50078
|
}
|
|
49957
50079
|
if (targetAgent === "test_engineer" && session.taskWorkflowStates) {
|
|
49958
|
-
for (const [taskId,
|
|
49959
|
-
if (
|
|
50080
|
+
for (const [taskId, state2] of session.taskWorkflowStates) {
|
|
50081
|
+
if (state2 === "reviewer_run") {
|
|
49960
50082
|
try {
|
|
49961
50083
|
advanceTaskState(session, taskId, "tests_run");
|
|
49962
50084
|
} catch (err2) {
|
|
49963
|
-
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)}`);
|
|
49964
50086
|
}
|
|
49965
50087
|
}
|
|
49966
50088
|
}
|
|
@@ -49976,12 +50098,12 @@ function createDelegationGateHook(config3, directory) {
|
|
|
49976
50098
|
if (seedTaskId && !otherSession.taskWorkflowStates.has(seedTaskId)) {
|
|
49977
50099
|
otherSession.taskWorkflowStates.set(seedTaskId, "coder_delegated");
|
|
49978
50100
|
}
|
|
49979
|
-
for (const [taskId,
|
|
49980
|
-
if (
|
|
50101
|
+
for (const [taskId, state2] of otherSession.taskWorkflowStates) {
|
|
50102
|
+
if (state2 === "coder_delegated" || state2 === "pre_check_passed") {
|
|
49981
50103
|
try {
|
|
49982
50104
|
advanceTaskState(otherSession, taskId, "reviewer_run");
|
|
49983
50105
|
} catch (err2) {
|
|
49984
|
-
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)}`);
|
|
49985
50107
|
}
|
|
49986
50108
|
}
|
|
49987
50109
|
}
|
|
@@ -49991,12 +50113,12 @@ function createDelegationGateHook(config3, directory) {
|
|
|
49991
50113
|
if (seedTaskId && !otherSession.taskWorkflowStates.has(seedTaskId)) {
|
|
49992
50114
|
otherSession.taskWorkflowStates.set(seedTaskId, "reviewer_run");
|
|
49993
50115
|
}
|
|
49994
|
-
for (const [taskId,
|
|
49995
|
-
if (
|
|
50116
|
+
for (const [taskId, state2] of otherSession.taskWorkflowStates) {
|
|
50117
|
+
if (state2 === "reviewer_run") {
|
|
49996
50118
|
try {
|
|
49997
50119
|
advanceTaskState(otherSession, taskId, "tests_run");
|
|
49998
50120
|
} catch (err2) {
|
|
49999
|
-
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)}`);
|
|
50000
50122
|
}
|
|
50001
50123
|
}
|
|
50002
50124
|
}
|
|
@@ -50060,23 +50182,23 @@ function createDelegationGateHook(config3, directory) {
|
|
|
50060
50182
|
session.qaSkipTaskIds = [];
|
|
50061
50183
|
}
|
|
50062
50184
|
if (hasReviewer && session.taskWorkflowStates) {
|
|
50063
|
-
for (const [taskId,
|
|
50064
|
-
if (
|
|
50185
|
+
for (const [taskId, state2] of session.taskWorkflowStates) {
|
|
50186
|
+
if (state2 === "coder_delegated" || state2 === "pre_check_passed") {
|
|
50065
50187
|
try {
|
|
50066
50188
|
advanceTaskState(session, taskId, "reviewer_run");
|
|
50067
50189
|
} catch (err2) {
|
|
50068
|
-
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)}`);
|
|
50069
50191
|
}
|
|
50070
50192
|
}
|
|
50071
50193
|
}
|
|
50072
50194
|
}
|
|
50073
50195
|
if (hasReviewer && hasTestEngineer && session.taskWorkflowStates) {
|
|
50074
|
-
for (const [taskId,
|
|
50075
|
-
if (
|
|
50196
|
+
for (const [taskId, state2] of session.taskWorkflowStates) {
|
|
50197
|
+
if (state2 === "reviewer_run") {
|
|
50076
50198
|
try {
|
|
50077
50199
|
advanceTaskState(session, taskId, "tests_run");
|
|
50078
50200
|
} catch (err2) {
|
|
50079
|
-
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)}`);
|
|
50080
50202
|
}
|
|
50081
50203
|
}
|
|
50082
50204
|
}
|
|
@@ -50091,12 +50213,12 @@ function createDelegationGateHook(config3, directory) {
|
|
|
50091
50213
|
if (seedTaskId && !otherSession.taskWorkflowStates.has(seedTaskId)) {
|
|
50092
50214
|
otherSession.taskWorkflowStates.set(seedTaskId, "coder_delegated");
|
|
50093
50215
|
}
|
|
50094
|
-
for (const [taskId,
|
|
50095
|
-
if (
|
|
50216
|
+
for (const [taskId, state2] of otherSession.taskWorkflowStates) {
|
|
50217
|
+
if (state2 === "coder_delegated" || state2 === "pre_check_passed") {
|
|
50096
50218
|
try {
|
|
50097
50219
|
advanceTaskState(otherSession, taskId, "reviewer_run");
|
|
50098
50220
|
} catch (err2) {
|
|
50099
|
-
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)}`);
|
|
50100
50222
|
}
|
|
50101
50223
|
}
|
|
50102
50224
|
}
|
|
@@ -50112,12 +50234,12 @@ function createDelegationGateHook(config3, directory) {
|
|
|
50112
50234
|
if (seedTaskId && !otherSession.taskWorkflowStates.has(seedTaskId)) {
|
|
50113
50235
|
otherSession.taskWorkflowStates.set(seedTaskId, "reviewer_run");
|
|
50114
50236
|
}
|
|
50115
|
-
for (const [taskId,
|
|
50116
|
-
if (
|
|
50237
|
+
for (const [taskId, state2] of otherSession.taskWorkflowStates) {
|
|
50238
|
+
if (state2 === "reviewer_run") {
|
|
50117
50239
|
try {
|
|
50118
50240
|
advanceTaskState(otherSession, taskId, "tests_run");
|
|
50119
50241
|
} catch (err2) {
|
|
50120
|
-
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)}`);
|
|
50121
50243
|
}
|
|
50122
50244
|
}
|
|
50123
50245
|
}
|
|
@@ -50363,7 +50485,7 @@ ${warningLines.join(`
|
|
|
50363
50485
|
}
|
|
50364
50486
|
// src/hooks/delegation-sanitizer.ts
|
|
50365
50487
|
init_utils2();
|
|
50366
|
-
import * as
|
|
50488
|
+
import * as fs17 from "fs";
|
|
50367
50489
|
var SANITIZATION_PATTERNS = [
|
|
50368
50490
|
/\b\d+(st|nd|rd|th)\s+(attempt|try|time)\b/gi,
|
|
50369
50491
|
/\b(5th|fifth|final|last)\s+attempt\b/gi,
|
|
@@ -50434,7 +50556,7 @@ function createDelegationSanitizerHook(directory) {
|
|
|
50434
50556
|
stripped_patterns: result.stripped,
|
|
50435
50557
|
timestamp: new Date().toISOString()
|
|
50436
50558
|
};
|
|
50437
|
-
|
|
50559
|
+
fs17.appendFileSync(eventsPath, `${JSON.stringify(event)}
|
|
50438
50560
|
`, "utf-8");
|
|
50439
50561
|
} catch {}
|
|
50440
50562
|
}
|
|
@@ -50684,13 +50806,13 @@ init_schema();
|
|
|
50684
50806
|
init_manager();
|
|
50685
50807
|
init_detector();
|
|
50686
50808
|
init_manager2();
|
|
50687
|
-
import * as
|
|
50809
|
+
import * as fs19 from "fs";
|
|
50688
50810
|
|
|
50689
50811
|
// src/services/decision-drift-analyzer.ts
|
|
50690
50812
|
init_utils2();
|
|
50691
50813
|
init_manager2();
|
|
50692
|
-
import * as
|
|
50693
|
-
import * as
|
|
50814
|
+
import * as fs18 from "fs";
|
|
50815
|
+
import * as path31 from "path";
|
|
50694
50816
|
var DEFAULT_DRIFT_CONFIG = {
|
|
50695
50817
|
staleThresholdPhases: 1,
|
|
50696
50818
|
detectContradictions: true,
|
|
@@ -50844,11 +50966,11 @@ async function analyzeDecisionDrift(directory, config3 = {}) {
|
|
|
50844
50966
|
currentPhase = legacyPhase;
|
|
50845
50967
|
}
|
|
50846
50968
|
}
|
|
50847
|
-
const contextPath =
|
|
50969
|
+
const contextPath = path31.join(directory, ".swarm", "context.md");
|
|
50848
50970
|
let contextContent = "";
|
|
50849
50971
|
try {
|
|
50850
|
-
if (
|
|
50851
|
-
contextContent =
|
|
50972
|
+
if (fs18.existsSync(contextPath)) {
|
|
50973
|
+
contextContent = fs18.readFileSync(contextPath, "utf-8");
|
|
50852
50974
|
}
|
|
50853
50975
|
} catch {
|
|
50854
50976
|
return {
|
|
@@ -51342,11 +51464,11 @@ function createSystemEnhancerHook(config3, directory) {
|
|
|
51342
51464
|
if (handoffContent) {
|
|
51343
51465
|
const handoffPath = validateSwarmPath(directory, "handoff.md");
|
|
51344
51466
|
const consumedPath = validateSwarmPath(directory, "handoff-consumed.md");
|
|
51345
|
-
if (
|
|
51467
|
+
if (fs19.existsSync(consumedPath)) {
|
|
51346
51468
|
warn("Duplicate handoff detected: handoff-consumed.md already exists");
|
|
51347
|
-
|
|
51469
|
+
fs19.unlinkSync(consumedPath);
|
|
51348
51470
|
}
|
|
51349
|
-
|
|
51471
|
+
fs19.renameSync(handoffPath, consumedPath);
|
|
51350
51472
|
const handoffBlock = `## HANDOFF \u2014 Resuming from model switch
|
|
51351
51473
|
The previous model's session ended. Here is your starting context:
|
|
51352
51474
|
|
|
@@ -51626,11 +51748,11 @@ ${budgetWarning}`);
|
|
|
51626
51748
|
if (handoffContent) {
|
|
51627
51749
|
const handoffPath = validateSwarmPath(directory, "handoff.md");
|
|
51628
51750
|
const consumedPath = validateSwarmPath(directory, "handoff-consumed.md");
|
|
51629
|
-
if (
|
|
51751
|
+
if (fs19.existsSync(consumedPath)) {
|
|
51630
51752
|
warn("Duplicate handoff detected: handoff-consumed.md already exists");
|
|
51631
|
-
|
|
51753
|
+
fs19.unlinkSync(consumedPath);
|
|
51632
51754
|
}
|
|
51633
|
-
|
|
51755
|
+
fs19.renameSync(handoffPath, consumedPath);
|
|
51634
51756
|
const handoffBlock = `## HANDOFF \u2014 Resuming from model switch
|
|
51635
51757
|
The previous model's session ended. Here is your starting context:
|
|
51636
51758
|
|
|
@@ -52308,14 +52430,14 @@ function createDarkMatterDetectorHook(directory) {
|
|
|
52308
52430
|
}
|
|
52309
52431
|
|
|
52310
52432
|
// src/hooks/incremental-verify.ts
|
|
52311
|
-
import * as
|
|
52312
|
-
import * as
|
|
52433
|
+
import * as fs20 from "fs";
|
|
52434
|
+
import * as path32 from "path";
|
|
52313
52435
|
function detectTypecheckCommand(projectDir) {
|
|
52314
|
-
const pkgPath =
|
|
52315
|
-
if (!
|
|
52436
|
+
const pkgPath = path32.join(projectDir, "package.json");
|
|
52437
|
+
if (!fs20.existsSync(pkgPath))
|
|
52316
52438
|
return null;
|
|
52317
52439
|
try {
|
|
52318
|
-
const pkg = JSON.parse(
|
|
52440
|
+
const pkg = JSON.parse(fs20.readFileSync(pkgPath, "utf8"));
|
|
52319
52441
|
const scripts = pkg.scripts;
|
|
52320
52442
|
if (scripts?.typecheck)
|
|
52321
52443
|
return ["bun", "run", "typecheck"];
|
|
@@ -52325,7 +52447,7 @@ function detectTypecheckCommand(projectDir) {
|
|
|
52325
52447
|
...pkg.dependencies,
|
|
52326
52448
|
...pkg.devDependencies
|
|
52327
52449
|
};
|
|
52328
|
-
if (!deps?.typescript && !
|
|
52450
|
+
if (!deps?.typescript && !fs20.existsSync(path32.join(projectDir, "tsconfig.json"))) {
|
|
52329
52451
|
return null;
|
|
52330
52452
|
}
|
|
52331
52453
|
return ["npx", "tsc", "--noEmit"];
|
|
@@ -52392,7 +52514,7 @@ ${errorSummary}`);
|
|
|
52392
52514
|
// src/hooks/knowledge-reader.ts
|
|
52393
52515
|
import { existsSync as existsSync19 } from "fs";
|
|
52394
52516
|
import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
|
|
52395
|
-
import * as
|
|
52517
|
+
import * as path33 from "path";
|
|
52396
52518
|
var JACCARD_THRESHOLD = 0.6;
|
|
52397
52519
|
var HIVE_TIER_BOOST = 0.05;
|
|
52398
52520
|
var SAME_PROJECT_PENALTY = -0.05;
|
|
@@ -52440,7 +52562,7 @@ function inferCategoriesFromPhase(phaseDescription) {
|
|
|
52440
52562
|
return ["process", "tooling"];
|
|
52441
52563
|
}
|
|
52442
52564
|
async function recordLessonsShown(directory, lessonIds, currentPhase) {
|
|
52443
|
-
const shownFile =
|
|
52565
|
+
const shownFile = path33.join(directory, ".swarm", ".knowledge-shown.json");
|
|
52444
52566
|
try {
|
|
52445
52567
|
let shownData = {};
|
|
52446
52568
|
if (existsSync19(shownFile)) {
|
|
@@ -52448,7 +52570,7 @@ async function recordLessonsShown(directory, lessonIds, currentPhase) {
|
|
|
52448
52570
|
shownData = JSON.parse(content);
|
|
52449
52571
|
}
|
|
52450
52572
|
shownData[currentPhase] = lessonIds;
|
|
52451
|
-
await mkdir4(
|
|
52573
|
+
await mkdir4(path33.dirname(shownFile), { recursive: true });
|
|
52452
52574
|
await writeFile4(shownFile, JSON.stringify(shownData, null, 2), "utf-8");
|
|
52453
52575
|
} catch {
|
|
52454
52576
|
console.warn("[swarm] Knowledge: failed to record shown lessons");
|
|
@@ -52543,7 +52665,7 @@ async function readMergedKnowledge(directory, config3, context) {
|
|
|
52543
52665
|
return topN;
|
|
52544
52666
|
}
|
|
52545
52667
|
async function updateRetrievalOutcome(directory, phaseInfo, phaseSucceeded) {
|
|
52546
|
-
const shownFile =
|
|
52668
|
+
const shownFile = path33.join(directory, ".swarm", ".knowledge-shown.json");
|
|
52547
52669
|
try {
|
|
52548
52670
|
if (!existsSync19(shownFile)) {
|
|
52549
52671
|
return;
|
|
@@ -53015,12 +53137,12 @@ Use this data to avoid repeating known failure patterns.`;
|
|
|
53015
53137
|
// src/hooks/curator-drift.ts
|
|
53016
53138
|
init_event_bus();
|
|
53017
53139
|
init_utils2();
|
|
53018
|
-
import * as
|
|
53019
|
-
import * as
|
|
53140
|
+
import * as fs21 from "fs";
|
|
53141
|
+
import * as path34 from "path";
|
|
53020
53142
|
var DRIFT_REPORT_PREFIX = "drift-report-phase-";
|
|
53021
53143
|
async function readPriorDriftReports(directory) {
|
|
53022
|
-
const swarmDir =
|
|
53023
|
-
const entries = await
|
|
53144
|
+
const swarmDir = path34.join(directory, ".swarm");
|
|
53145
|
+
const entries = await fs21.promises.readdir(swarmDir).catch(() => null);
|
|
53024
53146
|
if (entries === null)
|
|
53025
53147
|
return [];
|
|
53026
53148
|
const reportFiles = entries.filter((name2) => name2.startsWith(DRIFT_REPORT_PREFIX) && name2.endsWith(".json")).sort();
|
|
@@ -53046,10 +53168,10 @@ async function readPriorDriftReports(directory) {
|
|
|
53046
53168
|
async function writeDriftReport(directory, report) {
|
|
53047
53169
|
const filename = `${DRIFT_REPORT_PREFIX}${report.phase}.json`;
|
|
53048
53170
|
const filePath = validateSwarmPath(directory, filename);
|
|
53049
|
-
const swarmDir =
|
|
53050
|
-
await
|
|
53171
|
+
const swarmDir = path34.dirname(filePath);
|
|
53172
|
+
await fs21.promises.mkdir(swarmDir, { recursive: true });
|
|
53051
53173
|
try {
|
|
53052
|
-
await
|
|
53174
|
+
await fs21.promises.writeFile(filePath, JSON.stringify(report, null, 2), "utf-8");
|
|
53053
53175
|
} catch (err2) {
|
|
53054
53176
|
throw new Error(`[curator-drift] Failed to write drift report to ${filePath}: ${String(err2)}`);
|
|
53055
53177
|
}
|
|
@@ -53435,7 +53557,7 @@ Review before proceeding.`;
|
|
|
53435
53557
|
|
|
53436
53558
|
// src/hooks/steering-consumed.ts
|
|
53437
53559
|
init_utils2();
|
|
53438
|
-
import * as
|
|
53560
|
+
import * as fs22 from "fs";
|
|
53439
53561
|
function recordSteeringConsumed(directory, directiveId) {
|
|
53440
53562
|
try {
|
|
53441
53563
|
const eventsPath = validateSwarmPath(directory, "events.jsonl");
|
|
@@ -53444,7 +53566,7 @@ function recordSteeringConsumed(directory, directiveId) {
|
|
|
53444
53566
|
directiveId,
|
|
53445
53567
|
timestamp: new Date().toISOString()
|
|
53446
53568
|
};
|
|
53447
|
-
|
|
53569
|
+
fs22.appendFileSync(eventsPath, `${JSON.stringify(event)}
|
|
53448
53570
|
`, "utf-8");
|
|
53449
53571
|
} catch {}
|
|
53450
53572
|
}
|
|
@@ -53484,87 +53606,6 @@ function createSteeringConsumedHook(directory) {
|
|
|
53484
53606
|
return safeHook(hook);
|
|
53485
53607
|
}
|
|
53486
53608
|
|
|
53487
|
-
// src/services/compaction-service.ts
|
|
53488
|
-
import * as fs22 from "fs";
|
|
53489
|
-
import * as path34 from "path";
|
|
53490
|
-
function makeInitialState() {
|
|
53491
|
-
return {
|
|
53492
|
-
lastObservationAt: 0,
|
|
53493
|
-
lastReflectionAt: 0,
|
|
53494
|
-
lastEmergencyAt: 0,
|
|
53495
|
-
observationCount: 0,
|
|
53496
|
-
reflectionCount: 0,
|
|
53497
|
-
emergencyCount: 0
|
|
53498
|
-
};
|
|
53499
|
-
}
|
|
53500
|
-
function appendSnapshot(directory, tier, budgetPct, message) {
|
|
53501
|
-
try {
|
|
53502
|
-
const snapshotPath = path34.join(directory, ".swarm", "context-snapshot.md");
|
|
53503
|
-
const timestamp = new Date().toISOString();
|
|
53504
|
-
const entry = `
|
|
53505
|
-
## [${tier.toUpperCase()}] ${timestamp} \u2014 ${budgetPct.toFixed(1)}% used
|
|
53506
|
-
${message}
|
|
53507
|
-
`;
|
|
53508
|
-
fs22.appendFileSync(snapshotPath, entry, "utf-8");
|
|
53509
|
-
} catch {}
|
|
53510
|
-
}
|
|
53511
|
-
function buildObservationMessage(budgetPct) {
|
|
53512
|
-
return `[CONTEXT COMPACTION \u2014 OBSERVATION TIER]
|
|
53513
|
-
` + `Context window is ${budgetPct.toFixed(1)}% used. Initiating observation compaction.
|
|
53514
|
-
` + `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.
|
|
53515
|
-
` + `[/CONTEXT COMPACTION]`;
|
|
53516
|
-
}
|
|
53517
|
-
function buildReflectionMessage(budgetPct) {
|
|
53518
|
-
return `[CONTEXT COMPACTION \u2014 REFLECTION TIER]
|
|
53519
|
-
` + `Context window is ${budgetPct.toFixed(1)}% used. Initiating reflection compaction.
|
|
53520
|
-
` + `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.
|
|
53521
|
-
` + `[/CONTEXT COMPACTION]`;
|
|
53522
|
-
}
|
|
53523
|
-
function buildEmergencyMessage(budgetPct, preserveLastN) {
|
|
53524
|
-
return `[CONTEXT COMPACTION \u2014 EMERGENCY TIER]
|
|
53525
|
-
` + `Context window is ${budgetPct.toFixed(1)}% used. EMERGENCY compaction required.
|
|
53526
|
-
` + `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.
|
|
53527
|
-
` + `[/CONTEXT COMPACTION]`;
|
|
53528
|
-
}
|
|
53529
|
-
function createCompactionService(config3, directory, injectMessage) {
|
|
53530
|
-
const state = makeInitialState();
|
|
53531
|
-
return {
|
|
53532
|
-
toolAfter: async (_input, _output) => {
|
|
53533
|
-
if (!config3.enabled)
|
|
53534
|
-
return;
|
|
53535
|
-
const budgetPct = swarmState.lastBudgetPct ?? 0;
|
|
53536
|
-
if (budgetPct <= 0)
|
|
53537
|
-
return;
|
|
53538
|
-
const sessionId = _input.sessionID;
|
|
53539
|
-
try {
|
|
53540
|
-
if (budgetPct >= config3.emergencyThreshold && budgetPct > state.lastEmergencyAt + 5) {
|
|
53541
|
-
state.lastEmergencyAt = budgetPct;
|
|
53542
|
-
state.emergencyCount++;
|
|
53543
|
-
const msg = buildEmergencyMessage(budgetPct, config3.preserveLastNTurns);
|
|
53544
|
-
appendSnapshot(directory, "emergency", budgetPct, msg);
|
|
53545
|
-
injectMessage(sessionId, msg);
|
|
53546
|
-
return;
|
|
53547
|
-
}
|
|
53548
|
-
if (budgetPct >= config3.reflectionThreshold && budgetPct > state.lastReflectionAt + 5) {
|
|
53549
|
-
state.lastReflectionAt = budgetPct;
|
|
53550
|
-
state.reflectionCount++;
|
|
53551
|
-
const msg = buildReflectionMessage(budgetPct);
|
|
53552
|
-
appendSnapshot(directory, "reflection", budgetPct, msg);
|
|
53553
|
-
injectMessage(sessionId, msg);
|
|
53554
|
-
return;
|
|
53555
|
-
}
|
|
53556
|
-
if (budgetPct >= config3.observationThreshold && budgetPct > state.lastObservationAt + 5) {
|
|
53557
|
-
state.lastObservationAt = budgetPct;
|
|
53558
|
-
state.observationCount++;
|
|
53559
|
-
const msg = buildObservationMessage(budgetPct);
|
|
53560
|
-
appendSnapshot(directory, "observation", budgetPct, msg);
|
|
53561
|
-
injectMessage(sessionId, msg);
|
|
53562
|
-
}
|
|
53563
|
-
} catch {}
|
|
53564
|
-
}
|
|
53565
|
-
};
|
|
53566
|
-
}
|
|
53567
|
-
|
|
53568
53609
|
// src/index.ts
|
|
53569
53610
|
init_config_doctor();
|
|
53570
53611
|
|
|
@@ -61228,8 +61269,8 @@ function parsePackageResolved(content) {
|
|
|
61228
61269
|
const pins = resolved.pins || [];
|
|
61229
61270
|
for (const pin of pins) {
|
|
61230
61271
|
const identity = pin.identity || pin.package || "";
|
|
61231
|
-
const
|
|
61232
|
-
const version3 =
|
|
61272
|
+
const state2 = pin.state || {};
|
|
61273
|
+
const version3 = state2.version || state2.revision || "";
|
|
61233
61274
|
let org = "";
|
|
61234
61275
|
const location = pin.location || "";
|
|
61235
61276
|
const orgMatch = location.match(/github\.com\/([^/]+)\//);
|
|
@@ -62719,8 +62760,8 @@ function checkReviewerGate(taskId, workingDirectory) {
|
|
|
62719
62760
|
continue;
|
|
62720
62761
|
}
|
|
62721
62762
|
validSessionCount++;
|
|
62722
|
-
const
|
|
62723
|
-
if (
|
|
62763
|
+
const state2 = getTaskState(session, taskId);
|
|
62764
|
+
if (state2 === "tests_run" || state2 === "complete") {
|
|
62724
62765
|
return { blocked: false, reason: "" };
|
|
62725
62766
|
}
|
|
62726
62767
|
}
|
|
@@ -62731,8 +62772,8 @@ function checkReviewerGate(taskId, workingDirectory) {
|
|
|
62731
62772
|
for (const [sessionId, session] of swarmState.agentSessions) {
|
|
62732
62773
|
if (!(session.taskWorkflowStates instanceof Map))
|
|
62733
62774
|
continue;
|
|
62734
|
-
const
|
|
62735
|
-
stateEntries.push(`${sessionId}: ${
|
|
62775
|
+
const state2 = getTaskState(session, taskId);
|
|
62776
|
+
stateEntries.push(`${sessionId}: ${state2}`);
|
|
62736
62777
|
}
|
|
62737
62778
|
try {
|
|
62738
62779
|
const resolvedDir2 = workingDirectory ?? process.cwd();
|
|
@@ -63085,16 +63126,24 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
63085
63126
|
classThreshold: 3,
|
|
63086
63127
|
commentStripThreshold: 5,
|
|
63087
63128
|
diffLineThreshold: 200
|
|
63088
|
-
}, ctx.directory, (
|
|
63089
|
-
|
|
63129
|
+
}, ctx.directory, (sessionId, message) => {
|
|
63130
|
+
const s = swarmState.agentSessions.get(sessionId);
|
|
63131
|
+
if (s) {
|
|
63132
|
+
s.pendingAdvisoryMessages ??= [];
|
|
63133
|
+
s.pendingAdvisoryMessages.push(message);
|
|
63134
|
+
}
|
|
63090
63135
|
}) : null;
|
|
63091
63136
|
const incrementalVerifyHook = config3.incremental_verify?.enabled !== false ? createIncrementalVerifyHook(config3.incremental_verify ?? {
|
|
63092
63137
|
enabled: true,
|
|
63093
63138
|
command: null,
|
|
63094
63139
|
timeoutMs: 30000,
|
|
63095
63140
|
triggerAgents: ["coder"]
|
|
63096
|
-
}, ctx.directory, (
|
|
63097
|
-
|
|
63141
|
+
}, ctx.directory, (sessionId, message) => {
|
|
63142
|
+
const s = swarmState.agentSessions.get(sessionId);
|
|
63143
|
+
if (s) {
|
|
63144
|
+
s.pendingAdvisoryMessages ??= [];
|
|
63145
|
+
s.pendingAdvisoryMessages.push(message);
|
|
63146
|
+
}
|
|
63098
63147
|
}) : null;
|
|
63099
63148
|
const compactionServiceHook = config3.compaction_service?.enabled !== false ? createCompactionService(config3.compaction_service ?? {
|
|
63100
63149
|
enabled: true,
|
|
@@ -63102,8 +63151,12 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
63102
63151
|
reflectionThreshold: 60,
|
|
63103
63152
|
emergencyThreshold: 80,
|
|
63104
63153
|
preserveLastNTurns: 5
|
|
63105
|
-
}, ctx.directory, (
|
|
63106
|
-
|
|
63154
|
+
}, ctx.directory, (sessionId, message) => {
|
|
63155
|
+
const s = swarmState.agentSessions.get(sessionId);
|
|
63156
|
+
if (s) {
|
|
63157
|
+
s.pendingAdvisoryMessages ??= [];
|
|
63158
|
+
s.pendingAdvisoryMessages.push(message);
|
|
63159
|
+
}
|
|
63107
63160
|
}) : null;
|
|
63108
63161
|
const snapshotWriterHook = createSnapshotWriterHook(ctx.directory);
|
|
63109
63162
|
const automationConfig = AutomationConfigSchema.parse(config3.automation ?? {});
|
|
@@ -63381,7 +63434,8 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
63381
63434
|
const pressureSession = ensureAgentSession(input.sessionID, swarmState.activeAgent.get(input.sessionID) ?? ORCHESTRATOR_NAME);
|
|
63382
63435
|
if (!pressureSession.contextPressureWarningSent) {
|
|
63383
63436
|
pressureSession.contextPressureWarningSent = true;
|
|
63384
|
-
|
|
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.`);
|
|
63385
63439
|
}
|
|
63386
63440
|
}
|
|
63387
63441
|
await safeHook(activityHooks.toolBefore)(input, output);
|
|
@@ -21,3 +21,8 @@ export interface CompactionServiceHook {
|
|
|
21
21
|
}) => Promise<void>;
|
|
22
22
|
}
|
|
23
23
|
export declare function createCompactionService(config: CompactionConfig, directory: string, injectMessage: (sessionId: string, message: string) => void): CompactionServiceHook;
|
|
24
|
+
export declare function getCompactionMetrics(): {
|
|
25
|
+
compactionCount: number;
|
|
26
|
+
lastSnapshotAt: string | null;
|
|
27
|
+
};
|
|
28
|
+
export declare function resetCompactionState(): void;
|
package/dist/state.d.ts
CHANGED
|
@@ -129,6 +129,8 @@ export interface AgentSessionState {
|
|
|
129
129
|
};
|
|
130
130
|
/** Flag to track if the 50% context pressure warning has been sent this session */
|
|
131
131
|
contextPressureWarningSent?: boolean;
|
|
132
|
+
/** Queue of advisory messages (e.g., SLOP, context pressure) pending injection into next messagesTransform */
|
|
133
|
+
pendingAdvisoryMessages?: string[];
|
|
132
134
|
}
|
|
133
135
|
/**
|
|
134
136
|
* Represents a single agent invocation window with isolated guardrail budgets.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "6.29.
|
|
3
|
+
"version": "6.29.1",
|
|
4
4
|
"description": "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|