opencode-swarm 5.0.2 → 5.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/schema.d.ts +12 -0
- package/dist/index.js +53 -14
- package/dist/state.d.ts +13 -1
- package/package.json +1 -1
package/dist/config/schema.d.ts
CHANGED
|
@@ -62,6 +62,18 @@ export declare const GuardrailsConfigSchema: z.ZodObject<{
|
|
|
62
62
|
}, z.core.$strip>>>;
|
|
63
63
|
}, z.core.$strip>;
|
|
64
64
|
export type GuardrailsConfig = z.infer<typeof GuardrailsConfigSchema>;
|
|
65
|
+
/**
|
|
66
|
+
* Strip any swarm prefix from an agent name to get the base agent name.
|
|
67
|
+
* Works with any swarm name by checking if the name (or suffix after removing
|
|
68
|
+
* a prefix) matches a known agent name from ALL_AGENT_NAMES.
|
|
69
|
+
*
|
|
70
|
+
* Examples: 'local_architect' → 'architect', 'enterprise_coder' → 'coder',
|
|
71
|
+
* 'architect' → 'architect', 'unknown_thing' → 'unknown_thing'
|
|
72
|
+
*
|
|
73
|
+
* @param name - The agent name (possibly prefixed)
|
|
74
|
+
* @returns The base agent name if recognized, or the original name
|
|
75
|
+
*/
|
|
76
|
+
export declare function stripKnownSwarmPrefix(name: string): string;
|
|
65
77
|
/**
|
|
66
78
|
* Resolve guardrails configuration for a specific agent.
|
|
67
79
|
* Merges the base config with built-in defaults (for the architect) and
|
package/dist/index.js
CHANGED
|
@@ -13638,12 +13638,26 @@ var GuardrailsConfigSchema = exports_external.object({
|
|
|
13638
13638
|
warning_threshold: exports_external.number().min(0.1).max(0.9).default(0.5),
|
|
13639
13639
|
profiles: exports_external.record(exports_external.string(), GuardrailsProfileSchema).optional()
|
|
13640
13640
|
});
|
|
13641
|
+
function stripKnownSwarmPrefix(name) {
|
|
13642
|
+
if (!name)
|
|
13643
|
+
return name;
|
|
13644
|
+
if (ALL_AGENT_NAMES.includes(name))
|
|
13645
|
+
return name;
|
|
13646
|
+
for (const agentName of ALL_AGENT_NAMES) {
|
|
13647
|
+
const suffix = `_${agentName}`;
|
|
13648
|
+
if (name.endsWith(suffix)) {
|
|
13649
|
+
return agentName;
|
|
13650
|
+
}
|
|
13651
|
+
}
|
|
13652
|
+
return name;
|
|
13653
|
+
}
|
|
13641
13654
|
function resolveGuardrailsConfig(base, agentName) {
|
|
13642
13655
|
if (!agentName) {
|
|
13643
13656
|
return base;
|
|
13644
13657
|
}
|
|
13645
|
-
const
|
|
13646
|
-
const
|
|
13658
|
+
const baseName = stripKnownSwarmPrefix(agentName);
|
|
13659
|
+
const builtIn = baseName === ORCHESTRATOR_NAME ? DEFAULT_ARCHITECT_PROFILE : undefined;
|
|
13660
|
+
const userProfile = base.profiles?.[baseName] ?? base.profiles?.[agentName];
|
|
13647
13661
|
if (!builtIn && !userProfile) {
|
|
13648
13662
|
return base;
|
|
13649
13663
|
}
|
|
@@ -15941,11 +15955,11 @@ var swarmState = {
|
|
|
15941
15955
|
pendingEvents: 0,
|
|
15942
15956
|
agentSessions: new Map
|
|
15943
15957
|
};
|
|
15944
|
-
function startAgentSession(sessionId, agentName, staleDurationMs =
|
|
15958
|
+
function startAgentSession(sessionId, agentName, staleDurationMs = 7200000) {
|
|
15945
15959
|
const now = Date.now();
|
|
15946
15960
|
const staleIds = [];
|
|
15947
15961
|
for (const [id, session] of swarmState.agentSessions) {
|
|
15948
|
-
if (now - session.
|
|
15962
|
+
if (now - session.lastToolCallTime > staleDurationMs) {
|
|
15949
15963
|
staleIds.push(id);
|
|
15950
15964
|
}
|
|
15951
15965
|
}
|
|
@@ -15955,6 +15969,7 @@ function startAgentSession(sessionId, agentName, staleDurationMs = 3600000) {
|
|
|
15955
15969
|
const sessionState = {
|
|
15956
15970
|
agentName,
|
|
15957
15971
|
startTime: now,
|
|
15972
|
+
lastToolCallTime: now,
|
|
15958
15973
|
toolCallCount: 0,
|
|
15959
15974
|
consecutiveErrors: 0,
|
|
15960
15975
|
recentToolCalls: [],
|
|
@@ -15966,6 +15981,24 @@ function startAgentSession(sessionId, agentName, staleDurationMs = 3600000) {
|
|
|
15966
15981
|
function getAgentSession(sessionId) {
|
|
15967
15982
|
return swarmState.agentSessions.get(sessionId);
|
|
15968
15983
|
}
|
|
15984
|
+
function ensureAgentSession(sessionId, agentName) {
|
|
15985
|
+
const now = Date.now();
|
|
15986
|
+
let session = swarmState.agentSessions.get(sessionId);
|
|
15987
|
+
if (session) {
|
|
15988
|
+
if (agentName && session.agentName === "unknown") {
|
|
15989
|
+
session.agentName = agentName;
|
|
15990
|
+
session.startTime = now;
|
|
15991
|
+
}
|
|
15992
|
+
session.lastToolCallTime = now;
|
|
15993
|
+
return session;
|
|
15994
|
+
}
|
|
15995
|
+
startAgentSession(sessionId, agentName ?? "unknown");
|
|
15996
|
+
session = swarmState.agentSessions.get(sessionId);
|
|
15997
|
+
if (!session) {
|
|
15998
|
+
throw new Error(`Failed to create guardrail session for ${sessionId}`);
|
|
15999
|
+
}
|
|
16000
|
+
return session;
|
|
16001
|
+
}
|
|
15969
16002
|
|
|
15970
16003
|
// src/hooks/agent-activity.ts
|
|
15971
16004
|
function createAgentActivityHooks(config2, directory) {
|
|
@@ -16194,6 +16227,7 @@ function createDelegationTrackerHook(config2) {
|
|
|
16194
16227
|
}
|
|
16195
16228
|
const previousAgent = swarmState.activeAgent.get(input.sessionID);
|
|
16196
16229
|
swarmState.activeAgent.set(input.sessionID, input.agent);
|
|
16230
|
+
ensureAgentSession(input.sessionID, input.agent);
|
|
16197
16231
|
if (config2.hooks?.delegation_tracker === true && previousAgent && previousAgent !== input.agent) {
|
|
16198
16232
|
const entry = {
|
|
16199
16233
|
from: previousAgent,
|
|
@@ -16220,15 +16254,8 @@ function createGuardrailsHooks(config2) {
|
|
|
16220
16254
|
}
|
|
16221
16255
|
return {
|
|
16222
16256
|
toolBefore: async (input, output) => {
|
|
16223
|
-
|
|
16224
|
-
|
|
16225
|
-
startAgentSession(input.sessionID, "unknown");
|
|
16226
|
-
session = getAgentSession(input.sessionID);
|
|
16227
|
-
if (!session) {
|
|
16228
|
-
warn(`Failed to create session for ${input.sessionID}`);
|
|
16229
|
-
return;
|
|
16230
|
-
}
|
|
16231
|
-
}
|
|
16257
|
+
const agentName = swarmState.activeAgent.get(input.sessionID);
|
|
16258
|
+
const session = ensureAgentSession(input.sessionID, agentName);
|
|
16232
16259
|
const agentConfig = resolveGuardrailsConfig(config2, session.agentName);
|
|
16233
16260
|
if (session.hardLimitHit) {
|
|
16234
16261
|
throw new Error("\uD83D\uDED1 CIRCUIT BREAKER: Agent blocked. Hard limit was previously triggered. Stop making tool calls and return your progress summary.");
|
|
@@ -16258,10 +16285,22 @@ function createGuardrailsHooks(config2) {
|
|
|
16258
16285
|
const elapsedMinutes = (Date.now() - session.startTime) / 60000;
|
|
16259
16286
|
if (session.toolCallCount >= agentConfig.max_tool_calls) {
|
|
16260
16287
|
session.hardLimitHit = true;
|
|
16288
|
+
warn("Circuit breaker: tool call limit hit", {
|
|
16289
|
+
sessionID: input.sessionID,
|
|
16290
|
+
agentName: session.agentName,
|
|
16291
|
+
resolvedMaxCalls: agentConfig.max_tool_calls,
|
|
16292
|
+
currentCalls: session.toolCallCount
|
|
16293
|
+
});
|
|
16261
16294
|
throw new Error(`\uD83D\uDED1 CIRCUIT BREAKER: Tool call limit reached (${session.toolCallCount}/${agentConfig.max_tool_calls}). Stop making tool calls and return your progress summary.`);
|
|
16262
16295
|
}
|
|
16263
16296
|
if (elapsedMinutes >= agentConfig.max_duration_minutes) {
|
|
16264
16297
|
session.hardLimitHit = true;
|
|
16298
|
+
warn("Circuit breaker: duration limit hit", {
|
|
16299
|
+
sessionID: input.sessionID,
|
|
16300
|
+
agentName: session.agentName,
|
|
16301
|
+
resolvedMaxMinutes: agentConfig.max_duration_minutes,
|
|
16302
|
+
elapsedMinutes: Math.floor(elapsedMinutes)
|
|
16303
|
+
});
|
|
16265
16304
|
throw new Error(`\uD83D\uDED1 CIRCUIT BREAKER: Duration limit reached (${Math.floor(elapsedMinutes)} min). Stop making tool calls and return your progress summary.`);
|
|
16266
16305
|
}
|
|
16267
16306
|
if (repetitionCount >= agentConfig.max_repetitions) {
|
|
@@ -16467,7 +16506,7 @@ function extractAgentContext(contextContent, activeAgent, maxChars) {
|
|
|
16467
16506
|
const activitySection = activityMatch[1].trim();
|
|
16468
16507
|
if (!activitySection || activitySection === "No tool activity recorded yet.")
|
|
16469
16508
|
return null;
|
|
16470
|
-
const agentName = activeAgent
|
|
16509
|
+
const agentName = stripKnownSwarmPrefix(activeAgent);
|
|
16471
16510
|
let contextSummary;
|
|
16472
16511
|
switch (agentName) {
|
|
16473
16512
|
case "coder":
|
package/dist/state.d.ts
CHANGED
|
@@ -41,6 +41,8 @@ export interface AgentSessionState {
|
|
|
41
41
|
agentName: string;
|
|
42
42
|
/** Date.now() when session started */
|
|
43
43
|
startTime: number;
|
|
44
|
+
/** Timestamp of most recent tool call (for stale session eviction) */
|
|
45
|
+
lastToolCallTime: number;
|
|
44
46
|
/** Total tool calls in this session */
|
|
45
47
|
toolCallCount: number;
|
|
46
48
|
/** Consecutive errors (reset on success) */
|
|
@@ -82,7 +84,7 @@ export declare function resetSwarmState(): void;
|
|
|
82
84
|
* Also removes any stale sessions older than staleDurationMs.
|
|
83
85
|
* @param sessionId - The session identifier
|
|
84
86
|
* @param agentName - The agent associated with this session
|
|
85
|
-
* @param staleDurationMs - Age threshold for stale session eviction (default:
|
|
87
|
+
* @param staleDurationMs - Age threshold for stale session eviction (default: 120 min)
|
|
86
88
|
*/
|
|
87
89
|
export declare function startAgentSession(sessionId: string, agentName: string, staleDurationMs?: number): void;
|
|
88
90
|
/**
|
|
@@ -96,3 +98,13 @@ export declare function endAgentSession(sessionId: string): void;
|
|
|
96
98
|
* @returns The AgentSessionState or undefined if not found
|
|
97
99
|
*/
|
|
98
100
|
export declare function getAgentSession(sessionId: string): AgentSessionState | undefined;
|
|
101
|
+
/**
|
|
102
|
+
* Ensure a guardrail session exists for the given sessionID.
|
|
103
|
+
* If one exists and agentName is provided and different, update it.
|
|
104
|
+
* If none exists, create one.
|
|
105
|
+
* Always updates lastToolCallTime.
|
|
106
|
+
* @param sessionId - The session identifier
|
|
107
|
+
* @param agentName - Optional agent name (if known)
|
|
108
|
+
* @returns The AgentSessionState
|
|
109
|
+
*/
|
|
110
|
+
export declare function ensureAgentSession(sessionId: string, agentName?: string): AgentSessionState;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.4",
|
|
4
4
|
"description": "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|