opencode-swarm-plugin 0.44.0 → 0.44.1
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/bin/swarm.serve.test.ts +6 -4
- package/bin/swarm.ts +16 -10
- package/dist/compaction-prompt-scoring.js +139 -0
- package/dist/eval-capture.js +12811 -0
- package/dist/hive.d.ts.map +1 -1
- package/dist/index.js +7644 -62599
- package/dist/plugin.js +23766 -78721
- package/dist/swarm-orchestrate.d.ts.map +1 -1
- package/dist/swarm-prompts.d.ts.map +1 -1
- package/dist/swarm-review.d.ts.map +1 -1
- package/package.json +17 -5
- package/.changeset/swarm-insights-data-layer.md +0 -63
- package/.hive/analysis/eval-failure-analysis-2025-12-25.md +0 -331
- package/.hive/analysis/session-data-quality-audit.md +0 -320
- package/.hive/eval-results.json +0 -483
- package/.hive/issues.jsonl +0 -138
- package/.hive/memories.jsonl +0 -729
- package/.opencode/eval-history.jsonl +0 -327
- package/.turbo/turbo-build.log +0 -9
- package/CHANGELOG.md +0 -2286
- package/SCORER-ANALYSIS.md +0 -598
- package/docs/analysis/subagent-coordination-patterns.md +0 -902
- package/docs/analysis-socratic-planner-pattern.md +0 -504
- package/docs/planning/ADR-001-monorepo-structure.md +0 -171
- package/docs/planning/ADR-002-package-extraction.md +0 -393
- package/docs/planning/ADR-003-performance-improvements.md +0 -451
- package/docs/planning/ADR-004-message-queue-features.md +0 -187
- package/docs/planning/ADR-005-devtools-observability.md +0 -202
- package/docs/planning/ADR-007-swarm-enhancements-worktree-review.md +0 -168
- package/docs/planning/ADR-008-worker-handoff-protocol.md +0 -293
- package/docs/planning/ADR-009-oh-my-opencode-patterns.md +0 -353
- package/docs/planning/ADR-010-cass-inhousing.md +0 -1215
- package/docs/planning/ROADMAP.md +0 -368
- package/docs/semantic-memory-cli-syntax.md +0 -123
- package/docs/swarm-mail-architecture.md +0 -1147
- package/docs/testing/context-recovery-test.md +0 -470
- package/evals/ARCHITECTURE.md +0 -1189
- package/evals/README.md +0 -768
- package/evals/compaction-prompt.eval.ts +0 -149
- package/evals/compaction-resumption.eval.ts +0 -289
- package/evals/coordinator-behavior.eval.ts +0 -307
- package/evals/coordinator-session.eval.ts +0 -154
- package/evals/evalite.config.ts.bak +0 -15
- package/evals/example.eval.ts +0 -31
- package/evals/fixtures/cass-baseline.ts +0 -217
- package/evals/fixtures/compaction-cases.ts +0 -350
- package/evals/fixtures/compaction-prompt-cases.ts +0 -311
- package/evals/fixtures/coordinator-sessions.ts +0 -328
- package/evals/fixtures/decomposition-cases.ts +0 -105
- package/evals/lib/compaction-loader.test.ts +0 -248
- package/evals/lib/compaction-loader.ts +0 -320
- package/evals/lib/data-loader.evalite-test.ts +0 -289
- package/evals/lib/data-loader.test.ts +0 -345
- package/evals/lib/data-loader.ts +0 -281
- package/evals/lib/llm.ts +0 -115
- package/evals/scorers/compaction-prompt-scorers.ts +0 -145
- package/evals/scorers/compaction-scorers.ts +0 -305
- package/evals/scorers/coordinator-discipline.evalite-test.ts +0 -539
- package/evals/scorers/coordinator-discipline.ts +0 -325
- package/evals/scorers/index.test.ts +0 -146
- package/evals/scorers/index.ts +0 -328
- package/evals/scorers/outcome-scorers.evalite-test.ts +0 -27
- package/evals/scorers/outcome-scorers.ts +0 -349
- package/evals/swarm-decomposition.eval.ts +0 -121
- package/examples/commands/swarm.md +0 -745
- package/examples/plugin-wrapper-template.ts +0 -2515
- package/examples/skills/hive-workflow/SKILL.md +0 -212
- package/examples/skills/skill-creator/SKILL.md +0 -223
- package/examples/skills/swarm-coordination/SKILL.md +0 -292
- package/global-skills/cli-builder/SKILL.md +0 -344
- package/global-skills/cli-builder/references/advanced-patterns.md +0 -244
- package/global-skills/learning-systems/SKILL.md +0 -644
- package/global-skills/skill-creator/LICENSE.txt +0 -202
- package/global-skills/skill-creator/SKILL.md +0 -352
- package/global-skills/skill-creator/references/output-patterns.md +0 -82
- package/global-skills/skill-creator/references/workflows.md +0 -28
- package/global-skills/swarm-coordination/SKILL.md +0 -995
- package/global-skills/swarm-coordination/references/coordinator-patterns.md +0 -235
- package/global-skills/swarm-coordination/references/strategies.md +0 -138
- package/global-skills/system-design/SKILL.md +0 -213
- package/global-skills/testing-patterns/SKILL.md +0 -430
- package/global-skills/testing-patterns/references/dependency-breaking-catalog.md +0 -586
- package/opencode-swarm-plugin-0.30.7.tgz +0 -0
- package/opencode-swarm-plugin-0.31.0.tgz +0 -0
- package/scripts/cleanup-test-memories.ts +0 -346
- package/scripts/init-skill.ts +0 -222
- package/scripts/migrate-unknown-sessions.ts +0 -349
- package/scripts/validate-skill.ts +0 -204
- package/src/agent-mail.ts +0 -1724
- package/src/anti-patterns.test.ts +0 -1167
- package/src/anti-patterns.ts +0 -448
- package/src/compaction-capture.integration.test.ts +0 -257
- package/src/compaction-hook.test.ts +0 -838
- package/src/compaction-hook.ts +0 -1204
- package/src/compaction-observability.integration.test.ts +0 -139
- package/src/compaction-observability.test.ts +0 -187
- package/src/compaction-observability.ts +0 -324
- package/src/compaction-prompt-scorers.test.ts +0 -475
- package/src/compaction-prompt-scoring.ts +0 -300
- package/src/contributor-tools.test.ts +0 -133
- package/src/contributor-tools.ts +0 -201
- package/src/dashboard.test.ts +0 -611
- package/src/dashboard.ts +0 -462
- package/src/error-enrichment.test.ts +0 -403
- package/src/error-enrichment.ts +0 -219
- package/src/eval-capture.test.ts +0 -1015
- package/src/eval-capture.ts +0 -929
- package/src/eval-gates.test.ts +0 -306
- package/src/eval-gates.ts +0 -218
- package/src/eval-history.test.ts +0 -508
- package/src/eval-history.ts +0 -214
- package/src/eval-learning.test.ts +0 -378
- package/src/eval-learning.ts +0 -360
- package/src/eval-runner.test.ts +0 -223
- package/src/eval-runner.ts +0 -402
- package/src/export-tools.test.ts +0 -476
- package/src/export-tools.ts +0 -257
- package/src/hive.integration.test.ts +0 -2241
- package/src/hive.ts +0 -1628
- package/src/index.ts +0 -940
- package/src/learning.integration.test.ts +0 -1815
- package/src/learning.ts +0 -1079
- package/src/logger.test.ts +0 -189
- package/src/logger.ts +0 -135
- package/src/mandate-promotion.test.ts +0 -473
- package/src/mandate-promotion.ts +0 -239
- package/src/mandate-storage.integration.test.ts +0 -601
- package/src/mandate-storage.test.ts +0 -578
- package/src/mandate-storage.ts +0 -794
- package/src/mandates.ts +0 -540
- package/src/memory-tools.test.ts +0 -195
- package/src/memory-tools.ts +0 -344
- package/src/memory.integration.test.ts +0 -334
- package/src/memory.test.ts +0 -158
- package/src/memory.ts +0 -527
- package/src/model-selection.test.ts +0 -188
- package/src/model-selection.ts +0 -68
- package/src/observability-tools.test.ts +0 -359
- package/src/observability-tools.ts +0 -871
- package/src/output-guardrails.test.ts +0 -438
- package/src/output-guardrails.ts +0 -381
- package/src/pattern-maturity.test.ts +0 -1160
- package/src/pattern-maturity.ts +0 -525
- package/src/planning-guardrails.test.ts +0 -491
- package/src/planning-guardrails.ts +0 -438
- package/src/plugin.ts +0 -23
- package/src/post-compaction-tracker.test.ts +0 -251
- package/src/post-compaction-tracker.ts +0 -237
- package/src/query-tools.test.ts +0 -636
- package/src/query-tools.ts +0 -324
- package/src/rate-limiter.integration.test.ts +0 -466
- package/src/rate-limiter.ts +0 -774
- package/src/replay-tools.test.ts +0 -496
- package/src/replay-tools.ts +0 -240
- package/src/repo-crawl.integration.test.ts +0 -441
- package/src/repo-crawl.ts +0 -610
- package/src/schemas/cell-events.test.ts +0 -347
- package/src/schemas/cell-events.ts +0 -807
- package/src/schemas/cell.ts +0 -257
- package/src/schemas/evaluation.ts +0 -166
- package/src/schemas/index.test.ts +0 -199
- package/src/schemas/index.ts +0 -286
- package/src/schemas/mandate.ts +0 -232
- package/src/schemas/swarm-context.ts +0 -115
- package/src/schemas/task.ts +0 -161
- package/src/schemas/worker-handoff.test.ts +0 -302
- package/src/schemas/worker-handoff.ts +0 -131
- package/src/sessions/agent-discovery.test.ts +0 -137
- package/src/sessions/agent-discovery.ts +0 -112
- package/src/sessions/index.ts +0 -15
- package/src/skills.integration.test.ts +0 -1192
- package/src/skills.test.ts +0 -643
- package/src/skills.ts +0 -1549
- package/src/storage.integration.test.ts +0 -341
- package/src/storage.ts +0 -884
- package/src/structured.integration.test.ts +0 -817
- package/src/structured.test.ts +0 -1046
- package/src/structured.ts +0 -762
- package/src/swarm-decompose.test.ts +0 -188
- package/src/swarm-decompose.ts +0 -1302
- package/src/swarm-deferred.integration.test.ts +0 -157
- package/src/swarm-deferred.test.ts +0 -38
- package/src/swarm-insights.test.ts +0 -214
- package/src/swarm-insights.ts +0 -459
- package/src/swarm-mail.integration.test.ts +0 -970
- package/src/swarm-mail.ts +0 -739
- package/src/swarm-orchestrate.integration.test.ts +0 -282
- package/src/swarm-orchestrate.test.ts +0 -548
- package/src/swarm-orchestrate.ts +0 -3084
- package/src/swarm-prompts.test.ts +0 -1270
- package/src/swarm-prompts.ts +0 -2077
- package/src/swarm-research.integration.test.ts +0 -701
- package/src/swarm-research.test.ts +0 -698
- package/src/swarm-research.ts +0 -472
- package/src/swarm-review.integration.test.ts +0 -285
- package/src/swarm-review.test.ts +0 -879
- package/src/swarm-review.ts +0 -709
- package/src/swarm-strategies.ts +0 -407
- package/src/swarm-worktree.test.ts +0 -501
- package/src/swarm-worktree.ts +0 -575
- package/src/swarm.integration.test.ts +0 -2377
- package/src/swarm.ts +0 -38
- package/src/tool-adapter.integration.test.ts +0 -1221
- package/src/tool-availability.ts +0 -461
- package/tsconfig.json +0 -28
package/src/swarm-mail.ts
DELETED
|
@@ -1,739 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Swarm Mail Plugin Tools - Embedded event-sourced implementation
|
|
3
|
-
*
|
|
4
|
-
* Replaces the MCP-based agent-mail with embedded PGLite storage.
|
|
5
|
-
* Same tool API surface, but no external server dependency.
|
|
6
|
-
*
|
|
7
|
-
* Key features:
|
|
8
|
-
* - Event sourcing for full audit trail
|
|
9
|
-
* - Offset-based resumability (Durable Streams inspired)
|
|
10
|
-
* - Materialized views for fast queries
|
|
11
|
-
* - File reservation with conflict detection
|
|
12
|
-
*
|
|
13
|
-
* CRITICAL CONSTRAINTS (same as agent-mail):
|
|
14
|
-
* - swarmmail_inbox ALWAYS limits to 5 messages max
|
|
15
|
-
* - swarmmail_inbox ALWAYS excludes bodies by default
|
|
16
|
-
* - Use summarize_thread instead of fetching all messages
|
|
17
|
-
* - Auto-release reservations when tasks complete
|
|
18
|
-
*/
|
|
19
|
-
import { tool } from "@opencode-ai/plugin";
|
|
20
|
-
import {
|
|
21
|
-
initSwarmAgent,
|
|
22
|
-
sendSwarmMessage,
|
|
23
|
-
getSwarmInbox,
|
|
24
|
-
readSwarmMessage,
|
|
25
|
-
reserveSwarmFiles,
|
|
26
|
-
releaseSwarmFiles,
|
|
27
|
-
acknowledgeSwarmMessage,
|
|
28
|
-
checkSwarmHealth,
|
|
29
|
-
getActiveReservations,
|
|
30
|
-
type MailSessionState,
|
|
31
|
-
} from "swarm-mail";
|
|
32
|
-
import {
|
|
33
|
-
existsSync,
|
|
34
|
-
mkdirSync,
|
|
35
|
-
readFileSync,
|
|
36
|
-
writeFileSync,
|
|
37
|
-
unlinkSync,
|
|
38
|
-
} from "node:fs";
|
|
39
|
-
import { join } from "node:path";
|
|
40
|
-
import { tmpdir } from "node:os";
|
|
41
|
-
|
|
42
|
-
// ============================================================================
|
|
43
|
-
// Types
|
|
44
|
-
// ============================================================================
|
|
45
|
-
|
|
46
|
-
/** Tool execution context from OpenCode plugin */
|
|
47
|
-
interface ToolContext {
|
|
48
|
-
sessionID: string;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Swarm Mail session state
|
|
53
|
-
* @deprecated Use MailSessionState from streams/events.ts instead
|
|
54
|
-
* This is kept for backward compatibility and re-exported as an alias
|
|
55
|
-
*/
|
|
56
|
-
export type SwarmMailState = MailSessionState;
|
|
57
|
-
|
|
58
|
-
/** Init tool arguments */
|
|
59
|
-
interface InitArgs {
|
|
60
|
-
project_path?: string;
|
|
61
|
-
agent_name?: string;
|
|
62
|
-
task_description?: string;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/** Send tool arguments */
|
|
66
|
-
interface SendArgs {
|
|
67
|
-
to: string[];
|
|
68
|
-
subject: string;
|
|
69
|
-
body: string;
|
|
70
|
-
thread_id?: string;
|
|
71
|
-
importance?: "low" | "normal" | "high" | "urgent";
|
|
72
|
-
ack_required?: boolean;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/** Inbox tool arguments */
|
|
76
|
-
interface InboxArgs {
|
|
77
|
-
limit?: number;
|
|
78
|
-
urgent_only?: boolean;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/** Read message tool arguments */
|
|
82
|
-
interface ReadMessageArgs {
|
|
83
|
-
message_id: number;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/** Reserve tool arguments */
|
|
87
|
-
interface ReserveArgs {
|
|
88
|
-
paths: string[];
|
|
89
|
-
reason?: string;
|
|
90
|
-
exclusive?: boolean;
|
|
91
|
-
ttl_seconds?: number;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/** Release tool arguments */
|
|
95
|
-
interface ReleaseArgs {
|
|
96
|
-
paths?: string[];
|
|
97
|
-
reservation_ids?: number[];
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/** Ack tool arguments */
|
|
101
|
-
interface AckArgs {
|
|
102
|
-
message_id: number;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// ============================================================================
|
|
106
|
-
// Configuration
|
|
107
|
-
// ============================================================================
|
|
108
|
-
|
|
109
|
-
const MAX_INBOX_LIMIT = 5; // HARD CAP - context preservation
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Default project directory for Swarm Mail operations
|
|
113
|
-
*
|
|
114
|
-
* This is set by the plugin init to the actual working directory (from OpenCode).
|
|
115
|
-
* Without this, tools might use the plugin's directory instead of the project's.
|
|
116
|
-
*/
|
|
117
|
-
let swarmMailProjectDirectory: string | null = null;
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Set the default project directory for Swarm Mail operations
|
|
121
|
-
*
|
|
122
|
-
* Called during plugin initialization with the actual project directory.
|
|
123
|
-
*/
|
|
124
|
-
export function setSwarmMailProjectDirectory(directory: string): void {
|
|
125
|
-
swarmMailProjectDirectory = directory;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Get the default project directory
|
|
130
|
-
* Returns undefined if not set - let getDatabasePath use global fallback
|
|
131
|
-
*/
|
|
132
|
-
export function getSwarmMailProjectDirectory(): string | undefined {
|
|
133
|
-
return swarmMailProjectDirectory ?? undefined;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// ============================================================================
|
|
137
|
-
// Session State Management
|
|
138
|
-
// ============================================================================
|
|
139
|
-
|
|
140
|
-
const SESSION_STATE_DIR =
|
|
141
|
-
process.env.SWARM_STATE_DIR || join(tmpdir(), "swarm-sessions");
|
|
142
|
-
|
|
143
|
-
function getSessionStatePath(sessionID: string): string {
|
|
144
|
-
const safeID = sessionID.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
145
|
-
return join(SESSION_STATE_DIR, `${safeID}.json`);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function loadSessionState(sessionID: string): SwarmMailState | null {
|
|
149
|
-
const path = getSessionStatePath(sessionID);
|
|
150
|
-
try {
|
|
151
|
-
if (existsSync(path)) {
|
|
152
|
-
const data = readFileSync(path, "utf-8");
|
|
153
|
-
return JSON.parse(data) as SwarmMailState;
|
|
154
|
-
}
|
|
155
|
-
} catch (error) {
|
|
156
|
-
console.warn(`[swarm-mail] Could not load session state: ${error}`);
|
|
157
|
-
}
|
|
158
|
-
return null;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function saveSessionState(sessionID: string, state: SwarmMailState): boolean {
|
|
162
|
-
try {
|
|
163
|
-
if (!existsSync(SESSION_STATE_DIR)) {
|
|
164
|
-
mkdirSync(SESSION_STATE_DIR, { recursive: true });
|
|
165
|
-
}
|
|
166
|
-
const path = getSessionStatePath(sessionID);
|
|
167
|
-
writeFileSync(path, JSON.stringify(state, null, 2));
|
|
168
|
-
return true;
|
|
169
|
-
} catch (error) {
|
|
170
|
-
console.warn(`[swarm-mail] Could not save session state: ${error}`);
|
|
171
|
-
return false;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
export function clearSessionState(sessionID: string): void {
|
|
176
|
-
const path = getSessionStatePath(sessionID);
|
|
177
|
-
try {
|
|
178
|
-
if (existsSync(path)) {
|
|
179
|
-
unlinkSync(path);
|
|
180
|
-
}
|
|
181
|
-
} catch {
|
|
182
|
-
// Ignore errors
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// ============================================================================
|
|
187
|
-
// Plugin Tools
|
|
188
|
-
// ============================================================================
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Initialize Swarm Mail session
|
|
192
|
-
*/
|
|
193
|
-
export const swarmmail_init = tool({
|
|
194
|
-
description:
|
|
195
|
-
"Initialize Swarm Mail session. Creates agent identity and registers with the embedded event store.",
|
|
196
|
-
args: {
|
|
197
|
-
project_path: tool.schema
|
|
198
|
-
.string()
|
|
199
|
-
.optional()
|
|
200
|
-
.describe("Project path (defaults to current working directory)"),
|
|
201
|
-
agent_name: tool.schema
|
|
202
|
-
.string()
|
|
203
|
-
.optional()
|
|
204
|
-
.describe("Custom agent name (auto-generated if not provided)"),
|
|
205
|
-
task_description: tool.schema
|
|
206
|
-
.string()
|
|
207
|
-
.optional()
|
|
208
|
-
.describe("Description of the task this agent is working on"),
|
|
209
|
-
},
|
|
210
|
-
async execute(args: InitArgs, ctx: ToolContext): Promise<string> {
|
|
211
|
-
// For init, we need a project path - use provided, stored, or cwd
|
|
212
|
-
const projectPath =
|
|
213
|
-
args.project_path || getSwarmMailProjectDirectory() || process.cwd();
|
|
214
|
-
const sessionID = ctx.sessionID || "default";
|
|
215
|
-
|
|
216
|
-
// Check if already initialized
|
|
217
|
-
const existingState = loadSessionState(sessionID);
|
|
218
|
-
if (existingState) {
|
|
219
|
-
return JSON.stringify(
|
|
220
|
-
{
|
|
221
|
-
agent_name: existingState.agentName,
|
|
222
|
-
project_key: existingState.projectKey,
|
|
223
|
-
message: `Session already initialized as ${existingState.agentName}`,
|
|
224
|
-
already_initialized: true,
|
|
225
|
-
},
|
|
226
|
-
null,
|
|
227
|
-
2,
|
|
228
|
-
);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
try {
|
|
232
|
-
const result = await initSwarmAgent({
|
|
233
|
-
projectPath,
|
|
234
|
-
agentName: args.agent_name,
|
|
235
|
-
taskDescription: args.task_description,
|
|
236
|
-
});
|
|
237
|
-
|
|
238
|
-
// Save session state
|
|
239
|
-
const state: SwarmMailState = {
|
|
240
|
-
projectKey: result.projectKey,
|
|
241
|
-
agentName: result.agentName,
|
|
242
|
-
reservations: [],
|
|
243
|
-
startedAt: new Date().toISOString(),
|
|
244
|
-
};
|
|
245
|
-
saveSessionState(sessionID, state);
|
|
246
|
-
|
|
247
|
-
return JSON.stringify(
|
|
248
|
-
{
|
|
249
|
-
agent_name: result.agentName,
|
|
250
|
-
project_key: result.projectKey,
|
|
251
|
-
message: `Initialized as ${result.agentName}`,
|
|
252
|
-
},
|
|
253
|
-
null,
|
|
254
|
-
2,
|
|
255
|
-
);
|
|
256
|
-
} catch (error) {
|
|
257
|
-
return JSON.stringify(
|
|
258
|
-
{
|
|
259
|
-
error: `Failed to initialize: ${error instanceof Error ? error.message : String(error)}`,
|
|
260
|
-
},
|
|
261
|
-
null,
|
|
262
|
-
2,
|
|
263
|
-
);
|
|
264
|
-
}
|
|
265
|
-
},
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
/**
|
|
269
|
-
* Send message to other agents
|
|
270
|
-
*/
|
|
271
|
-
export const swarmmail_send = tool({
|
|
272
|
-
description: "Send message to other swarm agents",
|
|
273
|
-
args: {
|
|
274
|
-
to: tool.schema
|
|
275
|
-
.array(tool.schema.string())
|
|
276
|
-
.describe("List of recipient agent names"),
|
|
277
|
-
subject: tool.schema.string().describe("Message subject"),
|
|
278
|
-
body: tool.schema.string().describe("Message body"),
|
|
279
|
-
thread_id: tool.schema
|
|
280
|
-
.string()
|
|
281
|
-
.optional()
|
|
282
|
-
.describe("Thread ID for conversation tracking"),
|
|
283
|
-
importance: tool.schema
|
|
284
|
-
.enum(["low", "normal", "high", "urgent"])
|
|
285
|
-
.optional()
|
|
286
|
-
.describe("Message importance level"),
|
|
287
|
-
ack_required: tool.schema
|
|
288
|
-
.boolean()
|
|
289
|
-
.optional()
|
|
290
|
-
.describe("Whether acknowledgement is required"),
|
|
291
|
-
},
|
|
292
|
-
async execute(args: SendArgs, ctx: ToolContext): Promise<string> {
|
|
293
|
-
const sessionID = ctx.sessionID || "default";
|
|
294
|
-
const state = loadSessionState(sessionID);
|
|
295
|
-
|
|
296
|
-
if (!state) {
|
|
297
|
-
return JSON.stringify(
|
|
298
|
-
{ error: "Session not initialized. Call swarmmail_init first." },
|
|
299
|
-
null,
|
|
300
|
-
2,
|
|
301
|
-
);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
try {
|
|
305
|
-
const result = await sendSwarmMessage({
|
|
306
|
-
projectPath: state.projectKey,
|
|
307
|
-
fromAgent: state.agentName,
|
|
308
|
-
toAgents: args.to,
|
|
309
|
-
subject: args.subject,
|
|
310
|
-
body: args.body,
|
|
311
|
-
threadId: args.thread_id,
|
|
312
|
-
importance: args.importance,
|
|
313
|
-
ackRequired: args.ack_required,
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
return JSON.stringify(
|
|
317
|
-
{
|
|
318
|
-
success: result.success,
|
|
319
|
-
message_id: result.messageId,
|
|
320
|
-
thread_id: result.threadId,
|
|
321
|
-
recipient_count: result.recipientCount,
|
|
322
|
-
},
|
|
323
|
-
null,
|
|
324
|
-
2,
|
|
325
|
-
);
|
|
326
|
-
} catch (error) {
|
|
327
|
-
return JSON.stringify(
|
|
328
|
-
{
|
|
329
|
-
error: `Failed to send message: ${error instanceof Error ? error.message : String(error)}`,
|
|
330
|
-
},
|
|
331
|
-
null,
|
|
332
|
-
2,
|
|
333
|
-
);
|
|
334
|
-
}
|
|
335
|
-
},
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
/**
|
|
339
|
-
* Fetch inbox (CONTEXT-SAFE: bodies excluded, limit 5)
|
|
340
|
-
*/
|
|
341
|
-
export const swarmmail_inbox = tool({
|
|
342
|
-
description:
|
|
343
|
-
"Fetch inbox (CONTEXT-SAFE: bodies excluded by default, max 5 messages). Use swarmmail_read_message for full body.",
|
|
344
|
-
args: {
|
|
345
|
-
limit: tool.schema
|
|
346
|
-
.number()
|
|
347
|
-
.max(MAX_INBOX_LIMIT)
|
|
348
|
-
.optional()
|
|
349
|
-
.describe(`Max messages to fetch (hard cap: ${MAX_INBOX_LIMIT})`),
|
|
350
|
-
urgent_only: tool.schema
|
|
351
|
-
.boolean()
|
|
352
|
-
.optional()
|
|
353
|
-
.describe("Only fetch urgent messages"),
|
|
354
|
-
},
|
|
355
|
-
async execute(args: InboxArgs, ctx: ToolContext): Promise<string> {
|
|
356
|
-
const sessionID = ctx.sessionID || "default";
|
|
357
|
-
const state = loadSessionState(sessionID);
|
|
358
|
-
|
|
359
|
-
if (!state) {
|
|
360
|
-
return JSON.stringify(
|
|
361
|
-
{ error: "Session not initialized. Call swarmmail_init first." },
|
|
362
|
-
null,
|
|
363
|
-
2,
|
|
364
|
-
);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
try {
|
|
368
|
-
const result = await getSwarmInbox({
|
|
369
|
-
projectPath: state.projectKey,
|
|
370
|
-
agentName: state.agentName,
|
|
371
|
-
limit: Math.min(args.limit || MAX_INBOX_LIMIT, MAX_INBOX_LIMIT),
|
|
372
|
-
urgentOnly: args.urgent_only,
|
|
373
|
-
includeBodies: false, // ALWAYS false for context preservation
|
|
374
|
-
});
|
|
375
|
-
|
|
376
|
-
return JSON.stringify(
|
|
377
|
-
{
|
|
378
|
-
messages: result.messages.map((m) => ({
|
|
379
|
-
id: m.id,
|
|
380
|
-
from: m.from_agent,
|
|
381
|
-
subject: m.subject,
|
|
382
|
-
thread_id: m.thread_id,
|
|
383
|
-
importance: m.importance,
|
|
384
|
-
timestamp: m.created_at,
|
|
385
|
-
})),
|
|
386
|
-
total: result.total,
|
|
387
|
-
note: "Use swarmmail_read_message to fetch full body",
|
|
388
|
-
},
|
|
389
|
-
null,
|
|
390
|
-
2,
|
|
391
|
-
);
|
|
392
|
-
} catch (error) {
|
|
393
|
-
return JSON.stringify(
|
|
394
|
-
{
|
|
395
|
-
error: `Failed to fetch inbox: ${error instanceof Error ? error.message : String(error)}`,
|
|
396
|
-
},
|
|
397
|
-
null,
|
|
398
|
-
2,
|
|
399
|
-
);
|
|
400
|
-
}
|
|
401
|
-
},
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
/**
|
|
405
|
-
* Fetch ONE message body by ID
|
|
406
|
-
*/
|
|
407
|
-
export const swarmmail_read_message = tool({
|
|
408
|
-
description:
|
|
409
|
-
"Fetch ONE message body by ID. Use for reading full message content.",
|
|
410
|
-
args: {
|
|
411
|
-
message_id: tool.schema.number().describe("Message ID to read"),
|
|
412
|
-
},
|
|
413
|
-
async execute(args: ReadMessageArgs, ctx: ToolContext): Promise<string> {
|
|
414
|
-
const sessionID = ctx.sessionID || "default";
|
|
415
|
-
const state = loadSessionState(sessionID);
|
|
416
|
-
|
|
417
|
-
if (!state) {
|
|
418
|
-
return JSON.stringify(
|
|
419
|
-
{ error: "Session not initialized. Call swarmmail_init first." },
|
|
420
|
-
null,
|
|
421
|
-
2,
|
|
422
|
-
);
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
try {
|
|
426
|
-
const message = await readSwarmMessage({
|
|
427
|
-
projectPath: state.projectKey,
|
|
428
|
-
messageId: args.message_id,
|
|
429
|
-
agentName: state.agentName,
|
|
430
|
-
markAsRead: true,
|
|
431
|
-
});
|
|
432
|
-
|
|
433
|
-
if (!message) {
|
|
434
|
-
return JSON.stringify(
|
|
435
|
-
{ error: `Message ${args.message_id} not found` },
|
|
436
|
-
null,
|
|
437
|
-
2,
|
|
438
|
-
);
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
return JSON.stringify(
|
|
442
|
-
{
|
|
443
|
-
id: message.id,
|
|
444
|
-
from: message.from_agent,
|
|
445
|
-
subject: message.subject,
|
|
446
|
-
body: message.body,
|
|
447
|
-
thread_id: message.thread_id,
|
|
448
|
-
importance: message.importance,
|
|
449
|
-
timestamp: message.created_at,
|
|
450
|
-
},
|
|
451
|
-
null,
|
|
452
|
-
2,
|
|
453
|
-
);
|
|
454
|
-
} catch (error) {
|
|
455
|
-
return JSON.stringify(
|
|
456
|
-
{
|
|
457
|
-
error: `Failed to read message: ${error instanceof Error ? error.message : String(error)}`,
|
|
458
|
-
},
|
|
459
|
-
null,
|
|
460
|
-
2,
|
|
461
|
-
);
|
|
462
|
-
}
|
|
463
|
-
},
|
|
464
|
-
});
|
|
465
|
-
|
|
466
|
-
/**
|
|
467
|
-
* Reserve file paths for exclusive editing
|
|
468
|
-
*/
|
|
469
|
-
export const swarmmail_reserve = tool({
|
|
470
|
-
description:
|
|
471
|
-
"Reserve file paths for exclusive editing. Prevents conflicts with other agents.",
|
|
472
|
-
args: {
|
|
473
|
-
paths: tool.schema
|
|
474
|
-
.array(tool.schema.string())
|
|
475
|
-
.describe("File paths or glob patterns to reserve"),
|
|
476
|
-
reason: tool.schema
|
|
477
|
-
.string()
|
|
478
|
-
.optional()
|
|
479
|
-
.describe("Reason for reservation (e.g., bead ID)"),
|
|
480
|
-
exclusive: tool.schema
|
|
481
|
-
.boolean()
|
|
482
|
-
.optional()
|
|
483
|
-
.describe("Whether reservation is exclusive (default: true)"),
|
|
484
|
-
ttl_seconds: tool.schema
|
|
485
|
-
.number()
|
|
486
|
-
.optional()
|
|
487
|
-
.describe("Time-to-live in seconds (default: 3600)"),
|
|
488
|
-
},
|
|
489
|
-
async execute(args: ReserveArgs, ctx: ToolContext): Promise<string> {
|
|
490
|
-
const sessionID = ctx.sessionID || "default";
|
|
491
|
-
const state = loadSessionState(sessionID);
|
|
492
|
-
|
|
493
|
-
if (!state) {
|
|
494
|
-
return JSON.stringify(
|
|
495
|
-
{ error: "Session not initialized. Call swarmmail_init first." },
|
|
496
|
-
null,
|
|
497
|
-
2,
|
|
498
|
-
);
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
try {
|
|
502
|
-
const result = await reserveSwarmFiles({
|
|
503
|
-
projectPath: state.projectKey,
|
|
504
|
-
agentName: state.agentName,
|
|
505
|
-
paths: args.paths,
|
|
506
|
-
reason: args.reason,
|
|
507
|
-
exclusive: args.exclusive ?? true,
|
|
508
|
-
ttlSeconds: args.ttl_seconds,
|
|
509
|
-
});
|
|
510
|
-
|
|
511
|
-
// Track reservations in session state
|
|
512
|
-
if (result.granted.length > 0) {
|
|
513
|
-
state.reservations.push(...result.granted.map((r) => r.id));
|
|
514
|
-
saveSessionState(sessionID, state);
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
if (result.conflicts.length > 0) {
|
|
518
|
-
return JSON.stringify(
|
|
519
|
-
{
|
|
520
|
-
granted: result.granted,
|
|
521
|
-
conflicts: result.conflicts,
|
|
522
|
-
warning: `${result.conflicts.length} path(s) already reserved by other agents`,
|
|
523
|
-
},
|
|
524
|
-
null,
|
|
525
|
-
2,
|
|
526
|
-
);
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
return JSON.stringify(
|
|
530
|
-
{
|
|
531
|
-
granted: result.granted,
|
|
532
|
-
message: `Reserved ${result.granted.length} path(s)`,
|
|
533
|
-
},
|
|
534
|
-
null,
|
|
535
|
-
2,
|
|
536
|
-
);
|
|
537
|
-
} catch (error) {
|
|
538
|
-
return JSON.stringify(
|
|
539
|
-
{
|
|
540
|
-
error: `Failed to reserve files: ${error instanceof Error ? error.message : String(error)}`,
|
|
541
|
-
},
|
|
542
|
-
null,
|
|
543
|
-
2,
|
|
544
|
-
);
|
|
545
|
-
}
|
|
546
|
-
},
|
|
547
|
-
});
|
|
548
|
-
|
|
549
|
-
/**
|
|
550
|
-
* Release file reservations
|
|
551
|
-
*/
|
|
552
|
-
export const swarmmail_release = tool({
|
|
553
|
-
description: "Release file reservations. Call when done editing files.",
|
|
554
|
-
args: {
|
|
555
|
-
paths: tool.schema
|
|
556
|
-
.array(tool.schema.string())
|
|
557
|
-
.optional()
|
|
558
|
-
.describe("Specific paths to release (releases all if omitted)"),
|
|
559
|
-
reservation_ids: tool.schema
|
|
560
|
-
.array(tool.schema.number())
|
|
561
|
-
.optional()
|
|
562
|
-
.describe("Specific reservation IDs to release"),
|
|
563
|
-
},
|
|
564
|
-
async execute(args: ReleaseArgs, ctx: ToolContext): Promise<string> {
|
|
565
|
-
const sessionID = ctx.sessionID || "default";
|
|
566
|
-
const state = loadSessionState(sessionID);
|
|
567
|
-
|
|
568
|
-
if (!state) {
|
|
569
|
-
return JSON.stringify(
|
|
570
|
-
{ error: "Session not initialized. Call swarmmail_init first." },
|
|
571
|
-
null,
|
|
572
|
-
2,
|
|
573
|
-
);
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
try {
|
|
577
|
-
// Get current reservations to find which IDs correspond to paths
|
|
578
|
-
const currentReservations = await getActiveReservations(
|
|
579
|
-
state.projectKey,
|
|
580
|
-
state.projectKey,
|
|
581
|
-
state.agentName,
|
|
582
|
-
);
|
|
583
|
-
|
|
584
|
-
const result = await releaseSwarmFiles({
|
|
585
|
-
projectPath: state.projectKey,
|
|
586
|
-
agentName: state.agentName,
|
|
587
|
-
paths: args.paths,
|
|
588
|
-
reservationIds: args.reservation_ids,
|
|
589
|
-
});
|
|
590
|
-
|
|
591
|
-
// Clear tracked reservations
|
|
592
|
-
if (!args.paths && !args.reservation_ids) {
|
|
593
|
-
state.reservations = [];
|
|
594
|
-
} else if (args.reservation_ids) {
|
|
595
|
-
state.reservations = state.reservations.filter(
|
|
596
|
-
(id) => !args.reservation_ids!.includes(id),
|
|
597
|
-
);
|
|
598
|
-
} else if (args.paths) {
|
|
599
|
-
// When releasing by paths, find the reservation IDs that match those paths
|
|
600
|
-
const releasedIds = currentReservations
|
|
601
|
-
.filter((r: { path_pattern: string }) =>
|
|
602
|
-
args.paths!.includes(r.path_pattern),
|
|
603
|
-
)
|
|
604
|
-
.map((r: { id: number }) => r.id);
|
|
605
|
-
state.reservations = state.reservations.filter(
|
|
606
|
-
(id: number) => !releasedIds.includes(id),
|
|
607
|
-
);
|
|
608
|
-
}
|
|
609
|
-
saveSessionState(sessionID, state);
|
|
610
|
-
|
|
611
|
-
return JSON.stringify(
|
|
612
|
-
{
|
|
613
|
-
released: result.released,
|
|
614
|
-
released_at: result.releasedAt,
|
|
615
|
-
},
|
|
616
|
-
null,
|
|
617
|
-
2,
|
|
618
|
-
);
|
|
619
|
-
} catch (error) {
|
|
620
|
-
return JSON.stringify(
|
|
621
|
-
{
|
|
622
|
-
error: `Failed to release files: ${error instanceof Error ? error.message : String(error)}`,
|
|
623
|
-
},
|
|
624
|
-
null,
|
|
625
|
-
2,
|
|
626
|
-
);
|
|
627
|
-
}
|
|
628
|
-
},
|
|
629
|
-
});
|
|
630
|
-
|
|
631
|
-
/**
|
|
632
|
-
* Acknowledge a message
|
|
633
|
-
*/
|
|
634
|
-
export const swarmmail_ack = tool({
|
|
635
|
-
description:
|
|
636
|
-
"Acknowledge a message (for messages that require acknowledgement)",
|
|
637
|
-
args: {
|
|
638
|
-
message_id: tool.schema.number().describe("Message ID to acknowledge"),
|
|
639
|
-
},
|
|
640
|
-
async execute(args: AckArgs, ctx: ToolContext): Promise<string> {
|
|
641
|
-
const sessionID = ctx.sessionID || "default";
|
|
642
|
-
const state = loadSessionState(sessionID);
|
|
643
|
-
|
|
644
|
-
if (!state) {
|
|
645
|
-
return JSON.stringify(
|
|
646
|
-
{ error: "Session not initialized. Call swarmmail_init first." },
|
|
647
|
-
null,
|
|
648
|
-
2,
|
|
649
|
-
);
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
try {
|
|
653
|
-
const result = await acknowledgeSwarmMessage({
|
|
654
|
-
projectPath: state.projectKey,
|
|
655
|
-
messageId: args.message_id,
|
|
656
|
-
agentName: state.agentName,
|
|
657
|
-
});
|
|
658
|
-
|
|
659
|
-
return JSON.stringify(
|
|
660
|
-
{
|
|
661
|
-
acknowledged: result.acknowledged,
|
|
662
|
-
acknowledged_at: result.acknowledgedAt,
|
|
663
|
-
},
|
|
664
|
-
null,
|
|
665
|
-
2,
|
|
666
|
-
);
|
|
667
|
-
} catch (error) {
|
|
668
|
-
return JSON.stringify(
|
|
669
|
-
{
|
|
670
|
-
error: `Failed to acknowledge message: ${error instanceof Error ? error.message : String(error)}`,
|
|
671
|
-
},
|
|
672
|
-
null,
|
|
673
|
-
2,
|
|
674
|
-
);
|
|
675
|
-
}
|
|
676
|
-
},
|
|
677
|
-
});
|
|
678
|
-
|
|
679
|
-
/**
|
|
680
|
-
* Check if Swarm Mail is healthy
|
|
681
|
-
*/
|
|
682
|
-
export const swarmmail_health = tool({
|
|
683
|
-
description: "Check if Swarm Mail embedded store is healthy",
|
|
684
|
-
args: {},
|
|
685
|
-
async execute(
|
|
686
|
-
_args: Record<string, never>,
|
|
687
|
-
ctx: ToolContext,
|
|
688
|
-
): Promise<string> {
|
|
689
|
-
const sessionID = ctx.sessionID || "default";
|
|
690
|
-
const state = loadSessionState(sessionID);
|
|
691
|
-
// For health check, undefined is OK - database layer uses global fallback
|
|
692
|
-
const projectPath = state?.projectKey || getSwarmMailProjectDirectory();
|
|
693
|
-
|
|
694
|
-
try {
|
|
695
|
-
const result = await checkSwarmHealth(projectPath);
|
|
696
|
-
|
|
697
|
-
return JSON.stringify(
|
|
698
|
-
{
|
|
699
|
-
healthy: result.healthy,
|
|
700
|
-
database: result.database,
|
|
701
|
-
stats: result.stats,
|
|
702
|
-
session: state
|
|
703
|
-
? {
|
|
704
|
-
agent_name: state.agentName,
|
|
705
|
-
project_key: state.projectKey,
|
|
706
|
-
reservations: state.reservations.length,
|
|
707
|
-
}
|
|
708
|
-
: null,
|
|
709
|
-
},
|
|
710
|
-
null,
|
|
711
|
-
2,
|
|
712
|
-
);
|
|
713
|
-
} catch (error) {
|
|
714
|
-
return JSON.stringify(
|
|
715
|
-
{
|
|
716
|
-
healthy: false,
|
|
717
|
-
error: `Health check failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
718
|
-
},
|
|
719
|
-
null,
|
|
720
|
-
2,
|
|
721
|
-
);
|
|
722
|
-
}
|
|
723
|
-
},
|
|
724
|
-
});
|
|
725
|
-
|
|
726
|
-
// ============================================================================
|
|
727
|
-
// Exports
|
|
728
|
-
// ============================================================================
|
|
729
|
-
|
|
730
|
-
export const swarmMailTools = {
|
|
731
|
-
swarmmail_init: swarmmail_init,
|
|
732
|
-
swarmmail_send: swarmmail_send,
|
|
733
|
-
swarmmail_inbox: swarmmail_inbox,
|
|
734
|
-
swarmmail_read_message: swarmmail_read_message,
|
|
735
|
-
swarmmail_reserve: swarmmail_reserve,
|
|
736
|
-
swarmmail_release: swarmmail_release,
|
|
737
|
-
swarmmail_ack: swarmmail_ack,
|
|
738
|
-
swarmmail_health: swarmmail_health,
|
|
739
|
-
};
|