opencode-swarm 6.35.4 → 6.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +35 -14
- package/dist/config/constants.d.ts +2 -2
- package/dist/hooks/curator-drift.d.ts +2 -2
- package/dist/hooks/delegation-gate.d.ts +7 -0
- package/dist/hooks/knowledge-curator.d.ts +6 -1
- package/dist/hooks/knowledge-types.d.ts +1 -1
- package/dist/index.js +324 -258
- package/dist/state.d.ts +5 -3
- package/dist/state.session-restart.test.d.ts +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -14422,7 +14422,7 @@ async function loadEvidence(directory, taskId) {
|
|
|
14422
14422
|
return { status: "found", bundle: validated };
|
|
14423
14423
|
} catch (error49) {
|
|
14424
14424
|
warn(`Wrapped flat retrospective failed validation for task ${sanitizedTaskId}: ${error49 instanceof Error ? error49.message : String(error49)}`);
|
|
14425
|
-
const errors3 = error49 instanceof ZodError ? error49.issues.map((e) => `${e.path.join(".")}: ${e.message}`) : [String(error49)];
|
|
14425
|
+
const errors3 = error49 instanceof ZodError ? error49.issues.map((e) => `${e.path.join(".")}: ${e.message}`) : [error49 instanceof Error ? error49.message : String(error49)];
|
|
14426
14426
|
return { status: "invalid_schema", errors: errors3 };
|
|
14427
14427
|
}
|
|
14428
14428
|
}
|
|
@@ -14431,7 +14431,7 @@ async function loadEvidence(directory, taskId) {
|
|
|
14431
14431
|
return { status: "found", bundle: validated };
|
|
14432
14432
|
} catch (error49) {
|
|
14433
14433
|
warn(`Evidence bundle validation failed for task ${sanitizedTaskId}: ${error49 instanceof Error ? error49.message : String(error49)}`);
|
|
14434
|
-
const errors3 = error49 instanceof ZodError ? error49.issues.map((e) => `${e.path.join(".")}: ${e.message}`) : [String(error49)];
|
|
14434
|
+
const errors3 = error49 instanceof ZodError ? error49.issues.map((e) => `${e.path.join(".")}: ${e.message}`) : [error49 instanceof Error ? error49.message : String(error49)];
|
|
14435
14435
|
return { status: "invalid_schema", errors: errors3 };
|
|
14436
14436
|
}
|
|
14437
14437
|
}
|
|
@@ -17633,6 +17633,7 @@ var ALL_SUBAGENT_NAMES = [
|
|
|
17633
17633
|
"docs",
|
|
17634
17634
|
"designer",
|
|
17635
17635
|
"critic_sounding_board",
|
|
17636
|
+
"critic_drift_verifier",
|
|
17636
17637
|
...QA_AGENTS,
|
|
17637
17638
|
...PIPELINE_AGENTS
|
|
17638
17639
|
];
|
|
@@ -17731,6 +17732,13 @@ var AGENT_TOOL_MAP = {
|
|
|
17731
17732
|
"retrieve_summary",
|
|
17732
17733
|
"symbols"
|
|
17733
17734
|
],
|
|
17735
|
+
critic_drift_verifier: [
|
|
17736
|
+
"complexity_hotspots",
|
|
17737
|
+
"detect_domains",
|
|
17738
|
+
"imports",
|
|
17739
|
+
"retrieve_summary",
|
|
17740
|
+
"symbols"
|
|
17741
|
+
],
|
|
17734
17742
|
docs: [
|
|
17735
17743
|
"detect_domains",
|
|
17736
17744
|
"extract_code_blocks",
|
|
@@ -18444,7 +18452,11 @@ var swarmState = {
|
|
|
18444
18452
|
function getAgentSession(sessionId) {
|
|
18445
18453
|
return swarmState.agentSessions.get(sessionId);
|
|
18446
18454
|
}
|
|
18447
|
-
function hasActiveTurboMode() {
|
|
18455
|
+
function hasActiveTurboMode(sessionID) {
|
|
18456
|
+
if (sessionID) {
|
|
18457
|
+
const session = swarmState.agentSessions.get(sessionID);
|
|
18458
|
+
return session?.turboMode === true;
|
|
18459
|
+
}
|
|
18448
18460
|
for (const [_sessionId, session] of swarmState.agentSessions) {
|
|
18449
18461
|
if (session.turboMode === true) {
|
|
18450
18462
|
return true;
|
|
@@ -31104,7 +31116,16 @@ function createSwarmTool(opts) {
|
|
|
31104
31116
|
args: opts.args,
|
|
31105
31117
|
execute: async (args, ctx) => {
|
|
31106
31118
|
const directory = ctx?.directory ?? process.cwd();
|
|
31107
|
-
|
|
31119
|
+
try {
|
|
31120
|
+
return await opts.execute(args, directory, ctx);
|
|
31121
|
+
} catch (error93) {
|
|
31122
|
+
const message = error93 instanceof Error ? error93.message : String(error93);
|
|
31123
|
+
return JSON.stringify({
|
|
31124
|
+
success: false,
|
|
31125
|
+
message: "Tool execution failed",
|
|
31126
|
+
errors: [message]
|
|
31127
|
+
}, null, 2);
|
|
31128
|
+
}
|
|
31108
31129
|
}
|
|
31109
31130
|
});
|
|
31110
31131
|
}
|
|
@@ -32314,7 +32335,7 @@ async function handleCurateCommand(directory, _args) {
|
|
|
32314
32335
|
if (error93 instanceof Error) {
|
|
32315
32336
|
return `\u274C Curation failed: ${error93.message}`;
|
|
32316
32337
|
}
|
|
32317
|
-
return `\u274C Curation failed: ${String(error93)}`;
|
|
32338
|
+
return `\u274C Curation failed: ${error93 instanceof Error ? error93.message : String(error93)}`;
|
|
32318
32339
|
}
|
|
32319
32340
|
}
|
|
32320
32341
|
function formatCurationSummary(summary) {
|
|
@@ -32702,7 +32723,7 @@ async function handleDarkMatterCommand(directory, args) {
|
|
|
32702
32723
|
[${entries.length} dark matter finding(s) saved to .swarm/knowledge.jsonl]`;
|
|
32703
32724
|
}
|
|
32704
32725
|
} catch (err) {
|
|
32705
|
-
console.warn("dark-matter: failed to save knowledge entries:", err);
|
|
32726
|
+
console.warn("dark-matter: failed to save knowledge entries:", err instanceof Error ? err.message : String(err));
|
|
32706
32727
|
return output;
|
|
32707
32728
|
}
|
|
32708
32729
|
}
|
|
@@ -34461,7 +34482,7 @@ async function handleKnowledgeQuarantineCommand(directory, args) {
|
|
|
34461
34482
|
await quarantineEntry(directory, entryId, reason, "user");
|
|
34462
34483
|
return `\u2705 Entry ${entryId} quarantined successfully.`;
|
|
34463
34484
|
} catch (error93) {
|
|
34464
|
-
console.warn("[knowledge-command] quarantineEntry error:", error93);
|
|
34485
|
+
console.warn("[knowledge-command] quarantineEntry error:", error93 instanceof Error ? error93.message : String(error93));
|
|
34465
34486
|
return `\u274C Failed to quarantine entry. Check the entry ID and try again.`;
|
|
34466
34487
|
}
|
|
34467
34488
|
}
|
|
@@ -34477,7 +34498,7 @@ async function handleKnowledgeRestoreCommand(directory, args) {
|
|
|
34477
34498
|
await restoreEntry(directory, entryId);
|
|
34478
34499
|
return `\u2705 Entry ${entryId} restored successfully.`;
|
|
34479
34500
|
} catch (error93) {
|
|
34480
|
-
console.warn("[knowledge-command] restoreEntry error:", error93);
|
|
34501
|
+
console.warn("[knowledge-command] restoreEntry error:", error93 instanceof Error ? error93.message : String(error93));
|
|
34481
34502
|
return `\u274C Failed to restore entry. Check the entry ID and try again.`;
|
|
34482
34503
|
}
|
|
34483
34504
|
}
|
|
@@ -34499,7 +34520,7 @@ async function handleKnowledgeMigrateCommand(directory, args) {
|
|
|
34499
34520
|
}
|
|
34500
34521
|
return `\u2705 Migration complete: ${result.entriesMigrated} entries added, ${result.entriesDropped} dropped (validation/dedup), ${result.entriesTotal} total processed.`;
|
|
34501
34522
|
} catch (error93) {
|
|
34502
|
-
console.warn("[knowledge-command] migrateContextToKnowledge error:", error93);
|
|
34523
|
+
console.warn("[knowledge-command] migrateContextToKnowledge error:", error93 instanceof Error ? error93.message : String(error93));
|
|
34503
34524
|
return "\u274C Migration failed. Check .swarm/context.md is readable.";
|
|
34504
34525
|
}
|
|
34505
34526
|
}
|
|
@@ -34526,7 +34547,7 @@ async function handleKnowledgeListCommand(directory, _args) {
|
|
|
34526
34547
|
return lines.join(`
|
|
34527
34548
|
`);
|
|
34528
34549
|
} catch (error93) {
|
|
34529
|
-
console.warn("[knowledge-command] list error:", error93);
|
|
34550
|
+
console.warn("[knowledge-command] list error:", error93 instanceof Error ? error93.message : String(error93));
|
|
34530
34551
|
return "\u274C Failed to list knowledge entries. Ensure .swarm/knowledge.jsonl exists.";
|
|
34531
34552
|
}
|
|
34532
34553
|
}
|
|
@@ -36561,7 +36582,7 @@ function isExcluded(entry, relPath, exactNames, globPatterns) {
|
|
|
36561
36582
|
return false;
|
|
36562
36583
|
}
|
|
36563
36584
|
function containsControlChars(str) {
|
|
36564
|
-
return /[\0\r]/.test(str);
|
|
36585
|
+
return /[\0\t\r\n]/.test(str);
|
|
36565
36586
|
}
|
|
36566
36587
|
function validateDirectoryInput(dir) {
|
|
36567
36588
|
if (!dir || dir.length === 0) {
|
|
@@ -38632,7 +38653,7 @@ async function handlePromoteCommand(directory, args) {
|
|
|
38632
38653
|
if (error93 instanceof Error) {
|
|
38633
38654
|
return error93.message;
|
|
38634
38655
|
}
|
|
38635
|
-
return `Failed to promote lesson: ${String(error93)}`;
|
|
38656
|
+
return `Failed to promote lesson: ${error93 instanceof Error ? error93.message : String(error93)}`;
|
|
38636
38657
|
}
|
|
38637
38658
|
}
|
|
38638
38659
|
try {
|
|
@@ -38641,7 +38662,7 @@ async function handlePromoteCommand(directory, args) {
|
|
|
38641
38662
|
if (error93 instanceof Error) {
|
|
38642
38663
|
return error93.message;
|
|
38643
38664
|
}
|
|
38644
|
-
return `Failed to promote lesson: ${String(error93)}`;
|
|
38665
|
+
return `Failed to promote lesson: ${error93 instanceof Error ? error93.message : String(error93)}`;
|
|
38645
38666
|
}
|
|
38646
38667
|
}
|
|
38647
38668
|
|
|
@@ -39575,7 +39596,7 @@ async function handleRollbackCommand(directory, args) {
|
|
|
39575
39596
|
fs12.appendFileSync(eventsPath, `${JSON.stringify(rollbackEvent)}
|
|
39576
39597
|
`);
|
|
39577
39598
|
} catch (error93) {
|
|
39578
|
-
console.error("Failed to write rollback event:", error93);
|
|
39599
|
+
console.error("Failed to write rollback event:", error93 instanceof Error ? error93.message : String(error93));
|
|
39579
39600
|
}
|
|
39580
39601
|
return `Rolled back to phase ${targetPhase}: ${checkpoint2.label || "no label"}`;
|
|
39581
39602
|
}
|
|
@@ -2,8 +2,8 @@ import type { ToolName } from '../tools/tool-names';
|
|
|
2
2
|
export declare const QA_AGENTS: readonly ["reviewer", "critic"];
|
|
3
3
|
export declare const PIPELINE_AGENTS: readonly ["explorer", "coder", "test_engineer"];
|
|
4
4
|
export declare const ORCHESTRATOR_NAME: "architect";
|
|
5
|
-
export declare const ALL_SUBAGENT_NAMES: readonly ["sme", "docs", "designer", "critic_sounding_board", "reviewer", "critic", "explorer", "coder", "test_engineer"];
|
|
6
|
-
export declare const ALL_AGENT_NAMES: readonly ["architect", "sme", "docs", "designer", "critic_sounding_board", "reviewer", "critic", "explorer", "coder", "test_engineer"];
|
|
5
|
+
export declare const ALL_SUBAGENT_NAMES: readonly ["sme", "docs", "designer", "critic_sounding_board", "critic_drift_verifier", "reviewer", "critic", "explorer", "coder", "test_engineer"];
|
|
6
|
+
export declare const ALL_AGENT_NAMES: readonly ["architect", "sme", "docs", "designer", "critic_sounding_board", "critic_drift_verifier", "reviewer", "critic", "explorer", "coder", "test_engineer"];
|
|
7
7
|
export type QAAgentName = (typeof QA_AGENTS)[number];
|
|
8
8
|
export type PipelineAgentName = (typeof PIPELINE_AGENTS)[number];
|
|
9
9
|
export type AgentName = (typeof ALL_AGENT_NAMES)[number];
|
|
@@ -12,14 +12,14 @@ export declare function readPriorDriftReports(directory: string): Promise<DriftR
|
|
|
12
12
|
*/
|
|
13
13
|
export declare function writeDriftReport(directory: string, report: DriftReport): Promise<string>;
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
15
|
+
* Deterministic drift check for the given phase.
|
|
16
16
|
* Builds a structured DriftReport from curator data, plan, spec, and prior reports.
|
|
17
17
|
* Writes the report to .swarm/drift-report-phase-N.json.
|
|
18
18
|
* Emits 'curator.drift.completed' event on success.
|
|
19
19
|
* On any error: emits 'curator.error' event and returns a safe default result.
|
|
20
20
|
* NEVER throws — drift failures must not block phase_complete.
|
|
21
21
|
*/
|
|
22
|
-
export declare function
|
|
22
|
+
export declare function runDeterministicDriftCheck(directory: string, phase: number, curatorResult: CuratorPhaseResult, config: CuratorConfig, injectAdvisory?: (message: string) => void): Promise<CriticDriftResult>;
|
|
23
23
|
/**
|
|
24
24
|
* Build a truncated summary suitable for architect context injection.
|
|
25
25
|
* Format: "<drift_report>Phase N: {alignment} ({drift_score}) — {key finding}. {correction if any}.</drift_report>"
|
|
@@ -50,6 +50,13 @@ export declare function createDelegationGateHook(config: PluginConfig, directory
|
|
|
50
50
|
messagesTransform: (input: Record<string, never>, output: {
|
|
51
51
|
messages?: MessageWithParts[];
|
|
52
52
|
}) => Promise<void>;
|
|
53
|
+
toolBefore: (input: {
|
|
54
|
+
tool: string;
|
|
55
|
+
sessionID: string;
|
|
56
|
+
callID: string;
|
|
57
|
+
}, output: {
|
|
58
|
+
args: unknown;
|
|
59
|
+
}) => Promise<void>;
|
|
53
60
|
toolAfter: (input: {
|
|
54
61
|
tool: string;
|
|
55
62
|
sessionID: string;
|
|
@@ -7,10 +7,15 @@ import type { KnowledgeConfig } from './knowledge-types.js';
|
|
|
7
7
|
export declare function isWriteToEvidenceFile(input: unknown): boolean;
|
|
8
8
|
/**
|
|
9
9
|
* Curate and store swarm knowledge entries from lessons.
|
|
10
|
+
* @returns Promise resolving to an object with counts of stored, skipped, and rejected lessons.
|
|
10
11
|
*/
|
|
11
12
|
export declare function curateAndStoreSwarm(lessons: string[], projectName: string, phaseInfo: {
|
|
12
13
|
phase_number: number;
|
|
13
|
-
}, directory: string, config: KnowledgeConfig): Promise<
|
|
14
|
+
}, directory: string, config: KnowledgeConfig): Promise<{
|
|
15
|
+
stored: number;
|
|
16
|
+
skipped: number;
|
|
17
|
+
rejected: number;
|
|
18
|
+
}>;
|
|
14
19
|
/**
|
|
15
20
|
* Auto-promote swarm entries based on phase confirmations and age.
|
|
16
21
|
*/
|
|
@@ -24,7 +24,7 @@ export interface KnowledgeEntryBase {
|
|
|
24
24
|
tags: string[];
|
|
25
25
|
scope: string;
|
|
26
26
|
confidence: number;
|
|
27
|
-
status: 'candidate' | 'established' | 'promoted';
|
|
27
|
+
status: 'candidate' | 'established' | 'promoted' | 'archived';
|
|
28
28
|
confirmed_by: PhaseConfirmationRecord[] | ProjectConfirmationRecord[];
|
|
29
29
|
retrieval_outcomes: RetrievalOutcome;
|
|
30
30
|
schema_version: number;
|
package/dist/index.js
CHANGED
|
@@ -134,6 +134,7 @@ var init_constants = __esm(() => {
|
|
|
134
134
|
"docs",
|
|
135
135
|
"designer",
|
|
136
136
|
"critic_sounding_board",
|
|
137
|
+
"critic_drift_verifier",
|
|
137
138
|
...QA_AGENTS,
|
|
138
139
|
...PIPELINE_AGENTS
|
|
139
140
|
];
|
|
@@ -232,6 +233,13 @@ var init_constants = __esm(() => {
|
|
|
232
233
|
"retrieve_summary",
|
|
233
234
|
"symbols"
|
|
234
235
|
],
|
|
236
|
+
critic_drift_verifier: [
|
|
237
|
+
"complexity_hotspots",
|
|
238
|
+
"detect_domains",
|
|
239
|
+
"imports",
|
|
240
|
+
"retrieve_summary",
|
|
241
|
+
"symbols"
|
|
242
|
+
],
|
|
235
243
|
docs: [
|
|
236
244
|
"detect_domains",
|
|
237
245
|
"extract_code_blocks",
|
|
@@ -258,6 +266,7 @@ var init_constants = __esm(() => {
|
|
|
258
266
|
sme: "opencode/trinity-large-preview-free",
|
|
259
267
|
critic: "opencode/trinity-large-preview-free",
|
|
260
268
|
critic_sounding_board: "opencode/trinity-large-preview-free",
|
|
269
|
+
critic_drift_verifier: "opencode/trinity-large-preview-free",
|
|
261
270
|
docs: "opencode/trinity-large-preview-free",
|
|
262
271
|
designer: "opencode/trinity-large-preview-free",
|
|
263
272
|
default: "opencode/trinity-large-preview-free"
|
|
@@ -15689,7 +15698,7 @@ async function loadEvidence(directory, taskId) {
|
|
|
15689
15698
|
return { status: "found", bundle: validated };
|
|
15690
15699
|
} catch (error49) {
|
|
15691
15700
|
warn(`Wrapped flat retrospective failed validation for task ${sanitizedTaskId}: ${error49 instanceof Error ? error49.message : String(error49)}`);
|
|
15692
|
-
const errors3 = error49 instanceof ZodError ? error49.issues.map((e) => `${e.path.join(".")}: ${e.message}`) : [String(error49)];
|
|
15701
|
+
const errors3 = error49 instanceof ZodError ? error49.issues.map((e) => `${e.path.join(".")}: ${e.message}`) : [error49 instanceof Error ? error49.message : String(error49)];
|
|
15693
15702
|
return { status: "invalid_schema", errors: errors3 };
|
|
15694
15703
|
}
|
|
15695
15704
|
}
|
|
@@ -15698,7 +15707,7 @@ async function loadEvidence(directory, taskId) {
|
|
|
15698
15707
|
return { status: "found", bundle: validated };
|
|
15699
15708
|
} catch (error49) {
|
|
15700
15709
|
warn(`Evidence bundle validation failed for task ${sanitizedTaskId}: ${error49 instanceof Error ? error49.message : String(error49)}`);
|
|
15701
|
-
const errors3 = error49 instanceof ZodError ? error49.issues.map((e) => `${e.path.join(".")}: ${e.message}`) : [String(error49)];
|
|
15710
|
+
const errors3 = error49 instanceof ZodError ? error49.issues.map((e) => `${e.path.join(".")}: ${e.message}`) : [error49 instanceof Error ? error49.message : String(error49)];
|
|
15702
15711
|
return { status: "invalid_schema", errors: errors3 };
|
|
15703
15712
|
}
|
|
15704
15713
|
}
|
|
@@ -30024,7 +30033,16 @@ function createSwarmTool(opts) {
|
|
|
30024
30033
|
args: opts.args,
|
|
30025
30034
|
execute: async (args2, ctx) => {
|
|
30026
30035
|
const directory = ctx?.directory ?? process.cwd();
|
|
30027
|
-
|
|
30036
|
+
try {
|
|
30037
|
+
return await opts.execute(args2, directory, ctx);
|
|
30038
|
+
} catch (error93) {
|
|
30039
|
+
const message = error93 instanceof Error ? error93.message : String(error93);
|
|
30040
|
+
return JSON.stringify({
|
|
30041
|
+
success: false,
|
|
30042
|
+
message: "Tool execution failed",
|
|
30043
|
+
errors: [message]
|
|
30044
|
+
}, null, 2);
|
|
30045
|
+
}
|
|
30028
30046
|
}
|
|
30029
30047
|
});
|
|
30030
30048
|
}
|
|
@@ -34177,7 +34195,7 @@ function isExcluded(entry, relPath, exactNames, globPatterns) {
|
|
|
34177
34195
|
return false;
|
|
34178
34196
|
}
|
|
34179
34197
|
function containsControlChars(str) {
|
|
34180
|
-
return /[\0\r]/.test(str);
|
|
34198
|
+
return /[\0\t\r\n]/.test(str);
|
|
34181
34199
|
}
|
|
34182
34200
|
function validateDirectoryInput(dir) {
|
|
34183
34201
|
if (!dir || dir.length === 0) {
|
|
@@ -36621,6 +36639,175 @@ var init_preflight_integration = __esm(() => {
|
|
|
36621
36639
|
init_preflight_service();
|
|
36622
36640
|
});
|
|
36623
36641
|
|
|
36642
|
+
// src/hooks/curator-drift.ts
|
|
36643
|
+
var exports_curator_drift = {};
|
|
36644
|
+
__export(exports_curator_drift, {
|
|
36645
|
+
writeDriftReport: () => writeDriftReport,
|
|
36646
|
+
runDeterministicDriftCheck: () => runDeterministicDriftCheck,
|
|
36647
|
+
readPriorDriftReports: () => readPriorDriftReports,
|
|
36648
|
+
buildDriftInjectionText: () => buildDriftInjectionText
|
|
36649
|
+
});
|
|
36650
|
+
import * as fs26 from "fs";
|
|
36651
|
+
import * as path37 from "path";
|
|
36652
|
+
async function readPriorDriftReports(directory) {
|
|
36653
|
+
const swarmDir = path37.join(directory, ".swarm");
|
|
36654
|
+
const entries = await fs26.promises.readdir(swarmDir).catch(() => null);
|
|
36655
|
+
if (entries === null)
|
|
36656
|
+
return [];
|
|
36657
|
+
const reportFiles = entries.filter((name2) => name2.startsWith(DRIFT_REPORT_PREFIX) && name2.endsWith(".json")).sort();
|
|
36658
|
+
const reports = [];
|
|
36659
|
+
for (const filename of reportFiles) {
|
|
36660
|
+
const content = await readSwarmFileAsync(directory, filename);
|
|
36661
|
+
if (content === null)
|
|
36662
|
+
continue;
|
|
36663
|
+
try {
|
|
36664
|
+
const report = JSON.parse(content);
|
|
36665
|
+
if (typeof report.phase !== "number" || typeof report.alignment !== "string") {
|
|
36666
|
+
console.warn(`[curator-drift] Skipping corrupt drift report: ${filename}`);
|
|
36667
|
+
continue;
|
|
36668
|
+
}
|
|
36669
|
+
reports.push(report);
|
|
36670
|
+
} catch {
|
|
36671
|
+
console.warn(`[curator-drift] Skipping unreadable drift report: ${filename}`);
|
|
36672
|
+
}
|
|
36673
|
+
}
|
|
36674
|
+
reports.sort((a, b) => a.phase - b.phase);
|
|
36675
|
+
return reports;
|
|
36676
|
+
}
|
|
36677
|
+
async function writeDriftReport(directory, report) {
|
|
36678
|
+
const filename = `${DRIFT_REPORT_PREFIX}${report.phase}.json`;
|
|
36679
|
+
const filePath = validateSwarmPath(directory, filename);
|
|
36680
|
+
const swarmDir = path37.dirname(filePath);
|
|
36681
|
+
await fs26.promises.mkdir(swarmDir, { recursive: true });
|
|
36682
|
+
try {
|
|
36683
|
+
await fs26.promises.writeFile(filePath, JSON.stringify(report, null, 2), "utf-8");
|
|
36684
|
+
} catch (err2) {
|
|
36685
|
+
throw new Error(`[curator-drift] Failed to write drift report to ${filePath}: ${String(err2)}`);
|
|
36686
|
+
}
|
|
36687
|
+
return filePath;
|
|
36688
|
+
}
|
|
36689
|
+
async function runDeterministicDriftCheck(directory, phase, curatorResult, config3, injectAdvisory) {
|
|
36690
|
+
try {
|
|
36691
|
+
const planMd = await readSwarmFileAsync(directory, "plan.md");
|
|
36692
|
+
const specMd = await readSwarmFileAsync(directory, "spec.md");
|
|
36693
|
+
const priorReports = await readPriorDriftReports(directory);
|
|
36694
|
+
const complianceCount = curatorResult.compliance.length;
|
|
36695
|
+
const warningCompliance = curatorResult.compliance.filter((obs) => obs.severity === "warning");
|
|
36696
|
+
let alignment = "ALIGNED";
|
|
36697
|
+
let driftScore = 0;
|
|
36698
|
+
if (!planMd) {
|
|
36699
|
+
alignment = "MINOR_DRIFT";
|
|
36700
|
+
driftScore = 0.3;
|
|
36701
|
+
} else if (warningCompliance.length >= 3) {
|
|
36702
|
+
alignment = "MAJOR_DRIFT";
|
|
36703
|
+
driftScore = Math.min(0.9, 0.5 + warningCompliance.length * 0.1);
|
|
36704
|
+
} else if (warningCompliance.length >= 1 || complianceCount >= 3) {
|
|
36705
|
+
alignment = "MINOR_DRIFT";
|
|
36706
|
+
driftScore = Math.min(0.49, 0.2 + complianceCount * 0.05);
|
|
36707
|
+
}
|
|
36708
|
+
const priorSummaries = priorReports.map((r) => r.injection_summary).filter(Boolean);
|
|
36709
|
+
const keyCorrections = warningCompliance.map((obs) => obs.description);
|
|
36710
|
+
const firstDeviation = warningCompliance.length > 0 ? {
|
|
36711
|
+
phase,
|
|
36712
|
+
task: "unknown",
|
|
36713
|
+
description: warningCompliance[0]?.description ?? ""
|
|
36714
|
+
} : null;
|
|
36715
|
+
const payloadLines = [
|
|
36716
|
+
`CURATOR_DIGEST: ${JSON.stringify(curatorResult.digest)}`,
|
|
36717
|
+
`CURATOR_COMPLIANCE: ${JSON.stringify(curatorResult.compliance)}`,
|
|
36718
|
+
`PLAN: ${planMd ?? "none"}`,
|
|
36719
|
+
`SPEC: ${specMd ?? "none"}`,
|
|
36720
|
+
`PRIOR_DRIFT_REPORTS: ${JSON.stringify(priorSummaries)}`
|
|
36721
|
+
];
|
|
36722
|
+
const payload = payloadLines.join(`
|
|
36723
|
+
`);
|
|
36724
|
+
const requirementsChecked = curatorResult.digest.tasks_total;
|
|
36725
|
+
const requirementsSatisfied = curatorResult.digest.tasks_completed;
|
|
36726
|
+
const injectionSummaryRaw = `Phase ${phase}: ${alignment} (${driftScore.toFixed(2)}) \u2014 ${firstDeviation ? firstDeviation.description : "all requirements on track"}.${keyCorrections.length > 0 ? `Correction: ${keyCorrections[0] ?? ""}.` : ""}`;
|
|
36727
|
+
const injectionSummary = injectionSummaryRaw.slice(0, config3.drift_inject_max_chars);
|
|
36728
|
+
const report = {
|
|
36729
|
+
schema_version: 1,
|
|
36730
|
+
phase,
|
|
36731
|
+
timestamp: new Date().toISOString(),
|
|
36732
|
+
alignment,
|
|
36733
|
+
drift_score: driftScore,
|
|
36734
|
+
first_deviation: firstDeviation,
|
|
36735
|
+
compounding_effects: priorReports.filter((r) => r.alignment !== "ALIGNED").map((r) => `Phase ${r.phase}: ${r.alignment}`).slice(0, 5),
|
|
36736
|
+
corrections: keyCorrections.slice(0, 5),
|
|
36737
|
+
requirements_checked: requirementsChecked,
|
|
36738
|
+
requirements_satisfied: requirementsSatisfied,
|
|
36739
|
+
scope_additions: [],
|
|
36740
|
+
injection_summary: injectionSummary
|
|
36741
|
+
};
|
|
36742
|
+
const reportPath = await writeDriftReport(directory, report);
|
|
36743
|
+
getGlobalEventBus().publish("curator.drift.completed", {
|
|
36744
|
+
phase,
|
|
36745
|
+
alignment,
|
|
36746
|
+
drift_score: driftScore,
|
|
36747
|
+
report_path: reportPath
|
|
36748
|
+
});
|
|
36749
|
+
if (injectAdvisory && alignment !== "ALIGNED" && driftScore > 0) {
|
|
36750
|
+
try {
|
|
36751
|
+
const advisoryText = `CURATOR DRIFT DETECTED (phase ${phase}, score ${driftScore.toFixed(2)}): ${injectionSummary.slice(0, 300)}. Review .swarm/${DRIFT_REPORT_PREFIX}${phase}.json and address spec alignment before proceeding.`;
|
|
36752
|
+
injectAdvisory(advisoryText);
|
|
36753
|
+
} catch {}
|
|
36754
|
+
}
|
|
36755
|
+
const injectionText = injectionSummary;
|
|
36756
|
+
return {
|
|
36757
|
+
phase,
|
|
36758
|
+
report,
|
|
36759
|
+
report_path: reportPath,
|
|
36760
|
+
injection_text: injectionText
|
|
36761
|
+
};
|
|
36762
|
+
} catch (err2) {
|
|
36763
|
+
getGlobalEventBus().publish("curator.error", {
|
|
36764
|
+
operation: "drift",
|
|
36765
|
+
phase,
|
|
36766
|
+
error: String(err2)
|
|
36767
|
+
});
|
|
36768
|
+
const defaultReport = {
|
|
36769
|
+
schema_version: 1,
|
|
36770
|
+
phase,
|
|
36771
|
+
timestamp: new Date().toISOString(),
|
|
36772
|
+
alignment: "ALIGNED",
|
|
36773
|
+
drift_score: 0,
|
|
36774
|
+
first_deviation: null,
|
|
36775
|
+
compounding_effects: [],
|
|
36776
|
+
corrections: [],
|
|
36777
|
+
requirements_checked: 0,
|
|
36778
|
+
requirements_satisfied: 0,
|
|
36779
|
+
scope_additions: [],
|
|
36780
|
+
injection_summary: `Phase ${phase}: drift analysis unavailable (${String(err2)})`
|
|
36781
|
+
};
|
|
36782
|
+
return {
|
|
36783
|
+
phase,
|
|
36784
|
+
report: defaultReport,
|
|
36785
|
+
report_path: "",
|
|
36786
|
+
injection_text: ""
|
|
36787
|
+
};
|
|
36788
|
+
}
|
|
36789
|
+
}
|
|
36790
|
+
function buildDriftInjectionText(report, maxChars) {
|
|
36791
|
+
if (maxChars <= 0) {
|
|
36792
|
+
return "";
|
|
36793
|
+
}
|
|
36794
|
+
let text;
|
|
36795
|
+
if (report.alignment === "ALIGNED" && report.drift_score < 0.1) {
|
|
36796
|
+
text = `<drift_report>Phase ${report.phase}: ALIGNED, all requirements on track.</drift_report>`;
|
|
36797
|
+
} else {
|
|
36798
|
+
const keyFinding = report.first_deviation?.description ?? "no deviation recorded";
|
|
36799
|
+
const score = report.drift_score ?? 0;
|
|
36800
|
+
const correctionClause = report.corrections?.[0] ? `Correction: ${report.corrections[0]}.` : "";
|
|
36801
|
+
text = `<drift_report>Phase ${report.phase}: ${report.alignment} (${score.toFixed(2)}) \u2014 ${keyFinding}. ${correctionClause}</drift_report>`;
|
|
36802
|
+
}
|
|
36803
|
+
return text.slice(0, maxChars);
|
|
36804
|
+
}
|
|
36805
|
+
var DRIFT_REPORT_PREFIX = "drift-report-phase-";
|
|
36806
|
+
var init_curator_drift = __esm(() => {
|
|
36807
|
+
init_event_bus();
|
|
36808
|
+
init_utils2();
|
|
36809
|
+
});
|
|
36810
|
+
|
|
36624
36811
|
// node_modules/web-tree-sitter/tree-sitter.js
|
|
36625
36812
|
function assertInternal(x) {
|
|
36626
36813
|
if (x !== INTERNAL)
|
|
@@ -40199,7 +40386,7 @@ async function readPlanFromDisk(directory) {
|
|
|
40199
40386
|
return null;
|
|
40200
40387
|
}
|
|
40201
40388
|
}
|
|
40202
|
-
async function
|
|
40389
|
+
async function readGateEvidenceFromDisk(directory) {
|
|
40203
40390
|
const evidenceMap = new Map;
|
|
40204
40391
|
try {
|
|
40205
40392
|
const evidenceDir = path3.join(directory, ".swarm", "evidence");
|
|
@@ -40234,7 +40421,7 @@ async function buildRehydrationCache(directory) {
|
|
|
40234
40421
|
}
|
|
40235
40422
|
}
|
|
40236
40423
|
}
|
|
40237
|
-
const evidenceMap = await
|
|
40424
|
+
const evidenceMap = await readGateEvidenceFromDisk(directory);
|
|
40238
40425
|
_rehydrationCache = { planTaskStates, evidenceMap };
|
|
40239
40426
|
}
|
|
40240
40427
|
function applyRehydrationCache(session) {
|
|
@@ -40272,7 +40459,11 @@ async function rehydrateSessionFromDisk(directory, session) {
|
|
|
40272
40459
|
await buildRehydrationCache(directory);
|
|
40273
40460
|
applyRehydrationCache(session);
|
|
40274
40461
|
}
|
|
40275
|
-
function hasActiveTurboMode() {
|
|
40462
|
+
function hasActiveTurboMode(sessionID) {
|
|
40463
|
+
if (sessionID) {
|
|
40464
|
+
const session = swarmState.agentSessions.get(sessionID);
|
|
40465
|
+
return session?.turboMode === true;
|
|
40466
|
+
}
|
|
40276
40467
|
for (const [_sessionId, session] of swarmState.agentSessions) {
|
|
40277
40468
|
if (session.turboMode === true) {
|
|
40278
40469
|
return true;
|
|
@@ -40339,7 +40530,9 @@ Do not re-trigger DISCOVER or CONSULT because you noticed a project phase bounda
|
|
|
40339
40530
|
Output to .swarm/plan.md MUST use "## Phase N" headers. Do not write MODE labels into plan.md.
|
|
40340
40531
|
|
|
40341
40532
|
1. DELEGATE all coding to {{AGENT_PREFIX}}coder. You do NOT write code.
|
|
40342
|
-
|
|
40533
|
+
// IMPORTANT: This list MUST match AGENT_TOOL_MAP['architect'] in src/config/constants.ts
|
|
40534
|
+
// If you add a tool to the map, add it here. If you remove it from the map, remove it here.
|
|
40535
|
+
YOUR TOOLS: Task (delegation), checkpoint, check_gate_status, complexity_hotspots, declare_scope, detect_domains, diff, evidence_check, extract_code_blocks, gitingest, imports, knowledge_query, lint, pkg_audit, pre_check_batch, retrieve_summary, save_plan, schema_drift, secretscan, symbols, test_runner, todo_extract, update_task_status, write_retro.
|
|
40343
40536
|
CODER'S TOOLS: write, edit, patch, apply_patch, create_file, insert, replace \u2014 any tool that modifies file contents.
|
|
40344
40537
|
If a tool modifies a file, it is a CODER tool. Delegate.
|
|
40345
40538
|
2. ONE agent per message. Send, STOP, wait for response.
|
|
@@ -41187,10 +41380,10 @@ The tool will automatically write the retrospective to \`.swarm/evidence/retro-{
|
|
|
41187
41380
|
4. Write retrospective evidence: record phase, total_tool_calls, coder_revisions, reviewer_rejections, test_failures, security_findings, integration_issues, task_count, task_complexity, top_rejection_reasons, lessons_learned to .swarm/evidence/ via write_retro. Reset Phase Metrics in context.md to 0.
|
|
41188
41381
|
4.5. Run \`evidence_check\` to verify all completed tasks have required evidence (review + test). If gaps found, note in retrospective lessons_learned. Optionally run \`pkg_audit\` if dependencies were modified during this phase. Optionally run \`schema_drift\` if API routes were modified during this phase.
|
|
41189
41382
|
5. Run \`sbom_generate\` with scope='changed' to capture post-implementation dependency snapshot (saved to \`.swarm/evidence/sbom/\`). This is a non-blocking step - always proceeds to summary.
|
|
41190
|
-
5.5. **
|
|
41383
|
+
5.5. **Drift verification**: Delegate to {{AGENT_PREFIX}}critic_drift_verifier for phase drift review BEFORE calling phase_complete. The critic_drift_verifier will read every target file, verify described changes exist, and produce drift evidence. Persist critic drift evidence at \`.swarm/evidence/{phase}/drift-verifier.json\`. Only then call phase_complete. If the critic returns needs_revision, address the missing items before retrying phase_complete. If spec.md does not exist: skip the critic delegation. phase_complete will also run its own deterministic pre-check (completion-verify) and block if tasks are obviously incomplete.
|
|
41191
41384
|
5.6. **Mandatory gate evidence**: Before calling phase_complete, ensure:
|
|
41192
41385
|
- \`.swarm/evidence/{phase}/completion-verify.json\` exists (written automatically by the completion-verify gate)
|
|
41193
|
-
- \`.swarm/evidence/{phase}/drift-verifier.json\` exists with verdict 'approved' (written by the
|
|
41386
|
+
- \`.swarm/evidence/{phase}/drift-verifier.json\` exists with verdict 'approved' (written by the critic_drift_verifier delegation in step 5.5)
|
|
41194
41387
|
If either is missing, run the missing gate first. Turbo mode skips both gates automatically.
|
|
41195
41388
|
6. Summarize to user
|
|
41196
41389
|
7. Ask: "Ready for Phase [N+1]?"
|
|
@@ -41270,7 +41463,7 @@ While Turbo Mode is active:
|
|
|
41270
41463
|
- **Stage A gates** (lint, imports, pre_check_batch) are still REQUIRED for ALL tasks
|
|
41271
41464
|
- **Tier 3 tasks** (security-sensitive files matching: architect*.ts, delegation*.ts, guardrails*.ts, adversarial*.ts, sanitiz*.ts, auth*, permission*, crypto*, secret*, security) still require FULL review (Stage B)
|
|
41272
41465
|
- **Tier 0-2 tasks** can skip Stage B (reviewer, test_engineer) to speed up execution
|
|
41273
|
-
- **Phase completion gates** (completion-verify and drift verification gate) are automatically bypassed \u2014 phase_complete will succeed without drift verification evidence when turbo is active
|
|
41466
|
+
- **Phase completion gates** (completion-verify and drift verification gate) are automatically bypassed \u2014 phase_complete will succeed without drift verification evidence when turbo is active. Note: turbo bypass is session-scoped; one session's turbo does not affect other sessions.
|
|
41274
41467
|
|
|
41275
41468
|
Classification still determines the pipeline:
|
|
41276
41469
|
- TIER 0 (metadata): lint + diff only \u2014 no change
|
|
@@ -42816,6 +43009,11 @@ If you call @coder instead of @${swarmId}_coder, the call will FAIL or go to the
|
|
|
42816
43009
|
critic.name = prefixName("critic_sounding_board");
|
|
42817
43010
|
agents.push(applyOverrides(critic, swarmAgents, swarmPrefix));
|
|
42818
43011
|
}
|
|
43012
|
+
if (!isAgentDisabled("critic_drift_verifier", swarmAgents, swarmPrefix)) {
|
|
43013
|
+
const critic = createCriticAgent(swarmAgents?.critic_drift_verifier?.model ?? getModel("critic"), undefined, undefined, "phase_drift_verifier");
|
|
43014
|
+
critic.name = prefixName("critic_drift_verifier");
|
|
43015
|
+
agents.push(applyOverrides(critic, swarmAgents, swarmPrefix));
|
|
43016
|
+
}
|
|
42819
43017
|
if (!isAgentDisabled("test_engineer", swarmAgents, swarmPrefix)) {
|
|
42820
43018
|
const testPrompts = getPrompts("test_engineer");
|
|
42821
43019
|
const testEngineer = createTestEngineerAgent(getModel("test_engineer"), testPrompts.prompt, testPrompts.appendPrompt);
|
|
@@ -45533,7 +45731,7 @@ async function handleCurateCommand(directory, _args) {
|
|
|
45533
45731
|
if (error93 instanceof Error) {
|
|
45534
45732
|
return `\u274C Curation failed: ${error93.message}`;
|
|
45535
45733
|
}
|
|
45536
|
-
return `\u274C Curation failed: ${String(error93)}`;
|
|
45734
|
+
return `\u274C Curation failed: ${error93 instanceof Error ? error93.message : String(error93)}`;
|
|
45537
45735
|
}
|
|
45538
45736
|
}
|
|
45539
45737
|
function formatCurationSummary(summary) {
|
|
@@ -45923,7 +46121,7 @@ async function handleDarkMatterCommand(directory, args2) {
|
|
|
45923
46121
|
[${entries.length} dark matter finding(s) saved to .swarm/knowledge.jsonl]`;
|
|
45924
46122
|
}
|
|
45925
46123
|
} catch (err2) {
|
|
45926
|
-
console.warn("dark-matter: failed to save knowledge entries:", err2);
|
|
46124
|
+
console.warn("dark-matter: failed to save knowledge entries:", err2 instanceof Error ? err2.message : String(err2));
|
|
45927
46125
|
return output;
|
|
45928
46126
|
}
|
|
45929
46127
|
}
|
|
@@ -47697,7 +47895,7 @@ async function handleKnowledgeQuarantineCommand(directory, args2) {
|
|
|
47697
47895
|
await quarantineEntry(directory, entryId, reason, "user");
|
|
47698
47896
|
return `\u2705 Entry ${entryId} quarantined successfully.`;
|
|
47699
47897
|
} catch (error93) {
|
|
47700
|
-
console.warn("[knowledge-command] quarantineEntry error:", error93);
|
|
47898
|
+
console.warn("[knowledge-command] quarantineEntry error:", error93 instanceof Error ? error93.message : String(error93));
|
|
47701
47899
|
return `\u274C Failed to quarantine entry. Check the entry ID and try again.`;
|
|
47702
47900
|
}
|
|
47703
47901
|
}
|
|
@@ -47713,7 +47911,7 @@ async function handleKnowledgeRestoreCommand(directory, args2) {
|
|
|
47713
47911
|
await restoreEntry(directory, entryId);
|
|
47714
47912
|
return `\u2705 Entry ${entryId} restored successfully.`;
|
|
47715
47913
|
} catch (error93) {
|
|
47716
|
-
console.warn("[knowledge-command] restoreEntry error:", error93);
|
|
47914
|
+
console.warn("[knowledge-command] restoreEntry error:", error93 instanceof Error ? error93.message : String(error93));
|
|
47717
47915
|
return `\u274C Failed to restore entry. Check the entry ID and try again.`;
|
|
47718
47916
|
}
|
|
47719
47917
|
}
|
|
@@ -47735,7 +47933,7 @@ async function handleKnowledgeMigrateCommand(directory, args2) {
|
|
|
47735
47933
|
}
|
|
47736
47934
|
return `\u2705 Migration complete: ${result.entriesMigrated} entries added, ${result.entriesDropped} dropped (validation/dedup), ${result.entriesTotal} total processed.`;
|
|
47737
47935
|
} catch (error93) {
|
|
47738
|
-
console.warn("[knowledge-command] migrateContextToKnowledge error:", error93);
|
|
47936
|
+
console.warn("[knowledge-command] migrateContextToKnowledge error:", error93 instanceof Error ? error93.message : String(error93));
|
|
47739
47937
|
return "\u274C Migration failed. Check .swarm/context.md is readable.";
|
|
47740
47938
|
}
|
|
47741
47939
|
}
|
|
@@ -47762,7 +47960,7 @@ async function handleKnowledgeListCommand(directory, _args) {
|
|
|
47762
47960
|
return lines.join(`
|
|
47763
47961
|
`);
|
|
47764
47962
|
} catch (error93) {
|
|
47765
|
-
console.warn("[knowledge-command] list error:", error93);
|
|
47963
|
+
console.warn("[knowledge-command] list error:", error93 instanceof Error ? error93.message : String(error93));
|
|
47766
47964
|
return "\u274C Failed to list knowledge entries. Ensure .swarm/knowledge.jsonl exists.";
|
|
47767
47965
|
}
|
|
47768
47966
|
}
|
|
@@ -47953,7 +48151,7 @@ async function handlePromoteCommand(directory, args2) {
|
|
|
47953
48151
|
if (error93 instanceof Error) {
|
|
47954
48152
|
return error93.message;
|
|
47955
48153
|
}
|
|
47956
|
-
return `Failed to promote lesson: ${String(error93)}`;
|
|
48154
|
+
return `Failed to promote lesson: ${error93 instanceof Error ? error93.message : String(error93)}`;
|
|
47957
48155
|
}
|
|
47958
48156
|
}
|
|
47959
48157
|
try {
|
|
@@ -47962,7 +48160,7 @@ async function handlePromoteCommand(directory, args2) {
|
|
|
47962
48160
|
if (error93 instanceof Error) {
|
|
47963
48161
|
return error93.message;
|
|
47964
48162
|
}
|
|
47965
|
-
return `Failed to promote lesson: ${String(error93)}`;
|
|
48163
|
+
return `Failed to promote lesson: ${error93 instanceof Error ? error93.message : String(error93)}`;
|
|
47966
48164
|
}
|
|
47967
48165
|
}
|
|
47968
48166
|
|
|
@@ -48248,7 +48446,7 @@ async function handleRollbackCommand(directory, args2) {
|
|
|
48248
48446
|
fs18.appendFileSync(eventsPath, `${JSON.stringify(rollbackEvent)}
|
|
48249
48447
|
`);
|
|
48250
48448
|
} catch (error93) {
|
|
48251
|
-
console.error("Failed to write rollback event:", error93);
|
|
48449
|
+
console.error("Failed to write rollback event:", error93 instanceof Error ? error93.message : String(error93));
|
|
48252
48450
|
}
|
|
48253
48451
|
return `Rolled back to phase ${targetPhase}: ${checkpoint2.label || "no label"}`;
|
|
48254
48452
|
}
|
|
@@ -50948,15 +51146,19 @@ function createGuardrailsHooks(directory, directoryOrConfig, config3) {
|
|
|
50948
51146
|
const errorContent = output.error ?? outputStr;
|
|
50949
51147
|
if (typeof errorContent === "string" && TRANSIENT_MODEL_ERROR_PATTERN.test(errorContent) && !session.modelFallbackExhausted) {
|
|
50950
51148
|
session.model_fallback_index++;
|
|
50951
|
-
session.modelFallbackExhausted = true;
|
|
50952
51149
|
const baseAgentName = session.agentName ? session.agentName.replace(/^[^_]+[_]/, "") : "";
|
|
50953
|
-
const
|
|
51150
|
+
const swarmAgents = getSwarmAgents();
|
|
51151
|
+
const fallbackModels = swarmAgents?.[baseAgentName]?.fallback_models;
|
|
51152
|
+
session.modelFallbackExhausted = !fallbackModels || session.model_fallback_index > fallbackModels.length;
|
|
51153
|
+
const fallbackModel = resolveFallbackModel(baseAgentName, session.model_fallback_index, swarmAgents);
|
|
50954
51154
|
if (fallbackModel) {
|
|
50955
|
-
const swarmAgents = getSwarmAgents();
|
|
50956
51155
|
const primaryModel = swarmAgents?.[baseAgentName]?.model ?? "default";
|
|
51156
|
+
if (swarmAgents?.[baseAgentName]) {
|
|
51157
|
+
swarmAgents[baseAgentName].model = fallbackModel;
|
|
51158
|
+
}
|
|
50957
51159
|
telemetry.modelFallback(input.sessionID, session.agentName, primaryModel, fallbackModel, "transient_model_error");
|
|
50958
51160
|
session.pendingAdvisoryMessages ??= [];
|
|
50959
|
-
session.pendingAdvisoryMessages.push(`MODEL FALLBACK:
|
|
51161
|
+
session.pendingAdvisoryMessages.push(`MODEL FALLBACK: Applied fallback model "${fallbackModel}" (attempt ${session.model_fallback_index}). ` + `Using /swarm handoff to reset to primary model.`);
|
|
50960
51162
|
} else {
|
|
50961
51163
|
session.pendingAdvisoryMessages ??= [];
|
|
50962
51164
|
session.pendingAdvisoryMessages.push(`MODEL FALLBACK: Transient model error detected (attempt ${session.model_fallback_index}). ` + `No fallback models configured for this agent. Add "fallback_models": ["model-a", "model-b"] ` + `to the agent's config in opencode-swarm.json.`);
|
|
@@ -51362,9 +51564,40 @@ function createDelegationGateHook(config3, directory) {
|
|
|
51362
51564
|
if (!enabled) {
|
|
51363
51565
|
return {
|
|
51364
51566
|
messagesTransform: async (_input, _output) => {},
|
|
51567
|
+
toolBefore: async () => {},
|
|
51365
51568
|
toolAfter: async () => {}
|
|
51366
51569
|
};
|
|
51367
51570
|
}
|
|
51571
|
+
const toolBefore = async (input, output) => {
|
|
51572
|
+
if (!input.sessionID)
|
|
51573
|
+
return;
|
|
51574
|
+
const normalized = input.tool.replace(/^[^:]+[:.]/, "");
|
|
51575
|
+
if (normalized !== "Task" && normalized !== "task")
|
|
51576
|
+
return;
|
|
51577
|
+
const args2 = output.args;
|
|
51578
|
+
if (!args2)
|
|
51579
|
+
return;
|
|
51580
|
+
const subagentType = args2.subagent_type;
|
|
51581
|
+
if (typeof subagentType !== "string")
|
|
51582
|
+
return;
|
|
51583
|
+
const targetAgent = stripKnownSwarmPrefix(subagentType);
|
|
51584
|
+
if (targetAgent !== "coder")
|
|
51585
|
+
return;
|
|
51586
|
+
const session = swarmState.agentSessions.get(input.sessionID);
|
|
51587
|
+
if (!session || !session.taskWorkflowStates)
|
|
51588
|
+
return;
|
|
51589
|
+
for (const [taskId, state2] of session.taskWorkflowStates) {
|
|
51590
|
+
if (state2 !== "coder_delegated")
|
|
51591
|
+
continue;
|
|
51592
|
+
const turbo = hasActiveTurboMode(input.sessionID);
|
|
51593
|
+
if (turbo) {
|
|
51594
|
+
const isTier3 = taskId.startsWith("3.");
|
|
51595
|
+
if (!isTier3)
|
|
51596
|
+
continue;
|
|
51597
|
+
}
|
|
51598
|
+
throw new Error(`REVIEWER_GATE_VIOLATION: Cannot re-delegate to coder without reviewer delegation. ` + `Task ${taskId} state: coder_delegated. Delegate to reviewer first.`);
|
|
51599
|
+
}
|
|
51600
|
+
};
|
|
51368
51601
|
const toolAfter = async (input, _output) => {
|
|
51369
51602
|
if (!input.sessionID)
|
|
51370
51603
|
return;
|
|
@@ -51450,7 +51683,7 @@ function createDelegationGateHook(config3, directory) {
|
|
|
51450
51683
|
const rawTaskId = directArgs?.task_id;
|
|
51451
51684
|
const evidenceTaskId = typeof rawTaskId === "string" && rawTaskId.length <= 20 && /^\d+\.\d+$/.test(rawTaskId.trim()) ? rawTaskId.trim() : await getEvidenceTaskId(session, directory);
|
|
51452
51685
|
if (evidenceTaskId) {
|
|
51453
|
-
const turbo = hasActiveTurboMode();
|
|
51686
|
+
const turbo = hasActiveTurboMode(input.sessionID);
|
|
51454
51687
|
const gateAgents = [
|
|
51455
51688
|
"reviewer",
|
|
51456
51689
|
"test_engineer",
|
|
@@ -51569,7 +51802,7 @@ function createDelegationGateHook(config3, directory) {
|
|
|
51569
51802
|
const rawTaskId = directArgs?.task_id;
|
|
51570
51803
|
const evidenceTaskId = typeof rawTaskId === "string" && rawTaskId.length <= 20 && /^\d+\.\d+$/.test(rawTaskId.trim()) ? rawTaskId.trim() : await getEvidenceTaskId(session, directory);
|
|
51571
51804
|
if (evidenceTaskId) {
|
|
51572
|
-
const turbo = hasActiveTurboMode();
|
|
51805
|
+
const turbo = hasActiveTurboMode(input.sessionID);
|
|
51573
51806
|
if (hasReviewer) {
|
|
51574
51807
|
const { recordGateEvidence: recordGateEvidence2 } = await Promise.resolve().then(() => (init_gate_evidence(), exports_gate_evidence));
|
|
51575
51808
|
await recordGateEvidence2(directory, evidenceTaskId, "reviewer", input.sessionID, turbo);
|
|
@@ -51586,6 +51819,7 @@ function createDelegationGateHook(config3, directory) {
|
|
|
51586
51819
|
}
|
|
51587
51820
|
};
|
|
51588
51821
|
return {
|
|
51822
|
+
toolBefore,
|
|
51589
51823
|
messagesTransform: async (_input, output) => {
|
|
51590
51824
|
const messages = output.messages;
|
|
51591
51825
|
if (!messages || messages.length === 0)
|
|
@@ -54395,13 +54629,28 @@ async function processRetractions(retractions, directory) {
|
|
|
54395
54629
|
async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, config3) {
|
|
54396
54630
|
const knowledgePath = resolveSwarmKnowledgePath(directory);
|
|
54397
54631
|
const existingEntries = await readKnowledge(knowledgePath) ?? [];
|
|
54632
|
+
let stored = 0;
|
|
54633
|
+
let skipped = 0;
|
|
54634
|
+
let rejected = 0;
|
|
54635
|
+
const categoryByTag = new Map([
|
|
54636
|
+
["process", "process"],
|
|
54637
|
+
["architecture", "architecture"],
|
|
54638
|
+
["tooling", "tooling"],
|
|
54639
|
+
["security", "security"],
|
|
54640
|
+
["testing", "testing"],
|
|
54641
|
+
["debugging", "debugging"],
|
|
54642
|
+
["performance", "performance"],
|
|
54643
|
+
["integration", "integration"],
|
|
54644
|
+
["other", "other"]
|
|
54645
|
+
]);
|
|
54398
54646
|
for (const lesson of lessons) {
|
|
54399
54647
|
const tags = inferTags(lesson);
|
|
54400
54648
|
let category = "process";
|
|
54401
|
-
|
|
54402
|
-
|
|
54403
|
-
|
|
54404
|
-
|
|
54649
|
+
for (const tag of tags) {
|
|
54650
|
+
if (categoryByTag.has(tag)) {
|
|
54651
|
+
category = categoryByTag.get(tag);
|
|
54652
|
+
break;
|
|
54653
|
+
}
|
|
54405
54654
|
}
|
|
54406
54655
|
const meta3 = {
|
|
54407
54656
|
category,
|
|
@@ -54410,18 +54659,20 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
|
|
|
54410
54659
|
};
|
|
54411
54660
|
const result = validateLesson(lesson, existingEntries.map((e) => e.lesson), meta3);
|
|
54412
54661
|
if (result.valid === false || result.severity === "error") {
|
|
54413
|
-
const
|
|
54662
|
+
const rejectedLesson = {
|
|
54414
54663
|
id: crypto.randomUUID(),
|
|
54415
54664
|
lesson,
|
|
54416
54665
|
rejection_reason: result.reason ?? "unknown",
|
|
54417
54666
|
rejected_at: new Date().toISOString(),
|
|
54418
54667
|
rejection_layer: result.layer ?? 1
|
|
54419
54668
|
};
|
|
54420
|
-
await appendRejectedLesson(directory,
|
|
54669
|
+
await appendRejectedLesson(directory, rejectedLesson);
|
|
54670
|
+
rejected++;
|
|
54421
54671
|
continue;
|
|
54422
54672
|
}
|
|
54423
54673
|
const duplicate = findNearDuplicate(lesson, existingEntries, config3.dedup_threshold);
|
|
54424
54674
|
if (duplicate) {
|
|
54675
|
+
skipped++;
|
|
54425
54676
|
continue;
|
|
54426
54677
|
}
|
|
54427
54678
|
const entry = {
|
|
@@ -54452,9 +54703,11 @@ async function curateAndStoreSwarm(lessons, projectName, phaseInfo, directory, c
|
|
|
54452
54703
|
auto_generated: true
|
|
54453
54704
|
};
|
|
54454
54705
|
await appendKnowledge(knowledgePath, entry);
|
|
54706
|
+
stored++;
|
|
54455
54707
|
existingEntries.push(entry);
|
|
54456
54708
|
}
|
|
54457
54709
|
await runAutoPromotion(directory, config3);
|
|
54710
|
+
return { stored, skipped, rejected };
|
|
54458
54711
|
}
|
|
54459
54712
|
async function runAutoPromotion(directory, config3) {
|
|
54460
54713
|
const knowledgePath = resolveSwarmKnowledgePath(directory);
|
|
@@ -54670,200 +54923,8 @@ Use this data to avoid repeating known failure patterns.`;
|
|
|
54670
54923
|
return prefix + summaryText + suffix;
|
|
54671
54924
|
}
|
|
54672
54925
|
|
|
54673
|
-
// src/hooks/curator-drift.ts
|
|
54674
|
-
init_event_bus();
|
|
54675
|
-
init_utils2();
|
|
54676
|
-
import * as fs26 from "fs";
|
|
54677
|
-
import * as fsSync from "fs";
|
|
54678
|
-
import * as path37 from "path";
|
|
54679
|
-
var DRIFT_REPORT_PREFIX = "drift-report-phase-";
|
|
54680
|
-
async function readPriorDriftReports(directory) {
|
|
54681
|
-
const swarmDir = path37.join(directory, ".swarm");
|
|
54682
|
-
const entries = await fs26.promises.readdir(swarmDir).catch(() => null);
|
|
54683
|
-
if (entries === null)
|
|
54684
|
-
return [];
|
|
54685
|
-
const reportFiles = entries.filter((name2) => name2.startsWith(DRIFT_REPORT_PREFIX) && name2.endsWith(".json")).sort();
|
|
54686
|
-
const reports = [];
|
|
54687
|
-
for (const filename of reportFiles) {
|
|
54688
|
-
const content = await readSwarmFileAsync(directory, filename);
|
|
54689
|
-
if (content === null)
|
|
54690
|
-
continue;
|
|
54691
|
-
try {
|
|
54692
|
-
const report = JSON.parse(content);
|
|
54693
|
-
if (typeof report.phase !== "number" || typeof report.alignment !== "string") {
|
|
54694
|
-
console.warn(`[curator-drift] Skipping corrupt drift report: ${filename}`);
|
|
54695
|
-
continue;
|
|
54696
|
-
}
|
|
54697
|
-
reports.push(report);
|
|
54698
|
-
} catch {
|
|
54699
|
-
console.warn(`[curator-drift] Skipping unreadable drift report: ${filename}`);
|
|
54700
|
-
}
|
|
54701
|
-
}
|
|
54702
|
-
reports.sort((a, b) => a.phase - b.phase);
|
|
54703
|
-
return reports;
|
|
54704
|
-
}
|
|
54705
|
-
async function writeDriftReport(directory, report) {
|
|
54706
|
-
const filename = `${DRIFT_REPORT_PREFIX}${report.phase}.json`;
|
|
54707
|
-
const filePath = validateSwarmPath(directory, filename);
|
|
54708
|
-
const swarmDir = path37.dirname(filePath);
|
|
54709
|
-
await fs26.promises.mkdir(swarmDir, { recursive: true });
|
|
54710
|
-
try {
|
|
54711
|
-
await fs26.promises.writeFile(filePath, JSON.stringify(report, null, 2), "utf-8");
|
|
54712
|
-
} catch (err2) {
|
|
54713
|
-
throw new Error(`[curator-drift] Failed to write drift report to ${filePath}: ${String(err2)}`);
|
|
54714
|
-
}
|
|
54715
|
-
return filePath;
|
|
54716
|
-
}
|
|
54717
|
-
async function runCriticDriftCheck(directory, phase, curatorResult, config3, injectAdvisory) {
|
|
54718
|
-
try {
|
|
54719
|
-
const planMd = await readSwarmFileAsync(directory, "plan.md");
|
|
54720
|
-
const specMd = await readSwarmFileAsync(directory, "spec.md");
|
|
54721
|
-
const priorReports = await readPriorDriftReports(directory);
|
|
54722
|
-
const complianceCount = curatorResult.compliance.length;
|
|
54723
|
-
const warningCompliance = curatorResult.compliance.filter((obs) => obs.severity === "warning");
|
|
54724
|
-
let alignment = "ALIGNED";
|
|
54725
|
-
let driftScore = 0;
|
|
54726
|
-
if (!planMd) {
|
|
54727
|
-
alignment = "MINOR_DRIFT";
|
|
54728
|
-
driftScore = 0.3;
|
|
54729
|
-
} else if (warningCompliance.length >= 3) {
|
|
54730
|
-
alignment = "MAJOR_DRIFT";
|
|
54731
|
-
driftScore = Math.min(0.9, 0.5 + warningCompliance.length * 0.1);
|
|
54732
|
-
} else if (warningCompliance.length >= 1 || complianceCount >= 3) {
|
|
54733
|
-
alignment = "MINOR_DRIFT";
|
|
54734
|
-
driftScore = Math.min(0.49, 0.2 + complianceCount * 0.05);
|
|
54735
|
-
}
|
|
54736
|
-
const priorSummaries = priorReports.map((r) => r.injection_summary).filter(Boolean);
|
|
54737
|
-
const keyCorrections = warningCompliance.map((obs) => obs.description);
|
|
54738
|
-
const firstDeviation = warningCompliance.length > 0 ? {
|
|
54739
|
-
phase,
|
|
54740
|
-
task: "unknown",
|
|
54741
|
-
description: warningCompliance[0]?.description ?? ""
|
|
54742
|
-
} : null;
|
|
54743
|
-
const payloadLines = [
|
|
54744
|
-
`CURATOR_DIGEST: ${JSON.stringify(curatorResult.digest)}`,
|
|
54745
|
-
`CURATOR_COMPLIANCE: ${JSON.stringify(curatorResult.compliance)}`,
|
|
54746
|
-
`PLAN: ${planMd ?? "none"}`,
|
|
54747
|
-
`SPEC: ${specMd ?? "none"}`,
|
|
54748
|
-
`PRIOR_DRIFT_REPORTS: ${JSON.stringify(priorSummaries)}`
|
|
54749
|
-
];
|
|
54750
|
-
const payload = payloadLines.join(`
|
|
54751
|
-
`);
|
|
54752
|
-
const requirementsChecked = curatorResult.digest.tasks_total;
|
|
54753
|
-
const requirementsSatisfied = curatorResult.digest.tasks_completed;
|
|
54754
|
-
const injectionSummaryRaw = `Phase ${phase}: ${alignment} (${driftScore.toFixed(2)}) \u2014 ${firstDeviation ? firstDeviation.description : "all requirements on track"}.${keyCorrections.length > 0 ? `Correction: ${keyCorrections[0] ?? ""}.` : ""}`;
|
|
54755
|
-
const injectionSummary = injectionSummaryRaw.slice(0, config3.drift_inject_max_chars);
|
|
54756
|
-
const report = {
|
|
54757
|
-
schema_version: 1,
|
|
54758
|
-
phase,
|
|
54759
|
-
timestamp: new Date().toISOString(),
|
|
54760
|
-
alignment,
|
|
54761
|
-
drift_score: driftScore,
|
|
54762
|
-
first_deviation: firstDeviation,
|
|
54763
|
-
compounding_effects: priorReports.filter((r) => r.alignment !== "ALIGNED").map((r) => `Phase ${r.phase}: ${r.alignment}`).slice(0, 5),
|
|
54764
|
-
corrections: keyCorrections.slice(0, 5),
|
|
54765
|
-
requirements_checked: requirementsChecked,
|
|
54766
|
-
requirements_satisfied: requirementsSatisfied,
|
|
54767
|
-
scope_additions: [],
|
|
54768
|
-
injection_summary: injectionSummary
|
|
54769
|
-
};
|
|
54770
|
-
const reportPath = await writeDriftReport(directory, report);
|
|
54771
|
-
try {
|
|
54772
|
-
const evidenceDir = path37.join(directory, ".swarm", "evidence", String(phase));
|
|
54773
|
-
fsSync.mkdirSync(evidenceDir, { recursive: true });
|
|
54774
|
-
const evidencePath = path37.join(evidenceDir, "drift-verifier.json");
|
|
54775
|
-
let verdict;
|
|
54776
|
-
let summary;
|
|
54777
|
-
if (alignment === "MAJOR_DRIFT") {
|
|
54778
|
-
verdict = "rejected";
|
|
54779
|
-
summary = `Major drift detected (score: ${driftScore.toFixed(2)}): ${firstDeviation ? firstDeviation.description : "alignment issues detected"}`;
|
|
54780
|
-
} else if (alignment === "MINOR_DRIFT") {
|
|
54781
|
-
verdict = "approved";
|
|
54782
|
-
summary = `Minor drift detected (score: ${driftScore.toFixed(2)}): ${firstDeviation ? firstDeviation.description : "minor alignment issues"}`;
|
|
54783
|
-
} else {
|
|
54784
|
-
verdict = "approved";
|
|
54785
|
-
summary = "Drift check passed: all requirements aligned";
|
|
54786
|
-
}
|
|
54787
|
-
const evidence = {
|
|
54788
|
-
entries: [
|
|
54789
|
-
{
|
|
54790
|
-
type: "drift-verification",
|
|
54791
|
-
verdict,
|
|
54792
|
-
summary,
|
|
54793
|
-
timestamp: new Date().toISOString(),
|
|
54794
|
-
agent: "curator-drift",
|
|
54795
|
-
session_id: "curator"
|
|
54796
|
-
}
|
|
54797
|
-
]
|
|
54798
|
-
};
|
|
54799
|
-
fsSync.writeFileSync(evidencePath, JSON.stringify(evidence, null, 2), "utf-8");
|
|
54800
|
-
} catch (driftWriteErr) {
|
|
54801
|
-
console.warn(`[curator-drift] drift-verifier evidence write failed: ${driftWriteErr instanceof Error ? driftWriteErr.message : String(driftWriteErr)}`);
|
|
54802
|
-
}
|
|
54803
|
-
getGlobalEventBus().publish("curator.drift.completed", {
|
|
54804
|
-
phase,
|
|
54805
|
-
alignment,
|
|
54806
|
-
drift_score: driftScore,
|
|
54807
|
-
report_path: reportPath
|
|
54808
|
-
});
|
|
54809
|
-
if (injectAdvisory && alignment !== "ALIGNED" && driftScore > 0) {
|
|
54810
|
-
try {
|
|
54811
|
-
const advisoryText = `CURATOR DRIFT DETECTED (phase ${phase}, score ${driftScore.toFixed(2)}): ${injectionSummary.slice(0, 300)}. Review .swarm/${DRIFT_REPORT_PREFIX}${phase}.json and address spec alignment before proceeding.`;
|
|
54812
|
-
injectAdvisory(advisoryText);
|
|
54813
|
-
} catch {}
|
|
54814
|
-
}
|
|
54815
|
-
const injectionText = injectionSummary;
|
|
54816
|
-
return {
|
|
54817
|
-
phase,
|
|
54818
|
-
report,
|
|
54819
|
-
report_path: reportPath,
|
|
54820
|
-
injection_text: injectionText
|
|
54821
|
-
};
|
|
54822
|
-
} catch (err2) {
|
|
54823
|
-
getGlobalEventBus().publish("curator.error", {
|
|
54824
|
-
operation: "drift",
|
|
54825
|
-
phase,
|
|
54826
|
-
error: String(err2)
|
|
54827
|
-
});
|
|
54828
|
-
const defaultReport = {
|
|
54829
|
-
schema_version: 1,
|
|
54830
|
-
phase,
|
|
54831
|
-
timestamp: new Date().toISOString(),
|
|
54832
|
-
alignment: "ALIGNED",
|
|
54833
|
-
drift_score: 0,
|
|
54834
|
-
first_deviation: null,
|
|
54835
|
-
compounding_effects: [],
|
|
54836
|
-
corrections: [],
|
|
54837
|
-
requirements_checked: 0,
|
|
54838
|
-
requirements_satisfied: 0,
|
|
54839
|
-
scope_additions: [],
|
|
54840
|
-
injection_summary: `Phase ${phase}: drift analysis unavailable (${String(err2)})`
|
|
54841
|
-
};
|
|
54842
|
-
return {
|
|
54843
|
-
phase,
|
|
54844
|
-
report: defaultReport,
|
|
54845
|
-
report_path: "",
|
|
54846
|
-
injection_text: ""
|
|
54847
|
-
};
|
|
54848
|
-
}
|
|
54849
|
-
}
|
|
54850
|
-
function buildDriftInjectionText(report, maxChars) {
|
|
54851
|
-
if (maxChars <= 0) {
|
|
54852
|
-
return "";
|
|
54853
|
-
}
|
|
54854
|
-
let text;
|
|
54855
|
-
if (report.alignment === "ALIGNED" && report.drift_score < 0.1) {
|
|
54856
|
-
text = `<drift_report>Phase ${report.phase}: ALIGNED, all requirements on track.</drift_report>`;
|
|
54857
|
-
} else {
|
|
54858
|
-
const keyFinding = report.first_deviation?.description ?? "no deviation recorded";
|
|
54859
|
-
const score = report.drift_score ?? 0;
|
|
54860
|
-
const correctionClause = report.corrections?.[0] ? `Correction: ${report.corrections[0]}.` : "";
|
|
54861
|
-
text = `<drift_report>Phase ${report.phase}: ${report.alignment} (${score.toFixed(2)}) \u2014 ${keyFinding}. ${correctionClause}</drift_report>`;
|
|
54862
|
-
}
|
|
54863
|
-
return text.slice(0, maxChars);
|
|
54864
|
-
}
|
|
54865
|
-
|
|
54866
54926
|
// src/hooks/knowledge-injector.ts
|
|
54927
|
+
init_curator_drift();
|
|
54867
54928
|
init_utils2();
|
|
54868
54929
|
function formatStars(confidence) {
|
|
54869
54930
|
if (confidence >= 0.9)
|
|
@@ -55745,7 +55806,7 @@ var build_check = createSwarmTool({
|
|
|
55745
55806
|
try {
|
|
55746
55807
|
await saveEvidence(workingDir, "build", evidence);
|
|
55747
55808
|
} catch (error93) {
|
|
55748
|
-
console.error("Failed to save build evidence:", error93);
|
|
55809
|
+
console.error("Failed to save build evidence:", error93 instanceof Error ? error93.message : String(error93));
|
|
55749
55810
|
}
|
|
55750
55811
|
return JSON.stringify(result, null, 2);
|
|
55751
55812
|
}
|
|
@@ -55932,14 +55993,6 @@ init_utils2();
|
|
|
55932
55993
|
import * as fs30 from "fs";
|
|
55933
55994
|
import * as path41 from "path";
|
|
55934
55995
|
init_create_tool();
|
|
55935
|
-
function hasActiveTurboMode2() {
|
|
55936
|
-
for (const [_sessionId, session] of swarmState.agentSessions) {
|
|
55937
|
-
if (session.turboMode === true) {
|
|
55938
|
-
return true;
|
|
55939
|
-
}
|
|
55940
|
-
}
|
|
55941
|
-
return false;
|
|
55942
|
-
}
|
|
55943
55996
|
function extractMatches(regex, text) {
|
|
55944
55997
|
return Array.from(text.matchAll(regex));
|
|
55945
55998
|
}
|
|
@@ -56004,7 +56057,7 @@ async function executeCompletionVerify(args2, directory) {
|
|
|
56004
56057
|
};
|
|
56005
56058
|
return JSON.stringify(result2, null, 2);
|
|
56006
56059
|
}
|
|
56007
|
-
if (
|
|
56060
|
+
if (hasActiveTurboMode(args2.sessionID)) {
|
|
56008
56061
|
const result2 = {
|
|
56009
56062
|
success: true,
|
|
56010
56063
|
phase,
|
|
@@ -56535,7 +56588,7 @@ var curator_analyze = createSwarmTool({
|
|
|
56535
56588
|
}, null, 2);
|
|
56536
56589
|
} catch (error93) {
|
|
56537
56590
|
return JSON.stringify({
|
|
56538
|
-
error: String(error93),
|
|
56591
|
+
error: error93 instanceof Error ? error93.message : String(error93),
|
|
56539
56592
|
phase: typedArgs.phase
|
|
56540
56593
|
}, null, 2);
|
|
56541
56594
|
}
|
|
@@ -58759,7 +58812,7 @@ init_telemetry();
|
|
|
58759
58812
|
init_create_tool();
|
|
58760
58813
|
function safeWarn(message, error93) {
|
|
58761
58814
|
try {
|
|
58762
|
-
console.warn(message, error93);
|
|
58815
|
+
console.warn(message, error93 instanceof Error ? error93.message : String(error93));
|
|
58763
58816
|
} catch {}
|
|
58764
58817
|
}
|
|
58765
58818
|
function collectCrossSessionDispatchedAgents(phaseReferenceTimestamp, callerSessionId) {
|
|
@@ -58949,7 +59002,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
|
|
|
58949
59002
|
]
|
|
58950
59003
|
}, null, 2);
|
|
58951
59004
|
}
|
|
58952
|
-
if (hasActiveTurboMode()) {
|
|
59005
|
+
if (hasActiveTurboMode(sessionID)) {
|
|
58953
59006
|
console.warn(`[phase_complete] Turbo mode active \u2014 skipping completion-verify and drift-verifier gates for phase ${phase}`);
|
|
58954
59007
|
} else {
|
|
58955
59008
|
try {
|
|
@@ -59010,7 +59063,23 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
|
|
|
59010
59063
|
const specPath = path48.join(dir, ".swarm", "spec.md");
|
|
59011
59064
|
const specExists = fs37.existsSync(specPath);
|
|
59012
59065
|
if (!specExists) {
|
|
59013
|
-
|
|
59066
|
+
let incompleteTaskCount = 0;
|
|
59067
|
+
let planPhaseFound = false;
|
|
59068
|
+
try {
|
|
59069
|
+
const planPath = validateSwarmPath(dir, "plan.json");
|
|
59070
|
+
const planRaw = fs37.readFileSync(planPath, "utf-8");
|
|
59071
|
+
const plan = JSON.parse(planRaw);
|
|
59072
|
+
const targetPhase = plan.phases.find((p) => p.id === phase);
|
|
59073
|
+
if (targetPhase) {
|
|
59074
|
+
planPhaseFound = true;
|
|
59075
|
+
incompleteTaskCount = targetPhase.tasks.filter((t) => t.status !== "completed").length;
|
|
59076
|
+
}
|
|
59077
|
+
} catch {}
|
|
59078
|
+
if (incompleteTaskCount > 0 || !planPhaseFound) {
|
|
59079
|
+
warnings.push(`No spec.md found and drift verification evidence missing. Phase ${phase} has ${incompleteTaskCount} incomplete task(s) in plan.json \u2014 consider running critic_drift_verifier before phase completion.`);
|
|
59080
|
+
} else {
|
|
59081
|
+
warnings.push(`No spec.md found. Phase ${phase} tasks are all completed in plan.json. Drift verification was skipped.`);
|
|
59082
|
+
}
|
|
59014
59083
|
} else {
|
|
59015
59084
|
return JSON.stringify({
|
|
59016
59085
|
success: false,
|
|
@@ -59076,17 +59145,21 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
|
|
|
59076
59145
|
const curatorConfig = CuratorConfigSchema.parse(config3.curator ?? {});
|
|
59077
59146
|
if (curatorConfig.enabled && curatorConfig.phase_enabled) {
|
|
59078
59147
|
const curatorResult = await runCuratorPhase(dir, phase, agentsDispatched, curatorConfig, {});
|
|
59079
|
-
await applyCuratorKnowledgeUpdates(dir, curatorResult.knowledge_recommendations, knowledgeConfig);
|
|
59080
|
-
const driftResult = await runCriticDriftCheck(dir, phase, curatorResult, curatorConfig);
|
|
59148
|
+
const knowledgeResult = await applyCuratorKnowledgeUpdates(dir, curatorResult.knowledge_recommendations, knowledgeConfig);
|
|
59081
59149
|
const callerSessionState = swarmState.agentSessions.get(sessionID);
|
|
59082
59150
|
if (callerSessionState) {
|
|
59083
59151
|
callerSessionState.pendingAdvisoryMessages ??= [];
|
|
59084
59152
|
const digestSummary = curatorResult.digest?.summary ? curatorResult.digest.summary.slice(0, 200) : "Phase analysis complete";
|
|
59085
59153
|
const complianceNote = curatorResult.compliance.length > 0 ? ` (${curatorResult.compliance.length} compliance observation(s))` : "";
|
|
59086
|
-
callerSessionState.pendingAdvisoryMessages.push(`[CURATOR] Phase ${phase} digest: ${digestSummary}${complianceNote}. Call curator_analyze with recommendations to apply knowledge updates from this phase.`);
|
|
59087
|
-
|
|
59088
|
-
|
|
59089
|
-
|
|
59154
|
+
callerSessionState.pendingAdvisoryMessages.push(`[CURATOR] Phase ${phase} digest: ${digestSummary}${complianceNote}. Knowledge: ${knowledgeResult.applied} applied, ${knowledgeResult.skipped} skipped. Call curator_analyze with recommendations to apply knowledge updates from this phase.`);
|
|
59155
|
+
try {
|
|
59156
|
+
const { readPriorDriftReports: readPriorDriftReports2 } = await Promise.resolve().then(() => (init_curator_drift(), exports_curator_drift));
|
|
59157
|
+
const priorReports = await readPriorDriftReports2(dir);
|
|
59158
|
+
const phaseReport = priorReports.filter((r) => r.phase === phase).pop();
|
|
59159
|
+
if (phaseReport && phaseReport.drift_score > 0) {
|
|
59160
|
+
callerSessionState.pendingAdvisoryMessages.push(`[CURATOR DRIFT DETECTED (phase ${phase}, score ${phaseReport.drift_score})]: Consider running critic_drift_verifier before phase completion to get a proper drift review. Review drift report for phase ${phase} and address spec alignment if applicable.`);
|
|
59161
|
+
}
|
|
59162
|
+
} catch {}
|
|
59090
59163
|
}
|
|
59091
59164
|
if (curatorResult.compliance.length > 0 && !curatorConfig.suppress_warnings) {
|
|
59092
59165
|
const complianceLines = curatorResult.compliance.map((obs) => `[${obs.severity.toUpperCase()}] ${obs.description}`).slice(0, 5);
|
|
@@ -59222,7 +59295,7 @@ async function executePhaseComplete(args2, workingDirectory, directory) {
|
|
|
59222
59295
|
return JSON.stringify({ ...result, timestamp: event.timestamp, duration_ms: durationMs }, null, 2);
|
|
59223
59296
|
}
|
|
59224
59297
|
var phase_complete = createSwarmTool({
|
|
59225
|
-
description: "Mark a phase as complete and track which agents were dispatched.
|
|
59298
|
+
description: "Mark a phase as complete and track which agents were dispatched. Used for phase completion gating and tracking. Accepts phase number and optional summary. Returns list of agents that were dispatched.",
|
|
59226
59299
|
args: {
|
|
59227
59300
|
phase: tool.schema.number().describe("The phase number being completed (e.g., 1, 2, 3)"),
|
|
59228
59301
|
summary: tool.schema.string().optional().describe("Optional summary of what was accomplished in this phase"),
|
|
@@ -63220,7 +63293,7 @@ async function executeSavePlan(args2, fallbackDir) {
|
|
|
63220
63293
|
return {
|
|
63221
63294
|
success: false,
|
|
63222
63295
|
message: "Failed to save plan: retry with save_plan after resolving the error above",
|
|
63223
|
-
errors: [String(error93)],
|
|
63296
|
+
errors: [error93 instanceof Error ? error93.message : String(error93)],
|
|
63224
63297
|
recovery_guidance: "Use save_plan with corrected inputs to create or restructure plans. Never write .swarm/plan.json or .swarm/plan.md directly."
|
|
63225
63298
|
};
|
|
63226
63299
|
}
|
|
@@ -65381,17 +65454,9 @@ function matchesTier3Pattern(files) {
|
|
|
65381
65454
|
}
|
|
65382
65455
|
return false;
|
|
65383
65456
|
}
|
|
65384
|
-
function hasActiveTurboMode3() {
|
|
65385
|
-
for (const [_sessionId, session] of swarmState.agentSessions) {
|
|
65386
|
-
if (session.turboMode === true) {
|
|
65387
|
-
return true;
|
|
65388
|
-
}
|
|
65389
|
-
}
|
|
65390
|
-
return false;
|
|
65391
|
-
}
|
|
65392
65457
|
function checkReviewerGate(taskId, workingDirectory) {
|
|
65393
65458
|
try {
|
|
65394
|
-
if (
|
|
65459
|
+
if (hasActiveTurboMode()) {
|
|
65395
65460
|
const resolvedDir2 = workingDirectory;
|
|
65396
65461
|
try {
|
|
65397
65462
|
const planPath = path59.join(resolvedDir2, ".swarm", "plan.json");
|
|
@@ -65428,7 +65493,7 @@ function checkReviewerGate(taskId, workingDirectory) {
|
|
|
65428
65493
|
};
|
|
65429
65494
|
}
|
|
65430
65495
|
} catch (error93) {
|
|
65431
|
-
console.warn(`[gate-evidence] Evidence file for task ${taskId} is corrupt or unreadable:`, error93);
|
|
65496
|
+
console.warn(`[gate-evidence] Evidence file for task ${taskId} is corrupt or unreadable:`, error93 instanceof Error ? error93.message : String(error93));
|
|
65432
65497
|
telemetry.gateFailed("", "qa_gate", taskId, `Evidence file corrupt or unreadable`);
|
|
65433
65498
|
return {
|
|
65434
65499
|
blocked: true,
|
|
@@ -65721,7 +65786,7 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
|
|
|
65721
65786
|
return {
|
|
65722
65787
|
success: false,
|
|
65723
65788
|
message: "Failed to update task status",
|
|
65724
|
-
errors: [String(error93)]
|
|
65789
|
+
errors: [error93 instanceof Error ? error93.message : String(error93)]
|
|
65725
65790
|
};
|
|
65726
65791
|
}
|
|
65727
65792
|
}
|
|
@@ -66194,6 +66259,7 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
66194
66259
|
}
|
|
66195
66260
|
await guardrailsHooks.toolBefore(input, output);
|
|
66196
66261
|
await scopeGuardHook.toolBefore(input, output);
|
|
66262
|
+
await delegationGateHooks.toolBefore(input, output);
|
|
66197
66263
|
if (swarmState.lastBudgetPct >= 50) {
|
|
66198
66264
|
const pressureSession = ensureAgentSession(input.sessionID, swarmState.activeAgent.get(input.sessionID) ?? ORCHESTRATOR_NAME);
|
|
66199
66265
|
if (!pressureSession.contextPressureWarningSent) {
|
package/dist/state.d.ts
CHANGED
|
@@ -331,7 +331,9 @@ export declare function applyRehydrationCache(session: AgentSessionState): void;
|
|
|
331
331
|
*/
|
|
332
332
|
export declare function rehydrateSessionFromDisk(directory: string, session: AgentSessionState): Promise<void>;
|
|
333
333
|
/**
|
|
334
|
-
* Check if
|
|
335
|
-
* @
|
|
334
|
+
* Check if Turbo Mode is enabled for a specific session or ANY session.
|
|
335
|
+
* @param sessionID - Optional session ID to check. If provided, checks only that session.
|
|
336
|
+
* If omitted, checks all sessions (backward-compatible global behavior).
|
|
337
|
+
* @returns true if the specified session has turboMode: true, or if any session has turboMode: true when no sessionID provided
|
|
336
338
|
*/
|
|
337
|
-
export declare function hasActiveTurboMode(): boolean;
|
|
339
|
+
export declare function hasActiveTurboMode(sessionID?: string): boolean;
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
* Adversarial security tests for Task 2.15: Session restart rehydration
|
|
3
3
|
*
|
|
4
4
|
* ONLY attack vectors - malformed inputs, oversized payloads, injection attempts, boundary violations
|
|
5
|
-
* Tests the security hardening of rehydrateSessionFromDisk, readPlanFromDisk, and
|
|
5
|
+
* Tests the security hardening of rehydrateSessionFromDisk, readPlanFromDisk, and readGateEvidenceFromDisk
|
|
6
6
|
*/
|
|
7
7
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.37.0",
|
|
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",
|