opencode-swarm 6.22.10 → 6.22.12

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.
@@ -6,11 +6,15 @@
6
6
  * Wrapped in safeHook — errors must never propagate.
7
7
  */
8
8
  import type { PreflightTriggerManager } from '../background/trigger';
9
+ import type { CuratorConfig, CuratorInitResult } from './curator-types';
10
+ /** Injectable curator runner type — allows test injection without module mocking. */
11
+ export type CuratorInitRunner = (directory: string, config: CuratorConfig) => Promise<CuratorInitResult>;
9
12
  /**
10
13
  * Creates a hook that monitors plan phase transitions and triggers preflight.
11
14
  *
12
15
  * @param directory - Project directory (where .swarm/ lives)
13
16
  * @param preflightManager - The PreflightTriggerManager to call on phase change
17
+ * @param curatorRunner - Optional curator init runner (defaults to runCuratorInit; injectable for tests)
14
18
  * @returns A safeHook-wrapped system.transform handler
15
19
  */
16
- export declare function createPhaseMonitorHook(directory: string, preflightManager: PreflightTriggerManager): (input: unknown, output: unknown) => Promise<void>;
20
+ export declare function createPhaseMonitorHook(directory: string, preflightManager: PreflightTriggerManager, curatorRunner?: CuratorInitRunner): (input: unknown, output: unknown) => Promise<void>;
package/dist/index.js CHANGED
@@ -42216,8 +42216,8 @@ function recordPhaseAgentDispatch(sessionId, agentName) {
42216
42216
  session.phaseAgentsDispatched.add(normalizedName);
42217
42217
  }
42218
42218
  function advanceTaskState(session, taskId, newState) {
42219
- if (!session.taskWorkflowStates) {
42220
- session.taskWorkflowStates = new Map;
42219
+ if (!session || !(session.taskWorkflowStates instanceof Map)) {
42220
+ throw new Error("INVALID_SESSION: session.taskWorkflowStates must be a Map instance");
42221
42221
  }
42222
42222
  const STATE_ORDER = [
42223
42223
  "idle",
@@ -48616,7 +48616,9 @@ function createDelegationGateHook(config3) {
48616
48616
  if (state === "coder_delegated" || state === "pre_check_passed") {
48617
48617
  try {
48618
48618
  advanceTaskState(session, taskId, "reviewer_run");
48619
- } catch {}
48619
+ } catch (err2) {
48620
+ console.warn(`[delegation-gate] toolAfter: could not advance ${taskId} (${state}) \u2192 reviewer_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
48621
+ }
48620
48622
  }
48621
48623
  }
48622
48624
  }
@@ -48625,7 +48627,9 @@ function createDelegationGateHook(config3) {
48625
48627
  if (state === "reviewer_run") {
48626
48628
  try {
48627
48629
  advanceTaskState(session, taskId, "tests_run");
48628
- } catch {}
48630
+ } catch (err2) {
48631
+ console.warn(`[delegation-gate] toolAfter: could not advance ${taskId} (${state}) \u2192 tests_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
48632
+ }
48629
48633
  }
48630
48634
  }
48631
48635
  }
@@ -48640,7 +48644,9 @@ function createDelegationGateHook(config3) {
48640
48644
  if (state === "coder_delegated" || state === "pre_check_passed") {
48641
48645
  try {
48642
48646
  advanceTaskState(otherSession, taskId, "reviewer_run");
48643
- } catch {}
48647
+ } catch (err2) {
48648
+ console.warn(`[delegation-gate] toolAfter cross-session: could not advance ${taskId} (${state}) \u2192 reviewer_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
48649
+ }
48644
48650
  }
48645
48651
  }
48646
48652
  }
@@ -48649,7 +48655,9 @@ function createDelegationGateHook(config3) {
48649
48655
  if (state === "reviewer_run") {
48650
48656
  try {
48651
48657
  advanceTaskState(otherSession, taskId, "tests_run");
48652
- } catch {}
48658
+ } catch (err2) {
48659
+ console.warn(`[delegation-gate] toolAfter cross-session: could not advance ${taskId} (${state}) \u2192 tests_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
48660
+ }
48653
48661
  }
48654
48662
  }
48655
48663
  }
@@ -48690,7 +48698,9 @@ function createDelegationGateHook(config3) {
48690
48698
  if (state === "coder_delegated" || state === "pre_check_passed") {
48691
48699
  try {
48692
48700
  advanceTaskState(session, taskId, "reviewer_run");
48693
- } catch {}
48701
+ } catch (err2) {
48702
+ console.warn(`[delegation-gate] fallback: could not advance ${taskId} (${state}) \u2192 reviewer_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
48703
+ }
48694
48704
  }
48695
48705
  }
48696
48706
  }
@@ -48699,7 +48709,9 @@ function createDelegationGateHook(config3) {
48699
48709
  if (state === "reviewer_run") {
48700
48710
  try {
48701
48711
  advanceTaskState(session, taskId, "tests_run");
48702
- } catch {}
48712
+ } catch (err2) {
48713
+ console.warn(`[delegation-gate] fallback: could not advance ${taskId} (${state}) \u2192 tests_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
48714
+ }
48703
48715
  }
48704
48716
  }
48705
48717
  }
@@ -48713,7 +48725,9 @@ function createDelegationGateHook(config3) {
48713
48725
  if (state === "coder_delegated" || state === "pre_check_passed") {
48714
48726
  try {
48715
48727
  advanceTaskState(otherSession, taskId, "reviewer_run");
48716
- } catch {}
48728
+ } catch (err2) {
48729
+ console.warn(`[delegation-gate] fallback cross-session: could not advance ${taskId} (${state}) \u2192 reviewer_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
48730
+ }
48717
48731
  }
48718
48732
  }
48719
48733
  }
@@ -48728,7 +48742,9 @@ function createDelegationGateHook(config3) {
48728
48742
  if (state === "reviewer_run") {
48729
48743
  try {
48730
48744
  advanceTaskState(otherSession, taskId, "tests_run");
48731
- } catch {}
48745
+ } catch (err2) {
48746
+ console.warn(`[delegation-gate] fallback cross-session: could not advance ${taskId} (${state}) \u2192 tests_run: ${err2 instanceof Error ? err2.message : String(err2)}`);
48747
+ }
48732
48748
  }
48733
48749
  }
48734
48750
  }
@@ -48855,7 +48871,7 @@ ${trimComment}${after}`;
48855
48871
  let guidance;
48856
48872
  if (lastGate?.taskId) {
48857
48873
  const gateResult = lastGate.passed ? "PASSED" : "FAILED";
48858
- const sanitizedGate = lastGate.gate.replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\[/g, "(").replace(/\]/g, ")").replace(/[\r\n]/g, " ").slice(0, 64);
48874
+ const sanitizedGate = lastGate.gate.replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\[ \]/g, "()").replace(/\[/g, "(").replace(/\]/g, ")").replace(/[\r\n]/g, " ").slice(0, 64);
48859
48875
  const sanitizedTaskId = lastGate.taskId.replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/\[/g, "(").replace(/\]/g, ")").replace(/[\r\n]/g, " ").slice(0, 32);
48860
48876
  guidance = `[Last gate: ${sanitizedGate} ${gateResult} for task ${sanitizedTaskId}]
48861
48877
  [NEXT] Execute the next gate for the current task.`;
@@ -48876,27 +48892,35 @@ ${trimComment}${after}`;
48876
48892
  if (text.length > delegationMaxChars) {
48877
48893
  warnings.push(`Delegation exceeds recommended size (${text.length} chars, limit ${delegationMaxChars}). Consider splitting into smaller tasks.`);
48878
48894
  }
48879
- const fileMatches = text.match(/^FILE:/gm);
48880
- if (fileMatches && fileMatches.length > 1) {
48881
- warnings.push(`Multiple FILE: directives detected (${fileMatches.length}). Each coder task should target ONE file.`);
48895
+ if (isCoderDelegation) {
48896
+ const fileMatches = text.match(/^FILE:/gm);
48897
+ if (fileMatches && fileMatches.length > 1) {
48898
+ warnings.push(`Multiple FILE: directives detected (${fileMatches.length}). Each coder task should target ONE file.`);
48899
+ }
48882
48900
  }
48883
- const taskMatches = text.match(/^TASK:/gm);
48884
- if (taskMatches && taskMatches.length > 1) {
48885
- warnings.push(`Multiple TASK: sections detected (${taskMatches.length}). Send ONE task per coder call.`);
48901
+ if (isCoderDelegation) {
48902
+ const taskMatches = text.match(/^TASK:/gm);
48903
+ if (taskMatches && taskMatches.length > 1) {
48904
+ warnings.push(`Multiple TASK: sections detected (${taskMatches.length}). Send ONE task per coder call.`);
48905
+ }
48886
48906
  }
48887
- const batchingPattern = /\b(?:and also|then also|additionally|as well as|along with|while you'?re at it)[.,]?\b/gi;
48888
- const batchingMatches = text.match(batchingPattern);
48889
- if (batchingMatches && batchingMatches.length > 0) {
48890
- warnings.push(`Batching language detected (${batchingMatches.join(", ")}). Break compound objectives into separate coder calls.`);
48907
+ if (isCoderDelegation) {
48908
+ const batchingPattern = /\b(?:and also|then also|additionally|as well as|along with|while you'?re at it)[.,]?\b/gi;
48909
+ const batchingMatches = text.match(batchingPattern);
48910
+ if (batchingMatches && batchingMatches.length > 0) {
48911
+ warnings.push(`Batching language detected (${batchingMatches.join(", ")}). Break compound objectives into separate coder calls.`);
48912
+ }
48891
48913
  }
48892
- const taskLine = extractTaskLine(text);
48893
- if (taskLine) {
48894
- const andPattern = /\s+and\s+(update|add|remove|modify|refactor|implement|create|delete|fix|change|build|deploy|write|test|move|rename|extend|extract|convert|migrate|upgrade|replace)\b/i;
48895
- if (andPattern.test(taskLine)) {
48896
- warnings.push('TASK line contains "and" connecting separate actions');
48914
+ if (isCoderDelegation) {
48915
+ const taskLine = extractTaskLine(text);
48916
+ if (taskLine) {
48917
+ const andPattern = /\s+and\s+(update|add|remove|modify|refactor|implement|create|delete|fix|change|build|deploy|write|test|move|rename|extend|extract|convert|migrate|upgrade|replace)\b/i;
48918
+ if (andPattern.test(taskLine)) {
48919
+ warnings.push('TASK line contains "and" connecting separate actions');
48920
+ }
48897
48921
  }
48898
48922
  }
48899
- if (sessionID) {
48923
+ if (isCoderDelegation && sessionID) {
48900
48924
  const delegationChain = swarmState.delegationChains.get(sessionID);
48901
48925
  if (delegationChain && delegationChain.length >= 2) {
48902
48926
  const coderIndices = [];
@@ -48933,10 +48957,13 @@ ${trimComment}${after}`;
48933
48957
  Rule 3: ONE task per coder call. Split this into separate delegations.
48934
48958
  ${warningLines.join(`
48935
48959
  `)}`;
48936
- const originalText = textPart.text ?? "";
48937
- textPart.text = `${warningText}
48938
-
48939
- ${originalText}`;
48960
+ const batchWarnSystemIdx = messages.findIndex((m) => m && m.info?.role === "system");
48961
+ const batchWarnInsertIdx = batchWarnSystemIdx >= 0 ? batchWarnSystemIdx + 1 : 0;
48962
+ const batchWarnMessage = {
48963
+ info: { role: "system" },
48964
+ parts: [{ type: "text", text: warningText }]
48965
+ };
48966
+ messages.splice(batchWarnInsertIdx, 0, batchWarnMessage);
48940
48967
  },
48941
48968
  toolAfter
48942
48969
  };
@@ -49029,10 +49056,6 @@ init_schema();
49029
49056
  function createDelegationTrackerHook(config3, guardrailsEnabled = true) {
49030
49057
  return async (input, _output) => {
49031
49058
  const now = Date.now();
49032
- const debugSession = swarmState.agentSessions.get(input.sessionID);
49033
- const taskStates = debugSession?.taskWorkflowStates ? Object.entries(debugSession.taskWorkflowStates) : [];
49034
- const statesSummary = taskStates.length > 0 ? taskStates.map(([k, v]) => `${k}=${v}`).join(",") : "(none)";
49035
- console.log(`[swarm-debug-task] chat.message | session=${input.sessionID} agent=${input.agent ?? "(none)"} prevAgent=${swarmState.activeAgent.get(input.sessionID) ?? "(none)"} taskStates=[${statesSummary}]`);
49036
49059
  if (!input.agent || input.agent === "") {
49037
49060
  const session2 = swarmState.agentSessions.get(input.sessionID);
49038
49061
  if (session2?.delegationActive) {
@@ -49144,7 +49167,7 @@ function consolidateSystemMessages(messages) {
49144
49167
  init_schema();
49145
49168
  init_manager2();
49146
49169
  init_utils2();
49147
- function createPhaseMonitorHook(directory, preflightManager) {
49170
+ function createPhaseMonitorHook(directory, preflightManager, curatorRunner = runCuratorInit) {
49148
49171
  let lastKnownPhase = null;
49149
49172
  const handler = async (_input, _output) => {
49150
49173
  const plan = await loadPlan(directory);
@@ -49158,7 +49181,7 @@ function createPhaseMonitorHook(directory, preflightManager) {
49158
49181
  const { config: config3 } = loadPluginConfigWithMeta2(directory);
49159
49182
  const curatorConfig = CuratorConfigSchema.parse(config3.curator ?? {});
49160
49183
  if (curatorConfig.enabled && curatorConfig.init_enabled) {
49161
- await runCuratorInit(directory, curatorConfig);
49184
+ await curatorRunner(directory, curatorConfig);
49162
49185
  }
49163
49186
  } catch {}
49164
49187
  return;
@@ -60822,14 +60845,24 @@ function checkReviewerGate(taskId, workingDirectory) {
60822
60845
  if (swarmState.agentSessions.size === 0) {
60823
60846
  return { blocked: false, reason: "" };
60824
60847
  }
60848
+ let validSessionCount = 0;
60825
60849
  for (const [_sessionId, session] of swarmState.agentSessions) {
60850
+ if (!(session.taskWorkflowStates instanceof Map)) {
60851
+ continue;
60852
+ }
60853
+ validSessionCount++;
60826
60854
  const state = getTaskState(session, taskId);
60827
60855
  if (state === "tests_run" || state === "complete") {
60828
60856
  return { blocked: false, reason: "" };
60829
60857
  }
60830
60858
  }
60859
+ if (validSessionCount === 0) {
60860
+ return { blocked: false, reason: "" };
60861
+ }
60831
60862
  const stateEntries = [];
60832
60863
  for (const [sessionId, session] of swarmState.agentSessions) {
60864
+ if (!(session.taskWorkflowStates instanceof Map))
60865
+ continue;
60833
60866
  const state = getTaskState(session, taskId);
60834
60867
  stateEntries.push(`${sessionId}: ${state}`);
60835
60868
  }
package/dist/state.d.ts CHANGED
@@ -250,6 +250,7 @@ export declare function advanceTaskState(session: AgentSessionState, taskId: str
250
250
  /**
251
251
  * Get the current workflow state for a task.
252
252
  * Returns 'idle' if no entry exists.
253
+ * If taskWorkflowStates is missing/invalid, initializes it as a new Map.
253
254
  *
254
255
  * @param session - The agent session state
255
256
  * @param taskId - The task identifier
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm",
3
- "version": "6.22.10",
3
+ "version": "6.22.12",
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",