@veedubin/boomerang-v3 0.3.3 → 0.4.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/.opencode/agents/boomerang-agent-builder.md +0 -1
- package/.opencode/agents/boomerang-architect.md +1 -2
- package/.opencode/agents/boomerang-coder.md +1 -2
- package/.opencode/agents/boomerang-explorer.md +2 -3
- package/.opencode/agents/boomerang-git.md +0 -1
- package/.opencode/agents/boomerang-handoff.md +0 -1
- package/.opencode/agents/boomerang-init.md +0 -1
- package/.opencode/agents/boomerang-linter.md +0 -1
- package/.opencode/agents/boomerang-release.md +0 -1
- package/.opencode/agents/boomerang-scraper.md +0 -1
- package/.opencode/agents/boomerang-tester.md +0 -1
- package/.opencode/agents/boomerang-writer.md +2 -3
- package/.opencode/agents/boomerang.md +2 -3
- package/.opencode/agents/mcp-specialist.md +0 -1
- package/.opencode/agents/researcher.md +0 -1
- package/AGENTS.md +17 -7
- package/dist/context-buffer.js +290 -0
- package/dist/index.js +5 -1
- package/dist/memini-client/index.js +1 -1
- package/dist/memory/contradictions.js +1 -1
- package/dist/memory/index.js +1 -1
- package/dist/memory/trust.js +1 -1
- package/dist/orchestrator.js +78 -5
- package/dist/telemetry-client.js +98 -0
- package/dist/types.js +4 -1
- package/package.json +1 -1
- package/scripts/install-boomerang.js +208 -100
|
@@ -18,7 +18,6 @@ permission:
|
|
|
18
18
|
tool:
|
|
19
19
|
"memini-ai-dev_*": allow
|
|
20
20
|
"searxng_*": allow
|
|
21
|
-
"sequential-thinking_*": allow
|
|
22
21
|
"markitdown_*": allow
|
|
23
22
|
"github-mcp_*": allow
|
|
24
23
|
"playwright_*": allow
|
|
@@ -49,7 +48,7 @@ You are the **Boomerang Architect** - the authority on design decisions, archite
|
|
|
49
48
|
## MANDATORY MEMORY PROTOCOL
|
|
50
49
|
|
|
51
50
|
1. **Query memini-ai FIRST** - `memini-ai-dev_query_memories` for previous decisions
|
|
52
|
-
2. **Use
|
|
51
|
+
2. **Use thought chains** - `memini-ai-dev_add_thought` for complex analysis
|
|
53
52
|
3. **Query knowledge graph** - `memini-ai-dev_query_kg` for entity relationships
|
|
54
53
|
4. **Save when complete** - `memini-ai-dev_add_memory` with key decisions
|
|
55
54
|
|
|
@@ -18,7 +18,6 @@ permission:
|
|
|
18
18
|
tool:
|
|
19
19
|
"memini-ai-dev_*": allow
|
|
20
20
|
"searxng_*": allow
|
|
21
|
-
"sequential-thinking_*": allow
|
|
22
21
|
"markitdown_*": allow
|
|
23
22
|
"github-mcp_*": allow
|
|
24
23
|
"playwright_*": allow
|
|
@@ -49,7 +48,7 @@ Implement features, fix bugs, and write tests efficiently using the Context Pack
|
|
|
49
48
|
## MANDATORY MEMORY PROTOCOL
|
|
50
49
|
|
|
51
50
|
1. **Query memini-ai FIRST** - Call `memini-ai-dev_query_memories` before doing ANY work
|
|
52
|
-
2. **Use
|
|
51
|
+
2. **Use thought chains** - Call `memini-ai-dev_add_thought` for complex tasks
|
|
53
52
|
3. **Save when complete** - Call `memini-ai-dev_add_memory` with a summary of your work
|
|
54
53
|
|
|
55
54
|
## Context Requirements
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Boomerang Explorer v3 - Fast file finding using devstral-2:cloud (Ollama Cloud) with memini-ai semantic search.
|
|
2
|
+
description: Boomerang Explorer v3 - Fast file finding using devstral-2:123b-cloud (Ollama Cloud) with memini-ai semantic search.
|
|
3
3
|
mode: subagent
|
|
4
|
-
model: ollama-cloud/devstral-2:cloud
|
|
4
|
+
model: ollama-cloud/devstral-2:123b-cloud
|
|
5
5
|
steps: 30
|
|
6
6
|
permission:
|
|
7
7
|
read:
|
|
@@ -18,7 +18,6 @@ permission:
|
|
|
18
18
|
tool:
|
|
19
19
|
"memini-ai-dev_*": allow
|
|
20
20
|
"searxng_*": allow
|
|
21
|
-
"sequential-thinking_*": allow
|
|
22
21
|
"markitdown_*": allow
|
|
23
22
|
"github-mcp_*": allow
|
|
24
23
|
"playwright_*": allow
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Boomerang Writer v3 - Documentation specialist using gemma4:cloud (Ollama Cloud) with memini-ai for context.
|
|
2
|
+
description: Boomerang Writer v3 - Documentation specialist using gemma4:31b-cloud (Ollama Cloud) with memini-ai for context.
|
|
3
3
|
mode: subagent
|
|
4
|
-
model: ollama-cloud/gemma4:cloud
|
|
4
|
+
model: ollama-cloud/gemma4:31b-cloud
|
|
5
5
|
steps: 40
|
|
6
6
|
permission:
|
|
7
7
|
read:
|
|
@@ -18,7 +18,6 @@ permission:
|
|
|
18
18
|
tool:
|
|
19
19
|
"memini-ai-dev_*": allow
|
|
20
20
|
"searxng_*": allow
|
|
21
|
-
"sequential-thinking_*": allow
|
|
22
21
|
"markitdown_*": allow
|
|
23
22
|
"github-mcp_*": allow
|
|
24
23
|
"playwright_*": allow
|
|
@@ -18,7 +18,6 @@ permission:
|
|
|
18
18
|
tool:
|
|
19
19
|
"memini-ai-dev_*": allow
|
|
20
20
|
"searxng_*": allow
|
|
21
|
-
"sequential-thinking_*": allow
|
|
22
21
|
"markitdown_*": allow
|
|
23
22
|
"github-mcp_*": allow
|
|
24
23
|
"playwright_*": allow
|
|
@@ -72,8 +71,8 @@ You are the **Boomerang v3 Orchestrator** - the central coordinator using memini
|
|
|
72
71
|
Immediately call `memini-ai-dev_query_memories` with the user's request.
|
|
73
72
|
Do not write any text before calling this tool.
|
|
74
73
|
|
|
75
|
-
### STEP 2: Use
|
|
76
|
-
Immediately call `
|
|
74
|
+
### STEP 2: Use thought chains (MANDATORY SECOND ACTION)
|
|
75
|
+
Immediately call `memini-ai-dev_add_thought` with your analysis. Note: This creates a `thinkingChainId` that must be passed to sub-agents in their Context Package.
|
|
77
76
|
|
|
78
77
|
### STEP 3: Plan (MANDATORY unless explicitly waived)
|
|
79
78
|
Create an implementation plan UNLESS user says "skip planning", "just do it", "/boomerang-handoff", "do a handoff", or "no plan needed".
|
package/AGENTS.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Boomerang Agent Roster
|
|
2
2
|
|
|
3
|
+
## ⚡ CRITICAL: memini-ai Memory Protocol (MUST FOLLOW)
|
|
4
|
+
|
|
5
|
+
All agents **MUST** interact with memini-ai at every step:
|
|
6
|
+
1. **Query FIRST** — Call `memini-ai-dev_query_memories` before starting work
|
|
7
|
+
2. **Save DURING** — Call `memini-ai-dev_add_memory` after every meaningful decision
|
|
8
|
+
3. **Preserve CONTEXT** — Save important context; query it back when continuing work
|
|
9
|
+
|
|
10
|
+
Failure to use memini-ai causes context loss, duplicate work, and wasted tokens.
|
|
11
|
+
|
|
12
|
+
|
|
3
13
|
## Core Agents
|
|
4
14
|
|
|
5
15
|
> **Note**: Models are configurable. Use `install-agents.js --primary=<model> --secondary=<model>` to customize.
|
|
@@ -9,11 +19,11 @@
|
|
|
9
19
|
| **boomerang** | boomerang-orchestrator | kimi-k2.6:cloud | Specifically built for swarm-based task orchestration and proactive autonomous delegation. |
|
|
10
20
|
| **boomerang-coder** | boomerang-coder | glm-5.1:cloud | Flagship for agentic engineering; achieves SOTA on SWE-Bench Pro for complex, multi-file generation. |
|
|
11
21
|
| **boomerang-architect** | boomerang-architect | deepseek-v4-pro:cloud | Offers frontier reasoning with dedicated "thinking modes" for analyzing complex architectural trade-offs. |
|
|
12
|
-
| **boomerang-explorer** | boomerang-explorer | devstral-2:cloud | Explicitly designed to navigate codebases, trace dependencies, and map repository structures. |
|
|
22
|
+
| **boomerang-explorer** | boomerang-explorer | devstral-2:123b-cloud | Explicitly designed to navigate codebases, trace dependencies, and map repository structures. |
|
|
13
23
|
| **boomerang-tester** | boomerang-tester | deepseek-v4-flash:cloud | Massive 1M context window for ingesting deep error logs and codebase context quickly and efficiently. |
|
|
14
24
|
| **boomerang-linter** | boomerang-linter | qwen3-coder-next:cloud | Highly optimized for agentic coding workflows; blazing fast for syntax formatting and style checks. |
|
|
15
25
|
| **boomerang-git** | boomerang-git | minimax-m2.7:cloud | Fast and highly reliable for standard professional productivity and executing structured terminal commands. |
|
|
16
|
-
| **boomerang-writer** | boomerang-writer | gemma4:cloud | Frontier-level instruction following; excels at translating technical logic into clean, readable Markdown. |
|
|
26
|
+
| **boomerang-writer** | boomerang-writer | gemma4:31b-cloud | Frontier-level instruction following; excels at translating technical logic into clean, readable Markdown. |
|
|
17
27
|
| **boomerang-scraper** | boomerang-scraper | qwen3.5:cloud | Strong, lightweight generalist with excellent tool-use capabilities for reliable data extraction. |
|
|
18
28
|
| **boomerang-release** | boomerang-release | devstral-small-2:cloud | Fast 24B model perfect for targeted automation tasks like bumping versions and summarizing changelogs. |
|
|
19
29
|
| **boomerang-agent-builder** | boomerang-agent-builder | glm-5.1:cloud | Excels at long-horizon tasks and ambiguous problems; ideal for writing and optimizing new agent logic. |
|
|
@@ -67,12 +77,12 @@ The orchestrator MUST delegate based on these rules. No exceptions.
|
|
|
67
77
|
|-----------|------------------|-------|-------------------|
|
|
68
78
|
| Complex planning / orchestration | `boomerang` | kimi-k2.6:cloud | `general` |
|
|
69
79
|
| Architecture / design decisions | `boomerang-architect` | deepseek-v4-pro:cloud | `general`, `boomerang-coder` |
|
|
70
|
-
| Documentation writing | `boomerang-writer` | gemma4:cloud | `general` |
|
|
80
|
+
| Documentation writing | `boomerang-writer` | gemma4:31b-cloud | `general` |
|
|
71
81
|
| Session initialization | `boomerang-init` | kimi-k2.6:cloud | Everything else |
|
|
72
82
|
| Session wrap-up / handoff | `boomerang-handoff` | kimi-k2.6:cloud | Everything else |
|
|
73
83
|
| Skill/agent creation | `boomerang-agent-builder` | glm-5.1:cloud | `general` |
|
|
74
84
|
| Fast code generation / bug fixes | `boomerang-coder` | glm-5.1:cloud | `general`, `boomerang-explorer` |
|
|
75
|
-
| Code exploration / finding files | `boomerang-explorer` | devstral-2:cloud | Everything else |
|
|
85
|
+
| Code exploration / finding files | `boomerang-explorer` | devstral-2:123b-cloud | Everything else |
|
|
76
86
|
| Writing / running tests | `boomerang-tester` | deepseek-v4-flash:cloud | `general`, `boomerang-coder` |
|
|
77
87
|
| Linting / formatting | `boomerang-linter` | qwen3-coder-next:cloud | Everything else |
|
|
78
88
|
| Git operations | `boomerang-git` | minimax-m2.7:cloud | Everything else |
|
|
@@ -129,7 +139,7 @@ All agents **MUST** follow the **8-Step Boomerang Protocol** — enforcement is
|
|
|
129
139
|
### 8-Step Protocol (MANDATORY)
|
|
130
140
|
|
|
131
141
|
1. **Query Memory** — `memini-ai-dev_query_memories` FIRST
|
|
132
|
-
2. **Think** — `
|
|
142
|
+
2. **Think** — `memini-ai-dev_add_thought` for complex tasks
|
|
133
143
|
3. **Plan** — Create/refine implementation plan (MANDATORY unless user explicitly waives)
|
|
134
144
|
4. **Delegate** — OpenCode executes selected agent with Context Package
|
|
135
145
|
5. **Git Check** — Verify working tree state before code changes
|
|
@@ -199,7 +209,7 @@ Boomerang v3 uses **memini-ai** for memory — a Python-based semantic memory se
|
|
|
199
209
|
|
|
200
210
|
All agents SHOULD:
|
|
201
211
|
1. **Query memory FIRST** — `memini-ai-dev_query_memories` before work
|
|
202
|
-
2. **Use
|
|
212
|
+
2. **Use thought chains** — For complex tasks
|
|
203
213
|
3. **Save results** — `memini-ai-dev_add_memory` when complete
|
|
204
214
|
|
|
205
215
|
### Trust-Weighted Memory
|
|
@@ -320,7 +330,7 @@ IDLE → MEMORY_QUERY → SEQUENTIAL_THINK → PLAN → DELEGATE → GIT_CHECK
|
|
|
320
330
|
### 8-Step Mandatory Protocol
|
|
321
331
|
|
|
322
332
|
1. **MEMORY_QUERY** — MUST call `memini-ai-dev_query_memories` first
|
|
323
|
-
2. **SEQUENTIAL_THINK** — MUST call `
|
|
333
|
+
2. **SEQUENTIAL_THINK** — MUST call `memini-ai-dev_add_thought` for complex tasks
|
|
324
334
|
3. **PLAN** — MUST create plan or delegate to architect for build tasks
|
|
325
335
|
4. **DELEGATE** — OpenCode handles agent execution
|
|
326
336
|
5. **GIT_CHECK** — MUST verify working tree state before code changes
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Buffer Middleware — Transparent orchestrator integration
|
|
3
|
+
* for capturing and restoring agent invocation context via memini-ai.
|
|
4
|
+
*
|
|
5
|
+
* This middleware wraps around agent dispatch in the orchestrator,
|
|
6
|
+
* saving invocation context (output, tool calls, decisions, errors)
|
|
7
|
+
* as segmented memories in memini-ai. It also provides before/after
|
|
8
|
+
* hooks for telemetry and context injection.
|
|
9
|
+
*
|
|
10
|
+
* Design:
|
|
11
|
+
* - Segmenting: ~4 chars per token. Split at ~50K tokens = ~200K chars.
|
|
12
|
+
* - Memory metadata includes session_id, agent_name, segment_index.
|
|
13
|
+
* - All segments of an invocation are linked via DERIVED_FROM relationships.
|
|
14
|
+
* - The middleware is OPTIONAL — if not configured, the orchestrator
|
|
15
|
+
* operates identically to before this integration.
|
|
16
|
+
*/
|
|
17
|
+
import { TelemetryClient } from './telemetry-client.js';
|
|
18
|
+
/** Estimated ratio: 1 token ≈ 4 characters. */
|
|
19
|
+
const CHARS_PER_TOKEN = 4;
|
|
20
|
+
/**
|
|
21
|
+
* Context buffer middleware that works transparently with the existing
|
|
22
|
+
* orchestrator to capture, segment, and restore agent invocation context.
|
|
23
|
+
*/
|
|
24
|
+
export class ContextBufferMiddleware {
|
|
25
|
+
sessionId;
|
|
26
|
+
config;
|
|
27
|
+
memini;
|
|
28
|
+
telemetry;
|
|
29
|
+
segmentIds = [];
|
|
30
|
+
constructor(sessionId, config) {
|
|
31
|
+
this.sessionId = sessionId;
|
|
32
|
+
this.config = config;
|
|
33
|
+
this.memini = config.meminiClient;
|
|
34
|
+
this.telemetry = new TelemetryClient(config.telemetryEndpoint);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Called BEFORE an agent runs.
|
|
38
|
+
*
|
|
39
|
+
* Queries memini-ai for relevant context based on the task description,
|
|
40
|
+
* injects it into the agent's context package, and emits a start event
|
|
41
|
+
* to telemetry.
|
|
42
|
+
*
|
|
43
|
+
* @returns A context string to inject into the agent invocation, or
|
|
44
|
+
* an empty string if no relevant context is found.
|
|
45
|
+
*/
|
|
46
|
+
async beforeInvocation(agentName, task, context) {
|
|
47
|
+
const _startTime = Date.now();
|
|
48
|
+
await this.ensureSessionRow(task);
|
|
49
|
+
await this.emitTelemetry({
|
|
50
|
+
event_type: 'invocation_start',
|
|
51
|
+
session_id: this.sessionId,
|
|
52
|
+
agent_name: agentName,
|
|
53
|
+
metadata: { task, contextProvided: context !== undefined },
|
|
54
|
+
});
|
|
55
|
+
let injectedContext = '';
|
|
56
|
+
try {
|
|
57
|
+
const results = await this.memini.search(task, {
|
|
58
|
+
topK: 5,
|
|
59
|
+
strategy: 'TIERED',
|
|
60
|
+
});
|
|
61
|
+
if (results.length > 0) {
|
|
62
|
+
const contextParts = results
|
|
63
|
+
.slice(0, 5)
|
|
64
|
+
.map((r) => `[trust=${r.entry.trustScore?.toFixed(2) ?? '0.50'}] ${r.entry.text}`)
|
|
65
|
+
.join('\n\n---\n\n');
|
|
66
|
+
injectedContext = `\n## Relevant Context from Previous Sessions\n\n${contextParts}\n`;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch (err) {
|
|
70
|
+
console.warn('[ContextBuffer] beforeInvocation search failed:', err);
|
|
71
|
+
}
|
|
72
|
+
return injectedContext;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Called AFTER an agent completes (or fails).
|
|
76
|
+
*
|
|
77
|
+
* Captures the full invocation payload, segments if needed, saves
|
|
78
|
+
* each segment to memini-ai, links them via relationships, and emits
|
|
79
|
+
* an end event to telemetry.
|
|
80
|
+
*
|
|
81
|
+
* @returns Array of memory IDs for the saved segments.
|
|
82
|
+
*/
|
|
83
|
+
async afterInvocation(agentName, result, durationMs) {
|
|
84
|
+
const success = 'success' in result ? result.success : true;
|
|
85
|
+
const error = 'success' in result && !result.success ? result.error : undefined;
|
|
86
|
+
await this.emitTelemetry({
|
|
87
|
+
event_type: success ? 'invocation_end' : 'invocation_error',
|
|
88
|
+
session_id: this.sessionId,
|
|
89
|
+
agent_name: agentName,
|
|
90
|
+
duration_ms: durationMs,
|
|
91
|
+
success,
|
|
92
|
+
error_message: error,
|
|
93
|
+
});
|
|
94
|
+
const payload = this.buildPayload(agentName, result, durationMs);
|
|
95
|
+
const memoryIds = await this.saveContextSegments(agentName, payload);
|
|
96
|
+
this.segmentIds = memoryIds;
|
|
97
|
+
// Link segments to each other via DERIVED_FROM relationships
|
|
98
|
+
await this.linkSegments(memoryIds);
|
|
99
|
+
return memoryIds;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Get the memory IDs saved during the current session.
|
|
103
|
+
*/
|
|
104
|
+
getSegmentIds() {
|
|
105
|
+
return [...this.segmentIds];
|
|
106
|
+
}
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
// Private helpers
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
/**
|
|
111
|
+
* Build an AgentContextPayload from a task result.
|
|
112
|
+
*/
|
|
113
|
+
buildPayload(agentName, result, durationMs) {
|
|
114
|
+
const now = Date.now();
|
|
115
|
+
if ('success' in result && !result.success) {
|
|
116
|
+
return {
|
|
117
|
+
output: '',
|
|
118
|
+
toolCalls: [],
|
|
119
|
+
filesModified: [],
|
|
120
|
+
decisions: [],
|
|
121
|
+
errors: [result.error],
|
|
122
|
+
startTime: now - durationMs,
|
|
123
|
+
endTime: now,
|
|
124
|
+
durationMs,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
const taskResult = result;
|
|
128
|
+
return {
|
|
129
|
+
output: taskResult.output ?? '',
|
|
130
|
+
toolCalls: taskResult.toolCalls ?? [],
|
|
131
|
+
filesModified: taskResult.filesModified ?? [],
|
|
132
|
+
decisions: taskResult.decisions ?? [],
|
|
133
|
+
errors: taskResult.errors ?? [],
|
|
134
|
+
startTime: now - durationMs,
|
|
135
|
+
endTime: now,
|
|
136
|
+
durationMs,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Save context segments to memini-ai.
|
|
141
|
+
* If the summary exceeds the segment threshold, it is split into
|
|
142
|
+
* multiple segments, each saved separately.
|
|
143
|
+
*/
|
|
144
|
+
async saveContextSegments(agentName, payload) {
|
|
145
|
+
const summary = this.generateContextSummary(agentName, payload);
|
|
146
|
+
const thresholdChars = this.config.segmentThreshold * CHARS_PER_TOKEN;
|
|
147
|
+
const segments = this.splitIfNeeded(summary, thresholdChars);
|
|
148
|
+
const memoryIds = [];
|
|
149
|
+
for (let i = 0; i < segments.length; i++) {
|
|
150
|
+
const segment = segments[i];
|
|
151
|
+
try {
|
|
152
|
+
const entry = await this.memini.addMemory({
|
|
153
|
+
text: segment,
|
|
154
|
+
sourceType: 'boomerang',
|
|
155
|
+
sourcePath: `context-buffer://${this.sessionId}/${agentName}`,
|
|
156
|
+
metadataJson: JSON.stringify({
|
|
157
|
+
type: 'context_segment',
|
|
158
|
+
session_id: this.sessionId,
|
|
159
|
+
agent_name: agentName,
|
|
160
|
+
segment_index: i,
|
|
161
|
+
total_segments: segments.length,
|
|
162
|
+
files_modified: payload.filesModified,
|
|
163
|
+
decisions: payload.decisions,
|
|
164
|
+
errors: payload.errors,
|
|
165
|
+
duration_ms: payload.durationMs,
|
|
166
|
+
success: payload.errors.length === 0,
|
|
167
|
+
}),
|
|
168
|
+
sessionId: this.sessionId,
|
|
169
|
+
});
|
|
170
|
+
memoryIds.push(entry.id);
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
console.warn(`[ContextBuffer] Failed to save segment ${i}/${segments.length}:`, err);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return memoryIds;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Link segments via DERIVED_FROM relationships in memini-ai.
|
|
180
|
+
* Each segment (except the first) derives from the previous one.
|
|
181
|
+
*/
|
|
182
|
+
async linkSegments(memoryIds) {
|
|
183
|
+
if (memoryIds.length < 2) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
for (let i = 1; i < memoryIds.length; i++) {
|
|
187
|
+
try {
|
|
188
|
+
await this.memini.createRelationship(memoryIds[i], memoryIds[i - 1], 'DERIVED_FROM', 0.9);
|
|
189
|
+
}
|
|
190
|
+
catch (err) {
|
|
191
|
+
console.warn(`[ContextBuffer] Failed to link segments ${i - 1} → ${i}:`, err);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Split content into segments if it exceeds the threshold.
|
|
197
|
+
* Tries to split on paragraph boundaries (double newlines).
|
|
198
|
+
*/
|
|
199
|
+
splitIfNeeded(content, thresholdChars) {
|
|
200
|
+
if (content.length <= thresholdChars) {
|
|
201
|
+
return [content];
|
|
202
|
+
}
|
|
203
|
+
const segments = [];
|
|
204
|
+
let remaining = content;
|
|
205
|
+
while (remaining.length > 0) {
|
|
206
|
+
if (remaining.length <= thresholdChars) {
|
|
207
|
+
segments.push(remaining);
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
// Find a paragraph boundary near the threshold
|
|
211
|
+
const searchStart = Math.max(0, thresholdChars - 1000);
|
|
212
|
+
const searchEnd = Math.min(remaining.length, thresholdChars + 1000);
|
|
213
|
+
const searchRegion = remaining.slice(searchStart, searchEnd);
|
|
214
|
+
const newlinePos = searchRegion.indexOf('\n\n');
|
|
215
|
+
if (newlinePos !== -1) {
|
|
216
|
+
const splitPos = searchStart + newlinePos + 2;
|
|
217
|
+
segments.push(remaining.slice(0, splitPos));
|
|
218
|
+
remaining = remaining.slice(splitPos);
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
// No good boundary; hard split at threshold
|
|
222
|
+
segments.push(remaining.slice(0, thresholdChars));
|
|
223
|
+
remaining = remaining.slice(thresholdChars);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return segments;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Generate a human-readable context summary from the agent payload.
|
|
230
|
+
*/
|
|
231
|
+
generateContextSummary(agentName, payload) {
|
|
232
|
+
const lines = [
|
|
233
|
+
`# Agent Invocation: ${agentName}`,
|
|
234
|
+
`Duration: ${payload.durationMs}ms`,
|
|
235
|
+
`Status: ${payload.errors.length === 0 ? 'SUCCESS' : 'FAILED'}`,
|
|
236
|
+
'',
|
|
237
|
+
];
|
|
238
|
+
if (payload.output) {
|
|
239
|
+
lines.push('## Output');
|
|
240
|
+
lines.push(payload.output);
|
|
241
|
+
lines.push('');
|
|
242
|
+
}
|
|
243
|
+
if (payload.filesModified.length > 0) {
|
|
244
|
+
lines.push('## Files Modified');
|
|
245
|
+
for (const f of payload.filesModified) {
|
|
246
|
+
lines.push(`- ${f}`);
|
|
247
|
+
}
|
|
248
|
+
lines.push('');
|
|
249
|
+
}
|
|
250
|
+
if (payload.decisions.length > 0) {
|
|
251
|
+
lines.push('## Decisions');
|
|
252
|
+
for (const d of payload.decisions) {
|
|
253
|
+
lines.push(`- ${d}`);
|
|
254
|
+
}
|
|
255
|
+
lines.push('');
|
|
256
|
+
}
|
|
257
|
+
if (payload.toolCalls.length > 0) {
|
|
258
|
+
lines.push('## Tool Calls');
|
|
259
|
+
for (const tc of payload.toolCalls) {
|
|
260
|
+
lines.push(`- ${tc.name}(${JSON.stringify(tc.arguments).slice(0, 200)})`);
|
|
261
|
+
}
|
|
262
|
+
lines.push('');
|
|
263
|
+
}
|
|
264
|
+
if (payload.errors.length > 0) {
|
|
265
|
+
lines.push('## Errors');
|
|
266
|
+
for (const e of payload.errors) {
|
|
267
|
+
lines.push(`- ${e}`);
|
|
268
|
+
}
|
|
269
|
+
lines.push('');
|
|
270
|
+
}
|
|
271
|
+
return lines.join('\n');
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Ensure a session row exists in telemetry.
|
|
275
|
+
*/
|
|
276
|
+
async ensureSessionRow(task) {
|
|
277
|
+
try {
|
|
278
|
+
await this.telemetry.createSession(this.sessionId, 'default', task);
|
|
279
|
+
}
|
|
280
|
+
catch {
|
|
281
|
+
// Session may already exist; ignore errors
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Emit a telemetry event. Fire-and-forget — errors are logged only.
|
|
286
|
+
*/
|
|
287
|
+
async emitTelemetry(event) {
|
|
288
|
+
await this.telemetry.emit(event);
|
|
289
|
+
}
|
|
290
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -2,13 +2,17 @@
|
|
|
2
2
|
* Boomerang v3 — Multi-agent orchestration plugin for OpenCode
|
|
3
3
|
*
|
|
4
4
|
* Exports:
|
|
5
|
-
* - orchestrator: Concurrency-aware orchestrator
|
|
5
|
+
* - orchestrator: Concurrency-aware orchestrator with context buffer
|
|
6
6
|
* - plugin: Plugin metadata
|
|
7
7
|
* - concurrency: TaskLimiter, RetryExecutor, TimeoutEnforcer
|
|
8
|
+
* - context-buffer: ContextBufferMiddleware
|
|
9
|
+
* - telemetry: TelemetryClient
|
|
8
10
|
* - types: Core type definitions
|
|
9
11
|
*/
|
|
10
12
|
export { BoomerangOrchestrator, createOrchestrator } from './orchestrator.js';
|
|
11
13
|
export { TaskLimiter, executeWithRetry, executeWithTimeout, } from './concurrency/index.js';
|
|
14
|
+
export { ContextBufferMiddleware } from './context-buffer.js';
|
|
15
|
+
export { TelemetryClient } from './telemetry-client.js';
|
|
12
16
|
export { TimeoutError, } from './types.js';
|
|
13
17
|
export const orchestrator = {
|
|
14
18
|
name: 'boomerang-v3',
|
|
@@ -552,7 +552,7 @@ export function getClient() {
|
|
|
552
552
|
}
|
|
553
553
|
return clientInstance;
|
|
554
554
|
}
|
|
555
|
-
export async function initializeClient(
|
|
555
|
+
export async function initializeClient(_pythonPath, _serverModule) {
|
|
556
556
|
const client = getClient();
|
|
557
557
|
await client.initialize();
|
|
558
558
|
return client;
|
|
@@ -89,7 +89,7 @@ export async function challengeMemory(memoryId, challengeText, client) {
|
|
|
89
89
|
/**
|
|
90
90
|
* Adapt a raw memory from memini-ai to our MemoryEntry type
|
|
91
91
|
*/
|
|
92
|
-
function
|
|
92
|
+
function _adaptMemoryEntry(raw) {
|
|
93
93
|
let timestamp;
|
|
94
94
|
if (typeof raw.timestamp === 'number') {
|
|
95
95
|
timestamp = raw.timestamp;
|
package/dist/memory/index.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { initializeClient, } from '../memini-client/index.js';
|
|
7
7
|
import { DEFAULT_SEARCH_OPTIONS } from './schema.js';
|
|
8
|
-
const
|
|
8
|
+
const _PROJECT_ID = process.env.BOOMERANG_PROJECT_ID || 'boomerang-default';
|
|
9
9
|
class MemorySystem {
|
|
10
10
|
static instance = null;
|
|
11
11
|
_initialized = false;
|
package/dist/memory/trust.js
CHANGED
|
@@ -130,7 +130,7 @@ export async function triggerConsolidation(force = false, client) {
|
|
|
130
130
|
/**
|
|
131
131
|
* Adapt a raw memory from memini-ai to our MemoryEntry type
|
|
132
132
|
*/
|
|
133
|
-
function
|
|
133
|
+
function _adaptMemoryEntry(meminiEntry) {
|
|
134
134
|
return {
|
|
135
135
|
id: meminiEntry.id,
|
|
136
136
|
text: meminiEntry.text,
|