opencode-swarm 5.0.3 → 5.0.5

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.
@@ -45,7 +45,15 @@ export declare const GuardrailsProfileSchema: z.ZodObject<{
45
45
  warning_threshold: z.ZodOptional<z.ZodNumber>;
46
46
  }, z.core.$strip>;
47
47
  export type GuardrailsProfile = z.infer<typeof GuardrailsProfileSchema>;
48
- export declare const DEFAULT_ARCHITECT_PROFILE: GuardrailsProfile;
48
+ export declare const DEFAULT_AGENT_PROFILES: Record<string, GuardrailsProfile>;
49
+ /** @deprecated Use DEFAULT_AGENT_PROFILES.architect instead */
50
+ export declare const DEFAULT_ARCHITECT_PROFILE: {
51
+ max_tool_calls?: number | undefined;
52
+ max_duration_minutes?: number | undefined;
53
+ max_repetitions?: number | undefined;
54
+ max_consecutive_errors?: number | undefined;
55
+ warning_threshold?: number | undefined;
56
+ };
49
57
  export declare const GuardrailsConfigSchema: z.ZodObject<{
50
58
  enabled: z.ZodDefault<z.ZodBoolean>;
51
59
  max_tool_calls: z.ZodDefault<z.ZodNumber>;
@@ -76,7 +84,7 @@ export type GuardrailsConfig = z.infer<typeof GuardrailsConfigSchema>;
76
84
  export declare function stripKnownSwarmPrefix(name: string): string;
77
85
  /**
78
86
  * Resolve guardrails configuration for a specific agent.
79
- * Merges the base config with built-in defaults (for the architect) and
87
+ * Merges the base config with built-in agent-type defaults and
80
88
  * any per-agent profile overrides. Merge order: base < built-in < user profile.
81
89
  *
82
90
  * @param base - The base guardrails configuration
package/dist/index.js CHANGED
@@ -13623,19 +13623,52 @@ var GuardrailsProfileSchema = exports_external.object({
13623
13623
  max_consecutive_errors: exports_external.number().min(2).max(20).optional(),
13624
13624
  warning_threshold: exports_external.number().min(0.1).max(0.9).optional()
13625
13625
  });
13626
- var DEFAULT_ARCHITECT_PROFILE = {
13627
- max_tool_calls: 600,
13628
- max_duration_minutes: 90,
13629
- max_consecutive_errors: 8,
13630
- warning_threshold: 0.7
13626
+ var DEFAULT_AGENT_PROFILES = {
13627
+ architect: {
13628
+ max_tool_calls: 800,
13629
+ max_duration_minutes: 90,
13630
+ max_consecutive_errors: 8,
13631
+ warning_threshold: 0.75
13632
+ },
13633
+ coder: {
13634
+ max_tool_calls: 400,
13635
+ max_duration_minutes: 45,
13636
+ warning_threshold: 0.85
13637
+ },
13638
+ test_engineer: {
13639
+ max_tool_calls: 400,
13640
+ max_duration_minutes: 45,
13641
+ warning_threshold: 0.85
13642
+ },
13643
+ explorer: {
13644
+ max_tool_calls: 150,
13645
+ max_duration_minutes: 20,
13646
+ warning_threshold: 0.75
13647
+ },
13648
+ reviewer: {
13649
+ max_tool_calls: 200,
13650
+ max_duration_minutes: 30,
13651
+ warning_threshold: 0.65
13652
+ },
13653
+ critic: {
13654
+ max_tool_calls: 200,
13655
+ max_duration_minutes: 30,
13656
+ warning_threshold: 0.65
13657
+ },
13658
+ sme: {
13659
+ max_tool_calls: 200,
13660
+ max_duration_minutes: 30,
13661
+ warning_threshold: 0.65
13662
+ }
13631
13663
  };
13664
+ var DEFAULT_ARCHITECT_PROFILE = DEFAULT_AGENT_PROFILES.architect;
13632
13665
  var GuardrailsConfigSchema = exports_external.object({
13633
13666
  enabled: exports_external.boolean().default(true),
13634
13667
  max_tool_calls: exports_external.number().min(10).max(1000).default(200),
13635
13668
  max_duration_minutes: exports_external.number().min(1).max(120).default(30),
13636
13669
  max_repetitions: exports_external.number().min(3).max(50).default(10),
13637
13670
  max_consecutive_errors: exports_external.number().min(2).max(20).default(5),
13638
- warning_threshold: exports_external.number().min(0.1).max(0.9).default(0.5),
13671
+ warning_threshold: exports_external.number().min(0.1).max(0.9).default(0.75),
13639
13672
  profiles: exports_external.record(exports_external.string(), GuardrailsProfileSchema).optional()
13640
13673
  });
13641
13674
  function stripKnownSwarmPrefix(name) {
@@ -13656,7 +13689,7 @@ function resolveGuardrailsConfig(base, agentName) {
13656
13689
  return base;
13657
13690
  }
13658
13691
  const baseName = stripKnownSwarmPrefix(agentName);
13659
- const builtIn = baseName === ORCHESTRATOR_NAME ? DEFAULT_ARCHITECT_PROFILE : undefined;
13692
+ const builtIn = DEFAULT_AGENT_PROFILES[baseName];
13660
13693
  const userProfile = base.profiles?.[baseName] ?? base.profiles?.[agentName];
13661
13694
  if (!builtIn && !userProfile) {
13662
13695
  return base;
@@ -15955,11 +15988,11 @@ var swarmState = {
15955
15988
  pendingEvents: 0,
15956
15989
  agentSessions: new Map
15957
15990
  };
15958
- function startAgentSession(sessionId, agentName, staleDurationMs = 3600000) {
15991
+ function startAgentSession(sessionId, agentName, staleDurationMs = 7200000) {
15959
15992
  const now = Date.now();
15960
15993
  const staleIds = [];
15961
15994
  for (const [id, session] of swarmState.agentSessions) {
15962
- if (now - session.startTime > staleDurationMs) {
15995
+ if (now - session.lastToolCallTime > staleDurationMs) {
15963
15996
  staleIds.push(id);
15964
15997
  }
15965
15998
  }
@@ -15969,10 +16002,12 @@ function startAgentSession(sessionId, agentName, staleDurationMs = 3600000) {
15969
16002
  const sessionState = {
15970
16003
  agentName,
15971
16004
  startTime: now,
16005
+ lastToolCallTime: now,
15972
16006
  toolCallCount: 0,
15973
16007
  consecutiveErrors: 0,
15974
16008
  recentToolCalls: [],
15975
16009
  warningIssued: false,
16010
+ warningReason: "",
15976
16011
  hardLimitHit: false
15977
16012
  };
15978
16013
  swarmState.agentSessions.set(sessionId, sessionState);
@@ -15980,6 +16015,24 @@ function startAgentSession(sessionId, agentName, staleDurationMs = 3600000) {
15980
16015
  function getAgentSession(sessionId) {
15981
16016
  return swarmState.agentSessions.get(sessionId);
15982
16017
  }
16018
+ function ensureAgentSession(sessionId, agentName) {
16019
+ const now = Date.now();
16020
+ let session = swarmState.agentSessions.get(sessionId);
16021
+ if (session) {
16022
+ if (agentName && session.agentName === "unknown") {
16023
+ session.agentName = agentName;
16024
+ session.startTime = now;
16025
+ }
16026
+ session.lastToolCallTime = now;
16027
+ return session;
16028
+ }
16029
+ startAgentSession(sessionId, agentName ?? "unknown");
16030
+ session = swarmState.agentSessions.get(sessionId);
16031
+ if (!session) {
16032
+ throw new Error(`Failed to create guardrail session for ${sessionId}`);
16033
+ }
16034
+ return session;
16035
+ }
15983
16036
 
15984
16037
  // src/hooks/agent-activity.ts
15985
16038
  function createAgentActivityHooks(config2, directory) {
@@ -16208,6 +16261,7 @@ function createDelegationTrackerHook(config2) {
16208
16261
  }
16209
16262
  const previousAgent = swarmState.activeAgent.get(input.sessionID);
16210
16263
  swarmState.activeAgent.set(input.sessionID, input.agent);
16264
+ ensureAgentSession(input.sessionID, input.agent);
16211
16265
  if (config2.hooks?.delegation_tracker === true && previousAgent && previousAgent !== input.agent) {
16212
16266
  const entry = {
16213
16267
  from: previousAgent,
@@ -16234,22 +16288,8 @@ function createGuardrailsHooks(config2) {
16234
16288
  }
16235
16289
  return {
16236
16290
  toolBefore: async (input, output) => {
16237
- let session = getAgentSession(input.sessionID);
16238
- if (!session) {
16239
- const agentName = swarmState.activeAgent.get(input.sessionID) ?? "unknown";
16240
- startAgentSession(input.sessionID, agentName);
16241
- session = getAgentSession(input.sessionID);
16242
- if (!session) {
16243
- warn(`Failed to create session for ${input.sessionID}`);
16244
- return;
16245
- }
16246
- } else if (session.agentName === "unknown") {
16247
- const activeAgentName = swarmState.activeAgent.get(input.sessionID);
16248
- if (activeAgentName) {
16249
- session.agentName = activeAgentName;
16250
- session.startTime = Date.now();
16251
- }
16252
- }
16291
+ const agentName = swarmState.activeAgent.get(input.sessionID);
16292
+ const session = ensureAgentSession(input.sessionID, agentName);
16253
16293
  const agentConfig = resolveGuardrailsConfig(config2, session.agentName);
16254
16294
  if (session.hardLimitHit) {
16255
16295
  throw new Error("\uD83D\uDED1 CIRCUIT BREAKER: Agent blocked. Hard limit was previously triggered. Stop making tool calls and return your progress summary.");
@@ -16279,27 +16319,53 @@ function createGuardrailsHooks(config2) {
16279
16319
  const elapsedMinutes = (Date.now() - session.startTime) / 60000;
16280
16320
  if (session.toolCallCount >= agentConfig.max_tool_calls) {
16281
16321
  session.hardLimitHit = true;
16282
- 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.`);
16322
+ warn("Circuit breaker: tool call limit hit", {
16323
+ sessionID: input.sessionID,
16324
+ agentName: session.agentName,
16325
+ resolvedMaxCalls: agentConfig.max_tool_calls,
16326
+ currentCalls: session.toolCallCount
16327
+ });
16328
+ throw new Error(`\uD83D\uDED1 LIMIT REACHED: Tool calls exhausted (${session.toolCallCount}/${agentConfig.max_tool_calls}). Finish the current operation and return your progress summary.`);
16283
16329
  }
16284
16330
  if (elapsedMinutes >= agentConfig.max_duration_minutes) {
16285
16331
  session.hardLimitHit = true;
16286
- throw new Error(`\uD83D\uDED1 CIRCUIT BREAKER: Duration limit reached (${Math.floor(elapsedMinutes)} min). Stop making tool calls and return your progress summary.`);
16332
+ warn("Circuit breaker: duration limit hit", {
16333
+ sessionID: input.sessionID,
16334
+ agentName: session.agentName,
16335
+ resolvedMaxMinutes: agentConfig.max_duration_minutes,
16336
+ elapsedMinutes: Math.floor(elapsedMinutes)
16337
+ });
16338
+ throw new Error(`\uD83D\uDED1 LIMIT REACHED: Duration exhausted (${Math.floor(elapsedMinutes)}/${agentConfig.max_duration_minutes} min). Finish the current operation and return your progress summary.`);
16287
16339
  }
16288
16340
  if (repetitionCount >= agentConfig.max_repetitions) {
16289
16341
  session.hardLimitHit = true;
16290
- throw new Error(`\uD83D\uDED1 CIRCUIT BREAKER: Repetition detected (same call ${repetitionCount} times). Stop making tool calls and return your progress summary.`);
16342
+ throw new Error(`\uD83D\uDED1 LIMIT REACHED: Repeated the same tool call ${repetitionCount} times. This suggests a loop. Return your progress summary.`);
16291
16343
  }
16292
16344
  if (session.consecutiveErrors >= agentConfig.max_consecutive_errors) {
16293
16345
  session.hardLimitHit = true;
16294
- throw new Error(`\uD83D\uDED1 CIRCUIT BREAKER: Too many consecutive errors (${session.consecutiveErrors}). Stop making tool calls and return your progress summary.`);
16346
+ throw new Error(`\uD83D\uDED1 LIMIT REACHED: ${session.consecutiveErrors} consecutive tool errors detected. Return your progress summary with details of what went wrong.`);
16295
16347
  }
16296
16348
  if (!session.warningIssued) {
16297
- const toolWarning = session.toolCallCount >= agentConfig.max_tool_calls * agentConfig.warning_threshold;
16298
- const durationWarning = elapsedMinutes >= agentConfig.max_duration_minutes * agentConfig.warning_threshold;
16299
- const repetitionWarning = repetitionCount >= agentConfig.max_repetitions * agentConfig.warning_threshold;
16300
- const errorWarning = session.consecutiveErrors >= agentConfig.max_consecutive_errors * agentConfig.warning_threshold;
16301
- if (toolWarning || durationWarning || repetitionWarning || errorWarning) {
16349
+ const toolPct = session.toolCallCount / agentConfig.max_tool_calls;
16350
+ const durationPct = elapsedMinutes / agentConfig.max_duration_minutes;
16351
+ const repPct = repetitionCount / agentConfig.max_repetitions;
16352
+ const errorPct = session.consecutiveErrors / agentConfig.max_consecutive_errors;
16353
+ const reasons = [];
16354
+ if (toolPct >= agentConfig.warning_threshold) {
16355
+ reasons.push(`tool calls ${session.toolCallCount}/${agentConfig.max_tool_calls}`);
16356
+ }
16357
+ if (durationPct >= agentConfig.warning_threshold) {
16358
+ reasons.push(`duration ${Math.floor(elapsedMinutes)}/${agentConfig.max_duration_minutes} min`);
16359
+ }
16360
+ if (repPct >= agentConfig.warning_threshold) {
16361
+ reasons.push(`repetitions ${repetitionCount}/${agentConfig.max_repetitions}`);
16362
+ }
16363
+ if (errorPct >= agentConfig.warning_threshold) {
16364
+ reasons.push(`errors ${session.consecutiveErrors}/${agentConfig.max_consecutive_errors}`);
16365
+ }
16366
+ if (reasons.length > 0) {
16302
16367
  session.warningIssued = true;
16368
+ session.warningReason = reasons.join(", ");
16303
16369
  }
16304
16370
  }
16305
16371
  },
@@ -16342,11 +16408,12 @@ function createGuardrailsHooks(config2) {
16342
16408
  return;
16343
16409
  }
16344
16410
  if (session.hardLimitHit) {
16345
- textPart.text = `[\uD83D\uDED1 CIRCUIT BREAKER ACTIVE: You have exceeded your resource limits. Do NOT make any more tool calls. Immediately return a summary of your progress so far. Any further tool calls will be blocked.]
16411
+ textPart.text = `[\uD83D\uDED1 LIMIT REACHED: Your resource budget is exhausted. Do not make additional tool calls. Return a summary of your progress and any remaining work.]
16346
16412
 
16347
16413
  ` + textPart.text;
16348
16414
  } else if (session.warningIssued) {
16349
- textPart.text = `[\u26A0\uFE0F GUARDRAIL WARNING: You are approaching resource limits. Please wrap up your current task efficiently. Avoid unnecessary tool calls and prepare to return your results soon.]
16415
+ const reasonSuffix = session.warningReason ? ` (${session.warningReason})` : "";
16416
+ textPart.text = `[\u26A0\uFE0F APPROACHING LIMITS${reasonSuffix}: You still have capacity to finish your current step. Complete what you're working on, then return your results.]
16350
16417
 
16351
16418
  ` + textPart.text;
16352
16419
  }
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) */
@@ -53,6 +55,8 @@ export interface AgentSessionState {
53
55
  }>;
54
56
  /** Whether a soft warning has been issued */
55
57
  warningIssued: boolean;
58
+ /** Human-readable warning reason (set when warningIssued = true) */
59
+ warningReason: string;
56
60
  /** Whether a hard limit has been triggered */
57
61
  hardLimitHit: boolean;
58
62
  }
@@ -82,7 +86,7 @@ export declare function resetSwarmState(): void;
82
86
  * Also removes any stale sessions older than staleDurationMs.
83
87
  * @param sessionId - The session identifier
84
88
  * @param agentName - The agent associated with this session
85
- * @param staleDurationMs - Age threshold for stale session eviction (default: 60 min)
89
+ * @param staleDurationMs - Age threshold for stale session eviction (default: 120 min)
86
90
  */
87
91
  export declare function startAgentSession(sessionId: string, agentName: string, staleDurationMs?: number): void;
88
92
  /**
@@ -96,3 +100,13 @@ export declare function endAgentSession(sessionId: string): void;
96
100
  * @returns The AgentSessionState or undefined if not found
97
101
  */
98
102
  export declare function getAgentSession(sessionId: string): AgentSessionState | undefined;
103
+ /**
104
+ * Ensure a guardrail session exists for the given sessionID.
105
+ * If one exists and agentName is provided and different, update it.
106
+ * If none exists, create one.
107
+ * Always updates lastToolCallTime.
108
+ * @param sessionId - The session identifier
109
+ * @param agentName - Optional agent name (if known)
110
+ * @returns The AgentSessionState
111
+ */
112
+ 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",
3
+ "version": "5.0.5",
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",