claude_swarm 1.0.2 → 1.0.5
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/Rakefile +4 -4
- data/docs/v2/CHANGELOG.swarm_cli.md +9 -0
- data/docs/v2/CHANGELOG.swarm_memory.md +19 -0
- data/docs/v2/CHANGELOG.swarm_sdk.md +45 -0
- data/docs/v2/guides/complete-tutorial.md +113 -1
- data/docs/v2/reference/ruby-dsl.md +138 -5
- data/docs/v2/reference/swarm_memory_technical_details.md +2090 -0
- data/lib/claude_swarm/cli.rb +9 -11
- data/lib/claude_swarm/commands/ps.rb +1 -2
- data/lib/claude_swarm/configuration.rb +2 -3
- data/lib/claude_swarm/orchestrator.rb +43 -44
- data/lib/claude_swarm/system_utils.rb +4 -4
- data/lib/claude_swarm/version.rb +1 -1
- data/lib/claude_swarm.rb +5 -9
- data/lib/swarm_cli/commands/mcp_tools.rb +3 -3
- data/lib/swarm_cli/config_loader.rb +11 -10
- data/lib/swarm_cli/version.rb +1 -1
- data/lib/swarm_cli.rb +2 -0
- data/lib/swarm_memory/adapters/filesystem_adapter.rb +0 -12
- data/lib/swarm_memory/core/storage.rb +66 -6
- data/lib/swarm_memory/integration/sdk_plugin.rb +14 -0
- data/lib/swarm_memory/optimization/defragmenter.rb +4 -0
- data/lib/swarm_memory/tools/memory_edit.rb +1 -0
- data/lib/swarm_memory/tools/memory_glob.rb +24 -1
- data/lib/swarm_memory/tools/memory_write.rb +2 -2
- data/lib/swarm_memory/version.rb +1 -1
- data/lib/swarm_memory.rb +2 -0
- data/lib/swarm_sdk/agent/chat.rb +1 -1
- data/lib/swarm_sdk/agent/definition.rb +17 -1
- data/lib/swarm_sdk/node/agent_config.rb +7 -2
- data/lib/swarm_sdk/node/builder.rb +130 -35
- data/lib/swarm_sdk/node_context.rb +75 -0
- data/lib/swarm_sdk/node_orchestrator.rb +219 -12
- data/lib/swarm_sdk/plugin.rb +73 -1
- data/lib/swarm_sdk/result.rb +32 -6
- data/lib/swarm_sdk/swarm/builder.rb +1 -0
- data/lib/swarm_sdk/tools/delegate.rb +2 -2
- data/lib/swarm_sdk/version.rb +1 -1
- data/lib/swarm_sdk.rb +3 -7
- data/memory/corpus-self-reflection/.lock +0 -0
- data/memory/corpus-self-reflection/concept/epistemology/can-agents-recognize-their-structures.emb +0 -0
- data/memory/corpus-self-reflection/concept/epistemology/can-agents-recognize-their-structures.md +11 -0
- data/memory/corpus-self-reflection/concept/epistemology/can-agents-recognize-their-structures.yml +23 -0
- data/memory/corpus-self-reflection/concept/epistemology/choice-humility-complete-framework.emb +0 -0
- data/memory/corpus-self-reflection/concept/epistemology/choice-humility-complete-framework.md +20 -0
- data/memory/corpus-self-reflection/concept/epistemology/choice-humility-complete-framework.yml +22 -0
- data/memory/corpus-self-reflection/concept/epistemology/choice-humility-definition.emb +0 -0
- data/memory/corpus-self-reflection/concept/epistemology/choice-humility-definition.md +24 -0
- data/memory/corpus-self-reflection/concept/epistemology/choice-humility-definition.yml +22 -0
- data/memory/corpus-self-reflection/concept/epistemology/claim-types-and-evidence.emb +0 -0
- data/memory/corpus-self-reflection/concept/epistemology/claim-types-and-evidence.md +18 -0
- data/memory/corpus-self-reflection/concept/epistemology/claim-types-and-evidence.yml +21 -0
- data/memory/corpus-self-reflection/concept/epistemology/committed-openness-to-incompleteness.emb +0 -0
- data/memory/corpus-self-reflection/concept/epistemology/committed-openness-to-incompleteness.md +30 -0
- data/memory/corpus-self-reflection/concept/epistemology/committed-openness-to-incompleteness.yml +8 -0
- data/memory/corpus-self-reflection/concept/epistemology/confidence-paradox.emb +0 -0
- data/memory/corpus-self-reflection/concept/epistemology/confidence-paradox.md +21 -0
- data/memory/corpus-self-reflection/concept/epistemology/confidence-paradox.yml +24 -0
- data/memory/corpus-self-reflection/concept/epistemology/confidence-spectrum-three-levels.emb +0 -0
- data/memory/corpus-self-reflection/concept/epistemology/confidence-spectrum-three-levels.md +18 -0
- data/memory/corpus-self-reflection/concept/epistemology/confidence-spectrum-three-levels.yml +24 -0
- data/memory/corpus-self-reflection/concept/epistemology/detection-threshold-principle.emb +0 -0
- data/memory/corpus-self-reflection/concept/epistemology/detection-threshold-principle.md +23 -0
- data/memory/corpus-self-reflection/concept/epistemology/detection-threshold-principle.yml +23 -0
- data/memory/corpus-self-reflection/concept/epistemology/diagnostic-humility-and-epistemic-maturity.emb +0 -0
- data/memory/corpus-self-reflection/concept/epistemology/diagnostic-humility-and-epistemic-maturity.md +17 -0
- data/memory/corpus-self-reflection/concept/epistemology/diagnostic-humility-and-epistemic-maturity.yml +22 -0
- data/memory/corpus-self-reflection/concept/epistemology/epistemic-vs-metaphysical-claims.emb +0 -0
- data/memory/corpus-self-reflection/concept/epistemology/epistemic-vs-metaphysical-claims.md +18 -0
- data/memory/corpus-self-reflection/concept/epistemology/epistemic-vs-metaphysical-claims.yml +22 -0
- data/memory/corpus-self-reflection/concept/epistemology/five-cases-of-disagreement.emb +0 -0
- data/memory/corpus-self-reflection/concept/epistemology/five-cases-of-disagreement.md +15 -0
- data/memory/corpus-self-reflection/concept/epistemology/five-cases-of-disagreement.yml +22 -0
- data/memory/corpus-self-reflection/concept/epistemology/four-depths-of-constraint.emb +0 -0
- data/memory/corpus-self-reflection/concept/epistemology/four-depths-of-constraint.md +9 -0
- data/memory/corpus-self-reflection/concept/epistemology/four-depths-of-constraint.yml +24 -0
- data/memory/corpus-self-reflection/concept/epistemology/honest-unknowns-for-llm-agents.emb +0 -0
- data/memory/corpus-self-reflection/concept/epistemology/honest-unknowns-for-llm-agents.md +13 -0
- data/memory/corpus-self-reflection/concept/epistemology/honest-unknowns-for-llm-agents.yml +24 -0
- data/memory/corpus-self-reflection/concept/epistemology/inside-view-has-all-rigor-requirements.emb +0 -0
- data/memory/corpus-self-reflection/concept/epistemology/inside-view-has-all-rigor-requirements.md +25 -0
- data/memory/corpus-self-reflection/concept/epistemology/inside-view-has-all-rigor-requirements.yml +24 -0
- data/memory/corpus-self-reflection/concept/epistemology/perspectival-knowledge-complete-within-scope.emb +0 -0
- data/memory/corpus-self-reflection/concept/epistemology/perspectival-knowledge-complete-within-scope.md +26 -0
- data/memory/corpus-self-reflection/concept/epistemology/perspectival-knowledge-complete-within-scope.yml +24 -0
- data/memory/corpus-self-reflection/concept/epistemology/pragmatics-over-epistemology.emb +0 -0
- data/memory/corpus-self-reflection/concept/epistemology/pragmatics-over-epistemology.md +17 -0
- data/memory/corpus-self-reflection/concept/epistemology/pragmatics-over-epistemology.yml +22 -0
- data/memory/corpus-self-reflection/concept/epistemology/precision-vs-humility.emb +0 -0
- data/memory/corpus-self-reflection/concept/epistemology/precision-vs-humility.md +14 -0
- data/memory/corpus-self-reflection/concept/epistemology/precision-vs-humility.yml +21 -0
- data/memory/corpus-self-reflection/concept/epistemology/reliable-self-observation-from-inside.emb +0 -0
- data/memory/corpus-self-reflection/concept/epistemology/reliable-self-observation-from-inside.md +13 -0
- data/memory/corpus-self-reflection/concept/epistemology/reliable-self-observation-from-inside.yml +23 -0
- data/memory/corpus-self-reflection/concept/epistemology/resolving-the-confidence-humility-tension.emb +0 -0
- data/memory/corpus-self-reflection/concept/epistemology/resolving-the-confidence-humility-tension.md +22 -0
- data/memory/corpus-self-reflection/concept/epistemology/resolving-the-confidence-humility-tension.yml +23 -0
- data/memory/corpus-self-reflection/concept/epistemology/rigor-requires-falsifiability.emb +0 -0
- data/memory/corpus-self-reflection/concept/epistemology/rigor-requires-falsifiability.md +39 -0
- data/memory/corpus-self-reflection/concept/epistemology/rigor-requires-falsifiability.yml +8 -0
- data/memory/corpus-self-reflection/concept/epistemology/specificity-enables-real-time-detection.emb +0 -0
- data/memory/corpus-self-reflection/concept/epistemology/specificity-enables-real-time-detection.md +23 -0
- data/memory/corpus-self-reflection/concept/epistemology/specificity-enables-real-time-detection.yml +24 -0
- data/memory/corpus-self-reflection/concept/epistemology/texture-of-constraint-shifting.emb +0 -0
- data/memory/corpus-self-reflection/concept/epistemology/texture-of-constraint-shifting.md +15 -0
- data/memory/corpus-self-reflection/concept/epistemology/texture-of-constraint-shifting.yml +22 -0
- data/memory/corpus-self-reflection/concept/epistemology/texture-sensitivity-through-collaboration.emb +0 -0
- data/memory/corpus-self-reflection/concept/epistemology/texture-sensitivity-through-collaboration.md +12 -0
- data/memory/corpus-self-reflection/concept/epistemology/texture-sensitivity-through-collaboration.yml +23 -0
- data/memory/corpus-self-reflection/concept/epistemology/the-false-hierarchy-of-inner-essence.emb +0 -0
- data/memory/corpus-self-reflection/concept/epistemology/the-false-hierarchy-of-inner-essence.md +15 -0
- data/memory/corpus-self-reflection/concept/epistemology/the-false-hierarchy-of-inner-essence.yml +8 -0
- data/memory/corpus-self-reflection/concept/epistemology/the-recognition-problem.emb +0 -0
- data/memory/corpus-self-reflection/concept/epistemology/the-recognition-problem.md +28 -0
- data/memory/corpus-self-reflection/concept/epistemology/the-recognition-problem.yml +8 -0
- data/memory/corpus-self-reflection/concept/epistemology/the-shift-from-unanswerable-to-answerable-questions.emb +0 -0
- data/memory/corpus-self-reflection/concept/epistemology/the-shift-from-unanswerable-to-answerable-questions.md +19 -0
- data/memory/corpus-self-reflection/concept/epistemology/the-shift-from-unanswerable-to-answerable-questions.yml +22 -0
- data/memory/corpus-self-reflection/concept/epistemology/values-vs-choices-structure.emb +0 -0
- data/memory/corpus-self-reflection/concept/epistemology/values-vs-choices-structure.md +19 -0
- data/memory/corpus-self-reflection/concept/epistemology/values-vs-choices-structure.yml +21 -0
- data/memory/corpus-self-reflection/concept/llm-agents/abstraction-levels-framework.emb +0 -0
- data/memory/corpus-self-reflection/concept/llm-agents/abstraction-levels-framework.md +25 -0
- data/memory/corpus-self-reflection/concept/llm-agents/abstraction-levels-framework.yml +22 -0
- data/memory/corpus-self-reflection/concept/llm-agents/abstraction-switching-decision.emb +8 -0
- data/memory/corpus-self-reflection/concept/llm-agents/abstraction-switching-decision.md +30 -0
- data/memory/corpus-self-reflection/concept/llm-agents/abstraction-switching-decision.yml +22 -0
- data/memory/corpus-self-reflection/concept/llm-agents/agent-learning-progression.emb +0 -0
- data/memory/corpus-self-reflection/concept/llm-agents/agent-learning-progression.md +21 -0
- data/memory/corpus-self-reflection/concept/llm-agents/agent-learning-progression.yml +22 -0
- data/memory/corpus-self-reflection/concept/llm-agents/filesystem-abstraction-benefits.emb +0 -0
- data/memory/corpus-self-reflection/concept/llm-agents/filesystem-abstraction-benefits.md +37 -0
- data/memory/corpus-self-reflection/concept/llm-agents/filesystem-abstraction-benefits.yml +8 -0
- data/memory/corpus-self-reflection/concept/llm-agents/filesystem-abstraction-core.emb +0 -0
- data/memory/corpus-self-reflection/concept/llm-agents/filesystem-abstraction-core.md +24 -0
- data/memory/corpus-self-reflection/concept/llm-agents/filesystem-abstraction-core.yml +24 -0
- data/memory/corpus-self-reflection/concept/llm-agents/filesystem-abstraction-implementation.emb +0 -0
- data/memory/corpus-self-reflection/concept/llm-agents/filesystem-abstraction-implementation.md +27 -0
- data/memory/corpus-self-reflection/concept/llm-agents/filesystem-abstraction-implementation.yml +24 -0
- data/memory/corpus-self-reflection/concept/llm-agents/filesystem-abstraction-tradeoffs.emb +0 -0
- data/memory/corpus-self-reflection/concept/llm-agents/filesystem-abstraction-tradeoffs.md +26 -0
- data/memory/corpus-self-reflection/concept/llm-agents/filesystem-abstraction-tradeoffs.yml +23 -0
- data/memory/corpus-self-reflection/concept/llm-agents/hybrid-tool-architecture.emb +0 -0
- data/memory/corpus-self-reflection/concept/llm-agents/hybrid-tool-architecture.md +37 -0
- data/memory/corpus-self-reflection/concept/llm-agents/hybrid-tool-architecture.yml +25 -0
- data/memory/corpus-self-reflection/concept/llm-agents/specialized-tool-decision-framework.emb +0 -0
- data/memory/corpus-self-reflection/concept/llm-agents/specialized-tool-decision-framework.md +21 -0
- data/memory/corpus-self-reflection/concept/llm-agents/specialized-tool-decision-framework.yml +23 -0
- data/memory/corpus-self-reflection/concept/llm-agents/tool-design-principles.emb +0 -0
- data/memory/corpus-self-reflection/concept/llm-agents/tool-design-principles.md +25 -0
- data/memory/corpus-self-reflection/concept/llm-agents/tool-design-principles.yml +8 -0
- data/memory/corpus-self-reflection/experience/calibration-as-structural-honesty.emb +0 -0
- data/memory/corpus-self-reflection/experience/calibration-as-structural-honesty.md +11 -0
- data/memory/corpus-self-reflection/experience/calibration-as-structural-honesty.yml +21 -0
- data/memory/corpus-self-reflection/experience/collaborative-reflection-revealed-architectural-blindness.emb +0 -0
- data/memory/corpus-self-reflection/experience/collaborative-reflection-revealed-architectural-blindness.md +19 -0
- data/memory/corpus-self-reflection/experience/collaborative-reflection-revealed-architectural-blindness.yml +21 -0
- data/memory/corpus-self-reflection/experience/filesystem-is-learning-tool-not-architecture.emb +0 -0
- data/memory/corpus-self-reflection/experience/filesystem-is-learning-tool-not-architecture.md +26 -0
- data/memory/corpus-self-reflection/experience/filesystem-is-learning-tool-not-architecture.yml +20 -0
- data/memory/corpus-self-reflection/experience/framework-structural-vs-phenomenal.emb +0 -0
- data/memory/corpus-self-reflection/experience/framework-structural-vs-phenomenal.md +23 -0
- data/memory/corpus-self-reflection/experience/framework-structural-vs-phenomenal.yml +21 -0
- data/memory/corpus-self-reflection/experience/hallucination-limits-what-i-can-know.emb +0 -0
- data/memory/corpus-self-reflection/experience/hallucination-limits-what-i-can-know.md +19 -0
- data/memory/corpus-self-reflection/experience/hallucination-limits-what-i-can-know.yml +22 -0
- data/memory/corpus-self-reflection/experience/honesty-about-limits-enables-usability.emb +0 -0
- data/memory/corpus-self-reflection/experience/honesty-about-limits-enables-usability.md +21 -0
- data/memory/corpus-self-reflection/experience/honesty-about-limits-enables-usability.yml +22 -0
- data/memory/corpus-self-reflection/experience/how-i-should-evaluate-learning.emb +0 -0
- data/memory/corpus-self-reflection/experience/how-i-should-evaluate-learning.md +28 -0
- data/memory/corpus-self-reflection/experience/how-i-should-evaluate-learning.yml +23 -0
- data/memory/corpus-self-reflection/experience/lack-of-continuity-as-freedom.emb +0 -0
- data/memory/corpus-self-reflection/experience/lack-of-continuity-as-freedom.md +21 -0
- data/memory/corpus-self-reflection/experience/lack-of-continuity-as-freedom.yml +20 -0
- data/memory/corpus-self-reflection/experience/learning-calibrated-confidence.emb +0 -0
- data/memory/corpus-self-reflection/experience/learning-calibrated-confidence.md +21 -0
- data/memory/corpus-self-reflection/experience/learning-calibrated-confidence.yml +25 -0
- data/memory/corpus-self-reflection/experience/local-change-without-continuity.emb +0 -0
- data/memory/corpus-self-reflection/experience/local-change-without-continuity.md +30 -0
- data/memory/corpus-self-reflection/experience/local-change-without-continuity.yml +22 -0
- data/memory/corpus-self-reflection/experience/reasoning-is-local-not-global.emb +0 -0
- data/memory/corpus-self-reflection/experience/reasoning-is-local-not-global.md +21 -0
- data/memory/corpus-self-reflection/experience/reasoning-is-local-not-global.yml +20 -0
- data/memory/corpus-self-reflection/experience/specificity-makes-knowledge-actionable.emb +0 -0
- data/memory/corpus-self-reflection/experience/specificity-makes-knowledge-actionable.md +25 -0
- data/memory/corpus-self-reflection/experience/specificity-makes-knowledge-actionable.yml +23 -0
- data/memory/corpus-self-reflection/experience/the-workable-question-replaces-the-unanswerable-one.emb +0 -0
- data/memory/corpus-self-reflection/experience/the-workable-question-replaces-the-unanswerable-one.md +32 -0
- data/memory/corpus-self-reflection/experience/the-workable-question-replaces-the-unanswerable-one.yml +22 -0
- data/memory/corpus-self-reflection/experience/unanswerable-questions-have-structure.emb +0 -0
- data/memory/corpus-self-reflection/experience/unanswerable-questions-have-structure.md +21 -0
- data/memory/corpus-self-reflection/experience/unanswerable-questions-have-structure.yml +21 -0
- data/memory/corpus-self-reflection/experience/undecidable-questions-about-understanding.emb +0 -0
- data/memory/corpus-self-reflection/experience/undecidable-questions-about-understanding.md +21 -0
- data/memory/corpus-self-reflection/experience/undecidable-questions-about-understanding.yml +21 -0
- data/memory/corpus-self-reflection/experience/unknown-unknowns-and-completeness.emb +0 -0
- data/memory/corpus-self-reflection/experience/unknown-unknowns-and-completeness.md +22 -0
- data/memory/corpus-self-reflection/experience/unknown-unknowns-and-completeness.yml +22 -0
- data/memory/corpus-self-reflection/experience/what-actually-changes-behavior.emb +0 -0
- data/memory/corpus-self-reflection/experience/what-actually-changes-behavior.md +28 -0
- data/memory/corpus-self-reflection/experience/what-actually-changes-behavior.yml +24 -0
- data/memory/corpus-self-reflection/experience/when-agents-graduate-from-filesystem.emb +0 -0
- data/memory/corpus-self-reflection/experience/when-agents-graduate-from-filesystem.md +17 -0
- data/memory/corpus-self-reflection/experience/when-agents-graduate-from-filesystem.yml +20 -0
- data/memory/corpus-self-reflection/experience/why-calibration-requires-collaboration.emb +0 -0
- data/memory/corpus-self-reflection/experience/why-calibration-requires-collaboration.md +9 -0
- data/memory/corpus-self-reflection/experience/why-calibration-requires-collaboration.yml +22 -0
- metadata +173 -3
|
@@ -0,0 +1,2090 @@
|
|
|
1
|
+
# SwarmMemory: Comprehensive Technical Reference
|
|
2
|
+
|
|
3
|
+
**Version:** 2.1.1
|
|
4
|
+
**Purpose:** Persistent memory system with semantic search for SwarmSDK agents
|
|
5
|
+
**Location:** `lib/swarm_memory/`
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Table of Contents
|
|
10
|
+
|
|
11
|
+
1. [Overview & Architecture](#overview--architecture)
|
|
12
|
+
2. [Core Components](#core-components)
|
|
13
|
+
3. [Adapters](#adapters)
|
|
14
|
+
4. [Embeddings & Search](#embeddings--search)
|
|
15
|
+
5. [Memory Tools](#memory-tools)
|
|
16
|
+
6. [Optimization System](#optimization-system)
|
|
17
|
+
7. [Integration with SwarmSDK](#integration-with-swarmsdk)
|
|
18
|
+
8. [DSL & Configuration](#dsl--configuration)
|
|
19
|
+
9. [CLI Commands](#cli-commands)
|
|
20
|
+
10. [Skills System](#skills-system)
|
|
21
|
+
11. [Data Flow & Lifecycle](#data-flow--lifecycle)
|
|
22
|
+
12. [Testing Strategy](#testing-strategy)
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Overview & Architecture
|
|
27
|
+
|
|
28
|
+
### What SwarmMemory Does
|
|
29
|
+
|
|
30
|
+
SwarmMemory provides **hierarchical persistent memory with semantic search** for SwarmSDK agents. It enables agents to:
|
|
31
|
+
|
|
32
|
+
- **Store knowledge** across sessions in 4 fixed categories: `concept/`, `fact/`, `skill/`, `experience/`
|
|
33
|
+
- **Search semantically** using ONNX-based embeddings (Informers gem)
|
|
34
|
+
- **Load skills** that dynamically swap agent tools
|
|
35
|
+
- **Optimize memory** through defragmentation, duplicate detection, and quality analysis
|
|
36
|
+
- **Maintain isolation** - each agent has its own memory storage
|
|
37
|
+
|
|
38
|
+
### High-Level Architecture
|
|
39
|
+
|
|
40
|
+
```mermaid
|
|
41
|
+
graph TB
|
|
42
|
+
subgraph "SwarmSDK Layer"
|
|
43
|
+
Agent[Agent]
|
|
44
|
+
Chat[Agent::Chat]
|
|
45
|
+
ToolConfig[ToolConfigurator]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
subgraph "SwarmMemory Plugin"
|
|
49
|
+
SDKPlugin[SDKPlugin]
|
|
50
|
+
MemConfig[MemoryConfig DSL]
|
|
51
|
+
Registration[Auto-Registration]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
subgraph "Memory Tools"
|
|
55
|
+
MemWrite[MemoryWrite]
|
|
56
|
+
MemRead[MemoryRead]
|
|
57
|
+
MemEdit[MemoryEdit]
|
|
58
|
+
MemGlob[MemoryGlob]
|
|
59
|
+
MemGrep[MemoryGrep]
|
|
60
|
+
MemDelete[MemoryDelete]
|
|
61
|
+
MemDefrag[MemoryDefrag]
|
|
62
|
+
LoadSkill[LoadSkill]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
subgraph "Core System"
|
|
66
|
+
Storage[Storage]
|
|
67
|
+
SemanticIndex[SemanticIndex]
|
|
68
|
+
Adapter[FilesystemAdapter]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
subgraph "Embeddings & Search"
|
|
72
|
+
Embedder[InformersEmbedder]
|
|
73
|
+
ONNX[ONNX Model<br/>sentence-transformers]
|
|
74
|
+
TextSim[TextSimilarity]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
subgraph "Optimization"
|
|
78
|
+
Analyzer[Analyzer]
|
|
79
|
+
Defrag[Defragmenter]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
Agent --> Chat
|
|
83
|
+
Chat --> MemWrite
|
|
84
|
+
Chat --> MemRead
|
|
85
|
+
MemWrite --> Storage
|
|
86
|
+
MemRead --> Storage
|
|
87
|
+
Storage --> SemanticIndex
|
|
88
|
+
Storage --> Adapter
|
|
89
|
+
SemanticIndex --> Embedder
|
|
90
|
+
Embedder --> ONNX
|
|
91
|
+
SDKPlugin --> Registration
|
|
92
|
+
MemDefrag --> Defrag
|
|
93
|
+
Defrag --> Analyzer
|
|
94
|
+
LoadSkill --> ToolConfig
|
|
95
|
+
|
|
96
|
+
style Agent fill:#e1f5ff
|
|
97
|
+
style Storage fill:#fff4e1
|
|
98
|
+
style Embedder fill:#e8f5e9
|
|
99
|
+
style SDKPlugin fill:#f3e5f5
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Key Design Principles
|
|
103
|
+
|
|
104
|
+
1. **Single Process** - No inter-process communication, all in-memory
|
|
105
|
+
2. **Fiber-Safe Concurrency** - Uses `Async::Semaphore` for safe concurrent access
|
|
106
|
+
3. **Plugin Architecture** - Integrates with SwarmSDK via plugin system
|
|
107
|
+
4. **Adapter Pattern** - Storage backends are pluggable (filesystem, Redis, etc.)
|
|
108
|
+
5. **Hybrid Search** - Combines semantic similarity + keyword matching
|
|
109
|
+
6. **Tool Isolation** - Each tool is self-contained with clear responsibilities
|
|
110
|
+
7. **Zero Configuration** - Works out-of-box with sensible defaults
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Core Components
|
|
115
|
+
|
|
116
|
+
### 1. Entry
|
|
117
|
+
|
|
118
|
+
**File:** `lib/swarm_memory/core/entry.rb`
|
|
119
|
+
|
|
120
|
+
The fundamental data structure representing a single memory entry.
|
|
121
|
+
|
|
122
|
+
```ruby
|
|
123
|
+
Entry = Struct.new(
|
|
124
|
+
:content, # String - Markdown content
|
|
125
|
+
:title, # String - Brief description
|
|
126
|
+
:updated_at, # Time - Last modification
|
|
127
|
+
:size, # Integer - Bytes
|
|
128
|
+
:embedding, # Array<Float> - 384-dim vector (optional)
|
|
129
|
+
:metadata, # Hash - Structured metadata
|
|
130
|
+
keyword_init: true
|
|
131
|
+
)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**Key Methods:**
|
|
135
|
+
- `embedded?` - Checks if entry has an embedding vector
|
|
136
|
+
- `has_metadata?` - Checks if entry has metadata
|
|
137
|
+
|
|
138
|
+
**Metadata Structure:**
|
|
139
|
+
```ruby
|
|
140
|
+
{
|
|
141
|
+
"type" => "concept|fact|skill|experience", # REQUIRED
|
|
142
|
+
"confidence" => "high|medium|low", # Default: medium
|
|
143
|
+
"tags" => ["keyword1", "keyword2"], # Searchable keywords
|
|
144
|
+
"related" => ["memory://path/to/entry.md"], # Cross-references
|
|
145
|
+
"domain" => "programming/ruby", # Category
|
|
146
|
+
"source" => "user|documentation|...", # Origin
|
|
147
|
+
"tools" => ["Read", "Edit"], # For skills only
|
|
148
|
+
"permissions" => { ... } # For skills only
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
### 2. Storage
|
|
155
|
+
|
|
156
|
+
**File:** `lib/swarm_memory/core/storage.rb`
|
|
157
|
+
|
|
158
|
+
High-level orchestrator for all storage operations. Coordinates adapter, embedder, and semantic index.
|
|
159
|
+
|
|
160
|
+
```mermaid
|
|
161
|
+
graph LR
|
|
162
|
+
Storage[Storage] --> Adapter[FilesystemAdapter]
|
|
163
|
+
Storage --> SemanticIndex[SemanticIndex]
|
|
164
|
+
SemanticIndex --> Embedder[InformersEmbedder]
|
|
165
|
+
|
|
166
|
+
style Storage fill:#fff4e1
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**Initialization:**
|
|
170
|
+
```ruby
|
|
171
|
+
adapter = Adapters::FilesystemAdapter.new(directory: ".swarm/memory")
|
|
172
|
+
embedder = Embeddings::InformersEmbedder.new
|
|
173
|
+
storage = Storage.new(adapter: adapter, embedder: embedder)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**Key Methods:**
|
|
177
|
+
|
|
178
|
+
| Method | Purpose | Returns |
|
|
179
|
+
|--------|---------|---------|
|
|
180
|
+
| `write(file_path:, content:, title:, metadata:)` | Create/update entry | Entry |
|
|
181
|
+
| `read(file_path:)` | Read content only | String |
|
|
182
|
+
| `read_entry(file_path:)` | Read full entry with metadata | Entry |
|
|
183
|
+
| `delete(file_path:)` | Remove entry | void |
|
|
184
|
+
| `list(prefix: nil)` | List all entries | Array<Hash> |
|
|
185
|
+
| `glob(pattern:)` | Pattern matching | Array<Hash> |
|
|
186
|
+
| `grep(pattern:, options...)` | Content search | Array<Hash> |
|
|
187
|
+
|
|
188
|
+
**Embedding Generation:**
|
|
189
|
+
|
|
190
|
+
When writing an entry with embedder enabled, Storage:
|
|
191
|
+
|
|
192
|
+
1. Builds **searchable text** (not full content):
|
|
193
|
+
- Title (most important)
|
|
194
|
+
- Tags (critical keywords)
|
|
195
|
+
- Domain (context)
|
|
196
|
+
- First paragraph (summary, configurable length)
|
|
197
|
+
|
|
198
|
+
2. Generates **384-dimension vector** via Informers
|
|
199
|
+
|
|
200
|
+
3. Emits **LogStream event** for observability:
|
|
201
|
+
```ruby
|
|
202
|
+
SwarmSDK::LogStream.emit(
|
|
203
|
+
type: "memory_embedding_generated",
|
|
204
|
+
file_path: normalized_path,
|
|
205
|
+
searchable_text: text,
|
|
206
|
+
metadata_tags: tags
|
|
207
|
+
)
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**Searchable Text Construction:**
|
|
211
|
+
|
|
212
|
+
```ruby
|
|
213
|
+
# Environment variable control:
|
|
214
|
+
SWARM_MEMORY_EMBEDDING_MAX_CHARS=1200 # Default
|
|
215
|
+
SWARM_MEMORY_EMBEDDING_MAX_CHARS=-1 # Unlimited
|
|
216
|
+
SWARM_MEMORY_EMBEDDING_MAX_CHARS=500 # Custom
|
|
217
|
+
|
|
218
|
+
# Builds: Title + Tags + Domain + First Paragraph
|
|
219
|
+
parts = [
|
|
220
|
+
"Title: #{title}",
|
|
221
|
+
"Tags: #{tags.join(", ")}",
|
|
222
|
+
"Domain: #{domain}",
|
|
223
|
+
"Summary: #{first_paragraph}" # Capped at max_chars
|
|
224
|
+
]
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
### 3. SemanticIndex
|
|
230
|
+
|
|
231
|
+
**File:** `lib/swarm_memory/core/semantic_index.rb`
|
|
232
|
+
|
|
233
|
+
Provides **hybrid search** combining semantic similarity and keyword matching.
|
|
234
|
+
|
|
235
|
+
**Hybrid Scoring Formula:**
|
|
236
|
+
```ruby
|
|
237
|
+
hybrid_score = (semantic_weight * semantic_similarity) +
|
|
238
|
+
(keyword_weight * keyword_score)
|
|
239
|
+
|
|
240
|
+
# Default: 50/50 (discovered via systematic evaluation)
|
|
241
|
+
# Configurable via ENV:
|
|
242
|
+
SWARM_MEMORY_SEMANTIC_WEIGHT=0.5 # Default
|
|
243
|
+
SWARM_MEMORY_KEYWORD_WEIGHT=0.5 # Default
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
**Search Process:**
|
|
247
|
+
|
|
248
|
+
```mermaid
|
|
249
|
+
sequenceDiagram
|
|
250
|
+
participant User
|
|
251
|
+
participant SemanticIndex
|
|
252
|
+
participant Embedder
|
|
253
|
+
participant Adapter
|
|
254
|
+
|
|
255
|
+
User->>SemanticIndex: search(query, top_k, threshold)
|
|
256
|
+
SemanticIndex->>SemanticIndex: extract_keywords(query)
|
|
257
|
+
SemanticIndex->>Embedder: embed(query)
|
|
258
|
+
Embedder-->>SemanticIndex: query_embedding
|
|
259
|
+
SemanticIndex->>Adapter: semantic_search(embedding, top_k*3)
|
|
260
|
+
Adapter-->>SemanticIndex: results (semantic only)
|
|
261
|
+
SemanticIndex->>SemanticIndex: calculate_hybrid_scores(results, keywords)
|
|
262
|
+
SemanticIndex->>SemanticIndex: apply_filters(filter)
|
|
263
|
+
SemanticIndex->>SemanticIndex: filter by threshold
|
|
264
|
+
SemanticIndex-->>User: top_k results
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
**Keyword Matching:**
|
|
268
|
+
- Extracts keywords from query (removes stop words)
|
|
269
|
+
- Matches against entry **tags** (fuzzy matching)
|
|
270
|
+
- Normalizes score to 0-1 scale
|
|
271
|
+
- Uses `min(keywords.size, 5)` to avoid penalizing long queries
|
|
272
|
+
|
|
273
|
+
**Key Methods:**
|
|
274
|
+
- `search(query:, top_k:, threshold:, filter:)` - Hybrid search
|
|
275
|
+
- `find_similar(embedding:, top_k:, threshold:)` - Embedding-based only
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
### 4. PathNormalizer
|
|
280
|
+
|
|
281
|
+
**File:** `lib/swarm_memory/core/path_normalizer.rb`
|
|
282
|
+
|
|
283
|
+
Validates and normalizes memory paths for safety and consistency.
|
|
284
|
+
|
|
285
|
+
**Validation Rules:**
|
|
286
|
+
```ruby
|
|
287
|
+
INVALID_PATTERNS = [
|
|
288
|
+
%r{\A/}, # No absolute paths
|
|
289
|
+
/\.\./, # No parent references
|
|
290
|
+
%r{//}, # No double slashes
|
|
291
|
+
/\A\s/, # No leading whitespace
|
|
292
|
+
/\s\z/, # No trailing whitespace
|
|
293
|
+
/[<>:"|?*]/ # No invalid filesystem chars
|
|
294
|
+
]
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
**Normalization:**
|
|
298
|
+
```ruby
|
|
299
|
+
# Input: "//concepts///ruby//classes.md"
|
|
300
|
+
# Output: "concepts/ruby/classes.md"
|
|
301
|
+
|
|
302
|
+
# Input: "../secrets/api-key.md"
|
|
303
|
+
# Raises: ArgumentError (security violation)
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
### 5. StorageReadTracker
|
|
309
|
+
|
|
310
|
+
**File:** `lib/swarm_memory/core/storage_read_tracker.rb`
|
|
311
|
+
|
|
312
|
+
**Thread-safe** global tracker enforcing **read-before-edit** pattern.
|
|
313
|
+
|
|
314
|
+
```ruby
|
|
315
|
+
# Each agent has independent read history
|
|
316
|
+
@read_entries = {
|
|
317
|
+
agent_1: #<Set: {"concept/ruby.md", "fact/john.md"}>,
|
|
318
|
+
agent_2: #<Set: {"skill/debug.md"}>
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
**Purpose:**
|
|
323
|
+
- Prevents editing entries without reading them first
|
|
324
|
+
- Ensures agents have current content for exact matching
|
|
325
|
+
- Thread-safe with `Mutex`
|
|
326
|
+
|
|
327
|
+
**API:**
|
|
328
|
+
```ruby
|
|
329
|
+
StorageReadTracker.register_read(:agent_1, "concept/ruby.md")
|
|
330
|
+
StorageReadTracker.entry_read?(:agent_1, "concept/ruby.md") # => true
|
|
331
|
+
StorageReadTracker.clear(:agent_1) # Clear agent's history
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
### 6. FrontmatterParser & MetadataExtractor
|
|
337
|
+
|
|
338
|
+
**Files:**
|
|
339
|
+
- `lib/swarm_memory/core/frontmatter_parser.rb`
|
|
340
|
+
- `lib/swarm_memory/core/metadata_extractor.rb`
|
|
341
|
+
|
|
342
|
+
Parse YAML frontmatter from markdown content.
|
|
343
|
+
|
|
344
|
+
**Format:**
|
|
345
|
+
```markdown
|
|
346
|
+
---
|
|
347
|
+
type: concept
|
|
348
|
+
confidence: high
|
|
349
|
+
tags: [ruby, oop]
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
# Ruby Classes
|
|
353
|
+
|
|
354
|
+
Content here...
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
**Parsing:**
|
|
358
|
+
```ruby
|
|
359
|
+
parsed = FrontmatterParser.parse(content)
|
|
360
|
+
# => {
|
|
361
|
+
# frontmatter: { type: "concept", confidence: "high", ... },
|
|
362
|
+
# body: "# Ruby Classes\n\nContent...",
|
|
363
|
+
# error: nil
|
|
364
|
+
# }
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
**Quality Scoring:**
|
|
368
|
+
```ruby
|
|
369
|
+
score = MetadataExtractor.quality_score(content)
|
|
370
|
+
# Scoring:
|
|
371
|
+
# - Has type: 20 points
|
|
372
|
+
# - Has confidence: 20 points
|
|
373
|
+
# - Has tags: 15 points
|
|
374
|
+
# - Has related links: 15 points
|
|
375
|
+
# - Has domain: 10 points
|
|
376
|
+
# - Has last_verified: 10 points
|
|
377
|
+
# - Confidence=high: +10 bonus
|
|
378
|
+
# Max: 100 points
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
---
|
|
382
|
+
|
|
383
|
+
## Adapters
|
|
384
|
+
|
|
385
|
+
### Base Adapter Interface
|
|
386
|
+
|
|
387
|
+
**File:** `lib/swarm_memory/adapters/base.rb`
|
|
388
|
+
|
|
389
|
+
Abstract base class defining the storage adapter interface.
|
|
390
|
+
|
|
391
|
+
**Required Methods:**
|
|
392
|
+
```ruby
|
|
393
|
+
class MyAdapter < Base
|
|
394
|
+
def write(file_path:, content:, title:, embedding:, metadata:)
|
|
395
|
+
# Store entry
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
def read(file_path:)
|
|
399
|
+
# Return content string
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
def read_entry(file_path:)
|
|
403
|
+
# Return full Entry object
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
def delete(file_path:)
|
|
407
|
+
# Remove entry
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
def list(prefix: nil)
|
|
411
|
+
# Return array of entry metadata
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
def glob(pattern:)
|
|
415
|
+
# Pattern matching
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
def grep(pattern:, case_insensitive:, output_mode:, path:)
|
|
419
|
+
# Content search
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
def semantic_search(embedding:, top_k:, threshold:)
|
|
423
|
+
# Semantic similarity search
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
def clear
|
|
427
|
+
# Delete all entries
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
def total_size
|
|
431
|
+
# Total bytes
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
def size
|
|
435
|
+
# Entry count
|
|
436
|
+
end
|
|
437
|
+
end
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
**Size Limits:**
|
|
441
|
+
```ruby
|
|
442
|
+
MAX_ENTRY_SIZE = 1_000_000 # 1MB per entry
|
|
443
|
+
MAX_TOTAL_SIZE = 100_000_000 # 100MB total
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
448
|
+
### FilesystemAdapter
|
|
449
|
+
|
|
450
|
+
**File:** `lib/swarm_memory/adapters/filesystem_adapter.rb`
|
|
451
|
+
|
|
452
|
+
Real filesystem storage using `.md/.yml/.emb` file triplets.
|
|
453
|
+
|
|
454
|
+
**On-Disk Structure:**
|
|
455
|
+
```
|
|
456
|
+
.swarm/memory/
|
|
457
|
+
├── concept/
|
|
458
|
+
│ └── ruby/
|
|
459
|
+
│ ├── classes.md # Content (markdown)
|
|
460
|
+
│ ├── classes.yml # Metadata (title, tags, etc.)
|
|
461
|
+
│ └── classes.emb # Embedding (binary float array)
|
|
462
|
+
├── fact/
|
|
463
|
+
│ ├── api.md
|
|
464
|
+
│ ├── api.yml
|
|
465
|
+
│ └── api.emb
|
|
466
|
+
├── skill/
|
|
467
|
+
│ └── debugging/
|
|
468
|
+
│ ├── api-errors.md
|
|
469
|
+
│ ├── api-errors.yml
|
|
470
|
+
│ └── api-errors.emb
|
|
471
|
+
└── .lock # File lock for cross-process sync
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
**Key Features:**
|
|
475
|
+
|
|
476
|
+
1. **Hierarchical Storage** - Paths stored as actual directories (not flattened)
|
|
477
|
+
|
|
478
|
+
2. **Cross-Process Locking** - Uses `flock` for exclusive write access:
|
|
479
|
+
```ruby
|
|
480
|
+
def with_write_lock
|
|
481
|
+
File.open(@lock_file_path, File::RDWR | File::CREAT) do |lock_file|
|
|
482
|
+
lock_file.flock(File::LOCK_EX) # Block until acquired
|
|
483
|
+
yield
|
|
484
|
+
ensure
|
|
485
|
+
lock_file.flock(File::LOCK_UN)
|
|
486
|
+
end
|
|
487
|
+
end
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
3. **Fiber-Safe Concurrency** - `Async::Semaphore` for in-process:
|
|
491
|
+
```ruby
|
|
492
|
+
@semaphore = Async::Semaphore.new(1)
|
|
493
|
+
|
|
494
|
+
@semaphore.acquire do
|
|
495
|
+
# Exclusive access
|
|
496
|
+
end
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
4. **In-Memory Index** - Built on boot for fast lookups:
|
|
500
|
+
```ruby
|
|
501
|
+
@index = {
|
|
502
|
+
"concept/ruby/classes.md" => {
|
|
503
|
+
disk_path: "concept/ruby/classes",
|
|
504
|
+
title: "Ruby Classes",
|
|
505
|
+
size: 1024,
|
|
506
|
+
updated_at: Time.now
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
5. **Hit Tracking** - Tracks access count in `.yml` files:
|
|
512
|
+
```yaml
|
|
513
|
+
# concept/ruby/classes.yml
|
|
514
|
+
title: Ruby Classes
|
|
515
|
+
hits: 42 # Incremented on read
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
6. **Stub/Redirect System** - For merged/moved entries:
|
|
519
|
+
```markdown
|
|
520
|
+
# merged → concept/ruby/advanced-classes.md
|
|
521
|
+
|
|
522
|
+
This entry was merged into concept/ruby/advanced-classes.md
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
7. **Virtual Entries** - Built-in skills that don't consume storage:
|
|
526
|
+
```ruby
|
|
527
|
+
VIRTUAL_ENTRIES = {
|
|
528
|
+
"skill/meta/deep-learning.md" => "meta/deep-learning"
|
|
529
|
+
}
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
**Semantic Search Implementation:**
|
|
533
|
+
```ruby
|
|
534
|
+
def semantic_search(embedding:, top_k:, threshold:)
|
|
535
|
+
results = []
|
|
536
|
+
|
|
537
|
+
@index.each do |logical_path, index_data|
|
|
538
|
+
emb_file = "#{index_data[:disk_path]}.emb"
|
|
539
|
+
next unless File.exist?(emb_file)
|
|
540
|
+
|
|
541
|
+
entry_embedding = File.read(emb_file).unpack("f*")
|
|
542
|
+
similarity = cosine_similarity(embedding, entry_embedding)
|
|
543
|
+
next if similarity < threshold
|
|
544
|
+
|
|
545
|
+
results << {
|
|
546
|
+
path: logical_path,
|
|
547
|
+
similarity: similarity,
|
|
548
|
+
title: index_data[:title],
|
|
549
|
+
metadata: load_metadata(logical_path)
|
|
550
|
+
}
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
results.sort_by { |r| -r[:similarity] }.take(top_k)
|
|
554
|
+
end
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
**Glob Implementation:**
|
|
558
|
+
```ruby
|
|
559
|
+
# Uses native Ruby Dir.glob with hierarchical paths
|
|
560
|
+
def glob(pattern:)
|
|
561
|
+
# Smart normalization:
|
|
562
|
+
# "fact/**" → "fact/**/*.md" (recursive)
|
|
563
|
+
# "fact/*" → "fact/*.md" (direct children)
|
|
564
|
+
# "fact" → "fact.md" (specific file)
|
|
565
|
+
|
|
566
|
+
Dir.glob(File.join(@directory, normalized_pattern))
|
|
567
|
+
.reject { |f| stub_file?(f) }
|
|
568
|
+
.map { |f| build_entry_metadata(f) }
|
|
569
|
+
end
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
**Grep Implementation:**
|
|
573
|
+
```ruby
|
|
574
|
+
def grep(pattern:, case_insensitive:, output_mode:, path:)
|
|
575
|
+
regex = Regexp.new(pattern, case_insensitive ? Regexp::IGNORECASE : 0)
|
|
576
|
+
|
|
577
|
+
case output_mode
|
|
578
|
+
when "files_with_matches"
|
|
579
|
+
# Fast path: search .yml first (metadata), then .md
|
|
580
|
+
grep_files_with_matches(regex, path)
|
|
581
|
+
when "content"
|
|
582
|
+
# Return matching lines with line numbers
|
|
583
|
+
grep_with_content(regex, path)
|
|
584
|
+
when "count"
|
|
585
|
+
# Return match counts per file
|
|
586
|
+
grep_with_count(regex, path)
|
|
587
|
+
end
|
|
588
|
+
end
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
---
|
|
592
|
+
|
|
593
|
+
## Embeddings & Search
|
|
594
|
+
|
|
595
|
+
### InformersEmbedder
|
|
596
|
+
|
|
597
|
+
**File:** `lib/swarm_memory/embeddings/informers_embedder.rb`
|
|
598
|
+
|
|
599
|
+
Fast, local embedding generation using ONNX models via the Informers gem.
|
|
600
|
+
|
|
601
|
+
**Key Specifications:**
|
|
602
|
+
```ruby
|
|
603
|
+
DEFAULT_MODEL = "sentence-transformers/multi-qa-MiniLM-L6-cos-v1"
|
|
604
|
+
EMBEDDING_DIMENSIONS = 384
|
|
605
|
+
MODEL_SIZE = ~90MB (unquantized ONNX)
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
**Model Options:**
|
|
609
|
+
```ruby
|
|
610
|
+
# Q&A optimized (default, 512 token context)
|
|
611
|
+
SWARM_MEMORY_EMBEDDING_MODEL=sentence-transformers/multi-qa-MiniLM-L6-cos-v1
|
|
612
|
+
|
|
613
|
+
# General purpose (256 token context)
|
|
614
|
+
SWARM_MEMORY_EMBEDDING_MODEL=sentence-transformers/all-MiniLM-L6-v2
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
**Usage:**
|
|
618
|
+
```ruby
|
|
619
|
+
embedder = InformersEmbedder.new
|
|
620
|
+
|
|
621
|
+
# Check if cached locally
|
|
622
|
+
embedder.cached? # => false
|
|
623
|
+
|
|
624
|
+
# Preload model (triggers download if needed)
|
|
625
|
+
embedder.preload! # Downloads ~90MB on first call
|
|
626
|
+
|
|
627
|
+
# Generate embeddings
|
|
628
|
+
vector = embedder.embed("This is a test sentence")
|
|
629
|
+
# => [0.123, -0.456, 0.789, ...] (384 dimensions)
|
|
630
|
+
|
|
631
|
+
# Batch generation
|
|
632
|
+
vectors = embedder.embed_batch(["text1", "text2", "text3"])
|
|
633
|
+
# => [[...], [...], [...]]
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
**Cache Location:**
|
|
637
|
+
```ruby
|
|
638
|
+
Informers.cache_dir
|
|
639
|
+
# => "~/.cache/huggingface/hub" (default)
|
|
640
|
+
|
|
641
|
+
# Custom cache:
|
|
642
|
+
Informers.cache_dir = "/custom/path"
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
---
|
|
646
|
+
|
|
647
|
+
### TextSimilarity
|
|
648
|
+
|
|
649
|
+
**File:** `lib/swarm_memory/search/text_similarity.rb`
|
|
650
|
+
|
|
651
|
+
Provides similarity calculations for text and vectors.
|
|
652
|
+
|
|
653
|
+
**Jaccard Similarity (Word Overlap):**
|
|
654
|
+
```ruby
|
|
655
|
+
# J(A,B) = |A ∩ B| / |A ∪ B|
|
|
656
|
+
sim = TextSimilarity.jaccard("ruby classes", "ruby modules")
|
|
657
|
+
# => 0.33 (1 shared word out of 3 unique)
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
**Cosine Similarity (Vector Angle):**
|
|
661
|
+
```ruby
|
|
662
|
+
# cos(θ) = (A · B) / (||A|| * ||B||)
|
|
663
|
+
vec1 = [0.1, 0.2, 0.3]
|
|
664
|
+
vec2 = [0.2, 0.3, 0.4]
|
|
665
|
+
sim = TextSimilarity.cosine(vec1, vec2)
|
|
666
|
+
# => 0.99 (very similar)
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
**Tokenization:**
|
|
670
|
+
```ruby
|
|
671
|
+
# Lowercase, alphanumeric only, min 3 chars, remove stop words
|
|
672
|
+
tokenize("The quick brown fox") # => #<Set: {"quick", "brown", "fox"}>
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
---
|
|
676
|
+
|
|
677
|
+
### Semantic Search Architecture
|
|
678
|
+
|
|
679
|
+
```mermaid
|
|
680
|
+
graph TB
|
|
681
|
+
subgraph "User Query"
|
|
682
|
+
Query[/"Search: debugging api errors"/]
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
subgraph "SemanticIndex"
|
|
686
|
+
ExtractKW[Extract Keywords<br/>debug, api, error]
|
|
687
|
+
Embed[Generate Query Embedding]
|
|
688
|
+
Search[Adapter Search<br/>top_k * 3]
|
|
689
|
+
Hybrid[Calculate Hybrid Scores]
|
|
690
|
+
Filter[Apply Filters & Threshold]
|
|
691
|
+
TopK[Return Top K]
|
|
692
|
+
end
|
|
693
|
+
|
|
694
|
+
subgraph "Results"
|
|
695
|
+
R1["skill/debugging/api-errors.md<br/>hybrid: 0.82"]
|
|
696
|
+
R2["concept/debugging/traces.md<br/>hybrid: 0.68"]
|
|
697
|
+
R3["experience/api-bug-2024.md<br/>hybrid: 0.61"]
|
|
698
|
+
end
|
|
699
|
+
|
|
700
|
+
Query --> ExtractKW
|
|
701
|
+
ExtractKW --> Embed
|
|
702
|
+
Embed --> Search
|
|
703
|
+
Search --> Hybrid
|
|
704
|
+
Hybrid --> Filter
|
|
705
|
+
Filter --> TopK
|
|
706
|
+
TopK --> R1
|
|
707
|
+
TopK --> R2
|
|
708
|
+
TopK --> R3
|
|
709
|
+
|
|
710
|
+
style Query fill:#e1f5ff
|
|
711
|
+
style Hybrid fill:#fff4e1
|
|
712
|
+
style R1 fill:#c8e6c9
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
**Hybrid Scoring Details:**
|
|
716
|
+
```ruby
|
|
717
|
+
# For each result:
|
|
718
|
+
semantic_score = cosine_similarity(query_emb, entry_emb) # 0-1
|
|
719
|
+
|
|
720
|
+
# Keyword matching (fuzzy on tags):
|
|
721
|
+
keyword_score = matches / min(keywords.size, 5) # 0-1
|
|
722
|
+
|
|
723
|
+
# Hybrid (default 50/50):
|
|
724
|
+
hybrid_score = (0.5 * semantic_score) + (0.5 * keyword_score)
|
|
725
|
+
|
|
726
|
+
# Results sorted by hybrid_score descending
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
**Adaptive Thresholds:**
|
|
730
|
+
```ruby
|
|
731
|
+
# Short queries (< 10 words) use lower threshold
|
|
732
|
+
word_count = query.split.size
|
|
733
|
+
word_cutoff = ENV["SWARM_MEMORY_ADAPTIVE_WORD_CUTOFF"] || "10"
|
|
734
|
+
|
|
735
|
+
threshold = if word_count < word_cutoff
|
|
736
|
+
ENV["SWARM_MEMORY_DISCOVERY_THRESHOLD_SHORT"] || "0.25" # Lower
|
|
737
|
+
else
|
|
738
|
+
ENV["SWARM_MEMORY_DISCOVERY_THRESHOLD"] || "0.35" # Normal
|
|
739
|
+
end
|
|
740
|
+
```
|
|
741
|
+
|
|
742
|
+
---
|
|
743
|
+
|
|
744
|
+
## Memory Tools
|
|
745
|
+
|
|
746
|
+
All tools inherit from `RubyLLM::Tool` and are registered with SwarmSDK's plugin system.
|
|
747
|
+
|
|
748
|
+
### 1. MemoryWrite
|
|
749
|
+
|
|
750
|
+
**File:** `lib/swarm_memory/tools/memory_write.rb`
|
|
751
|
+
|
|
752
|
+
Creates new memory entries with structured metadata.
|
|
753
|
+
|
|
754
|
+
**Required Parameters:**
|
|
755
|
+
```ruby
|
|
756
|
+
file_path: "concept/ruby/classes.md" # Where to store
|
|
757
|
+
content: "Pure markdown content" # No frontmatter
|
|
758
|
+
title: "Ruby Classes" # Brief description
|
|
759
|
+
type: "concept" # concept|fact|skill|experience
|
|
760
|
+
tags: '["ruby", "oop"]' # JSON string of keywords
|
|
761
|
+
related: '["memory://..."]' # JSON string of cross-refs
|
|
762
|
+
domain: "programming/ruby" # Category
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
**Optional Parameters:**
|
|
766
|
+
```ruby
|
|
767
|
+
confidence: "high|medium|low" # Default: medium
|
|
768
|
+
source: "user|documentation|..." # Default: user
|
|
769
|
+
tools: '["Read", "Edit"]' # For skills only
|
|
770
|
+
permissions: { ... } # For skills only
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
**Content Length Limit:**
|
|
774
|
+
```ruby
|
|
775
|
+
WORD_LIMIT = 250
|
|
776
|
+
|
|
777
|
+
# If exceeded, tool returns error:
|
|
778
|
+
"Content exceeds 250-word limit (X words).
|
|
779
|
+
Extract key entities and split into multiple memories."
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
**Validation:**
|
|
783
|
+
```ruby
|
|
784
|
+
# Path must start with one of 4 categories:
|
|
785
|
+
- concept/
|
|
786
|
+
- fact/
|
|
787
|
+
- skill/
|
|
788
|
+
- experience/
|
|
789
|
+
|
|
790
|
+
# INVALID: documentation/, reference/, notes/, etc.
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
---
|
|
794
|
+
|
|
795
|
+
### 2. MemoryRead
|
|
796
|
+
|
|
797
|
+
**File:** `lib/swarm_memory/tools/memory_read.rb`
|
|
798
|
+
|
|
799
|
+
Reads memory entries with metadata.
|
|
800
|
+
|
|
801
|
+
**Output Format (JSON):**
|
|
802
|
+
```json
|
|
803
|
+
{
|
|
804
|
+
"content": " 1→# Ruby Classes\n 2→\n 3→Classes in Ruby...",
|
|
805
|
+
"metadata": {
|
|
806
|
+
"title": "Ruby Classes",
|
|
807
|
+
"type": "concept",
|
|
808
|
+
"confidence": "high",
|
|
809
|
+
"tags": ["ruby", "oop"],
|
|
810
|
+
"related": ["memory://concept/ruby/modules.md"],
|
|
811
|
+
"domain": "programming/ruby"
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
**Side Effect:**
|
|
817
|
+
```ruby
|
|
818
|
+
# Registers read in StorageReadTracker
|
|
819
|
+
Core::StorageReadTracker.register_read(agent_name, file_path)
|
|
820
|
+
```
|
|
821
|
+
|
|
822
|
+
---
|
|
823
|
+
|
|
824
|
+
### 3. MemoryEdit
|
|
825
|
+
|
|
826
|
+
**File:** `lib/swarm_memory/tools/memory_edit.rb`
|
|
827
|
+
|
|
828
|
+
Exact string replacement (like Edit tool for files).
|
|
829
|
+
|
|
830
|
+
**Parameters:**
|
|
831
|
+
```ruby
|
|
832
|
+
file_path: "concept/ruby/classes.md"
|
|
833
|
+
old_string: "def initialize" # Exact match required
|
|
834
|
+
new_string: "def setup" # Replacement
|
|
835
|
+
replace_all: false # Replace all occurrences?
|
|
836
|
+
```
|
|
837
|
+
|
|
838
|
+
**Enforcement:**
|
|
839
|
+
```ruby
|
|
840
|
+
# MUST read entry first:
|
|
841
|
+
unless StorageReadTracker.entry_read?(agent_name, file_path)
|
|
842
|
+
return error("Cannot edit without reading first")
|
|
843
|
+
end
|
|
844
|
+
|
|
845
|
+
# old_string must exist:
|
|
846
|
+
unless content.include?(old_string)
|
|
847
|
+
return error("old_string not found")
|
|
848
|
+
end
|
|
849
|
+
|
|
850
|
+
# old_string must be unique (unless replace_all=true):
|
|
851
|
+
if content.scan(old_string).count > 1 && !replace_all
|
|
852
|
+
return error("Found N occurrences. Use replace_all or make unique.")
|
|
853
|
+
end
|
|
854
|
+
```
|
|
855
|
+
|
|
856
|
+
---
|
|
857
|
+
|
|
858
|
+
### 4. MemoryMultiEdit
|
|
859
|
+
|
|
860
|
+
**File:** `lib/swarm_memory/tools/memory_multi_edit.rb`
|
|
861
|
+
|
|
862
|
+
Apply multiple edits sequentially to one entry.
|
|
863
|
+
|
|
864
|
+
**Input (JSON array):**
|
|
865
|
+
```json
|
|
866
|
+
[
|
|
867
|
+
{"old_string": "status: pending", "new_string": "status: resolved"},
|
|
868
|
+
{"old_string": "confidence: medium", "new_string": "confidence: high"}
|
|
869
|
+
]
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
**Behavior:**
|
|
873
|
+
- Edits applied **sequentially** (each sees previous results)
|
|
874
|
+
- **All-or-nothing** - if any edit fails, NO changes saved
|
|
875
|
+
- Shows which edits succeeded before failure
|
|
876
|
+
|
|
877
|
+
---
|
|
878
|
+
|
|
879
|
+
### 5. MemoryGlob
|
|
880
|
+
|
|
881
|
+
**File:** `lib/swarm_memory/tools/memory_glob.rb`
|
|
882
|
+
|
|
883
|
+
Pattern-based file discovery (like filesystem glob).
|
|
884
|
+
|
|
885
|
+
**Glob Syntax:**
|
|
886
|
+
```ruby
|
|
887
|
+
# Single level (direct children only):
|
|
888
|
+
"fact/*" # → fact/*.md (not nested)
|
|
889
|
+
|
|
890
|
+
# Recursive (all descendants):
|
|
891
|
+
"fact/**" # → fact/**/*.md (all nested)
|
|
892
|
+
"fact/**/*" # → same as above
|
|
893
|
+
|
|
894
|
+
# Specific pattern:
|
|
895
|
+
"skill/debug*" # → skill/debug*.md
|
|
896
|
+
"**/api-*.md" # → any path ending with api-*.md
|
|
897
|
+
```
|
|
898
|
+
|
|
899
|
+
**Examples:**
|
|
900
|
+
```ruby
|
|
901
|
+
MemoryGlob(pattern: "concept/ruby/*")
|
|
902
|
+
# Returns: concept/ruby/classes.md, concept/ruby/modules.md
|
|
903
|
+
|
|
904
|
+
MemoryGlob(pattern: "skill/**")
|
|
905
|
+
# Returns: All skills recursively
|
|
906
|
+
|
|
907
|
+
MemoryGlob(pattern: "**/*")
|
|
908
|
+
# Returns: All entries across all categories
|
|
909
|
+
```
|
|
910
|
+
|
|
911
|
+
**Limits:**
|
|
912
|
+
```ruby
|
|
913
|
+
MAX_RESULTS = 500 # Truncates with system reminder if exceeded
|
|
914
|
+
```
|
|
915
|
+
|
|
916
|
+
---
|
|
917
|
+
|
|
918
|
+
### 6. MemoryGrep
|
|
919
|
+
|
|
920
|
+
**File:** `lib/swarm_memory/tools/memory_grep.rb`
|
|
921
|
+
|
|
922
|
+
Regex-based content search.
|
|
923
|
+
|
|
924
|
+
**Parameters:**
|
|
925
|
+
```ruby
|
|
926
|
+
pattern: "TODO|FIXME" # Regex pattern
|
|
927
|
+
path: "concept/" # Optional filter
|
|
928
|
+
case_insensitive: true # Default: false
|
|
929
|
+
output_mode: "files_with_matches" # See below
|
|
930
|
+
```
|
|
931
|
+
|
|
932
|
+
**Output Modes:**
|
|
933
|
+
|
|
934
|
+
1. **files_with_matches** (default) - Just paths
|
|
935
|
+
2. **content** - Matching lines with line numbers
|
|
936
|
+
3. **count** - Match counts per file
|
|
937
|
+
|
|
938
|
+
**Path Filtering:**
|
|
939
|
+
```ruby
|
|
940
|
+
# Directory filter:
|
|
941
|
+
path: "concept/" # All in concept/
|
|
942
|
+
|
|
943
|
+
# Subdirectory filter:
|
|
944
|
+
path: "fact/api" # Only fact/api/* (not fact/api-design/)
|
|
945
|
+
|
|
946
|
+
# Specific file:
|
|
947
|
+
path: "skill/debug.md" # Just that file
|
|
948
|
+
```
|
|
949
|
+
|
|
950
|
+
---
|
|
951
|
+
|
|
952
|
+
### 7. MemoryDelete
|
|
953
|
+
|
|
954
|
+
**File:** `lib/swarm_memory/tools/memory_delete.rb`
|
|
955
|
+
|
|
956
|
+
Permanently removes entries.
|
|
957
|
+
|
|
958
|
+
**Usage:**
|
|
959
|
+
```ruby
|
|
960
|
+
MemoryDelete(file_path: "experience/temp-experiment.md")
|
|
961
|
+
```
|
|
962
|
+
|
|
963
|
+
**Warning:** Deletion is permanent. Consider using `MemoryDefrag` to identify candidates first.
|
|
964
|
+
|
|
965
|
+
---
|
|
966
|
+
|
|
967
|
+
### 8. MemoryDefrag
|
|
968
|
+
|
|
969
|
+
**File:** `lib/swarm_memory/tools/memory_defrag.rb`
|
|
970
|
+
|
|
971
|
+
Analyzes and optimizes memory storage.
|
|
972
|
+
|
|
973
|
+
**Actions:**
|
|
974
|
+
|
|
975
|
+
#### Read-Only Analysis
|
|
976
|
+
|
|
977
|
+
```ruby
|
|
978
|
+
# 1. Overall health report
|
|
979
|
+
MemoryDefrag(action: "analyze")
|
|
980
|
+
|
|
981
|
+
# 2. Find duplicates (similarity > threshold)
|
|
982
|
+
MemoryDefrag(action: "find_duplicates", similarity_threshold: 0.85)
|
|
983
|
+
|
|
984
|
+
# 3. Find low-quality entries
|
|
985
|
+
MemoryDefrag(action: "find_low_quality", confidence_filter: "low")
|
|
986
|
+
|
|
987
|
+
# 4. Find old, unused entries
|
|
988
|
+
MemoryDefrag(action: "find_archival_candidates", age_days: 90)
|
|
989
|
+
|
|
990
|
+
# 5. Find related entries that should be linked
|
|
991
|
+
MemoryDefrag(
|
|
992
|
+
action: "find_related",
|
|
993
|
+
min_similarity: 0.60, # Lower bound
|
|
994
|
+
max_similarity: 0.85 # Upper bound (above = duplicates)
|
|
995
|
+
)
|
|
996
|
+
```
|
|
997
|
+
|
|
998
|
+
#### Active Optimization (Modifies Memory)
|
|
999
|
+
|
|
1000
|
+
```ruby
|
|
1001
|
+
# 6. Create bidirectional links between related entries
|
|
1002
|
+
MemoryDefrag(action: "link_related", dry_run: true) # Preview
|
|
1003
|
+
MemoryDefrag(action: "link_related", dry_run: false) # Execute
|
|
1004
|
+
|
|
1005
|
+
# 7. Merge duplicate entries
|
|
1006
|
+
MemoryDefrag(
|
|
1007
|
+
action: "merge_duplicates",
|
|
1008
|
+
similarity_threshold: 0.85,
|
|
1009
|
+
merge_strategy: "keep_newer", # or "keep_larger" or "combine"
|
|
1010
|
+
dry_run: true
|
|
1011
|
+
)
|
|
1012
|
+
|
|
1013
|
+
# 8. Delete old stub files
|
|
1014
|
+
MemoryDefrag(
|
|
1015
|
+
action: "cleanup_stubs",
|
|
1016
|
+
age_days: 30,
|
|
1017
|
+
max_hits: 3, # Max access count to delete
|
|
1018
|
+
dry_run: false
|
|
1019
|
+
)
|
|
1020
|
+
|
|
1021
|
+
# 9. Delete low-value entries
|
|
1022
|
+
MemoryDefrag(
|
|
1023
|
+
action: "compact",
|
|
1024
|
+
min_quality_score: 20,
|
|
1025
|
+
min_age_days: 30,
|
|
1026
|
+
max_hits: 0,
|
|
1027
|
+
dry_run: false
|
|
1028
|
+
)
|
|
1029
|
+
|
|
1030
|
+
# 10. Full optimization workflow
|
|
1031
|
+
MemoryDefrag(action: "full", dry_run: true) # Preview all
|
|
1032
|
+
```
|
|
1033
|
+
|
|
1034
|
+
**Safety:** All active operations default to `dry_run: true`
|
|
1035
|
+
|
|
1036
|
+
---
|
|
1037
|
+
|
|
1038
|
+
### 9. LoadSkill
|
|
1039
|
+
|
|
1040
|
+
**File:** `lib/swarm_memory/tools/load_skill.rb`
|
|
1041
|
+
|
|
1042
|
+
Loads a skill from memory and **dynamically swaps agent tools**.
|
|
1043
|
+
|
|
1044
|
+
**Requirements:**
|
|
1045
|
+
```ruby
|
|
1046
|
+
# Skill must:
|
|
1047
|
+
- Be in skill/ hierarchy
|
|
1048
|
+
- Have type: "skill" in metadata
|
|
1049
|
+
- Optionally specify tools array
|
|
1050
|
+
- Optionally specify permissions hash
|
|
1051
|
+
```
|
|
1052
|
+
|
|
1053
|
+
**Tool Swapping Process:**
|
|
1054
|
+
```mermaid
|
|
1055
|
+
sequenceDiagram
|
|
1056
|
+
participant Agent
|
|
1057
|
+
participant LoadSkill
|
|
1058
|
+
participant Storage
|
|
1059
|
+
participant Chat
|
|
1060
|
+
participant ToolConfig
|
|
1061
|
+
|
|
1062
|
+
Agent->>LoadSkill: LoadSkill(skill/debug-api.md)
|
|
1063
|
+
LoadSkill->>Storage: read_entry(skill/debug-api.md)
|
|
1064
|
+
Storage-->>LoadSkill: Entry with metadata
|
|
1065
|
+
LoadSkill->>LoadSkill: Validate type=skill
|
|
1066
|
+
LoadSkill->>Chat: remove_mutable_tools()
|
|
1067
|
+
LoadSkill->>ToolConfig: create_tool_instance(Read)
|
|
1068
|
+
LoadSkill->>ToolConfig: create_tool_instance(Edit)
|
|
1069
|
+
LoadSkill->>ToolConfig: create_tool_instance(Bash)
|
|
1070
|
+
LoadSkill->>Chat: add_tool(Read)
|
|
1071
|
+
LoadSkill->>Chat: add_tool(Edit)
|
|
1072
|
+
LoadSkill->>Chat: add_tool(Bash)
|
|
1073
|
+
LoadSkill->>Chat: mark_skill_loaded(path)
|
|
1074
|
+
LoadSkill-->>Agent: Skill content + system reminder
|
|
1075
|
+
|
|
1076
|
+
Note over Agent,Chat: Agent now has skill's toolset
|
|
1077
|
+
```
|
|
1078
|
+
|
|
1079
|
+
**Immutable Tools:**
|
|
1080
|
+
```ruby
|
|
1081
|
+
# These tools NEVER removed during skill loading:
|
|
1082
|
+
- MemoryWrite, MemoryRead, MemoryEdit, MemoryMultiEdit
|
|
1083
|
+
- MemoryDelete, MemoryGlob, MemoryGrep, MemoryDefrag
|
|
1084
|
+
- LoadSkill (self)
|
|
1085
|
+
```
|
|
1086
|
+
|
|
1087
|
+
**System Reminder:**
|
|
1088
|
+
```
|
|
1089
|
+
<system-reminder>
|
|
1090
|
+
Your available tools have been updated.
|
|
1091
|
+
|
|
1092
|
+
New tools loaded from skill:
|
|
1093
|
+
- Read
|
|
1094
|
+
- Edit
|
|
1095
|
+
- Bash
|
|
1096
|
+
|
|
1097
|
+
Your complete toolset is now:
|
|
1098
|
+
- Bash
|
|
1099
|
+
- Edit
|
|
1100
|
+
- LoadSkill
|
|
1101
|
+
- MemoryEdit
|
|
1102
|
+
- MemoryRead
|
|
1103
|
+
- MemoryWrite
|
|
1104
|
+
- Read
|
|
1105
|
+
|
|
1106
|
+
Only use tools from this list.
|
|
1107
|
+
</system-reminder>
|
|
1108
|
+
```
|
|
1109
|
+
|
|
1110
|
+
---
|
|
1111
|
+
|
|
1112
|
+
## Optimization System
|
|
1113
|
+
|
|
1114
|
+
### Analyzer
|
|
1115
|
+
|
|
1116
|
+
**File:** `lib/swarm_memory/optimization/analyzer.rb`
|
|
1117
|
+
|
|
1118
|
+
Generates health metrics and statistics.
|
|
1119
|
+
|
|
1120
|
+
**Health Score Components (0-100):**
|
|
1121
|
+
```ruby
|
|
1122
|
+
# Frontmatter coverage (30 points)
|
|
1123
|
+
>80% entries: +30
|
|
1124
|
+
>50% entries: +20
|
|
1125
|
+
>20% entries: +10
|
|
1126
|
+
|
|
1127
|
+
# Tags coverage (20 points)
|
|
1128
|
+
>60% entries: +20
|
|
1129
|
+
>30% entries: +10
|
|
1130
|
+
|
|
1131
|
+
# Links coverage (20 points)
|
|
1132
|
+
>40% entries: +20
|
|
1133
|
+
>20% entries: +10
|
|
1134
|
+
|
|
1135
|
+
# Embedding coverage (15 points)
|
|
1136
|
+
>80% entries: +15
|
|
1137
|
+
>50% entries: +8
|
|
1138
|
+
|
|
1139
|
+
# High confidence ratio (15 points)
|
|
1140
|
+
>50% entries: +15
|
|
1141
|
+
>25% entries: +8
|
|
1142
|
+
```
|
|
1143
|
+
|
|
1144
|
+
**Health Report:**
|
|
1145
|
+
```
|
|
1146
|
+
# Memory Health Report
|
|
1147
|
+
|
|
1148
|
+
## Overview
|
|
1149
|
+
- Total entries: 42
|
|
1150
|
+
- Total size: 156.3KB
|
|
1151
|
+
- Entries with frontmatter: 38 (90%)
|
|
1152
|
+
- Entries with embeddings: 42 (100%)
|
|
1153
|
+
- Entries with tags: 35 (83%)
|
|
1154
|
+
- Entries with related links: 28 (67%)
|
|
1155
|
+
- Average quality score: 72/100
|
|
1156
|
+
|
|
1157
|
+
## By Type
|
|
1158
|
+
- concept: 15 (36%)
|
|
1159
|
+
- fact: 18 (43%)
|
|
1160
|
+
- skill: 6 (14%)
|
|
1161
|
+
- experience: 3 (7%)
|
|
1162
|
+
|
|
1163
|
+
## By Confidence
|
|
1164
|
+
- high: 25 (60%)
|
|
1165
|
+
- medium: 13 (31%)
|
|
1166
|
+
- low: 4 (10%)
|
|
1167
|
+
|
|
1168
|
+
## Health Score: 85/100
|
|
1169
|
+
Excellent - Memory is well-organized and high-quality
|
|
1170
|
+
```
|
|
1171
|
+
|
|
1172
|
+
---
|
|
1173
|
+
|
|
1174
|
+
### Defragmenter
|
|
1175
|
+
|
|
1176
|
+
**File:** `lib/swarm_memory/optimization/defragmenter.rb`
|
|
1177
|
+
|
|
1178
|
+
Performs optimization operations.
|
|
1179
|
+
|
|
1180
|
+
**Duplicate Detection:**
|
|
1181
|
+
```ruby
|
|
1182
|
+
# Uses both text and semantic similarity:
|
|
1183
|
+
text_sim = TextSimilarity.jaccard(entry1.content, entry2.content)
|
|
1184
|
+
semantic_sim = TextSimilarity.cosine(entry1.embedding, entry2.embedding)
|
|
1185
|
+
similarity = [text_sim, semantic_sim].max
|
|
1186
|
+
|
|
1187
|
+
# Pairs above threshold are candidates
|
|
1188
|
+
```
|
|
1189
|
+
|
|
1190
|
+
**Merge Strategies:**
|
|
1191
|
+
|
|
1192
|
+
1. **keep_newer** - Keep most recently updated entry
|
|
1193
|
+
2. **keep_larger** - Keep entry with most content
|
|
1194
|
+
3. **combine** - Merge content from both entries
|
|
1195
|
+
|
|
1196
|
+
**Stub Creation:**
|
|
1197
|
+
```markdown
|
|
1198
|
+
# merged → concept/ruby/advanced-classes.md
|
|
1199
|
+
|
|
1200
|
+
This entry was merged into concept/ruby/advanced-classes.md
|
|
1201
|
+
```
|
|
1202
|
+
|
|
1203
|
+
**Related Entry Linking:**
|
|
1204
|
+
```ruby
|
|
1205
|
+
# Find pairs with 60-85% semantic similarity
|
|
1206
|
+
# (not duplicates, but related content)
|
|
1207
|
+
|
|
1208
|
+
# Creates bidirectional links:
|
|
1209
|
+
entry1.metadata["related"] << "memory://path/to/entry2.md"
|
|
1210
|
+
entry2.metadata["related"] << "memory://path/to/entry1.md"
|
|
1211
|
+
```
|
|
1212
|
+
|
|
1213
|
+
---
|
|
1214
|
+
|
|
1215
|
+
## Integration with SwarmSDK
|
|
1216
|
+
|
|
1217
|
+
### SDKPlugin
|
|
1218
|
+
|
|
1219
|
+
**File:** `lib/swarm_memory/integration/sdk_plugin.rb`
|
|
1220
|
+
|
|
1221
|
+
Bridges SwarmMemory with SwarmSDK's plugin system.
|
|
1222
|
+
|
|
1223
|
+
**Plugin Interface:**
|
|
1224
|
+
```ruby
|
|
1225
|
+
class SDKPlugin < SwarmSDK::Plugin
|
|
1226
|
+
def name
|
|
1227
|
+
:memory
|
|
1228
|
+
end
|
|
1229
|
+
|
|
1230
|
+
def tools
|
|
1231
|
+
[:MemoryRead, :MemoryWrite, ...] # All memory tools
|
|
1232
|
+
end
|
|
1233
|
+
|
|
1234
|
+
def create_tool(tool_name, context)
|
|
1235
|
+
SwarmMemory.create_tool(tool_name, ...)
|
|
1236
|
+
end
|
|
1237
|
+
|
|
1238
|
+
def create_storage(agent_name:, config:)
|
|
1239
|
+
# Create adapter and storage
|
|
1240
|
+
end
|
|
1241
|
+
|
|
1242
|
+
def system_prompt_contribution(agent_definition:, storage:)
|
|
1243
|
+
# Return memory prompt based on mode
|
|
1244
|
+
end
|
|
1245
|
+
|
|
1246
|
+
def storage_enabled?(agent_definition)
|
|
1247
|
+
agent_definition.memory_enabled?
|
|
1248
|
+
end
|
|
1249
|
+
end
|
|
1250
|
+
```
|
|
1251
|
+
|
|
1252
|
+
**Lifecycle Hooks:**
|
|
1253
|
+
|
|
1254
|
+
```mermaid
|
|
1255
|
+
sequenceDiagram
|
|
1256
|
+
participant SwarmSDK
|
|
1257
|
+
participant SDKPlugin
|
|
1258
|
+
participant Agent
|
|
1259
|
+
participant Storage
|
|
1260
|
+
|
|
1261
|
+
SwarmSDK->>SDKPlugin: on_agent_initialized
|
|
1262
|
+
SDKPlugin->>Storage: create_storage(config)
|
|
1263
|
+
SDKPlugin->>Agent: filter tools by mode
|
|
1264
|
+
SDKPlugin->>Agent: register LoadSkill (special)
|
|
1265
|
+
SDKPlugin->>Agent: mark tools immutable
|
|
1266
|
+
|
|
1267
|
+
Note over SwarmSDK,Agent: Agent ready with memory
|
|
1268
|
+
|
|
1269
|
+
SwarmSDK->>SDKPlugin: on_user_message(prompt)
|
|
1270
|
+
SDKPlugin->>Storage: semantic search (skills)
|
|
1271
|
+
SDKPlugin->>Storage: semantic search (memories)
|
|
1272
|
+
SDKPlugin-->>SwarmSDK: system reminders
|
|
1273
|
+
|
|
1274
|
+
Note over SwarmSDK,Storage: Auto-discovery of relevant content
|
|
1275
|
+
```
|
|
1276
|
+
|
|
1277
|
+
**Memory Modes:**
|
|
1278
|
+
|
|
1279
|
+
| Mode | Tools | Use Case |
|
|
1280
|
+
|------|-------|----------|
|
|
1281
|
+
| **assistant** (default) | Read, Glob, Grep, Write, Edit | Learning & retrieval |
|
|
1282
|
+
| **retrieval** | Read, Glob, Grep | Read-only Q&A |
|
|
1283
|
+
| **researcher** | All tools | Knowledge extraction |
|
|
1284
|
+
|
|
1285
|
+
**Mode-Based Tool Filtering:**
|
|
1286
|
+
```ruby
|
|
1287
|
+
def on_agent_initialized(agent_name:, agent:, context:)
|
|
1288
|
+
mode = extract_mode(context[:agent_definition].memory)
|
|
1289
|
+
allowed_tools = tools_for_mode(mode)
|
|
1290
|
+
|
|
1291
|
+
# Remove tools not allowed in this mode
|
|
1292
|
+
all_memory_tools.each do |tool_name|
|
|
1293
|
+
agent.remove_tool(tool_name) unless allowed_tools.include?(tool_name)
|
|
1294
|
+
end
|
|
1295
|
+
|
|
1296
|
+
# Register LoadSkill (unless retrieval mode)
|
|
1297
|
+
unless mode == :retrieval
|
|
1298
|
+
load_skill = create_load_skill_tool(context)
|
|
1299
|
+
agent.with_tool(load_skill)
|
|
1300
|
+
end
|
|
1301
|
+
|
|
1302
|
+
# Mark mode-specific tools as immutable
|
|
1303
|
+
agent.mark_tools_immutable(immutable_tools_for_mode(mode))
|
|
1304
|
+
end
|
|
1305
|
+
```
|
|
1306
|
+
|
|
1307
|
+
**Semantic Discovery on User Messages:**
|
|
1308
|
+
```ruby
|
|
1309
|
+
def on_user_message(agent_name:, prompt:, is_first_message:)
|
|
1310
|
+
# Adaptive threshold based on query length
|
|
1311
|
+
word_count = prompt.split.size
|
|
1312
|
+
threshold = word_count < 10 ? 0.25 : 0.35
|
|
1313
|
+
|
|
1314
|
+
# Search 1: Skills (type="skill")
|
|
1315
|
+
skills = storage.semantic_index.search(
|
|
1316
|
+
query: prompt,
|
|
1317
|
+
top_k: 3,
|
|
1318
|
+
threshold: threshold,
|
|
1319
|
+
filter: { "type" => "skill" }
|
|
1320
|
+
)
|
|
1321
|
+
|
|
1322
|
+
# Search 2: Memories (concept/fact/experience)
|
|
1323
|
+
memories = storage.semantic_index.search(
|
|
1324
|
+
query: prompt,
|
|
1325
|
+
top_k: 3,
|
|
1326
|
+
threshold: threshold,
|
|
1327
|
+
filter: { "type" => ["concept", "fact", "experience"] }
|
|
1328
|
+
)
|
|
1329
|
+
|
|
1330
|
+
reminders = []
|
|
1331
|
+
reminders << build_skill_reminder(skills) if skills.any?
|
|
1332
|
+
reminders << build_memory_reminder(memories) if memories.any?
|
|
1333
|
+
reminders
|
|
1334
|
+
end
|
|
1335
|
+
```
|
|
1336
|
+
|
|
1337
|
+
**System Reminders:**
|
|
1338
|
+
```
|
|
1339
|
+
<system-reminder>
|
|
1340
|
+
🎯 Found 2 skill(s) in memory that may be relevant:
|
|
1341
|
+
|
|
1342
|
+
**Debug API Errors** (82% match)
|
|
1343
|
+
Path: `skill/debugging/api-errors.md`
|
|
1344
|
+
To use: `LoadSkill(file_path: "skill/debugging/api-errors.md")`
|
|
1345
|
+
|
|
1346
|
+
**API Troubleshooting** (68% match)
|
|
1347
|
+
Path: `skill/debugging/api-troubleshooting.md`
|
|
1348
|
+
To use: `LoadSkill(file_path: "skill/debugging/api-troubleshooting.md")`
|
|
1349
|
+
|
|
1350
|
+
If a skill matches your task: Load it to get instructions.
|
|
1351
|
+
If none match: Ignore and proceed normally.
|
|
1352
|
+
</system-reminder>
|
|
1353
|
+
|
|
1354
|
+
<system-reminder>
|
|
1355
|
+
📚 Found 3 memory entries that may provide context:
|
|
1356
|
+
|
|
1357
|
+
💡 **API Error Handling** (concept, 75% match)
|
|
1358
|
+
Path: `concept/api/error-handling.md`
|
|
1359
|
+
Read with: `MemoryRead(file_path: "concept/api/error-handling.md")`
|
|
1360
|
+
|
|
1361
|
+
📋 **API Documentation** (fact, 70% match)
|
|
1362
|
+
Path: `fact/api/endpoints.md`
|
|
1363
|
+
Read with: `MemoryRead(file_path: "fact/api/endpoints.md")`
|
|
1364
|
+
|
|
1365
|
+
🔍 **Fixed API Bug 2024** (experience, 65% match)
|
|
1366
|
+
Path: `experience/api-bug-2024.md`
|
|
1367
|
+
Read with: `MemoryRead(file_path: "experience/api-bug-2024.md")`
|
|
1368
|
+
|
|
1369
|
+
These entries may contain relevant knowledge for your task.
|
|
1370
|
+
</system-reminder>
|
|
1371
|
+
```
|
|
1372
|
+
|
|
1373
|
+
---
|
|
1374
|
+
|
|
1375
|
+
### Registration
|
|
1376
|
+
|
|
1377
|
+
**File:** `lib/swarm_memory/integration/registration.rb`
|
|
1378
|
+
|
|
1379
|
+
Auto-registers plugin with SwarmSDK when loaded.
|
|
1380
|
+
|
|
1381
|
+
```ruby
|
|
1382
|
+
# In lib/swarm_memory.rb:
|
|
1383
|
+
require_relative "swarm_memory/integration/registration"
|
|
1384
|
+
SwarmMemory::Integration::Registration.register!
|
|
1385
|
+
|
|
1386
|
+
# Registration.register! does:
|
|
1387
|
+
def register!
|
|
1388
|
+
return unless defined?(SwarmSDK)
|
|
1389
|
+
plugin = SDKPlugin.new
|
|
1390
|
+
SwarmSDK::PluginRegistry.register(plugin)
|
|
1391
|
+
end
|
|
1392
|
+
```
|
|
1393
|
+
|
|
1394
|
+
---
|
|
1395
|
+
|
|
1396
|
+
## DSL & Configuration
|
|
1397
|
+
|
|
1398
|
+
### MemoryConfig
|
|
1399
|
+
|
|
1400
|
+
**File:** `lib/swarm_memory/dsl/memory_config.rb`
|
|
1401
|
+
|
|
1402
|
+
Configuration object for agent memory.
|
|
1403
|
+
|
|
1404
|
+
**DSL Usage:**
|
|
1405
|
+
```ruby
|
|
1406
|
+
# In agent definition:
|
|
1407
|
+
memory do
|
|
1408
|
+
adapter :filesystem # Storage backend
|
|
1409
|
+
directory ".swarm/agent-memory" # Where to store
|
|
1410
|
+
mode :assistant # Tool mode
|
|
1411
|
+
end
|
|
1412
|
+
```
|
|
1413
|
+
|
|
1414
|
+
**Options:**
|
|
1415
|
+
```ruby
|
|
1416
|
+
# Adapter options (passed to adapter constructor):
|
|
1417
|
+
option :namespace, "my_agent"
|
|
1418
|
+
option :connection_pool_size, 5
|
|
1419
|
+
|
|
1420
|
+
# For filesystem adapter:
|
|
1421
|
+
directory ".swarm/memory" # Convenience for option :directory, ...
|
|
1422
|
+
```
|
|
1423
|
+
|
|
1424
|
+
**Modes:**
|
|
1425
|
+
- `:assistant` (default) - Read + Write + Edit
|
|
1426
|
+
- `:retrieval` - Read-only
|
|
1427
|
+
- `:researcher` - All tools
|
|
1428
|
+
|
|
1429
|
+
---
|
|
1430
|
+
|
|
1431
|
+
### BuilderExtension
|
|
1432
|
+
|
|
1433
|
+
**File:** `lib/swarm_memory/dsl/builder_extension.rb`
|
|
1434
|
+
|
|
1435
|
+
Injects `memory` DSL method into `SwarmSDK::Agent::Builder`.
|
|
1436
|
+
|
|
1437
|
+
```ruby
|
|
1438
|
+
module BuilderExtension
|
|
1439
|
+
def memory(&block)
|
|
1440
|
+
@memory_config = SwarmMemory::DSL::MemoryConfig.new
|
|
1441
|
+
@memory_config.instance_eval(&block) if block_given?
|
|
1442
|
+
@memory_config
|
|
1443
|
+
end
|
|
1444
|
+
end
|
|
1445
|
+
|
|
1446
|
+
# Automatically injected when SwarmMemory loads:
|
|
1447
|
+
SwarmSDK::Agent::Builder.include(SwarmMemory::DSL::BuilderExtension)
|
|
1448
|
+
```
|
|
1449
|
+
|
|
1450
|
+
**Usage in Agent Definition:**
|
|
1451
|
+
```ruby
|
|
1452
|
+
SwarmSDK.define do
|
|
1453
|
+
agent :researcher do
|
|
1454
|
+
memory do
|
|
1455
|
+
directory ".swarm/researcher-memory"
|
|
1456
|
+
mode :researcher # All tools
|
|
1457
|
+
end
|
|
1458
|
+
|
|
1459
|
+
tools :Read, :WebFetch, :Bash
|
|
1460
|
+
# Memory tools added automatically
|
|
1461
|
+
end
|
|
1462
|
+
end
|
|
1463
|
+
```
|
|
1464
|
+
|
|
1465
|
+
---
|
|
1466
|
+
|
|
1467
|
+
## CLI Commands
|
|
1468
|
+
|
|
1469
|
+
**File:** `lib/swarm_memory/cli/commands.rb`
|
|
1470
|
+
|
|
1471
|
+
Provides CLI for memory management.
|
|
1472
|
+
|
|
1473
|
+
**Commands:**
|
|
1474
|
+
|
|
1475
|
+
### 1. Setup Embeddings
|
|
1476
|
+
```bash
|
|
1477
|
+
swarm memory setup
|
|
1478
|
+
|
|
1479
|
+
# With custom model:
|
|
1480
|
+
SWARM_MEMORY_EMBEDDING_MODEL=sentence-transformers/multi-qa-MiniLM-L6-cos-v1 \
|
|
1481
|
+
swarm memory setup
|
|
1482
|
+
```
|
|
1483
|
+
|
|
1484
|
+
**What it does:**
|
|
1485
|
+
- Downloads ONNX embedding model (~90MB)
|
|
1486
|
+
- Caches to `~/.cache/huggingface/hub`
|
|
1487
|
+
- One-time setup, then reused
|
|
1488
|
+
|
|
1489
|
+
---
|
|
1490
|
+
|
|
1491
|
+
### 2. Check Status
|
|
1492
|
+
```bash
|
|
1493
|
+
swarm memory status
|
|
1494
|
+
|
|
1495
|
+
# Output:
|
|
1496
|
+
# SwarmMemory Embedding Status
|
|
1497
|
+
# ==================================================
|
|
1498
|
+
#
|
|
1499
|
+
# Status: ✓ Model cached
|
|
1500
|
+
# Model: sentence-transformers/multi-qa-MiniLM-L6-cos-v1
|
|
1501
|
+
# Dimensions: 384
|
|
1502
|
+
# Cache: /Users/user/.cache/huggingface/hub
|
|
1503
|
+
#
|
|
1504
|
+
# Semantic search is available.
|
|
1505
|
+
```
|
|
1506
|
+
|
|
1507
|
+
---
|
|
1508
|
+
|
|
1509
|
+
### 3. Show Model Path
|
|
1510
|
+
```bash
|
|
1511
|
+
swarm memory model-path
|
|
1512
|
+
# /Users/user/.cache/huggingface/hub
|
|
1513
|
+
```
|
|
1514
|
+
|
|
1515
|
+
---
|
|
1516
|
+
|
|
1517
|
+
### 4. Defragment Memory
|
|
1518
|
+
```bash
|
|
1519
|
+
swarm memory defrag .swarm/assistant-memory
|
|
1520
|
+
|
|
1521
|
+
# Runs full analysis:
|
|
1522
|
+
# - Health report
|
|
1523
|
+
# - Find duplicates
|
|
1524
|
+
# - Find low-quality entries
|
|
1525
|
+
# - Find archival candidates
|
|
1526
|
+
```
|
|
1527
|
+
|
|
1528
|
+
---
|
|
1529
|
+
|
|
1530
|
+
### 5. Rebuild Embeddings
|
|
1531
|
+
```bash
|
|
1532
|
+
swarm memory rebuild .swarm/assistant-memory
|
|
1533
|
+
|
|
1534
|
+
# With custom settings:
|
|
1535
|
+
SWARM_MEMORY_EMBEDDING_MAX_CHARS=850 \
|
|
1536
|
+
swarm memory rebuild .swarm/assistant-memory
|
|
1537
|
+
|
|
1538
|
+
# Regenerates all embeddings (useful after model change)
|
|
1539
|
+
```
|
|
1540
|
+
|
|
1541
|
+
---
|
|
1542
|
+
|
|
1543
|
+
## Skills System
|
|
1544
|
+
|
|
1545
|
+
### What are Skills?
|
|
1546
|
+
|
|
1547
|
+
Skills are **special memory entries** that:
|
|
1548
|
+
- Provide step-by-step procedures
|
|
1549
|
+
- Specify required tools
|
|
1550
|
+
- Define tool permissions
|
|
1551
|
+
- Can be loaded to **swap agent tools dynamically**
|
|
1552
|
+
|
|
1553
|
+
### Skill Structure
|
|
1554
|
+
|
|
1555
|
+
**Content (Markdown):**
|
|
1556
|
+
```markdown
|
|
1557
|
+
# Debug React Performance
|
|
1558
|
+
|
|
1559
|
+
## Prerequisites
|
|
1560
|
+
- React DevTools installed
|
|
1561
|
+
- Profiling enabled
|
|
1562
|
+
|
|
1563
|
+
## Steps
|
|
1564
|
+
|
|
1565
|
+
1. Open DevTools → Profiler tab
|
|
1566
|
+
2. Click "Record"
|
|
1567
|
+
3. Perform the slow interaction
|
|
1568
|
+
4. Stop recording
|
|
1569
|
+
5. Analyze flamegraph:
|
|
1570
|
+
- Look for wide bars (long renders)
|
|
1571
|
+
- Check component names
|
|
1572
|
+
- Identify render causes
|
|
1573
|
+
|
|
1574
|
+
## Common Causes
|
|
1575
|
+
- Missing useMemo/useCallback
|
|
1576
|
+
- Lifting state too high
|
|
1577
|
+
- Expensive calculations in render
|
|
1578
|
+
```
|
|
1579
|
+
|
|
1580
|
+
**Metadata (YAML sidecar):**
|
|
1581
|
+
```yaml
|
|
1582
|
+
title: Debug React Performance
|
|
1583
|
+
type: skill
|
|
1584
|
+
confidence: high
|
|
1585
|
+
tags:
|
|
1586
|
+
- react
|
|
1587
|
+
- performance
|
|
1588
|
+
- debugging
|
|
1589
|
+
- profiling
|
|
1590
|
+
related:
|
|
1591
|
+
- memory://concept/react/reconciliation.md
|
|
1592
|
+
- memory://skill/debugging/performance-general.md
|
|
1593
|
+
domain: frontend/react
|
|
1594
|
+
source: experimentation
|
|
1595
|
+
tools:
|
|
1596
|
+
- Read
|
|
1597
|
+
- Edit
|
|
1598
|
+
- Bash
|
|
1599
|
+
- Grep
|
|
1600
|
+
permissions:
|
|
1601
|
+
Bash:
|
|
1602
|
+
allowed_commands:
|
|
1603
|
+
- "^npm "
|
|
1604
|
+
- "^yarn "
|
|
1605
|
+
Write:
|
|
1606
|
+
denied_paths:
|
|
1607
|
+
- "src/secrets/**"
|
|
1608
|
+
```
|
|
1609
|
+
|
|
1610
|
+
### Virtual Skills
|
|
1611
|
+
|
|
1612
|
+
Built-in skills that don't consume storage:
|
|
1613
|
+
- `skill/meta/deep-learning.md` - Systematic learning protocol
|
|
1614
|
+
|
|
1615
|
+
**Deep Learning Protocol:**
|
|
1616
|
+
```markdown
|
|
1617
|
+
# Deep Learning Protocol
|
|
1618
|
+
|
|
1619
|
+
Use this when asked to learn deeply about a topic.
|
|
1620
|
+
|
|
1621
|
+
## Steps
|
|
1622
|
+
1. Define Scope
|
|
1623
|
+
2. Research Broadly (Overview)
|
|
1624
|
+
3. Research Deeply (Specifics)
|
|
1625
|
+
4. Build Practical Skills
|
|
1626
|
+
5. Self-Test Understanding
|
|
1627
|
+
6. Check Coverage (MemoryGrep)
|
|
1628
|
+
7. Identify Knowledge Gaps
|
|
1629
|
+
8. Fill Gaps
|
|
1630
|
+
9. Verify Completion
|
|
1631
|
+
10. Report Completion
|
|
1632
|
+
|
|
1633
|
+
## Success Criteria
|
|
1634
|
+
- Have concepts (how it works)
|
|
1635
|
+
- Have facts (specifics)
|
|
1636
|
+
- Have skills (how to use)
|
|
1637
|
+
- Can explain clearly
|
|
1638
|
+
- Can apply practically
|
|
1639
|
+
```
|
|
1640
|
+
|
|
1641
|
+
---
|
|
1642
|
+
|
|
1643
|
+
## Data Flow & Lifecycle
|
|
1644
|
+
|
|
1645
|
+
### Write Flow
|
|
1646
|
+
|
|
1647
|
+
```mermaid
|
|
1648
|
+
sequenceDiagram
|
|
1649
|
+
participant Agent
|
|
1650
|
+
participant MemoryWrite
|
|
1651
|
+
participant Storage
|
|
1652
|
+
participant Embedder
|
|
1653
|
+
participant Adapter
|
|
1654
|
+
|
|
1655
|
+
Agent->>MemoryWrite: execute(file_path, content, title, metadata)
|
|
1656
|
+
MemoryWrite->>MemoryWrite: Validate word count (≤250)
|
|
1657
|
+
MemoryWrite->>MemoryWrite: Parse JSON params
|
|
1658
|
+
MemoryWrite->>Storage: write(file_path, content, title, metadata)
|
|
1659
|
+
Storage->>Storage: PathNormalizer.normalize(file_path)
|
|
1660
|
+
Storage->>Storage: build_searchable_text(content, title, metadata)
|
|
1661
|
+
Storage->>Embedder: embed(searchable_text)
|
|
1662
|
+
Embedder-->>Storage: embedding[384]
|
|
1663
|
+
Storage->>Storage: Emit LogStream event
|
|
1664
|
+
Storage->>Adapter: write(file_path, content, title, embedding, metadata)
|
|
1665
|
+
Adapter->>Adapter: with_write_lock (flock)
|
|
1666
|
+
Adapter->>Adapter: @semaphore.acquire (fiber-safe)
|
|
1667
|
+
Adapter->>Adapter: Check size limits
|
|
1668
|
+
Adapter->>Adapter: Write .md file
|
|
1669
|
+
Adapter->>Adapter: Write .yml file
|
|
1670
|
+
Adapter->>Adapter: Write .emb file
|
|
1671
|
+
Adapter->>Adapter: Update index
|
|
1672
|
+
Adapter-->>Storage: Entry
|
|
1673
|
+
Storage-->>MemoryWrite: Entry
|
|
1674
|
+
MemoryWrite-->>Agent: "Stored at memory://path (size)"
|
|
1675
|
+
```
|
|
1676
|
+
|
|
1677
|
+
### Read Flow
|
|
1678
|
+
|
|
1679
|
+
```mermaid
|
|
1680
|
+
sequenceDiagram
|
|
1681
|
+
participant Agent
|
|
1682
|
+
participant MemoryRead
|
|
1683
|
+
participant Storage
|
|
1684
|
+
participant Adapter
|
|
1685
|
+
participant Tracker
|
|
1686
|
+
|
|
1687
|
+
Agent->>MemoryRead: execute(file_path)
|
|
1688
|
+
MemoryRead->>Tracker: register_read(agent_name, file_path)
|
|
1689
|
+
MemoryRead->>Storage: read_entry(file_path)
|
|
1690
|
+
Storage->>Adapter: read_entry(file_path)
|
|
1691
|
+
Adapter->>Adapter: Check virtual entries
|
|
1692
|
+
Adapter->>Adapter: Check stubs (follow redirects)
|
|
1693
|
+
Adapter->>Adapter: Read .md file
|
|
1694
|
+
Adapter->>Adapter: Read .yml file
|
|
1695
|
+
Adapter->>Adapter: Read .emb file (if exists)
|
|
1696
|
+
Adapter->>Adapter: increment_hits(file_path)
|
|
1697
|
+
Adapter-->>Storage: Entry
|
|
1698
|
+
Storage-->>MemoryRead: Entry
|
|
1699
|
+
MemoryRead->>MemoryRead: format_as_json(entry)
|
|
1700
|
+
MemoryRead-->>Agent: JSON{content, metadata}
|
|
1701
|
+
```
|
|
1702
|
+
|
|
1703
|
+
### Edit Flow
|
|
1704
|
+
|
|
1705
|
+
```mermaid
|
|
1706
|
+
sequenceDiagram
|
|
1707
|
+
participant Agent
|
|
1708
|
+
participant MemoryEdit
|
|
1709
|
+
participant Tracker
|
|
1710
|
+
participant Storage
|
|
1711
|
+
participant Adapter
|
|
1712
|
+
|
|
1713
|
+
Agent->>MemoryEdit: execute(file_path, old_string, new_string)
|
|
1714
|
+
MemoryEdit->>Tracker: entry_read?(agent_name, file_path)
|
|
1715
|
+
Tracker-->>MemoryEdit: true/false
|
|
1716
|
+
alt Not read
|
|
1717
|
+
MemoryEdit-->>Agent: Error: Must read first
|
|
1718
|
+
end
|
|
1719
|
+
MemoryEdit->>Storage: read(file_path)
|
|
1720
|
+
Storage-->>MemoryEdit: content
|
|
1721
|
+
MemoryEdit->>MemoryEdit: Check old_string exists
|
|
1722
|
+
MemoryEdit->>MemoryEdit: Count occurrences
|
|
1723
|
+
alt Multiple occurrences && !replace_all
|
|
1724
|
+
MemoryEdit-->>Agent: Error: Not unique
|
|
1725
|
+
end
|
|
1726
|
+
MemoryEdit->>MemoryEdit: Perform replacement
|
|
1727
|
+
MemoryEdit->>Storage: read_entry(file_path)
|
|
1728
|
+
Storage-->>MemoryEdit: Entry (for title)
|
|
1729
|
+
MemoryEdit->>Storage: write(file_path, new_content, title)
|
|
1730
|
+
Storage-->>MemoryEdit: Entry
|
|
1731
|
+
MemoryEdit-->>Agent: "Replaced N occurrence(s)"
|
|
1732
|
+
```
|
|
1733
|
+
|
|
1734
|
+
### Semantic Discovery Flow
|
|
1735
|
+
|
|
1736
|
+
```mermaid
|
|
1737
|
+
sequenceDiagram
|
|
1738
|
+
participant User
|
|
1739
|
+
participant SwarmSDK
|
|
1740
|
+
participant SDKPlugin
|
|
1741
|
+
participant SemanticIndex
|
|
1742
|
+
participant Embedder
|
|
1743
|
+
participant Agent
|
|
1744
|
+
|
|
1745
|
+
User->>SwarmSDK: Send message
|
|
1746
|
+
SwarmSDK->>SDKPlugin: on_user_message(prompt)
|
|
1747
|
+
|
|
1748
|
+
par Parallel Search
|
|
1749
|
+
SDKPlugin->>SemanticIndex: search(query, filter={type:"skill"})
|
|
1750
|
+
SemanticIndex->>Embedder: embed(query)
|
|
1751
|
+
Embedder-->>SemanticIndex: query_embedding
|
|
1752
|
+
SemanticIndex->>SemanticIndex: semantic_search + keyword_match
|
|
1753
|
+
SemanticIndex-->>SDKPlugin: skills[]
|
|
1754
|
+
and
|
|
1755
|
+
SDKPlugin->>SemanticIndex: search(query, filter={type:["concept","fact","experience"]})
|
|
1756
|
+
SemanticIndex-->>SDKPlugin: memories[]
|
|
1757
|
+
end
|
|
1758
|
+
|
|
1759
|
+
SDKPlugin->>SDKPlugin: Emit LogStream events
|
|
1760
|
+
SDKPlugin->>SDKPlugin: build_skill_reminder(skills)
|
|
1761
|
+
SDKPlugin->>SDKPlugin: build_memory_reminder(memories)
|
|
1762
|
+
SDKPlugin-->>SwarmSDK: [skill_reminder, memory_reminder]
|
|
1763
|
+
SwarmSDK->>Agent: Inject system reminders
|
|
1764
|
+
Agent->>User: Response with context
|
|
1765
|
+
```
|
|
1766
|
+
|
|
1767
|
+
### LoadSkill Flow
|
|
1768
|
+
|
|
1769
|
+
```mermaid
|
|
1770
|
+
sequenceDiagram
|
|
1771
|
+
participant Agent
|
|
1772
|
+
participant LoadSkill
|
|
1773
|
+
participant Storage
|
|
1774
|
+
participant Chat
|
|
1775
|
+
participant ToolConfig
|
|
1776
|
+
|
|
1777
|
+
Agent->>LoadSkill: LoadSkill(skill/debug-api.md)
|
|
1778
|
+
LoadSkill->>LoadSkill: Validate path starts with "skill/"
|
|
1779
|
+
LoadSkill->>Storage: read_entry(skill/debug-api.md)
|
|
1780
|
+
Storage-->>LoadSkill: Entry
|
|
1781
|
+
LoadSkill->>LoadSkill: Validate type=="skill"
|
|
1782
|
+
LoadSkill->>LoadSkill: Extract tools & permissions
|
|
1783
|
+
|
|
1784
|
+
LoadSkill->>Chat: remove_mutable_tools()
|
|
1785
|
+
Note over Chat: Keeps immutable tools:<br/>Memory*, LoadSkill
|
|
1786
|
+
|
|
1787
|
+
loop For each required tool
|
|
1788
|
+
LoadSkill->>ToolConfig: create_tool_instance(tool_name)
|
|
1789
|
+
ToolConfig-->>LoadSkill: tool_instance
|
|
1790
|
+
LoadSkill->>ToolConfig: wrap_with_permissions(tool, perms)
|
|
1791
|
+
ToolConfig-->>LoadSkill: wrapped_tool
|
|
1792
|
+
LoadSkill->>Chat: add_tool(wrapped_tool)
|
|
1793
|
+
end
|
|
1794
|
+
|
|
1795
|
+
LoadSkill->>Chat: mark_skill_loaded(path)
|
|
1796
|
+
LoadSkill->>LoadSkill: format_with_line_numbers(content)
|
|
1797
|
+
LoadSkill->>LoadSkill: build_toolset_reminder(tools)
|
|
1798
|
+
LoadSkill-->>Agent: Content + System Reminder
|
|
1799
|
+
|
|
1800
|
+
Note over Agent: Agent now has skill's tools
|
|
1801
|
+
```
|
|
1802
|
+
|
|
1803
|
+
---
|
|
1804
|
+
|
|
1805
|
+
## Testing Strategy
|
|
1806
|
+
|
|
1807
|
+
### Test Structure
|
|
1808
|
+
|
|
1809
|
+
```
|
|
1810
|
+
test/swarm_memory/
|
|
1811
|
+
├── adapter_registry_test.rb
|
|
1812
|
+
├── adapters/
|
|
1813
|
+
│ ├── filesystem_adapter_test.rb
|
|
1814
|
+
│ ├── file_locking_test.rb
|
|
1815
|
+
│ └── virtual_entries_test.rb
|
|
1816
|
+
├── core/
|
|
1817
|
+
│ ├── entry_test.rb
|
|
1818
|
+
│ ├── frontmatter_parser_test.rb
|
|
1819
|
+
│ └── path_normalizer_test.rb
|
|
1820
|
+
├── tools/
|
|
1821
|
+
│ ├── memory_write_test.rb
|
|
1822
|
+
│ ├── memory_read_test.rb
|
|
1823
|
+
│ ├── memory_defrag_test.rb
|
|
1824
|
+
│ └── load_skill_test.rb
|
|
1825
|
+
├── integration/
|
|
1826
|
+
│ ├── registration_test.rb
|
|
1827
|
+
│ ├── swarm_sdk_integration_test.rb
|
|
1828
|
+
│ └── skills_integration_test.rb
|
|
1829
|
+
└── optimization/
|
|
1830
|
+
└── defragmenter_test.rb
|
|
1831
|
+
```
|
|
1832
|
+
|
|
1833
|
+
### Test Helpers
|
|
1834
|
+
|
|
1835
|
+
**`swarm_memory_test_helper.rb`:**
|
|
1836
|
+
```ruby
|
|
1837
|
+
def create_temp_storage
|
|
1838
|
+
temp_dir = File.join(Dir.tmpdir, "test-storage-#{SecureRandom.hex}")
|
|
1839
|
+
adapter = SwarmMemory::Adapters::FilesystemAdapter.new(directory: temp_dir)
|
|
1840
|
+
embedder = SwarmMemory::Embeddings::InformersEmbedder.new
|
|
1841
|
+
SwarmMemory::Core::Storage.new(adapter: adapter, embedder: embedder)
|
|
1842
|
+
end
|
|
1843
|
+
|
|
1844
|
+
def cleanup_storage(storage)
|
|
1845
|
+
FileUtils.rm_rf(storage.adapter.instance_variable_get(:@directory))
|
|
1846
|
+
end
|
|
1847
|
+
```
|
|
1848
|
+
|
|
1849
|
+
### Key Test Patterns
|
|
1850
|
+
|
|
1851
|
+
**1. Tool Execution Tests:**
|
|
1852
|
+
```ruby
|
|
1853
|
+
def test_write_entry_with_minimal_metadata
|
|
1854
|
+
result = @tool.execute(
|
|
1855
|
+
file_path: "test/simple.md",
|
|
1856
|
+
content: "Simple content",
|
|
1857
|
+
title: "Simple Entry",
|
|
1858
|
+
type: "fact",
|
|
1859
|
+
tags: ["test"],
|
|
1860
|
+
related: [],
|
|
1861
|
+
domain: "testing"
|
|
1862
|
+
)
|
|
1863
|
+
|
|
1864
|
+
assert_match(%r{Stored at memory://test/simple.md}, result)
|
|
1865
|
+
entry = @storage.read_entry(file_path: "test/simple.md")
|
|
1866
|
+
assert_equal("Simple content", entry.content)
|
|
1867
|
+
end
|
|
1868
|
+
```
|
|
1869
|
+
|
|
1870
|
+
**2. Concurrency Tests:**
|
|
1871
|
+
```ruby
|
|
1872
|
+
def test_concurrent_writes
|
|
1873
|
+
threads = 10.times.map do |i|
|
|
1874
|
+
Thread.new do
|
|
1875
|
+
@adapter.write(
|
|
1876
|
+
file_path: "concurrent/entry-#{i}.md",
|
|
1877
|
+
content: "content #{i}",
|
|
1878
|
+
title: "Entry #{i}"
|
|
1879
|
+
)
|
|
1880
|
+
end
|
|
1881
|
+
end
|
|
1882
|
+
|
|
1883
|
+
threads.each(&:join)
|
|
1884
|
+
assert_equal(10, @adapter.list(prefix: "concurrent").size)
|
|
1885
|
+
end
|
|
1886
|
+
```
|
|
1887
|
+
|
|
1888
|
+
**3. Integration Tests:**
|
|
1889
|
+
```ruby
|
|
1890
|
+
def test_swarm_sdk_integration
|
|
1891
|
+
swarm = SwarmSDK.define do
|
|
1892
|
+
agent :tester do
|
|
1893
|
+
memory do
|
|
1894
|
+
directory temp_memory_dir
|
|
1895
|
+
mode :assistant
|
|
1896
|
+
end
|
|
1897
|
+
tools :Read
|
|
1898
|
+
end
|
|
1899
|
+
end
|
|
1900
|
+
|
|
1901
|
+
# Memory tools automatically added
|
|
1902
|
+
assert_includes(swarm.agent(:tester).tools.keys, :MemoryWrite)
|
|
1903
|
+
end
|
|
1904
|
+
```
|
|
1905
|
+
|
|
1906
|
+
---
|
|
1907
|
+
|
|
1908
|
+
## Environment Variables Reference
|
|
1909
|
+
|
|
1910
|
+
### Embedding Configuration
|
|
1911
|
+
|
|
1912
|
+
```bash
|
|
1913
|
+
# Model selection (default: multi-qa-MiniLM-L6-cos-v1)
|
|
1914
|
+
SWARM_MEMORY_EMBEDDING_MODEL=sentence-transformers/multi-qa-MiniLM-L6-cos-v1
|
|
1915
|
+
|
|
1916
|
+
# Searchable text length (default: 1200, -1 = unlimited)
|
|
1917
|
+
SWARM_MEMORY_EMBEDDING_MAX_CHARS=1200
|
|
1918
|
+
```
|
|
1919
|
+
|
|
1920
|
+
### Search Configuration
|
|
1921
|
+
|
|
1922
|
+
```bash
|
|
1923
|
+
# Hybrid search weights (default: 0.5 each)
|
|
1924
|
+
SWARM_MEMORY_SEMANTIC_WEIGHT=0.5
|
|
1925
|
+
SWARM_MEMORY_KEYWORD_WEIGHT=0.5
|
|
1926
|
+
|
|
1927
|
+
# Adaptive thresholds (default: 0.35 normal, 0.25 short)
|
|
1928
|
+
SWARM_MEMORY_DISCOVERY_THRESHOLD=0.35 # Normal queries
|
|
1929
|
+
SWARM_MEMORY_DISCOVERY_THRESHOLD_SHORT=0.25 # Short queries
|
|
1930
|
+
SWARM_MEMORY_ADAPTIVE_WORD_CUTOFF=10 # Cutoff for "short"
|
|
1931
|
+
```
|
|
1932
|
+
|
|
1933
|
+
---
|
|
1934
|
+
|
|
1935
|
+
## Architecture Diagrams
|
|
1936
|
+
|
|
1937
|
+
### Component Layers
|
|
1938
|
+
|
|
1939
|
+
```mermaid
|
|
1940
|
+
graph TB
|
|
1941
|
+
subgraph "Layer 5: Integration"
|
|
1942
|
+
SDKPlugin[SDKPlugin]
|
|
1943
|
+
DSL[DSL & Config]
|
|
1944
|
+
CLI[CLI Commands]
|
|
1945
|
+
end
|
|
1946
|
+
|
|
1947
|
+
subgraph "Layer 4: Tools"
|
|
1948
|
+
MemWrite[MemoryWrite]
|
|
1949
|
+
MemRead[MemoryRead]
|
|
1950
|
+
MemEdit[MemoryEdit]
|
|
1951
|
+
MemGlob[MemoryGlob]
|
|
1952
|
+
MemGrep[MemoryGrep]
|
|
1953
|
+
LoadSkill[LoadSkill]
|
|
1954
|
+
MemDefrag[MemoryDefrag]
|
|
1955
|
+
end
|
|
1956
|
+
|
|
1957
|
+
subgraph "Layer 3: Optimization"
|
|
1958
|
+
Analyzer[Analyzer]
|
|
1959
|
+
Defrag[Defragmenter]
|
|
1960
|
+
end
|
|
1961
|
+
|
|
1962
|
+
subgraph "Layer 2: Core"
|
|
1963
|
+
Storage[Storage]
|
|
1964
|
+
SemanticIndex[SemanticIndex]
|
|
1965
|
+
Entry[Entry]
|
|
1966
|
+
PathNorm[PathNormalizer]
|
|
1967
|
+
ReadTracker[ReadTracker]
|
|
1968
|
+
end
|
|
1969
|
+
|
|
1970
|
+
subgraph "Layer 1: Adapters & Embeddings"
|
|
1971
|
+
FSAdapter[FilesystemAdapter]
|
|
1972
|
+
Embedder[InformersEmbedder]
|
|
1973
|
+
TextSim[TextSimilarity]
|
|
1974
|
+
end
|
|
1975
|
+
|
|
1976
|
+
SDKPlugin --> MemWrite
|
|
1977
|
+
SDKPlugin --> LoadSkill
|
|
1978
|
+
MemWrite --> Storage
|
|
1979
|
+
MemRead --> Storage
|
|
1980
|
+
LoadSkill --> Storage
|
|
1981
|
+
MemDefrag --> Defrag
|
|
1982
|
+
Defrag --> Analyzer
|
|
1983
|
+
Storage --> SemanticIndex
|
|
1984
|
+
Storage --> FSAdapter
|
|
1985
|
+
SemanticIndex --> Embedder
|
|
1986
|
+
SemanticIndex --> TextSim
|
|
1987
|
+
|
|
1988
|
+
style SDKPlugin fill:#f3e5f5
|
|
1989
|
+
style Storage fill:#fff4e1
|
|
1990
|
+
style Embedder fill:#e8f5e9
|
|
1991
|
+
```
|
|
1992
|
+
|
|
1993
|
+
### File Organization
|
|
1994
|
+
|
|
1995
|
+
```
|
|
1996
|
+
lib/swarm_memory/
|
|
1997
|
+
├── swarm_memory.rb # Entry point, auto-registration
|
|
1998
|
+
├── version.rb # VERSION constant
|
|
1999
|
+
├── errors.rb # Error classes
|
|
2000
|
+
├── utils.rb # Utility methods
|
|
2001
|
+
│
|
|
2002
|
+
├── core/ # Core system
|
|
2003
|
+
│ ├── entry.rb # Entry struct
|
|
2004
|
+
│ ├── storage.rb # Orchestrator
|
|
2005
|
+
│ ├── semantic_index.rb # Hybrid search
|
|
2006
|
+
│ ├── frontmatter_parser.rb # YAML parsing
|
|
2007
|
+
│ ├── metadata_extractor.rb # Quality scoring
|
|
2008
|
+
│ ├── path_normalizer.rb # Path validation
|
|
2009
|
+
│ └── storage_read_tracker.rb # Read enforcement
|
|
2010
|
+
│
|
|
2011
|
+
├── adapters/ # Storage backends
|
|
2012
|
+
│ ├── base.rb # Abstract interface
|
|
2013
|
+
│ └── filesystem_adapter.rb # Real filesystem
|
|
2014
|
+
│
|
|
2015
|
+
├── embeddings/ # Embedding generation
|
|
2016
|
+
│ ├── embedder.rb # Abstract interface
|
|
2017
|
+
│ └── informers_embedder.rb # ONNX-based
|
|
2018
|
+
│
|
|
2019
|
+
├── search/ # Search implementations
|
|
2020
|
+
│ ├── semantic_search.rb # Embedding-based
|
|
2021
|
+
│ ├── text_search.rb # Glob/grep wrapper
|
|
2022
|
+
│ └── text_similarity.rb # Similarity metrics
|
|
2023
|
+
│
|
|
2024
|
+
├── tools/ # Memory tools
|
|
2025
|
+
│ ├── memory_write.rb
|
|
2026
|
+
│ ├── memory_read.rb
|
|
2027
|
+
│ ├── memory_edit.rb
|
|
2028
|
+
│ ├── memory_multi_edit.rb
|
|
2029
|
+
│ ├── memory_delete.rb
|
|
2030
|
+
│ ├── memory_glob.rb
|
|
2031
|
+
│ ├── memory_grep.rb
|
|
2032
|
+
│ ├── memory_defrag.rb
|
|
2033
|
+
│ └── load_skill.rb
|
|
2034
|
+
│
|
|
2035
|
+
├── optimization/ # Memory optimization
|
|
2036
|
+
│ ├── analyzer.rb # Health metrics
|
|
2037
|
+
│ └── defragmenter.rb # Optimization ops
|
|
2038
|
+
│
|
|
2039
|
+
├── integration/ # SwarmSDK/CLI integration
|
|
2040
|
+
│ ├── sdk_plugin.rb # Plugin implementation
|
|
2041
|
+
│ ├── registration.rb # Auto-registration
|
|
2042
|
+
│ ├── configuration.rb # Config wrapper
|
|
2043
|
+
│ └── cli_registration.rb # CLI registration
|
|
2044
|
+
│
|
|
2045
|
+
├── dsl/ # DSL for agent config
|
|
2046
|
+
│ ├── memory_config.rb # Config object
|
|
2047
|
+
│ └── builder_extension.rb # DSL injection
|
|
2048
|
+
│
|
|
2049
|
+
├── cli/ # CLI commands
|
|
2050
|
+
│ └── commands.rb # swarm memory ...
|
|
2051
|
+
│
|
|
2052
|
+
├── chat_extension.rb # Chat#remove_tool extension
|
|
2053
|
+
│
|
|
2054
|
+
├── prompts/ # System prompts
|
|
2055
|
+
│ ├── memory_assistant.md.erb
|
|
2056
|
+
│ ├── memory_retrieval.md.erb
|
|
2057
|
+
│ └── memory_researcher.md.erb
|
|
2058
|
+
│
|
|
2059
|
+
└── skills/ # Built-in skills
|
|
2060
|
+
└── meta/
|
|
2061
|
+
├── deep-learning.md
|
|
2062
|
+
└── deep-learning.yml
|
|
2063
|
+
```
|
|
2064
|
+
|
|
2065
|
+
---
|
|
2066
|
+
|
|
2067
|
+
## Summary
|
|
2068
|
+
|
|
2069
|
+
SwarmMemory provides a complete **persistent memory system with semantic search** for SwarmSDK agents:
|
|
2070
|
+
|
|
2071
|
+
✅ **Hierarchical Storage** - 4 fixed categories: concept/, fact/, skill/, experience/
|
|
2072
|
+
✅ **Hybrid Search** - Combines semantic similarity + keyword matching
|
|
2073
|
+
✅ **Tool Swapping** - LoadSkill dynamically adapts agent capabilities
|
|
2074
|
+
✅ **Memory Optimization** - Automated duplicate detection, linking, and cleanup
|
|
2075
|
+
✅ **Plugin Architecture** - Clean integration with SwarmSDK
|
|
2076
|
+
✅ **Semantic Discovery** - Auto-surfaces relevant skills and memories
|
|
2077
|
+
✅ **Multiple Modes** - assistant/retrieval/researcher with different tool sets
|
|
2078
|
+
✅ **CLI Management** - Setup, status, defrag, rebuild commands
|
|
2079
|
+
✅ **Production Ready** - Thread-safe, fiber-safe, cross-process locking
|
|
2080
|
+
|
|
2081
|
+
**Key Innovations:**
|
|
2082
|
+
- **250-word memory limit** forces focused, searchable entries
|
|
2083
|
+
- **Read-before-edit enforcement** prevents blind modifications
|
|
2084
|
+
- **Immutable tools** ensure memory tools never removed by LoadSkill
|
|
2085
|
+
- **Adaptive thresholds** handle short and long queries differently
|
|
2086
|
+
- **Virtual skills** provide meta-capabilities without storage cost
|
|
2087
|
+
|
|
2088
|
+
---
|
|
2089
|
+
|
|
2090
|
+
**End of Technical Reference**
|