opencode-swarm-plugin 0.11.2 → 0.12.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm-plugin",
3
- "version": "0.11.2",
3
+ "version": "0.12.0",
4
4
  "description": "Multi-agent swarm coordination for OpenCode with learning capabilities, beads integration, and Agent Mail",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/src/agent-mail.ts CHANGED
@@ -62,9 +62,83 @@ export interface AgentMailState {
62
62
  // Module-level state (keyed by sessionID)
63
63
  // ============================================================================
64
64
 
65
+ import {
66
+ existsSync,
67
+ mkdirSync,
68
+ readFileSync,
69
+ writeFileSync,
70
+ unlinkSync,
71
+ } from "fs";
72
+ import { join } from "path";
73
+ import { tmpdir } from "os";
74
+
75
+ /**
76
+ * Directory for persisting session state across CLI invocations
77
+ * This allows `swarm tool` commands to share state
78
+ */
79
+ const SESSION_STATE_DIR =
80
+ process.env.SWARM_STATE_DIR || join(tmpdir(), "swarm-sessions");
81
+
82
+ /**
83
+ * Get the file path for a session's state
84
+ */
85
+ function getSessionStatePath(sessionID: string): string {
86
+ // Sanitize sessionID to be filesystem-safe
87
+ const safeID = sessionID.replace(/[^a-zA-Z0-9_-]/g, "_");
88
+ return join(SESSION_STATE_DIR, `${safeID}.json`);
89
+ }
90
+
91
+ /**
92
+ * Load session state from disk
93
+ */
94
+ function loadSessionState(sessionID: string): AgentMailState | null {
95
+ const path = getSessionStatePath(sessionID);
96
+ try {
97
+ if (existsSync(path)) {
98
+ const data = readFileSync(path, "utf-8");
99
+ return JSON.parse(data) as AgentMailState;
100
+ }
101
+ } catch (error) {
102
+ // File might be corrupted or inaccessible - ignore and return null
103
+ console.warn(`[agent-mail] Could not load session state: ${error}`);
104
+ }
105
+ return null;
106
+ }
107
+
108
+ /**
109
+ * Save session state to disk
110
+ */
111
+ function saveSessionState(sessionID: string, state: AgentMailState): void {
112
+ try {
113
+ // Ensure directory exists
114
+ if (!existsSync(SESSION_STATE_DIR)) {
115
+ mkdirSync(SESSION_STATE_DIR, { recursive: true });
116
+ }
117
+ const path = getSessionStatePath(sessionID);
118
+ writeFileSync(path, JSON.stringify(state, null, 2));
119
+ } catch (error) {
120
+ // Non-fatal - state just won't persist
121
+ console.warn(`[agent-mail] Could not save session state: ${error}`);
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Delete session state from disk
127
+ */
128
+ function deleteSessionState(sessionID: string): void {
129
+ const path = getSessionStatePath(sessionID);
130
+ try {
131
+ if (existsSync(path)) {
132
+ unlinkSync(path);
133
+ }
134
+ } catch {
135
+ // Ignore errors on cleanup
136
+ }
137
+ }
138
+
65
139
  /**
66
140
  * State storage keyed by sessionID.
67
- * Since ToolContext doesn't have persistent state, we use a module-level map.
141
+ * In-memory cache that also persists to disk for CLI usage.
68
142
  */
69
143
  const sessionStates = new Map<string, AgentMailState>();
70
144
 
@@ -691,9 +765,23 @@ export async function mcpCall<T>(
691
765
 
692
766
  /**
693
767
  * Get Agent Mail state for a session, or throw if not initialized
768
+ *
769
+ * Checks in-memory cache first, then falls back to disk storage.
770
+ * This allows CLI invocations to share state across calls.
694
771
  */
695
772
  function requireState(sessionID: string): AgentMailState {
696
- const state = sessionStates.get(sessionID);
773
+ // Check in-memory cache first
774
+ let state = sessionStates.get(sessionID);
775
+
776
+ // If not in memory, try loading from disk
777
+ if (!state) {
778
+ state = loadSessionState(sessionID) ?? undefined;
779
+ if (state) {
780
+ // Cache in memory for subsequent calls in same process
781
+ sessionStates.set(sessionID, state);
782
+ }
783
+ }
784
+
697
785
  if (!state) {
698
786
  throw new AgentMailNotInitializedError();
699
787
  }
@@ -702,23 +790,38 @@ function requireState(sessionID: string): AgentMailState {
702
790
 
703
791
  /**
704
792
  * Store Agent Mail state for a session
793
+ *
794
+ * Saves to both in-memory cache and disk for CLI persistence.
705
795
  */
706
796
  function setState(sessionID: string, state: AgentMailState): void {
707
797
  sessionStates.set(sessionID, state);
798
+ saveSessionState(sessionID, state);
708
799
  }
709
800
 
710
801
  /**
711
802
  * Get state if exists (for cleanup hooks)
803
+ *
804
+ * Checks in-memory cache first, then falls back to disk storage.
712
805
  */
713
806
  function getState(sessionID: string): AgentMailState | undefined {
714
- return sessionStates.get(sessionID);
807
+ let state = sessionStates.get(sessionID);
808
+ if (!state) {
809
+ state = loadSessionState(sessionID) ?? undefined;
810
+ if (state) {
811
+ sessionStates.set(sessionID, state);
812
+ }
813
+ }
814
+ return state;
715
815
  }
716
816
 
717
817
  /**
718
818
  * Clear state for a session
819
+ *
820
+ * Removes from both in-memory cache and disk.
719
821
  */
720
822
  function clearState(sessionID: string): void {
721
823
  sessionStates.delete(sessionID);
824
+ deleteSessionState(sessionID);
722
825
  }
723
826
 
724
827
  // ============================================================================
package/src/index.ts CHANGED
@@ -280,6 +280,28 @@ export {
280
280
  type StrategyDefinition,
281
281
  } from "./swarm";
282
282
 
283
+ // =============================================================================
284
+ // Unified Tool Registry for CLI
285
+ // =============================================================================
286
+
287
+ /**
288
+ * All tools in a single registry for CLI tool execution
289
+ *
290
+ * This is used by `swarm tool <name>` command to dynamically execute tools.
291
+ * Each tool has an `execute` function that takes (args, ctx) and returns a string.
292
+ */
293
+ export const allTools = {
294
+ ...beadsTools,
295
+ ...agentMailTools,
296
+ ...structuredTools,
297
+ ...swarmTools,
298
+ } as const;
299
+
300
+ /**
301
+ * Type for CLI tool names (all available tools)
302
+ */
303
+ export type CLIToolName = keyof typeof allTools;
304
+
283
305
  /**
284
306
  * Re-export storage module
285
307
  *