opencode-swarm 5.0.9 → 5.1.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/config/constants.d.ts +10 -0
- package/dist/config/schema.d.ts +108 -0
- package/dist/hooks/context-scoring.d.ts +70 -0
- package/dist/index.js +272 -49
- package/dist/state.d.ts +2 -0
- package/package.json +1 -1
|
@@ -9,3 +9,13 @@ export type AgentName = (typeof ALL_AGENT_NAMES)[number];
|
|
|
9
9
|
export declare const DEFAULT_MODELS: Record<string, string>;
|
|
10
10
|
export declare function isQAAgent(name: string): name is QAAgentName;
|
|
11
11
|
export declare function isSubagent(name: string): boolean;
|
|
12
|
+
import type { ScoringConfig } from './schema';
|
|
13
|
+
export declare const DEFAULT_SCORING_CONFIG: ScoringConfig;
|
|
14
|
+
/**
|
|
15
|
+
* Resolve scoring configuration by deep-merging user config with defaults.
|
|
16
|
+
* Missing scoring block → use defaults; partial weights → merge with defaults.
|
|
17
|
+
*
|
|
18
|
+
* @param userConfig - Optional user-provided scoring configuration
|
|
19
|
+
* @returns The effective scoring configuration with all defaults applied
|
|
20
|
+
*/
|
|
21
|
+
export declare function resolveScoringConfig(userConfig?: ScoringConfig): ScoringConfig;
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -22,12 +22,93 @@ export declare const HooksConfigSchema: z.ZodObject<{
|
|
|
22
22
|
agent_awareness_max_chars: z.ZodDefault<z.ZodNumber>;
|
|
23
23
|
}, z.core.$strip>;
|
|
24
24
|
export type HooksConfig = z.infer<typeof HooksConfigSchema>;
|
|
25
|
+
export declare const ScoringWeightsSchema: z.ZodObject<{
|
|
26
|
+
phase: z.ZodDefault<z.ZodNumber>;
|
|
27
|
+
current_task: z.ZodDefault<z.ZodNumber>;
|
|
28
|
+
blocked_task: z.ZodDefault<z.ZodNumber>;
|
|
29
|
+
recent_failure: z.ZodDefault<z.ZodNumber>;
|
|
30
|
+
recent_success: z.ZodDefault<z.ZodNumber>;
|
|
31
|
+
evidence_presence: z.ZodDefault<z.ZodNumber>;
|
|
32
|
+
decision_recency: z.ZodDefault<z.ZodNumber>;
|
|
33
|
+
dependency_proximity: z.ZodDefault<z.ZodNumber>;
|
|
34
|
+
}, z.core.$strip>;
|
|
35
|
+
export type ScoringWeights = z.infer<typeof ScoringWeightsSchema>;
|
|
36
|
+
export declare const DecisionDecaySchema: z.ZodObject<{
|
|
37
|
+
mode: z.ZodDefault<z.ZodEnum<{
|
|
38
|
+
linear: "linear";
|
|
39
|
+
exponential: "exponential";
|
|
40
|
+
}>>;
|
|
41
|
+
half_life_hours: z.ZodDefault<z.ZodNumber>;
|
|
42
|
+
}, z.core.$strip>;
|
|
43
|
+
export type DecisionDecay = z.infer<typeof DecisionDecaySchema>;
|
|
44
|
+
export declare const TokenRatiosSchema: z.ZodObject<{
|
|
45
|
+
prose: z.ZodDefault<z.ZodNumber>;
|
|
46
|
+
code: z.ZodDefault<z.ZodNumber>;
|
|
47
|
+
markdown: z.ZodDefault<z.ZodNumber>;
|
|
48
|
+
json: z.ZodDefault<z.ZodNumber>;
|
|
49
|
+
}, z.core.$strip>;
|
|
50
|
+
export type TokenRatios = z.infer<typeof TokenRatiosSchema>;
|
|
51
|
+
export declare const ScoringConfigSchema: z.ZodObject<{
|
|
52
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
53
|
+
max_candidates: z.ZodDefault<z.ZodNumber>;
|
|
54
|
+
weights: z.ZodOptional<z.ZodObject<{
|
|
55
|
+
phase: z.ZodDefault<z.ZodNumber>;
|
|
56
|
+
current_task: z.ZodDefault<z.ZodNumber>;
|
|
57
|
+
blocked_task: z.ZodDefault<z.ZodNumber>;
|
|
58
|
+
recent_failure: z.ZodDefault<z.ZodNumber>;
|
|
59
|
+
recent_success: z.ZodDefault<z.ZodNumber>;
|
|
60
|
+
evidence_presence: z.ZodDefault<z.ZodNumber>;
|
|
61
|
+
decision_recency: z.ZodDefault<z.ZodNumber>;
|
|
62
|
+
dependency_proximity: z.ZodDefault<z.ZodNumber>;
|
|
63
|
+
}, z.core.$strip>>;
|
|
64
|
+
decision_decay: z.ZodOptional<z.ZodObject<{
|
|
65
|
+
mode: z.ZodDefault<z.ZodEnum<{
|
|
66
|
+
linear: "linear";
|
|
67
|
+
exponential: "exponential";
|
|
68
|
+
}>>;
|
|
69
|
+
half_life_hours: z.ZodDefault<z.ZodNumber>;
|
|
70
|
+
}, z.core.$strip>>;
|
|
71
|
+
token_ratios: z.ZodOptional<z.ZodObject<{
|
|
72
|
+
prose: z.ZodDefault<z.ZodNumber>;
|
|
73
|
+
code: z.ZodDefault<z.ZodNumber>;
|
|
74
|
+
markdown: z.ZodDefault<z.ZodNumber>;
|
|
75
|
+
json: z.ZodDefault<z.ZodNumber>;
|
|
76
|
+
}, z.core.$strip>>;
|
|
77
|
+
}, z.core.$strip>;
|
|
78
|
+
export type ScoringConfig = z.infer<typeof ScoringConfigSchema>;
|
|
25
79
|
export declare const ContextBudgetConfigSchema: z.ZodObject<{
|
|
26
80
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
27
81
|
warn_threshold: z.ZodDefault<z.ZodNumber>;
|
|
28
82
|
critical_threshold: z.ZodDefault<z.ZodNumber>;
|
|
29
83
|
model_limits: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodNumber>>;
|
|
30
84
|
max_injection_tokens: z.ZodDefault<z.ZodNumber>;
|
|
85
|
+
scoring: z.ZodOptional<z.ZodObject<{
|
|
86
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
87
|
+
max_candidates: z.ZodDefault<z.ZodNumber>;
|
|
88
|
+
weights: z.ZodOptional<z.ZodObject<{
|
|
89
|
+
phase: z.ZodDefault<z.ZodNumber>;
|
|
90
|
+
current_task: z.ZodDefault<z.ZodNumber>;
|
|
91
|
+
blocked_task: z.ZodDefault<z.ZodNumber>;
|
|
92
|
+
recent_failure: z.ZodDefault<z.ZodNumber>;
|
|
93
|
+
recent_success: z.ZodDefault<z.ZodNumber>;
|
|
94
|
+
evidence_presence: z.ZodDefault<z.ZodNumber>;
|
|
95
|
+
decision_recency: z.ZodDefault<z.ZodNumber>;
|
|
96
|
+
dependency_proximity: z.ZodDefault<z.ZodNumber>;
|
|
97
|
+
}, z.core.$strip>>;
|
|
98
|
+
decision_decay: z.ZodOptional<z.ZodObject<{
|
|
99
|
+
mode: z.ZodDefault<z.ZodEnum<{
|
|
100
|
+
linear: "linear";
|
|
101
|
+
exponential: "exponential";
|
|
102
|
+
}>>;
|
|
103
|
+
half_life_hours: z.ZodDefault<z.ZodNumber>;
|
|
104
|
+
}, z.core.$strip>>;
|
|
105
|
+
token_ratios: z.ZodOptional<z.ZodObject<{
|
|
106
|
+
prose: z.ZodDefault<z.ZodNumber>;
|
|
107
|
+
code: z.ZodDefault<z.ZodNumber>;
|
|
108
|
+
markdown: z.ZodDefault<z.ZodNumber>;
|
|
109
|
+
json: z.ZodDefault<z.ZodNumber>;
|
|
110
|
+
}, z.core.$strip>>;
|
|
111
|
+
}, z.core.$strip>>;
|
|
31
112
|
}, z.core.$strip>;
|
|
32
113
|
export type ContextBudgetConfig = z.infer<typeof ContextBudgetConfigSchema>;
|
|
33
114
|
export declare const EvidenceConfigSchema: z.ZodObject<{
|
|
@@ -126,6 +207,33 @@ export declare const PluginConfigSchema: z.ZodObject<{
|
|
|
126
207
|
critical_threshold: z.ZodDefault<z.ZodNumber>;
|
|
127
208
|
model_limits: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodNumber>>;
|
|
128
209
|
max_injection_tokens: z.ZodDefault<z.ZodNumber>;
|
|
210
|
+
scoring: z.ZodOptional<z.ZodObject<{
|
|
211
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
212
|
+
max_candidates: z.ZodDefault<z.ZodNumber>;
|
|
213
|
+
weights: z.ZodOptional<z.ZodObject<{
|
|
214
|
+
phase: z.ZodDefault<z.ZodNumber>;
|
|
215
|
+
current_task: z.ZodDefault<z.ZodNumber>;
|
|
216
|
+
blocked_task: z.ZodDefault<z.ZodNumber>;
|
|
217
|
+
recent_failure: z.ZodDefault<z.ZodNumber>;
|
|
218
|
+
recent_success: z.ZodDefault<z.ZodNumber>;
|
|
219
|
+
evidence_presence: z.ZodDefault<z.ZodNumber>;
|
|
220
|
+
decision_recency: z.ZodDefault<z.ZodNumber>;
|
|
221
|
+
dependency_proximity: z.ZodDefault<z.ZodNumber>;
|
|
222
|
+
}, z.core.$strip>>;
|
|
223
|
+
decision_decay: z.ZodOptional<z.ZodObject<{
|
|
224
|
+
mode: z.ZodDefault<z.ZodEnum<{
|
|
225
|
+
linear: "linear";
|
|
226
|
+
exponential: "exponential";
|
|
227
|
+
}>>;
|
|
228
|
+
half_life_hours: z.ZodDefault<z.ZodNumber>;
|
|
229
|
+
}, z.core.$strip>>;
|
|
230
|
+
token_ratios: z.ZodOptional<z.ZodObject<{
|
|
231
|
+
prose: z.ZodDefault<z.ZodNumber>;
|
|
232
|
+
code: z.ZodDefault<z.ZodNumber>;
|
|
233
|
+
markdown: z.ZodDefault<z.ZodNumber>;
|
|
234
|
+
json: z.ZodDefault<z.ZodNumber>;
|
|
235
|
+
}, z.core.$strip>>;
|
|
236
|
+
}, z.core.$strip>>;
|
|
129
237
|
}, z.core.$strip>>;
|
|
130
238
|
guardrails: z.ZodOptional<z.ZodObject<{
|
|
131
239
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Scoring Utility
|
|
3
|
+
*
|
|
4
|
+
* Pure scoring/ranking helpers for context injection budget.
|
|
5
|
+
* Implements deterministic, reproducible candidate ranking based on configurable weights.
|
|
6
|
+
*/
|
|
7
|
+
export type ContentType = 'prose' | 'code' | 'markdown' | 'json';
|
|
8
|
+
export type CandidateKind = 'phase' | 'task' | 'decision' | 'evidence' | 'agent_context';
|
|
9
|
+
export interface ContextCandidate {
|
|
10
|
+
id: string;
|
|
11
|
+
kind: CandidateKind;
|
|
12
|
+
text: string;
|
|
13
|
+
tokens: number;
|
|
14
|
+
priority: number;
|
|
15
|
+
metadata: {
|
|
16
|
+
contentType: ContentType;
|
|
17
|
+
dependencyDepth?: number;
|
|
18
|
+
decisionAgeHours?: number;
|
|
19
|
+
isCurrentTask?: boolean;
|
|
20
|
+
isBlockedTask?: boolean;
|
|
21
|
+
hasFailure?: boolean;
|
|
22
|
+
hasSuccess?: boolean;
|
|
23
|
+
hasEvidence?: boolean;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export interface RankedCandidate extends ContextCandidate {
|
|
27
|
+
score: number;
|
|
28
|
+
}
|
|
29
|
+
export interface ScoringWeights {
|
|
30
|
+
phase: number;
|
|
31
|
+
current_task: number;
|
|
32
|
+
blocked_task: number;
|
|
33
|
+
recent_failure: number;
|
|
34
|
+
recent_success: number;
|
|
35
|
+
evidence_presence: number;
|
|
36
|
+
decision_recency: number;
|
|
37
|
+
dependency_proximity: number;
|
|
38
|
+
}
|
|
39
|
+
export interface DecisionDecayConfig {
|
|
40
|
+
mode: 'linear' | 'exponential';
|
|
41
|
+
half_life_hours: number;
|
|
42
|
+
}
|
|
43
|
+
export interface TokenRatios {
|
|
44
|
+
prose: number;
|
|
45
|
+
code: number;
|
|
46
|
+
markdown: number;
|
|
47
|
+
json: number;
|
|
48
|
+
}
|
|
49
|
+
export interface ScoringConfig {
|
|
50
|
+
enabled: boolean;
|
|
51
|
+
max_candidates: number;
|
|
52
|
+
weights: ScoringWeights;
|
|
53
|
+
decision_decay: DecisionDecayConfig;
|
|
54
|
+
token_ratios: TokenRatios;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Rank context candidates by importance score.
|
|
58
|
+
*
|
|
59
|
+
* Scoring formula:
|
|
60
|
+
* - base_score = sum of (weight * feature_flag)
|
|
61
|
+
* - For items with dependency depth: adjusted_score = base_score / (1 + depth)
|
|
62
|
+
* - For decisions with age: age_factor = 2^(-age_hours / half_life_hours), score = decision_recency * age_factor
|
|
63
|
+
*
|
|
64
|
+
* Tie-breaker: score desc → priority desc → id asc (stable sort)
|
|
65
|
+
*
|
|
66
|
+
* @param candidates - Array of context candidates
|
|
67
|
+
* @param config - Scoring configuration
|
|
68
|
+
* @returns Ranked candidates (truncated to max_candidates, original order if disabled)
|
|
69
|
+
*/
|
|
70
|
+
export declare function rankCandidates(candidates: ContextCandidate[], config: ScoringConfig): RankedCandidate[];
|
package/dist/index.js
CHANGED
|
@@ -26,29 +26,6 @@ var __export = (target, all) => {
|
|
|
26
26
|
};
|
|
27
27
|
var __require = import.meta.require;
|
|
28
28
|
|
|
29
|
-
// src/config/constants.ts
|
|
30
|
-
var QA_AGENTS = ["reviewer", "critic"];
|
|
31
|
-
var PIPELINE_AGENTS = ["explorer", "coder", "test_engineer"];
|
|
32
|
-
var ORCHESTRATOR_NAME = "architect";
|
|
33
|
-
var ALL_SUBAGENT_NAMES = [
|
|
34
|
-
"sme",
|
|
35
|
-
...QA_AGENTS,
|
|
36
|
-
...PIPELINE_AGENTS
|
|
37
|
-
];
|
|
38
|
-
var ALL_AGENT_NAMES = [
|
|
39
|
-
ORCHESTRATOR_NAME,
|
|
40
|
-
...ALL_SUBAGENT_NAMES
|
|
41
|
-
];
|
|
42
|
-
var DEFAULT_MODELS = {
|
|
43
|
-
architect: "anthropic/claude-sonnet-4-5",
|
|
44
|
-
explorer: "google/gemini-2.0-flash",
|
|
45
|
-
coder: "anthropic/claude-sonnet-4-5",
|
|
46
|
-
test_engineer: "google/gemini-2.0-flash",
|
|
47
|
-
sme: "google/gemini-2.0-flash",
|
|
48
|
-
reviewer: "google/gemini-2.0-flash",
|
|
49
|
-
critic: "google/gemini-2.0-flash",
|
|
50
|
-
default: "google/gemini-2.0-flash"
|
|
51
|
-
};
|
|
52
29
|
// src/config/loader.ts
|
|
53
30
|
import * as fs from "fs";
|
|
54
31
|
import * as os from "os";
|
|
@@ -13603,12 +13580,40 @@ var HooksConfigSchema = exports_external.object({
|
|
|
13603
13580
|
delegation_tracker: exports_external.boolean().default(false),
|
|
13604
13581
|
agent_awareness_max_chars: exports_external.number().min(50).max(2000).default(300)
|
|
13605
13582
|
});
|
|
13583
|
+
var ScoringWeightsSchema = exports_external.object({
|
|
13584
|
+
phase: exports_external.number().min(0).max(5).default(1),
|
|
13585
|
+
current_task: exports_external.number().min(0).max(5).default(2),
|
|
13586
|
+
blocked_task: exports_external.number().min(0).max(5).default(1.5),
|
|
13587
|
+
recent_failure: exports_external.number().min(0).max(5).default(2.5),
|
|
13588
|
+
recent_success: exports_external.number().min(0).max(5).default(0.5),
|
|
13589
|
+
evidence_presence: exports_external.number().min(0).max(5).default(1),
|
|
13590
|
+
decision_recency: exports_external.number().min(0).max(5).default(1.5),
|
|
13591
|
+
dependency_proximity: exports_external.number().min(0).max(5).default(1)
|
|
13592
|
+
});
|
|
13593
|
+
var DecisionDecaySchema = exports_external.object({
|
|
13594
|
+
mode: exports_external.enum(["linear", "exponential"]).default("exponential"),
|
|
13595
|
+
half_life_hours: exports_external.number().min(1).max(168).default(24)
|
|
13596
|
+
});
|
|
13597
|
+
var TokenRatiosSchema = exports_external.object({
|
|
13598
|
+
prose: exports_external.number().min(0.1).max(1).default(0.25),
|
|
13599
|
+
code: exports_external.number().min(0.1).max(1).default(0.4),
|
|
13600
|
+
markdown: exports_external.number().min(0.1).max(1).default(0.3),
|
|
13601
|
+
json: exports_external.number().min(0.1).max(1).default(0.35)
|
|
13602
|
+
});
|
|
13603
|
+
var ScoringConfigSchema = exports_external.object({
|
|
13604
|
+
enabled: exports_external.boolean().default(false),
|
|
13605
|
+
max_candidates: exports_external.number().min(10).max(500).default(100),
|
|
13606
|
+
weights: ScoringWeightsSchema.optional(),
|
|
13607
|
+
decision_decay: DecisionDecaySchema.optional(),
|
|
13608
|
+
token_ratios: TokenRatiosSchema.optional()
|
|
13609
|
+
});
|
|
13606
13610
|
var ContextBudgetConfigSchema = exports_external.object({
|
|
13607
13611
|
enabled: exports_external.boolean().default(true),
|
|
13608
13612
|
warn_threshold: exports_external.number().min(0).max(1).default(0.7),
|
|
13609
13613
|
critical_threshold: exports_external.number().min(0).max(1).default(0.9),
|
|
13610
13614
|
model_limits: exports_external.record(exports_external.string(), exports_external.number().min(1000)).default({ default: 128000 }),
|
|
13611
|
-
max_injection_tokens: exports_external.number().min(100).max(50000).default(4000)
|
|
13615
|
+
max_injection_tokens: exports_external.number().min(100).max(50000).default(4000),
|
|
13616
|
+
scoring: ScoringConfigSchema.optional()
|
|
13612
13617
|
});
|
|
13613
13618
|
var EvidenceConfigSchema = exports_external.object({
|
|
13614
13619
|
enabled: exports_external.boolean().default(true),
|
|
@@ -13691,8 +13696,10 @@ function resolveGuardrailsConfig(base, agentName) {
|
|
|
13691
13696
|
return base;
|
|
13692
13697
|
}
|
|
13693
13698
|
const baseName = stripKnownSwarmPrefix(agentName);
|
|
13694
|
-
const
|
|
13695
|
-
const
|
|
13699
|
+
const builtInLookup = DEFAULT_AGENT_PROFILES[baseName];
|
|
13700
|
+
const effectiveName = builtInLookup ? baseName : ORCHESTRATOR_NAME;
|
|
13701
|
+
const builtIn = DEFAULT_AGENT_PROFILES[effectiveName];
|
|
13702
|
+
const userProfile = base.profiles?.[effectiveName] ?? base.profiles?.[baseName] ?? base.profiles?.[agentName];
|
|
13696
13703
|
if (!builtIn && !userProfile) {
|
|
13697
13704
|
return base;
|
|
13698
13705
|
}
|
|
@@ -13803,6 +13810,54 @@ function loadAgentPrompt(agentName) {
|
|
|
13803
13810
|
}
|
|
13804
13811
|
return result;
|
|
13805
13812
|
}
|
|
13813
|
+
|
|
13814
|
+
// src/config/constants.ts
|
|
13815
|
+
var QA_AGENTS = ["reviewer", "critic"];
|
|
13816
|
+
var PIPELINE_AGENTS = ["explorer", "coder", "test_engineer"];
|
|
13817
|
+
var ORCHESTRATOR_NAME = "architect";
|
|
13818
|
+
var ALL_SUBAGENT_NAMES = [
|
|
13819
|
+
"sme",
|
|
13820
|
+
...QA_AGENTS,
|
|
13821
|
+
...PIPELINE_AGENTS
|
|
13822
|
+
];
|
|
13823
|
+
var ALL_AGENT_NAMES = [
|
|
13824
|
+
ORCHESTRATOR_NAME,
|
|
13825
|
+
...ALL_SUBAGENT_NAMES
|
|
13826
|
+
];
|
|
13827
|
+
var DEFAULT_MODELS = {
|
|
13828
|
+
architect: "anthropic/claude-sonnet-4-5",
|
|
13829
|
+
explorer: "google/gemini-2.0-flash",
|
|
13830
|
+
coder: "anthropic/claude-sonnet-4-5",
|
|
13831
|
+
test_engineer: "google/gemini-2.0-flash",
|
|
13832
|
+
sme: "google/gemini-2.0-flash",
|
|
13833
|
+
reviewer: "google/gemini-2.0-flash",
|
|
13834
|
+
critic: "google/gemini-2.0-flash",
|
|
13835
|
+
default: "google/gemini-2.0-flash"
|
|
13836
|
+
};
|
|
13837
|
+
var DEFAULT_SCORING_CONFIG = {
|
|
13838
|
+
enabled: false,
|
|
13839
|
+
max_candidates: 100,
|
|
13840
|
+
weights: {
|
|
13841
|
+
phase: 1,
|
|
13842
|
+
current_task: 2,
|
|
13843
|
+
blocked_task: 1.5,
|
|
13844
|
+
recent_failure: 2.5,
|
|
13845
|
+
recent_success: 0.5,
|
|
13846
|
+
evidence_presence: 1,
|
|
13847
|
+
decision_recency: 1.5,
|
|
13848
|
+
dependency_proximity: 1
|
|
13849
|
+
},
|
|
13850
|
+
decision_decay: {
|
|
13851
|
+
mode: "exponential",
|
|
13852
|
+
half_life_hours: 24
|
|
13853
|
+
},
|
|
13854
|
+
token_ratios: {
|
|
13855
|
+
prose: 0.25,
|
|
13856
|
+
code: 0.4,
|
|
13857
|
+
markdown: 0.3,
|
|
13858
|
+
json: 0.35
|
|
13859
|
+
}
|
|
13860
|
+
};
|
|
13806
13861
|
// src/config/plan-schema.ts
|
|
13807
13862
|
var TaskStatusSchema = exports_external.enum([
|
|
13808
13863
|
"pending",
|
|
@@ -16011,7 +16066,8 @@ function startAgentSession(sessionId, agentName, staleDurationMs = 7200000) {
|
|
|
16011
16066
|
warningIssued: false,
|
|
16012
16067
|
warningReason: "",
|
|
16013
16068
|
hardLimitHit: false,
|
|
16014
|
-
lastSuccessTime: now
|
|
16069
|
+
lastSuccessTime: now,
|
|
16070
|
+
delegationActive: false
|
|
16015
16071
|
};
|
|
16016
16072
|
swarmState.agentSessions.set(sessionId, sessionState);
|
|
16017
16073
|
}
|
|
@@ -16032,6 +16088,7 @@ function ensureAgentSession(sessionId, agentName) {
|
|
|
16032
16088
|
session.warningReason = "";
|
|
16033
16089
|
session.hardLimitHit = false;
|
|
16034
16090
|
session.lastSuccessTime = now;
|
|
16091
|
+
session.delegationActive = false;
|
|
16035
16092
|
}
|
|
16036
16093
|
session.lastToolCallTime = now;
|
|
16037
16094
|
return session;
|
|
@@ -16267,15 +16324,21 @@ function createContextBudgetHandler(config2) {
|
|
|
16267
16324
|
function createDelegationTrackerHook(config2) {
|
|
16268
16325
|
return async (input, _output) => {
|
|
16269
16326
|
if (!input.agent || input.agent === "") {
|
|
16327
|
+
const session2 = swarmState.agentSessions.get(input.sessionID);
|
|
16328
|
+
if (session2) {
|
|
16329
|
+
session2.delegationActive = false;
|
|
16330
|
+
}
|
|
16270
16331
|
return;
|
|
16271
16332
|
}
|
|
16333
|
+
const agentName = input.agent;
|
|
16272
16334
|
const previousAgent = swarmState.activeAgent.get(input.sessionID);
|
|
16273
|
-
swarmState.activeAgent.set(input.sessionID,
|
|
16274
|
-
ensureAgentSession(input.sessionID,
|
|
16275
|
-
|
|
16335
|
+
swarmState.activeAgent.set(input.sessionID, agentName);
|
|
16336
|
+
const session = ensureAgentSession(input.sessionID, agentName);
|
|
16337
|
+
session.delegationActive = true;
|
|
16338
|
+
if (config2.hooks?.delegation_tracker === true && previousAgent && previousAgent !== agentName) {
|
|
16276
16339
|
const entry = {
|
|
16277
16340
|
from: previousAgent,
|
|
16278
|
-
to:
|
|
16341
|
+
to: agentName,
|
|
16279
16342
|
timestamp: Date.now()
|
|
16280
16343
|
};
|
|
16281
16344
|
if (!swarmState.delegationChains.has(input.sessionID)) {
|
|
@@ -16506,7 +16569,69 @@ ${originalText}`;
|
|
|
16506
16569
|
})
|
|
16507
16570
|
};
|
|
16508
16571
|
}
|
|
16572
|
+
// src/hooks/context-scoring.ts
|
|
16573
|
+
function calculateAgeFactor(ageHours, config2) {
|
|
16574
|
+
if (ageHours <= 0) {
|
|
16575
|
+
return 1;
|
|
16576
|
+
}
|
|
16577
|
+
if (config2.mode === "exponential") {
|
|
16578
|
+
return 2 ** (-ageHours / config2.half_life_hours);
|
|
16579
|
+
} else {
|
|
16580
|
+
const linearFactor = 1 - ageHours / (config2.half_life_hours * 2);
|
|
16581
|
+
return Math.max(0, linearFactor);
|
|
16582
|
+
}
|
|
16583
|
+
}
|
|
16584
|
+
function calculateBaseScore(candidate, weights, decayConfig) {
|
|
16585
|
+
const { kind, metadata } = candidate;
|
|
16586
|
+
const phase = kind === "phase" ? 1 : 0;
|
|
16587
|
+
const currentTask = metadata.isCurrentTask ? 1 : 0;
|
|
16588
|
+
const blockedTask = metadata.isBlockedTask ? 1 : 0;
|
|
16589
|
+
const recentFailure = metadata.hasFailure ? 1 : 0;
|
|
16590
|
+
const recentSuccess = metadata.hasSuccess ? 1 : 0;
|
|
16591
|
+
const evidencePresence = metadata.hasEvidence ? 1 : 0;
|
|
16592
|
+
let decisionRecency = 0;
|
|
16593
|
+
if (kind === "decision" && metadata.decisionAgeHours !== undefined) {
|
|
16594
|
+
decisionRecency = calculateAgeFactor(metadata.decisionAgeHours, decayConfig);
|
|
16595
|
+
}
|
|
16596
|
+
const dependencyProximity = 1 / (1 + (metadata.dependencyDepth ?? 0));
|
|
16597
|
+
return weights.phase * phase + weights.current_task * currentTask + weights.blocked_task * blockedTask + weights.recent_failure * recentFailure + weights.recent_success * recentSuccess + weights.evidence_presence * evidencePresence + weights.decision_recency * decisionRecency + weights.dependency_proximity * dependencyProximity;
|
|
16598
|
+
}
|
|
16599
|
+
function rankCandidates(candidates, config2) {
|
|
16600
|
+
if (!config2.enabled) {
|
|
16601
|
+
return candidates.map((c) => ({ ...c, score: 0 }));
|
|
16602
|
+
}
|
|
16603
|
+
if (candidates.length === 0) {
|
|
16604
|
+
return [];
|
|
16605
|
+
}
|
|
16606
|
+
const scored = candidates.map((candidate) => {
|
|
16607
|
+
const score = calculateBaseScore(candidate, config2.weights, config2.decision_decay);
|
|
16608
|
+
return { ...candidate, score };
|
|
16609
|
+
});
|
|
16610
|
+
scored.sort((a, b) => {
|
|
16611
|
+
if (b.score !== a.score) {
|
|
16612
|
+
return b.score - a.score;
|
|
16613
|
+
}
|
|
16614
|
+
if (b.priority !== a.priority) {
|
|
16615
|
+
return b.priority - a.priority;
|
|
16616
|
+
}
|
|
16617
|
+
return a.id.localeCompare(b.id);
|
|
16618
|
+
});
|
|
16619
|
+
return scored.slice(0, config2.max_candidates);
|
|
16620
|
+
}
|
|
16621
|
+
|
|
16509
16622
|
// src/hooks/system-enhancer.ts
|
|
16623
|
+
function estimateContentType(text) {
|
|
16624
|
+
if (text.includes("```") || text.includes("function ") || text.includes("const ")) {
|
|
16625
|
+
return "code";
|
|
16626
|
+
}
|
|
16627
|
+
if (text.startsWith("{") || text.startsWith("[")) {
|
|
16628
|
+
return "json";
|
|
16629
|
+
}
|
|
16630
|
+
if (text.includes("#") || text.includes("*") || text.includes("- ")) {
|
|
16631
|
+
return "markdown";
|
|
16632
|
+
}
|
|
16633
|
+
return "prose";
|
|
16634
|
+
}
|
|
16510
16635
|
function createSystemEnhancerHook(config2, directory) {
|
|
16511
16636
|
const enabled = config2.hooks?.system_enhancer !== false;
|
|
16512
16637
|
if (!enabled) {
|
|
@@ -16526,44 +16651,133 @@ function createSystemEnhancerHook(config2, directory) {
|
|
|
16526
16651
|
const maxInjectionTokens = config2.context_budget?.max_injection_tokens ?? Number.POSITIVE_INFINITY;
|
|
16527
16652
|
let injectedTokens = 0;
|
|
16528
16653
|
const contextContent = await readSwarmFileAsync(directory, "context.md");
|
|
16529
|
-
const
|
|
16530
|
-
if (
|
|
16531
|
-
const
|
|
16532
|
-
if (
|
|
16533
|
-
|
|
16654
|
+
const scoringEnabled = config2.context_budget?.scoring?.enabled === true;
|
|
16655
|
+
if (!scoringEnabled) {
|
|
16656
|
+
const plan2 = await loadPlan(directory);
|
|
16657
|
+
if (plan2 && plan2.migration_status !== "migration_failed") {
|
|
16658
|
+
const currentPhase2 = extractCurrentPhaseFromPlan(plan2);
|
|
16659
|
+
if (currentPhase2) {
|
|
16660
|
+
tryInject(`[SWARM CONTEXT] Current phase: ${currentPhase2}`);
|
|
16661
|
+
}
|
|
16662
|
+
const currentTask2 = extractCurrentTaskFromPlan(plan2);
|
|
16663
|
+
if (currentTask2) {
|
|
16664
|
+
tryInject(`[SWARM CONTEXT] Current task: ${currentTask2}`);
|
|
16665
|
+
}
|
|
16666
|
+
} else {
|
|
16667
|
+
const planContent = await readSwarmFileAsync(directory, "plan.md");
|
|
16668
|
+
if (planContent) {
|
|
16669
|
+
const currentPhase2 = extractCurrentPhase(planContent);
|
|
16670
|
+
if (currentPhase2) {
|
|
16671
|
+
tryInject(`[SWARM CONTEXT] Current phase: ${currentPhase2}`);
|
|
16672
|
+
}
|
|
16673
|
+
const currentTask2 = extractCurrentTask(planContent);
|
|
16674
|
+
if (currentTask2) {
|
|
16675
|
+
tryInject(`[SWARM CONTEXT] Current task: ${currentTask2}`);
|
|
16676
|
+
}
|
|
16677
|
+
}
|
|
16534
16678
|
}
|
|
16535
|
-
|
|
16536
|
-
|
|
16537
|
-
|
|
16679
|
+
if (contextContent) {
|
|
16680
|
+
const decisions = extractDecisions(contextContent, 200);
|
|
16681
|
+
if (decisions) {
|
|
16682
|
+
tryInject(`[SWARM CONTEXT] Key decisions: ${decisions}`);
|
|
16683
|
+
}
|
|
16684
|
+
if (config2.hooks?.agent_activity !== false && _input.sessionID) {
|
|
16685
|
+
const activeAgent = swarmState.activeAgent.get(_input.sessionID);
|
|
16686
|
+
if (activeAgent) {
|
|
16687
|
+
const agentContext = extractAgentContext(contextContent, activeAgent, config2.hooks?.agent_awareness_max_chars ?? 300);
|
|
16688
|
+
if (agentContext) {
|
|
16689
|
+
tryInject(`[SWARM AGENT CONTEXT] ${agentContext}`);
|
|
16690
|
+
}
|
|
16691
|
+
}
|
|
16692
|
+
}
|
|
16538
16693
|
}
|
|
16694
|
+
return;
|
|
16695
|
+
}
|
|
16696
|
+
const userScoringConfig = config2.context_budget?.scoring;
|
|
16697
|
+
const candidates = [];
|
|
16698
|
+
let idCounter = 0;
|
|
16699
|
+
const effectiveConfig = userScoringConfig?.weights ? {
|
|
16700
|
+
...DEFAULT_SCORING_CONFIG,
|
|
16701
|
+
...userScoringConfig,
|
|
16702
|
+
weights: userScoringConfig.weights
|
|
16703
|
+
} : DEFAULT_SCORING_CONFIG;
|
|
16704
|
+
const plan = await loadPlan(directory);
|
|
16705
|
+
let currentPhase = null;
|
|
16706
|
+
let currentTask = null;
|
|
16707
|
+
if (plan && plan.migration_status !== "migration_failed") {
|
|
16708
|
+
currentPhase = extractCurrentPhaseFromPlan(plan);
|
|
16709
|
+
currentTask = extractCurrentTaskFromPlan(plan);
|
|
16539
16710
|
} else {
|
|
16540
16711
|
const planContent = await readSwarmFileAsync(directory, "plan.md");
|
|
16541
16712
|
if (planContent) {
|
|
16542
|
-
|
|
16543
|
-
|
|
16544
|
-
tryInject(`[SWARM CONTEXT] Current phase: ${currentPhase}`);
|
|
16545
|
-
}
|
|
16546
|
-
const currentTask = extractCurrentTask(planContent);
|
|
16547
|
-
if (currentTask) {
|
|
16548
|
-
tryInject(`[SWARM CONTEXT] Current task: ${currentTask}`);
|
|
16549
|
-
}
|
|
16713
|
+
currentPhase = extractCurrentPhase(planContent);
|
|
16714
|
+
currentTask = extractCurrentTask(planContent);
|
|
16550
16715
|
}
|
|
16551
16716
|
}
|
|
16717
|
+
if (currentPhase) {
|
|
16718
|
+
const text = `[SWARM CONTEXT] Current phase: ${currentPhase}`;
|
|
16719
|
+
candidates.push({
|
|
16720
|
+
id: `candidate-${idCounter++}`,
|
|
16721
|
+
kind: "phase",
|
|
16722
|
+
text,
|
|
16723
|
+
tokens: estimateTokens(text),
|
|
16724
|
+
priority: 1,
|
|
16725
|
+
metadata: { contentType: estimateContentType(text) }
|
|
16726
|
+
});
|
|
16727
|
+
}
|
|
16728
|
+
if (currentTask) {
|
|
16729
|
+
const text = `[SWARM CONTEXT] Current task: ${currentTask}`;
|
|
16730
|
+
candidates.push({
|
|
16731
|
+
id: `candidate-${idCounter++}`,
|
|
16732
|
+
kind: "task",
|
|
16733
|
+
text,
|
|
16734
|
+
tokens: estimateTokens(text),
|
|
16735
|
+
priority: 2,
|
|
16736
|
+
metadata: {
|
|
16737
|
+
contentType: estimateContentType(text),
|
|
16738
|
+
isCurrentTask: true
|
|
16739
|
+
}
|
|
16740
|
+
});
|
|
16741
|
+
}
|
|
16552
16742
|
if (contextContent) {
|
|
16553
16743
|
const decisions = extractDecisions(contextContent, 200);
|
|
16554
16744
|
if (decisions) {
|
|
16555
|
-
|
|
16745
|
+
const text = `[SWARM CONTEXT] Key decisions: ${decisions}`;
|
|
16746
|
+
candidates.push({
|
|
16747
|
+
id: `candidate-${idCounter++}`,
|
|
16748
|
+
kind: "decision",
|
|
16749
|
+
text,
|
|
16750
|
+
tokens: estimateTokens(text),
|
|
16751
|
+
priority: 3,
|
|
16752
|
+
metadata: { contentType: estimateContentType(text) }
|
|
16753
|
+
});
|
|
16556
16754
|
}
|
|
16557
16755
|
if (config2.hooks?.agent_activity !== false && _input.sessionID) {
|
|
16558
16756
|
const activeAgent = swarmState.activeAgent.get(_input.sessionID);
|
|
16559
16757
|
if (activeAgent) {
|
|
16560
16758
|
const agentContext = extractAgentContext(contextContent, activeAgent, config2.hooks?.agent_awareness_max_chars ?? 300);
|
|
16561
16759
|
if (agentContext) {
|
|
16562
|
-
|
|
16760
|
+
const text = `[SWARM AGENT CONTEXT] ${agentContext}`;
|
|
16761
|
+
candidates.push({
|
|
16762
|
+
id: `candidate-${idCounter++}`,
|
|
16763
|
+
kind: "agent_context",
|
|
16764
|
+
text,
|
|
16765
|
+
tokens: estimateTokens(text),
|
|
16766
|
+
priority: 4,
|
|
16767
|
+
metadata: { contentType: estimateContentType(text) }
|
|
16768
|
+
});
|
|
16563
16769
|
}
|
|
16564
16770
|
}
|
|
16565
16771
|
}
|
|
16566
16772
|
}
|
|
16773
|
+
const ranked = rankCandidates(candidates, effectiveConfig);
|
|
16774
|
+
for (const candidate of ranked) {
|
|
16775
|
+
if (injectedTokens + candidate.tokens > maxInjectionTokens) {
|
|
16776
|
+
continue;
|
|
16777
|
+
}
|
|
16778
|
+
output.system.push(candidate.text);
|
|
16779
|
+
injectedTokens += candidate.tokens;
|
|
16780
|
+
}
|
|
16567
16781
|
} catch (error49) {
|
|
16568
16782
|
warn("System enhancer failed:", error49);
|
|
16569
16783
|
}
|
|
@@ -29361,6 +29575,15 @@ var OpenCodeSwarm = async (ctx) => {
|
|
|
29361
29575
|
"experimental.session.compacting": compactionHook["experimental.session.compacting"],
|
|
29362
29576
|
"command.execute.before": safeHook(commandHandler),
|
|
29363
29577
|
"tool.execute.before": async (input, output) => {
|
|
29578
|
+
if (!swarmState.activeAgent.has(input.sessionID)) {
|
|
29579
|
+
swarmState.activeAgent.set(input.sessionID, ORCHESTRATOR_NAME);
|
|
29580
|
+
}
|
|
29581
|
+
const session = swarmState.agentSessions.get(input.sessionID);
|
|
29582
|
+
const activeAgent = swarmState.activeAgent.get(input.sessionID);
|
|
29583
|
+
if (session && activeAgent && activeAgent !== ORCHESTRATOR_NAME && session.delegationActive === false) {
|
|
29584
|
+
swarmState.activeAgent.set(input.sessionID, ORCHESTRATOR_NAME);
|
|
29585
|
+
ensureAgentSession(input.sessionID, ORCHESTRATOR_NAME);
|
|
29586
|
+
}
|
|
29364
29587
|
await guardrailsHooks.toolBefore(input, output);
|
|
29365
29588
|
await safeHook(activityHooks.toolBefore)(input, output);
|
|
29366
29589
|
},
|
package/dist/state.d.ts
CHANGED
|
@@ -61,6 +61,8 @@ export interface AgentSessionState {
|
|
|
61
61
|
hardLimitHit: boolean;
|
|
62
62
|
/** Timestamp of most recent SUCCESSFUL tool call (for idle timeout) */
|
|
63
63
|
lastSuccessTime: number;
|
|
64
|
+
/** Whether active delegation is in progress for this session */
|
|
65
|
+
delegationActive: boolean;
|
|
64
66
|
}
|
|
65
67
|
/**
|
|
66
68
|
* Singleton state object for sharing data across hooks
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "5.0
|
|
3
|
+
"version": "5.1.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",
|