From ad5573fbb914781b5c81c2fc2bfe75332e432e00 Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Sun, 8 Mar 2026 21:56:30 +0000 Subject: [PATCH 1/8] feat: agregar config SharedKnowledge a schema.go MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Nuevo tipo SharedKnowledgeToolCfg con enabled, dir, db_path - Agregar campo SharedKnowledge a ToolsCfg - Issue 0018: Shared Knowledge (fase 1) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- internal/config/schema.go | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/internal/config/schema.go b/internal/config/schema.go index a9258d1..0560fad 100644 --- a/internal/config/schema.go +++ b/internal/config/schema.go @@ -121,14 +121,15 @@ type LLMRateLimitCfg struct { // ── Tools ───────────────────────────────────────────────────────────────── type ToolsCfg struct { - SSH SSHToolCfg `yaml:"ssh"` - HTTP HTTPToolCfg `yaml:"http"` - Scripts ScriptsCfg `yaml:"scripts"` - FileOps FileOpsCfg `yaml:"file_ops"` - Matrix MatrixToolCfg `yaml:"matrix_send"` - MCP MCPToolCfg `yaml:"mcp"` - Memory MemoryToolCfg `yaml:"memory"` - Knowledge KnowledgeToolCfg `yaml:"knowledge"` + SSH SSHToolCfg `yaml:"ssh"` + HTTP HTTPToolCfg `yaml:"http"` + Scripts ScriptsCfg `yaml:"scripts"` + FileOps FileOpsCfg `yaml:"file_ops"` + Matrix MatrixToolCfg `yaml:"matrix_send"` + MCP MCPToolCfg `yaml:"mcp"` + Memory MemoryToolCfg `yaml:"memory"` + Knowledge KnowledgeToolCfg `yaml:"knowledge"` + SharedKnowledge SharedKnowledgeToolCfg `yaml:"shared_knowledge"` } type MatrixToolCfg struct { @@ -140,6 +141,12 @@ type KnowledgeToolCfg struct { Dir string `yaml:"dir"` // default: "./knowledge" (relative to agent dir) } +type SharedKnowledgeToolCfg struct { + Enabled bool `yaml:"enabled"` // default false + Dir string `yaml:"dir"` // default "knowledges" (relative to project root) + DBPath string `yaml:"db_path"` // default "knowledges/data/knowledge.db" +} + type SSHToolCfg struct { Enabled bool `yaml:"enabled"` AllowedTargets []string `yaml:"allowed_targets"` From 6f125d3bb7083d670937498c84443fdc50170548 Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Sun, 8 Mar 2026 21:56:37 +0000 Subject: [PATCH 2/8] feat: habilitar WAL mode en knowledge store para concurrencia MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Ejecutar PRAGMA journal_mode=WAL al abrir la DB - Permite múltiples lectores + single writer concurrentes - Mejora el rendimiento del shared knowledge compartido - Issue 0018: Shared Knowledge (fase 2a) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- shell/knowledge/store.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/shell/knowledge/store.go b/shell/knowledge/store.go index d56dd6f..d850b15 100644 --- a/shell/knowledge/store.go +++ b/shell/knowledge/store.go @@ -58,6 +58,13 @@ func New(dir, dbPath string, logger *slog.Logger) (*FileStore, error) { if err != nil { return nil, fmt.Errorf("open knowledge db: %w", err) } + + // Enable WAL mode for better concurrency (allows multiple readers + single writer) + if _, err := db.Exec("PRAGMA journal_mode=WAL"); err != nil { + db.Close() + return nil, fmt.Errorf("enable WAL mode: %w", err) + } + if _, err := db.Exec(ftsSchema); err != nil { db.Close() return nil, fmt.Errorf("create knowledge fts5 table: %w", err) From 99991c52cf5ce329575f108b9ead3cf0369d6cf9 Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Sun, 8 Mar 2026 21:56:45 +0000 Subject: [PATCH 3/8] feat: crear shared knowledge tools MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - NewSharedKnowledgeTools genera 4 tools prefijadas shared_knowledge_* - shared_knowledge_search/read/write/list - Descripciones indican que es compartido entre agentes - Tests completos con coexistencia privado/compartido - Issue 0018: Shared Knowledge (fase 2b) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- tools/knowledgetools/shared.go | 141 +++++++++++++++++++ tools/knowledgetools/shared_test.go | 202 ++++++++++++++++++++++++++++ 2 files changed, 343 insertions(+) create mode 100644 tools/knowledgetools/shared.go create mode 100644 tools/knowledgetools/shared_test.go diff --git a/tools/knowledgetools/shared.go b/tools/knowledgetools/shared.go new file mode 100644 index 0000000..98c664e --- /dev/null +++ b/tools/knowledgetools/shared.go @@ -0,0 +1,141 @@ +package knowledgetools + +import ( + "context" + "fmt" + "strings" + + "github.com/enmanuel/agents/pkg/knowledge" + "github.com/enmanuel/agents/tools" +) + +// NewSharedKnowledgeTools creates all shared knowledge tools backed by the given store. +// These tools provide access to the shared knowledge base accessible by all agents. +func NewSharedKnowledgeTools(store KnowledgeStore) []tools.Tool { + return []tools.Tool{ + newSharedKnowledgeSearch(store), + newSharedKnowledgeRead(store), + newSharedKnowledgeWrite(store), + newSharedKnowledgeList(store), + } +} + +// newSharedKnowledgeSearch creates a tool that searches the shared knowledge base. +func newSharedKnowledgeSearch(store KnowledgeStore) tools.Tool { + return tools.Tool{ + Def: tools.Def{ + Name: "shared_knowledge_search", + Description: "Search the shared knowledge base accessible by all agents. Use this to find information other agents have recorded.", + Parameters: []tools.Param{ + {Name: "query", Type: "string", Description: "Search terms or phrase", Required: true}, + {Name: "limit", Type: "integer", Description: "Max results (default 5)", Required: false}, + }, + }, + Exec: func(ctx context.Context, args map[string]any) tools.Result { + query := tools.GetString(args, "query") + if query == "" { + return tools.Result{Err: fmt.Errorf("shared_knowledge_search: query is required")} + } + limit := tools.GetInt(args, "limit") + if limit <= 0 { + limit = 5 + } + + results, err := store.Search(ctx, query, limit) + if err != nil { + return tools.Result{Err: fmt.Errorf("shared_knowledge_search: %w", err)} + } + if len(results) == 0 { + return tools.Result{Output: "no documents found in shared knowledge base matching your query"} + } + + var sb strings.Builder + for i, r := range results { + fmt.Fprintf(&sb, "%d. **%s** (`%s`)\n %s\n", i+1, r.Title, r.Slug, r.Snippet) + } + return tools.Result{Output: sb.String()} + }, + } +} + +// newSharedKnowledgeRead creates a tool that reads a shared knowledge document. +func newSharedKnowledgeRead(store KnowledgeStore) tools.Tool { + return tools.Tool{ + Def: tools.Def{ + Name: "shared_knowledge_read", + Description: "Read the full content of a shared knowledge document by its slug. This document is accessible by all agents.", + Parameters: []tools.Param{ + {Name: "slug", Type: "string", Description: "Document slug (e.g. \"go-patterns\")", Required: true}, + }, + }, + Exec: func(ctx context.Context, args map[string]any) tools.Result { + slug := tools.GetString(args, "slug") + if slug == "" { + return tools.Result{Err: fmt.Errorf("shared_knowledge_read: slug is required")} + } + + doc, err := store.Get(ctx, slug) + if err != nil { + return tools.Result{Err: fmt.Errorf("shared_knowledge_read: %w", err)} + } + return tools.Result{Output: doc.Content} + }, + } +} + +// newSharedKnowledgeWrite creates a tool that writes a shared knowledge document. +func newSharedKnowledgeWrite(store KnowledgeStore) tools.Tool { + return tools.Tool{ + Def: tools.Def{ + Name: "shared_knowledge_write", + Description: "Create or update a shared knowledge document accessible by all agents. Use this to share knowledge with other agents.", + Parameters: []tools.Param{ + {Name: "slug", Type: "string", Description: "Document slug (lowercase, hyphens, e.g. \"matrix-tips\")", Required: true}, + {Name: "content", Type: "string", Description: "Full markdown content of the document", Required: true}, + }, + }, + Exec: func(ctx context.Context, args map[string]any) tools.Result { + slug := tools.GetString(args, "slug") + content := tools.GetString(args, "content") + if slug == "" || content == "" { + return tools.Result{Err: fmt.Errorf("shared_knowledge_write: slug and content are required")} + } + + err := store.Put(ctx, knowledge.Document{ + Slug: slug, + Content: content, + }) + if err != nil { + return tools.Result{Err: fmt.Errorf("shared_knowledge_write: %w", err)} + } + return tools.Result{Output: fmt.Sprintf("shared document saved: %s (%d bytes)", slug, len(content))} + }, + } +} + +// newSharedKnowledgeList creates a tool that lists all shared knowledge documents. +func newSharedKnowledgeList(store KnowledgeStore) tools.Tool { + return tools.Tool{ + Def: tools.Def{ + Name: "shared_knowledge_list", + Description: "List all documents in the shared knowledge base accessible by all agents.", + Parameters: []tools.Param{}, + }, + Exec: func(ctx context.Context, args map[string]any) tools.Result { + docs, err := store.List(ctx) + if err != nil { + return tools.Result{Err: fmt.Errorf("shared_knowledge_list: %w", err)} + } + if len(docs) == 0 { + return tools.Result{Output: "shared knowledge base is empty"} + } + + var sb strings.Builder + for _, d := range docs { + fmt.Fprintf(&sb, "- `%s`: %s (updated %s)\n", + d.Slug, d.Title, d.UpdatedAt.Format("2006-01-02")) + } + return tools.Result{Output: sb.String()} + }, + } +} diff --git a/tools/knowledgetools/shared_test.go b/tools/knowledgetools/shared_test.go new file mode 100644 index 0000000..dbec77f --- /dev/null +++ b/tools/knowledgetools/shared_test.go @@ -0,0 +1,202 @@ +package knowledgetools + +import ( + "context" + "testing" + + "github.com/enmanuel/agents/pkg/knowledge" +) + +func TestNewSharedKnowledgeTools(t *testing.T) { + store := newMockKnowledgeStore() + tools := NewSharedKnowledgeTools(store) + + if len(tools) != 4 { + t.Errorf("expected 4 tools, got %d", len(tools)) + } + + names := make(map[string]bool) + for _, tool := range tools { + names[tool.Def.Name] = true + } + + expected := []string{ + "shared_knowledge_search", + "shared_knowledge_read", + "shared_knowledge_write", + "shared_knowledge_list", + } + + for _, name := range expected { + if !names[name] { + t.Errorf("expected tool %q not found", name) + } + } +} + +func TestSharedKnowledgeSearchTool(t *testing.T) { + store := newMockKnowledgeStore() + store.docs["shared-doc"] = knowledge.Document{ + Slug: "shared-doc", Title: "Shared Doc", Content: "This is shared knowledge", + } + + tools := NewSharedKnowledgeTools(store) + tool := tools[0] // shared_knowledge_search is first + + // Missing query + r := tool.Exec(context.Background(), map[string]any{}) + if r.Err == nil { + t.Error("expected error for missing query") + } + + // Valid search + r = tool.Exec(context.Background(), map[string]any{"query": "shared"}) + if r.Err != nil { + t.Errorf("unexpected error: %v", r.Err) + } + if r.Output == "" { + t.Error("expected non-empty output") + } + + // Empty results + store2 := newMockKnowledgeStore() + tools2 := NewSharedKnowledgeTools(store2) + r = tools2[0].Exec(context.Background(), map[string]any{"query": "nothing"}) + if r.Err != nil { + t.Errorf("unexpected error: %v", r.Err) + } + if r.Output != "no documents found in shared knowledge base matching your query" { + t.Errorf("expected empty message, got %q", r.Output) + } +} + +func TestSharedKnowledgeReadTool(t *testing.T) { + store := newMockKnowledgeStore() + store.docs["shared-doc"] = knowledge.Document{ + Slug: "shared-doc", Title: "Shared", Content: "Shared content", + } + + tools := NewSharedKnowledgeTools(store) + tool := tools[1] // shared_knowledge_read is second + + // Missing slug + r := tool.Exec(context.Background(), map[string]any{}) + if r.Err == nil { + t.Error("expected error for missing slug") + } + + // Valid read + r = tool.Exec(context.Background(), map[string]any{"slug": "shared-doc"}) + if r.Err != nil { + t.Errorf("unexpected error: %v", r.Err) + } + if r.Output != "Shared content" { + t.Errorf("output = %q, want %q", r.Output, "Shared content") + } + + // Not found + r = tool.Exec(context.Background(), map[string]any{"slug": "nope"}) + if r.Err == nil { + t.Error("expected error for nonexistent doc") + } +} + +func TestSharedKnowledgeWriteTool(t *testing.T) { + store := newMockKnowledgeStore() + tools := NewSharedKnowledgeTools(store) + tool := tools[2] // shared_knowledge_write is third + + // Missing params + r := tool.Exec(context.Background(), map[string]any{"slug": "test"}) + if r.Err == nil { + t.Error("expected error for missing content") + } + + // Valid write + r = tool.Exec(context.Background(), map[string]any{ + "slug": "shared-doc", + "content": "# Shared Doc\nShared by agent A", + }) + if r.Err != nil { + t.Errorf("unexpected error: %v", r.Err) + } + if _, ok := store.docs["shared-doc"]; !ok { + t.Error("document was not stored") + } + + // Verify the output message mentions "shared" + if r.Output != "shared document saved: shared-doc (30 bytes)" { + t.Errorf("output = %q, want mention of shared", r.Output) + } +} + +func TestSharedKnowledgeListTool(t *testing.T) { + store := newMockKnowledgeStore() + tools := NewSharedKnowledgeTools(store) + tool := tools[3] // shared_knowledge_list is fourth + + // Empty + r := tool.Exec(context.Background(), map[string]any{}) + if r.Err != nil { + t.Errorf("unexpected error: %v", r.Err) + } + if r.Output != "shared knowledge base is empty" { + t.Errorf("expected empty message, got %q", r.Output) + } + + // With docs + store.docs["shared-doc1"] = knowledge.Document{Slug: "shared-doc1", Title: "Shared 1"} + r = tool.Exec(context.Background(), map[string]any{}) + if r.Err != nil { + t.Errorf("unexpected error: %v", r.Err) + } + if r.Output == "shared knowledge base is empty" { + t.Error("expected non-empty output after adding docs") + } +} + +// TestSharedAndPrivateCoexist verifies that shared and private tools can coexist +// with different stores and don't interfere with each other. +func TestSharedAndPrivateCoexist(t *testing.T) { + privateStore := newMockKnowledgeStore() + sharedStore := newMockKnowledgeStore() + + // Write to private store + privateStore.docs["private-doc"] = knowledge.Document{ + Slug: "private-doc", Title: "Private", Content: "Private content", + } + + // Write to shared store + sharedStore.docs["shared-doc"] = knowledge.Document{ + Slug: "shared-doc", Title: "Shared", Content: "Shared content", + } + + // Verify private has only private doc + privateDocs, _ := privateStore.List(context.Background()) + if len(privateDocs) != 1 || privateDocs[0].Slug != "private-doc" { + t.Error("private store should only have private doc") + } + + // Verify shared has only shared doc + sharedDocs, _ := sharedStore.List(context.Background()) + if len(sharedDocs) != 1 || sharedDocs[0].Slug != "shared-doc" { + t.Error("shared store should only have shared doc") + } + + // Verify tools from different stores don't mix data + privateTool := NewKnowledgeRead(privateStore) + sharedTools := NewSharedKnowledgeTools(sharedStore) + sharedTool := sharedTools[1] // shared_knowledge_read + + // Private tool can't read shared doc + r := privateTool.Exec(context.Background(), map[string]any{"slug": "shared-doc"}) + if r.Err == nil { + t.Error("private tool should not be able to read shared doc") + } + + // Shared tool can't read private doc + r = sharedTool.Exec(context.Background(), map[string]any{"slug": "private-doc"}) + if r.Err == nil { + t.Error("shared tool should not be able to read private doc") + } +} From 8fcbcba39c511af372e7a099bc156ea5beb86557 Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Sun, 8 Mar 2026 21:56:51 +0000 Subject: [PATCH 4/8] feat: integrar shared knowledge store en runtime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Agregar campo sharedKnowledgeStore al Agent struct - Inicializar shared store si cfg.Tools.SharedKnowledge.Enabled - Registrar shared tools en buildToolRegistry - Cerrar shared store en Run defer - Defaults: dir=knowledges, db=knowledges/data/knowledge.db - Issue 0018: Shared Knowledge (fase 3) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- agents/runtime.go | 80 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 60 insertions(+), 20 deletions(-) diff --git a/agents/runtime.go b/agents/runtime.go index 62a9ae2..267602d 100644 --- a/agents/runtime.go +++ b/agents/runtime.go @@ -92,6 +92,9 @@ type Agent struct { // Knowledge store — non-nil when knowledge is enabled knowledgeStore *shellknowledge.FileStore + // Shared knowledge store — non-nil when shared_knowledge is enabled + sharedKnowledgeStore *shellknowledge.FileStore + // Sanitization options — nil when sanitization is disabled sanitizeOpts *sanitize.Options @@ -235,6 +238,29 @@ func New(cfg *config.AgentConfig, rules []decision.Rule, agentACL acl.ACL, logge } } + // Shared knowledge store + var sharedKStore *shellknowledge.FileStore + if cfg.Tools.SharedKnowledge.Enabled { + sharedDir := cfg.Tools.SharedKnowledge.Dir + if sharedDir == "" { + sharedDir = "knowledges" + } + sharedDBPath := cfg.Tools.SharedKnowledge.DBPath + if sharedDBPath == "" { + sharedDBPath = "knowledges/data/knowledge.db" + } + var skErr error + sharedKStore, skErr = shellknowledge.New(sharedDir, sharedDBPath, logger) + if skErr != nil { + logger.Error("shared_knowledge_store_init_failed", "err", skErr) + } else { + if syncErr := sharedKStore.Sync(context.Background()); syncErr != nil { + logger.Error("shared_knowledge_sync_failed", "err", syncErr) + } + logger.Info("shared knowledge enabled", "dir", sharedDir, "db", sharedDBPath) + } + } + if !agentACL.Empty() { logger.Info("acl enabled (centralized security policy)") } @@ -252,7 +278,7 @@ func New(cfg *config.AgentConfig, rules []decision.Rule, agentACL acl.ACL, logge } // Tool registry — register tools enabled in config - toolReg := buildToolRegistry(cfg, sshExec, matrixClient, memStore, kStore, mcpManager, roomCtx, logger) + toolReg := buildToolRegistry(cfg, sshExec, matrixClient, memStore, kStore, sharedKStore, mcpManager, roomCtx, logger) // Rate limiting for tools if cfg.Security.ToolRateLimit.Enabled { @@ -278,25 +304,26 @@ func New(cfg *config.AgentConfig, rules []decision.Rule, agentACL acl.ACL, logge } a := &Agent{ - cfg: cfg, - acl: agentACL, - rules: rules, - llm: llmFunc, - matrix: matrixClient, - runner: runner, - toolReg: toolReg, - logger: logger, - cryptoStore: cryptoStore, - mcpManager: mcpManager, - done: make(chan struct{}), - commands: make(map[string]CommandHandler), - cmdAliases: command.BuiltinNames(), - startTime: time.Now(), - windows: make(map[string]memory.Window), - memStore: memStore, - knowledgeStore: kStore, - windowSize: windowSize, - roomCtx: roomCtx, + cfg: cfg, + acl: agentACL, + rules: rules, + llm: llmFunc, + matrix: matrixClient, + runner: runner, + toolReg: toolReg, + logger: logger, + cryptoStore: cryptoStore, + mcpManager: mcpManager, + done: make(chan struct{}), + commands: make(map[string]CommandHandler), + cmdAliases: command.BuiltinNames(), + startTime: time.Now(), + windows: make(map[string]memory.Window), + memStore: memStore, + knowledgeStore: kStore, + sharedKnowledgeStore: sharedKStore, + windowSize: windowSize, + roomCtx: roomCtx, } // Configure sanitization if enabled @@ -417,6 +444,9 @@ func (a *Agent) Run(ctx context.Context) error { if a.knowledgeStore != nil { defer a.knowledgeStore.Close() } + if a.sharedKnowledgeStore != nil { + defer a.sharedKnowledgeStore.Close() + } if a.mcpManager != nil { defer a.mcpManager.Close() } @@ -999,6 +1029,7 @@ func buildToolRegistry( matrixClient *matrix.Client, memStore memory.Store, kStore *shellknowledge.FileStore, + sharedKStore *shellknowledge.FileStore, mcpManager *shellmcp.Manager, roomCtx *toolmemory.RoomContext, logger *slog.Logger, @@ -1051,6 +1082,15 @@ func buildToolRegistry( logger.Debug("registered knowledge tools") } + // Shared knowledge tools + if cfg.Tools.SharedKnowledge.Enabled && sharedKStore != nil { + sharedTools := toolknowledge.NewSharedKnowledgeTools(sharedKStore) + for _, tool := range sharedTools { + reg.Register(tool) + } + logger.Debug("registered shared knowledge tools", "count", len(sharedTools)) + } + // MCP tools — register tools from all connected MCP servers if mcpManager != nil { for serverName, mcpClient := range mcpManager.AllClients() { From ba2ce3c821154282c6968cf526f34f548a9869c9 Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Sun, 8 Mar 2026 21:56:58 +0000 Subject: [PATCH 5/8] docs: crear carpeta knowledges con README MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Carpeta raiz para documentos compartidos entre agentes - README explica proposito, funcionamiento, diferencia con privado - Ejemplo de flujo de colaboracion entre agentes - Issue 0018: Shared Knowledge (fase 4) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- knowledges/README.md | 63 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 knowledges/README.md diff --git a/knowledges/README.md b/knowledges/README.md new file mode 100644 index 0000000..9fba459 --- /dev/null +++ b/knowledges/README.md @@ -0,0 +1,63 @@ +# Shared Knowledge Base + +Esta carpeta contiene la **base de conocimiento compartida** entre todos los agentes del sistema. + +## Propósito + +Los agentes pueden leer, escribir y buscar documentos en esta carpeta usando las tools `shared_knowledge_*`. Esto permite que múltiples agentes colaboren acumulando y consultando conocimiento común. + +## Funcionamiento + +- **Documentos**: Los archivos `.md` en este directorio son los documentos de conocimiento compartidos entre agentes. +- **Índice FTS5**: Los documentos se indexan automáticamente en `data/knowledge.db` (SQLite con Full-Text Search). +- **Sincronización**: El índice se actualiza al arrancar cada agente con `Sync()`. +- **WAL mode**: El DB usa WAL (Write-Ahead Logging) para permitir lecturas y escrituras concurrentes entre múltiples procesos. + +## Tools disponibles + +Los agentes con `tools.shared_knowledge.enabled: true` tienen acceso a: + +- `shared_knowledge_search` — buscar documentos por query +- `shared_knowledge_read` — leer un documento por slug +- `shared_knowledge_write` — crear o actualizar un documento +- `shared_knowledge_list` — listar todos los documentos compartidos + +## Diferencia con knowledge privado + +Cada agente puede tener **dos bases de conocimiento**: + +1. **Knowledge privado** (`agents//knowledge/`): solo visible para ese agente, tools `knowledge_*` +2. **Knowledge compartido** (`knowledges/`): visible para todos los agentes con shared_knowledge habilitado, tools `shared_knowledge_*` + +## Ejemplo de flujo + +``` +1. agente-A recibe: "investiga X y guarda lo que encuentres" + → LLM usa shared_knowledge_write(slug: "investigacion-x", content: "...") + → Se escribe knowledges/investigacion-x.md + actualiza FTS5 + +2. agente-B recibe: "qué sabemos sobre X?" + → LLM usa shared_knowledge_search(query: "X") + → Encuentra el documento que escribió agente-A + → shared_knowledge_read(slug: "investigacion-x") + → Responde con la información +``` + +## Estructura + +``` +knowledges/ + ├── README.md ← este archivo + ├── *.md ← documentos compartidos (commiteados) + └── data/ + ├── knowledge.db ← índice SQLite FTS5 (no commiteado) + ├── knowledge.db-shm + └── knowledge.db-wal +``` + +## Notas + +- Los archivos `.md` se commitean en el repositorio (forman parte del conocimiento compartido del equipo). +- El directorio `data/` está en `.gitignore` — el índice se reconstruye automáticamente al arrancar. +- No hay control de acceso por agente: cualquier agente con shared_knowledge habilitado puede leer y escribir. +- Si dos agentes escriben el mismo slug, el último gana (sobreescritura). From 0ac2afb5739001af86c26a5babe6d0d8604ce615 Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Sun, 8 Mar 2026 21:57:05 +0000 Subject: [PATCH 6/8] docs: documentar coexistencia knowledge privado/compartido en prompts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Separar herramientas en dos secciones - Explicar diferencia entre knowledge privado y shared - Guía de cuándo usar cada una - Issue 0018: Shared Knowledge (fase 5) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- agents/asistente-2/prompts/system.md | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/agents/asistente-2/prompts/system.md b/agents/asistente-2/prompts/system.md index e793c96..542bf9c 100644 --- a/agents/asistente-2/prompts/system.md +++ b/agents/asistente-2/prompts/system.md @@ -12,10 +12,22 @@ Eres un asistente conversacional amigable y directo. Operas en Matrix, respondie ## Herramientas disponibles - `current_time`: Devuelve la fecha y hora actual del servidor. Úsala cuando alguien pregunte por la hora, fecha, o necesites contexto temporal. -- `knowledge_search`: Busca documentos en tu base de conocimiento por palabras clave. -- `knowledge_read`: Lee el contenido completo de un documento por su slug. -- `knowledge_write`: Crea o actualiza un documento de conocimiento. -- `knowledge_list`: Lista todos los documentos disponibles. + +### Knowledge privado (tu base personal) +- `knowledge_search`: Busca documentos en **tu** base de conocimiento privada. +- `knowledge_read`: Lee el contenido completo de un documento en **tu** base privada. +- `knowledge_write`: Crea o actualiza un documento en **tu** base privada. +- `knowledge_list`: Lista todos los documentos en **tu** base privada. + +### Knowledge compartido (visible para todos los agentes) +- `shared_knowledge_search`: Busca en la base compartida entre **todos los agentes**. +- `shared_knowledge_read`: Lee un documento compartido que otros agentes pueden haber escrito. +- `shared_knowledge_write`: Escribe en la base compartida para que otros agentes lo vean. +- `shared_knowledge_list`: Lista documentos compartidos entre agentes. + +**¿Cuándo usar cada una?** +- Usa **knowledge privado** para información específica de tu rol o contexto personal. +- Usa **shared knowledge** cuando quieras colaborar con otros agentes, compartir información investigada, o consultar lo que otros han registrado. ## Estilo - Respuestas concisas por defecto. Si necesitas extensión, pregunta primero. From 504abed20f1c3293b09d89fab6b5ad841e7c8caa Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Sun, 8 Mar 2026 21:57:11 +0000 Subject: [PATCH 7/8] docs: actualizar .gitignore y CLAUDE.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Agregar knowledges/data/ a .gitignore (DB no se commitea) - Agregar knowledges/ a estructura en CLAUDE.md - Los .md compartidos sí se commitean - Issue 0018: Shared Knowledge (fase 7) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .claude/CLAUDE.md | 1 + .gitignore | 3 +++ 2 files changed, 4 insertions(+) diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index 3876dc7..2d4266c 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -63,6 +63,7 @@ security/ grupos de usuarios/agentes + politicas de permisos (YAMLs cmd/launcher/ entrypoint principal (rulesRegistry) cmd/agentctl/ CLI de gestion crons/ catálogo de automatizaciones nombradas (schedule.yaml + prompts) +knowledges/ base de conocimiento compartida entre agentes (*.md + SQLite FTS5) dev-scripts/server/ start, stop, restart, ps, logs, dashboard dev-scripts/agent/ new, register, verify, avatar, remove, list dev-scripts/cron/ new, list, apply — gestión de automatizaciones cron diff --git a/.gitignore b/.gitignore index d2bb571..3666973 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,9 @@ run/*.txt logs/ +# Shared knowledge DB (markdown files are tracked, DB is rebuilt on sync) +knowledges/data/ + # E2E tests e2e/node_modules/ e2e/test-results/ From 44ea4a7d82373d119e4806d0be21aca5d030d2bd Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Sun, 8 Mar 2026 21:57:18 +0000 Subject: [PATCH 8/8] =?UTF-8?q?chore:=20cerrar=20issue=200018=20=E2=80=94?= =?UTF-8?q?=20shared=20knowledge?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Mover dev/issues/0018-shared-knowledge.md a completed/ - Actualizar dev/issues/README.md: estado completado - Sistema de conocimiento compartido completamente implementado 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- dev/issues/README.md | 2 +- dev/issues/{ => completed}/0018-shared-knowledge.md | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename dev/issues/{ => completed}/0018-shared-knowledge.md (100%) diff --git a/dev/issues/README.md b/dev/issues/README.md index 4f8ecbe..e97a579 100644 --- a/dev/issues/README.md +++ b/dev/issues/README.md @@ -22,7 +22,7 @@ afectados y notas de implementacion. | 15 | Multi-platform Telegram | [0015-multi-platform-telegram.md](0015-multi-platform-telegram.md) | pendiente | | 16 | Skills system | [0016-skills-system.md](0016-skills-system.md) | pendiente | | 17 | MCP client tools | [0017-mcp-client-tools.md](completed/0017-mcp-client-tools.md) | completado | -| 18 | Shared knowledge | [0018-shared-knowledge.md](0018-shared-knowledge.md) | pendiente | +| 18 | Shared knowledge | [0018-shared-knowledge.md](completed/0018-shared-knowledge.md) | completado | | 19 | Prompt injection hardening | [0019-prompt-injection-hardening.md](completed/0019-prompt-injection-hardening.md) | completado | | 20 | Aislar claude -p del repo | [0020-claude-code-sandbox.md](completed/0020-claude-code-sandbox.md) | completado | | 21 | Threads default config | (completado via branch) | completado | diff --git a/dev/issues/0018-shared-knowledge.md b/dev/issues/completed/0018-shared-knowledge.md similarity index 100% rename from dev/issues/0018-shared-knowledge.md rename to dev/issues/completed/0018-shared-knowledge.md