opencode-swarm 5.1.5 → 5.1.7

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.
@@ -170,7 +170,12 @@ export type GuardrailsConfig = z.infer<typeof GuardrailsConfigSchema>;
170
170
  * Works with any swarm name by checking if the name (or suffix after removing
171
171
  * a prefix) matches a known agent name from ALL_AGENT_NAMES.
172
172
  *
173
+ * Normalization handles:
174
+ * - Case-insensitive matching (e.g., "PAID_ARCHITECT" → "architect")
175
+ * - Multiple separators: underscore, hyphen, space (e.g., "paid-architect", "paid architect")
176
+ *
173
177
  * Examples: 'local_architect' → 'architect', 'enterprise_coder' → 'coder',
178
+ * 'paid-architect' → 'architect', 'PAID_ARCHITECT' → 'architect',
174
179
  * 'architect' → 'architect', 'unknown_thing' → 'unknown_thing'
175
180
  *
176
181
  * @param name - The agent name (possibly prefixed)
package/dist/index.js CHANGED
@@ -13687,14 +13687,20 @@ var GuardrailsConfigSchema = exports_external.object({
13687
13687
  idle_timeout_minutes: exports_external.number().min(5).max(240).default(60),
13688
13688
  profiles: exports_external.record(exports_external.string(), GuardrailsProfileSchema).optional()
13689
13689
  });
13690
+ function normalizeAgentName(name) {
13691
+ return name.toLowerCase().replace(/[-\s]+/g, "_");
13692
+ }
13690
13693
  function stripKnownSwarmPrefix(name) {
13691
13694
  if (!name)
13692
13695
  return name;
13693
13696
  if (ALL_AGENT_NAMES.includes(name))
13694
13697
  return name;
13698
+ const normalized = normalizeAgentName(name);
13695
13699
  for (const agentName of ALL_AGENT_NAMES) {
13696
- const suffix = `_${agentName}`;
13697
- if (name.endsWith(suffix)) {
13700
+ const normalizedAgent = normalizeAgentName(agentName);
13701
+ if (normalized === normalizedAgent)
13702
+ return agentName;
13703
+ if (normalized.endsWith("_" + normalizedAgent)) {
13698
13704
  return agentName;
13699
13705
  }
13700
13706
  }
@@ -13706,9 +13712,8 @@ function resolveGuardrailsConfig(base, agentName) {
13706
13712
  }
13707
13713
  const baseName = stripKnownSwarmPrefix(agentName);
13708
13714
  const builtInLookup = DEFAULT_AGENT_PROFILES[baseName];
13709
- const effectiveName = builtInLookup ? baseName : ORCHESTRATOR_NAME;
13710
- const builtIn = DEFAULT_AGENT_PROFILES[effectiveName];
13711
- const userProfile = base.profiles?.[effectiveName] ?? base.profiles?.[baseName] ?? base.profiles?.[agentName];
13715
+ const builtIn = builtInLookup;
13716
+ const userProfile = base.profiles?.[baseName] ?? base.profiles?.[agentName];
13712
13717
  if (!builtIn && !userProfile) {
13713
13718
  return base;
13714
13719
  }
@@ -15046,6 +15051,7 @@ function startAgentSession(sessionId, agentName, staleDurationMs = 7200000) {
15046
15051
  agentName,
15047
15052
  startTime: now,
15048
15053
  lastToolCallTime: now,
15054
+ lastAgentEventTime: now,
15049
15055
  toolCallCount: 0,
15050
15056
  consecutiveErrors: 0,
15051
15057
  recentToolCalls: [],
@@ -15075,6 +15081,7 @@ function ensureAgentSession(sessionId, agentName) {
15075
15081
  session.hardLimitHit = false;
15076
15082
  session.lastSuccessTime = now;
15077
15083
  session.delegationActive = false;
15084
+ session.lastAgentEventTime = now;
15078
15085
  }
15079
15086
  session.lastToolCallTime = now;
15080
15087
  return session;
@@ -15086,6 +15093,12 @@ function ensureAgentSession(sessionId, agentName) {
15086
15093
  }
15087
15094
  return session;
15088
15095
  }
15096
+ function updateAgentEventTime(sessionId) {
15097
+ const session = swarmState.agentSessions.get(sessionId);
15098
+ if (session) {
15099
+ session.lastAgentEventTime = Date.now();
15100
+ }
15101
+ }
15089
15102
 
15090
15103
  // src/commands/benchmark.ts
15091
15104
  var CI = {
@@ -16733,6 +16746,7 @@ ${originalText}`;
16733
16746
  // src/hooks/delegation-tracker.ts
16734
16747
  function createDelegationTrackerHook(config2) {
16735
16748
  return async (input, _output) => {
16749
+ const now = Date.now();
16736
16750
  if (!input.agent || input.agent === "") {
16737
16751
  const session2 = swarmState.agentSessions.get(input.sessionID);
16738
16752
  if (session2) {
@@ -16740,18 +16754,21 @@ function createDelegationTrackerHook(config2) {
16740
16754
  }
16741
16755
  swarmState.activeAgent.set(input.sessionID, ORCHESTRATOR_NAME);
16742
16756
  ensureAgentSession(input.sessionID, ORCHESTRATOR_NAME);
16757
+ updateAgentEventTime(input.sessionID);
16743
16758
  return;
16744
16759
  }
16745
16760
  const agentName = input.agent;
16746
16761
  const previousAgent = swarmState.activeAgent.get(input.sessionID);
16747
16762
  swarmState.activeAgent.set(input.sessionID, agentName);
16763
+ const strippedAgent = stripKnownSwarmPrefix(agentName);
16764
+ const isArchitect = strippedAgent === ORCHESTRATOR_NAME;
16748
16765
  const session = ensureAgentSession(input.sessionID, agentName);
16749
- session.delegationActive = true;
16766
+ session.delegationActive = !isArchitect;
16750
16767
  if (config2.hooks?.delegation_tracker === true && previousAgent && previousAgent !== agentName) {
16751
16768
  const entry = {
16752
16769
  from: previousAgent,
16753
16770
  to: agentName,
16754
- timestamp: Date.now()
16771
+ timestamp: now
16755
16772
  };
16756
16773
  if (!swarmState.delegationChains.has(input.sessionID)) {
16757
16774
  swarmState.delegationChains.set(input.sessionID, []);
@@ -16792,6 +16809,9 @@ function createGuardrailsHooks(config2) {
16792
16809
  return;
16793
16810
  }
16794
16811
  const agentConfig = resolveGuardrailsConfig(config2, session.agentName);
16812
+ if (agentConfig.max_duration_minutes === 0 && agentConfig.max_tool_calls === 0) {
16813
+ return;
16814
+ }
16795
16815
  if (session.hardLimitHit) {
16796
16816
  throw new Error("\uD83D\uDED1 CIRCUIT BREAKER: Agent blocked. Hard limit was previously triggered. Stop making tool calls and return your progress summary.");
16797
16817
  }
@@ -30165,7 +30185,7 @@ var OpenCodeSwarm = async (ctx) => {
30165
30185
  if (session && activeAgent && activeAgent !== ORCHESTRATOR_NAME) {
30166
30186
  const stripActive = stripKnownSwarmPrefix(activeAgent);
30167
30187
  if (stripActive !== ORCHESTRATOR_NAME) {
30168
- const staleDelegation = !session.delegationActive || Date.now() - session.lastToolCallTime > 1e4;
30188
+ const staleDelegation = !session.delegationActive || Date.now() - session.lastAgentEventTime > 1e4;
30169
30189
  if (staleDelegation) {
30170
30190
  swarmState.activeAgent.set(input.sessionID, ORCHESTRATOR_NAME);
30171
30191
  ensureAgentSession(input.sessionID, ORCHESTRATOR_NAME);
@@ -30175,7 +30195,21 @@ var OpenCodeSwarm = async (ctx) => {
30175
30195
  await guardrailsHooks.toolBefore(input, output);
30176
30196
  await safeHook(activityHooks.toolBefore)(input, output);
30177
30197
  },
30178
- "tool.execute.after": composeHandlers(activityHooks.toolAfter, guardrailsHooks.toolAfter, toolSummarizerHook),
30198
+ "tool.execute.after": async (input, output) => {
30199
+ await activityHooks.toolAfter(input, output);
30200
+ await guardrailsHooks.toolAfter(input, output);
30201
+ await toolSummarizerHook?.(input, output);
30202
+ if (input.tool === "task") {
30203
+ const sessionId = input.sessionID;
30204
+ swarmState.activeAgent.set(sessionId, ORCHESTRATOR_NAME);
30205
+ ensureAgentSession(sessionId, ORCHESTRATOR_NAME);
30206
+ const session = swarmState.agentSessions.get(sessionId);
30207
+ if (session) {
30208
+ session.delegationActive = false;
30209
+ session.lastAgentEventTime = Date.now();
30210
+ }
30211
+ }
30212
+ },
30179
30213
  "chat.message": safeHook(delegationHandler)
30180
30214
  };
30181
30215
  };
package/dist/state.d.ts CHANGED
@@ -43,6 +43,8 @@ export interface AgentSessionState {
43
43
  startTime: number;
44
44
  /** Timestamp of most recent tool call (for stale session eviction) */
45
45
  lastToolCallTime: number;
46
+ /** Timestamp of most recent agent identity event (chat.message sets/changes identity) */
47
+ lastAgentEventTime: number;
46
48
  /** Total tool calls in this session */
47
49
  toolCallCount: number;
48
50
  /** Consecutive errors (reset on success) */
@@ -114,3 +116,9 @@ export declare function getAgentSession(sessionId: string): AgentSessionState |
114
116
  * @returns The AgentSessionState
115
117
  */
116
118
  export declare function ensureAgentSession(sessionId: string, agentName?: string): AgentSessionState;
119
+ /**
120
+ * Update only the agent event timestamp (for stale detection).
121
+ * Does NOT change agent name or reset guardrail state.
122
+ * @param sessionId - The session identifier
123
+ */
124
+ export declare function updateAgentEventTime(sessionId: string): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm",
3
- "version": "5.1.5",
3
+ "version": "5.1.7",
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",