mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-14 22:17:36 +00:00
feat(v3): core architecture redesign — pipeline, memory, vault, evolution, providers, orchestration (#790)
* feat(v3): add core interface contracts and migration for v3 redesign
Foundation interfaces: TokenCounter, WorkspaceContext, DomainEventBus,
ProviderAdapter/Capabilities. Pipeline: Stage, RunState, MessageBuffer,
substates, Pipeline orchestrator. Memory: EpisodicStore, AutoInjector,
KG temporal extensions, consolidation workers. System integration:
PromptConfig, ToolCapability, Retriever. Orchestration: OrchestrationMode,
EvolutionMetrics/SuggestionStore. Migration 000037: episodic_summaries,
evolution tables, KG temporal columns. Schema version 36→37.
* refactor(plans): mark all v3 design phases complete with file references
* fix(v3): address code review findings on design contracts
- C1: add missing l0_abstract column to episodic_summaries migration
- C2: align EpisodicSummary ID/TenantID/AgentID to uuid.UUID
- H1: document tenant_id scoping requirement on EpisodicStore
- H2: add UNIQUE constraint on (agent_id, user_id, source_id) for dedup
- H4: clarify ProviderAdapter vs Provider relationship in doc
- M3: set state.ExitCode on BreakLoop/AbortRun in pipeline
- M6: store full PipelineConfig in Pipeline struct
- Edge: add WHERE embedding IS NOT NULL on HNSW index
* fix(v3): second-pass review fixes
- H1: use context.WithoutCancel for finalize + set ExitCode on ctx cancel
- H2: use utf8.RuneCountInString consistently in FallbackCounter
- H3: longest-prefix-match in ModelContextWindow (prevents wrong tokenizer)
- H4: return unsubscribe cleanup func from consolidation.Register
* feat(v3): implement DomainEventBus with worker pool, dedup, and retry
Worker pool processes events from buffered channel. SourceID-based dedup
prevents duplicate processing. Exponential backoff retry on handler error.
Panic recovery per handler. Graceful shutdown via Drain(). 8/8 tests pass
with race detector.
* feat(v3): implement ProviderAdapter for Anthropic, OpenAI, DashScope, Codex
Add CapabilitiesAware to all 6 providers. Create ProviderAdapter
implementations that delegate to existing buildRequestBody/parseResponse
for DRY. ClaudeCLI and ACP get capabilities only (subprocess transport).
DashScope wraps OpenAI adapter with StreamWithTools=false override.
* feat(v3): implement WorkspaceContext Resolver for 6 scenarios
Stateless resolver produces immutable WorkspaceContext at run start.
Handles personal/group/predefined/team-shared/team-isolated/delegation.
Wired into loop_context.go behind v3PipelineEnabled flag (additive,
v2 path unchanged). Includes delegation path boundary check,
master tenant bypass, and tenant slug path composition.
* feat(v3): implement tiktoken TokenCounter with BPE encoding + cache
Adds tiktoken-go for accurate cl100k_base/o200k_base token counting.
Per-message FNV-1a hash cache avoids re-encoding unchanged history.
Falls back to rune/3 heuristic for unknown models. NewTokenCounter
factory selects implementation at build time.
* feat(v3): promote 12 other_config JSONB fields to dedicated agent columns
Extract emoji, agent_description, thinking_level, max_tokens,
self_evolve, skill_evolve, skill_nudge_interval, reasoning_config,
workspace_sharing, chatgpt_oauth_routing, shell_deny_groups, and
kg_dedup_config from the catch-all other_config JSONB into proper
columns with DB-level types and defaults.
- Migration: PG (000037) + SQLite (schema v6→7) with backfill
- Go: AgentData struct + simplified Parse* methods
- Store: SELECT/INSERT/scan updated for both PG and SQLite
- Gateway: create/update handlers accept promoted fields
- HTTP: export/import with legacy backward compat
- Web UI: all 15 frontend files read/write from top level
* feat(v3): implement Knowledge Vault with unified search, wikilinks, and FS sync
Migration 000038 adds vault_documents (FTS+pgvector), vault_links, vault_versions
tables. VaultStore interface with PG implementation for document CRUD, hybrid
FTS+vector search, and bidirectional link management. All queries enforce
tenant_id isolation including JOIN-based scoping on link operations.
FS sync layer: SHA-256 content hashing, VaultInterceptor hooks into write_file/
read_file for auto-registration and lazy sync, fsnotify watcher with 500ms
debounce. Wikilink engine parses [[target]] syntax, resolves targets via
3-step strategy, and maintains vault_links on write.
VaultSearchService fans out queries across vault, episodic, and KG stores in
parallel with per-source score normalization and weighted merge. AutoInjector
and Retriever implementations for pipeline integration.
Three agent tools: vault_search (unified discovery), vault_link (explicit
linking), vault_backlinks (dependency tracing). Feature-flagged via
v3_vault_enabled agent setting.
* feat(v3): wire vault into gateway startup + add unit tests
Wire VaultStore embedding provider, VaultSearchService, VaultInterceptor
on read/write tools, and register vault_search/vault_link/vault_backlinks
tools in gateway_vault_wiring.go. All wiring gated by stores.Vault != nil.
Add 28 unit tests for ContentHash, ContentHashFile, and ExtractWikilinks
covering edge cases, unicode, display text, context windows, and offsets.
* feat(v3): implement stage-based pipeline loop with 8 pluggable stages
Decompose monolithic agent loop into internal/pipeline/ package:
- 6 stages: Context, Think, Prune+MemoryFlush, Tool, Observe+Checkpoint, Finalize
- Foundation types: Stage interface, RunState with 7 typed substates, MessageBuffer
- Pipeline orchestrator with setup/iteration/finalize 3-phase execution
- Callback-based PipelineDeps avoids circular import with agent package
- Feature-flagged via v3PipelineEnabled in Loop.Run()
- All 7 exit conditions preserved (no tools, max iter, truncation, loop kill,
read-only streak, tool budget, ctx cancel)
* feat(v3): wire pipeline callbacks to Loop methods + add 71 unit tests
Wire 15 of 17 PipelineDeps callbacks from Loop methods via closures:
- Context: LoadContextFiles, BuildMessages, EnrichMedia, InjectReminders
- Think: BuildFilteredTools, CallLLM (stream/sync)
- Prune: PruneMessages, CompactMessages
- Memory: RunMemoryFlush
- Finalize: SanitizeContent, FlushMessages, UpdateMetadata, BootstrapCleanup, MaybeSummarize
- Remaining: ExecuteToolCall, CheckReadOnly (deep loop.go integration)
Add comprehensive test suite (71 tests, all passing with -race):
- MessageBuffer: 10 tests (append, flush, replace, counts)
- Pipeline.Run: 14 tests (3-phase flow, exit conditions, ctx cancel)
- Stage tests: 47 tests (ThinkStage nudges/truncation, PruneStage budget,
ToolStage parallel/exit, ObserveStage content, CheckpointStage interval,
FinalizeStage cleanup)
* feat(v3): wire remaining 2 callbacks (ExecuteToolCall, CheckReadOnly)
Complete callback wiring — 17/17 PipelineDeps callbacks now active:
- ExecuteToolCall: resolves tool name, executes via registry, processes
result via existing processToolResult with loop detection bridge
- CheckReadOnly: delegates to checkReadOnlyStreak via bridge runState
- Bridge runState shares loop detection state between pipeline and agent
* fix(v3): eliminate data race in tool execution + capture injected messages
- Remove parallel tool execution path — serialize all tool calls to avoid
data races on shared bridgeRS (loop detector, media results, deliverables)
- Loop kill checked after each tool (mid-batch early exit)
- BuildFilteredTools: capture and append injected tool-awareness messages
- Rename test to reflect sequential execution
* feat(v3): wire ResolveWorkspace, safe parallel tools, ContextStage tests
- Wire ResolveWorkspace callback via workspace.NewResolver() with
ResolveParams from Loop fields (no longer a nil stub)
- Re-add safe parallel tool execution: split into ExecuteToolRaw
(parallel I/O) + ProcessToolResult (sequential state mutation)
with opaque rawData pass-through (no double execution)
- Add 12 unit tests for ContextStage (8) + MemoryFlushStage (3)
- Split tool callbacks to loop_pipeline_tool_callbacks.go (under 200 lines)
- Capture buildFilteredTools injected messages
* feat(v3): add episodic memory store + temporal KG columns
Phase 1 — Episodic Store:
- Migration 000039: episodic_summaries table with pgvector, FTS, L0 abstracts
- EpisodicStore PG impl: CRUD, hybrid FTS+vector search, ExistsBySourceID,
PruneExpired. Idempotent via source_id UNIQUE constraint.
Phase 2 — Temporal KG:
- Migration 000040: valid_from/valid_until on kg_entities + kg_relations,
partial indexes for current-facts queries, epoch→timestamptz backfill
- ListEntitiesTemporal: current-only, point-in-time, or include-expired modes
- SupersedeEntity: atomic expire-old + insert-new in single transaction
Schema version bumped to 40.
* fix(v3): review fixes for episodic store + temporal KG
- C1: Fix column name mismatch turn_count vs message_count in Go SQL
- C2: Remove redundant migration 000040 (000037 already adds temporal KG columns)
- H1: Use time.Time not int64 for TIMESTAMPTZ columns in SupersedeEntity
- H2: Add tenant_id scoping to Get/Delete for tenant isolation
- M2: Fix scanEntityTemporal to convert TIMESTAMPTZ→UnixMilli correctly
- L1: Remove unused uuid import from episodic_search.go
- Schema version corrected to 39 (only 000039 is new)
* feat(v3): implement consolidation pipeline with 3 event-driven workers
Event chain: session.completed → EpisodicWorker → episodic.created →
SemanticWorker → entity.upserted → DedupWorker
- EpisodicWorker: reuses compaction summary or calls LLM, generates L0
abstract (extractive), idempotent via source_id check
- SemanticWorker: extracts KG facts from episodic summary via existing
Extractor, sets temporal valid_from, publishes entity.upserted
- DedupWorker: runs DedupAfterExtraction on new entity IDs (terminal)
- L0 abstract: sentence-based extraction (~50 tokens), no LLM needed
- All workers registered via DomainEventBus.Subscribe()
* feat(v3): implement progressive loading with L0 auto-inject + unified search
- AutoInjector: searches episodic store, builds L0 prompt section (~200 tokens),
skips trivial messages via stopword filter
- L1Cache: in-memory LRU (500 entries, 1h TTL) for structured overviews
- UnifiedSearch: cross-tier search merging episodic + document results by score
- ContextStage integration: AutoInject callback appends memory section to system prompt
- MemorySection field added to ContextState for observability
* feat(v3): add memory_expand tool for L2 episodic retrieval
New tool: memory_expand(id) returns full episodic summary with metadata.
Complements memory_search L0/L1 results with deep L2 access.
Nil-safe: returns error message when episodic store not available.
Gateway wiring + memory_search depth param + kg_search temporal param
deferred to runtime integration phase.
* feat(v3): complete Phase 5 — tool extensions + gateway wiring
- memory_search: add depth param + episodic tier search merged with docs
- kg_search: add as_of temporal param, use ListEntitiesTemporal
- memory_expand: registered in gateway startup
- Gateway: Episodic field in Stores, PGEpisodicStore in factory,
embedding provider wired, tools connected to episodic store
* fix(v3): Phase 3 review fixes — tenant isolation + AutoInject args
- C1: Add tenant_id filter to ftsSearch, vectorSearch, List queries
(prevents cross-tenant episodic memory leaks)
- C2: Fix AutoInject callback signature — agent/tenant captured by
closure, only userMessage + userID passed explicitly
- H1: Add tenant_id to List query
* feat(v3): wire per-agent v3 flags from DB into dual-mode gate
Parse v3_pipeline_enabled, v3_memory_enabled, v3_retrieval_enabled from
agent other_config JSONB via ParseV3Flags(). Resolver now sets all flags
on LoopConfig so the existing gate in loop_run.go reads from DB.
- V3Flags struct + ParseV3Flags() + ValidateV3Flags() in store layer
- v3MemoryEnabled/v3RetrievalEnabled added to Loop, LoopConfig, PipelineConfig
- Auto-inject gated on V3RetrievalEnabled (was unconditional)
- Structured perf logging for v3 pipeline runs
- v3 flag validation on both WS agent.update and HTTP PUT endpoints
* feat(v3): wire AutoInjector into pipeline for L0 memory auto-inject
Create AutoInjector at gateway startup from episodic store, pass through
ResolverDeps → LoopConfig → Loop. Pipeline adapter builds AutoInject
callback capturing agent/tenant context via closure.
ContextStage already gates on V3RetrievalEnabled + AutoInject != nil.
* feat(v3): add tool metadata map + capability-based deny rules
Registry gains per-tool ToolMetadata map with RegisterWithMetadata()
and GetMetadata() (infers defaults from tool name when not explicit).
PolicyEngine gains DenyCapability() for RBAC integration — tools with
denied capabilities filtered at step 8 after existing 7-step pipeline.
* fix(v3): add RWMutex to PolicyEngine capability deny fields
DenyCapability() and SetRegistry() now guarded by sync.RWMutex.
FilterTools reads snapshot under RLock. Prevents data race when
capability rules are modified concurrently with tool filtering.
* feat(v3): implement delegate tool for inter-agent task delegation
New `delegate` tool wraps existing agent_links infrastructure
(CanDelegate, DelegateTargets). Supports async (fire-and-forget)
and sync (block with timeout) modes. Permission checked via
AgentLinkStore. Events emitted: delegate.sent/completed/failed.
DelegateRunFunc injected by gateway to avoid circular dependency.
* feat(v3): complete 3 deferred implementations
1. OrchestrationMode resolution: ResolveOrchestrationMode() checks
team membership → delegate links → spawn (priority order).
2. PG EvolutionMetricsStore: RecordMetric, QueryMetrics, aggregate
tool/retrieval metrics, TTL cleanup. All queries tenant-scoped.
3. BridgePromptBuilder: implements PromptBuilder interface by
delegating to existing BuildSystemPrompt(). Appends v3 memory
L0 section when enabled. Ready for template engine swap later.
* fix(v3): address code review findings on commits 5-6
- C1: CanDelegate now tenant-scoped (fail-closed on missing tenant)
- H1: Sync delegate timeout capped at 600s
- H2: Async goroutine gets 10min deadline (prevents leaks)
- H3: JSONB casts use COALESCE/NULLIF guards (handles missing fields)
- M1/M2: Remove dead code (formatVaultSection, memoryL0ToStrings)
* fix(teams): stop auto-creating agent_links for team members
Teams use agent_team_members table directly — agent_links caused
context confusion between team dispatch and delegation systems.
- Remove autoCreateTeamLinks() calls from team create + member add
- Remove link cleanup from member remove
- Remove dead autoCreateTeamLinks() function
- Append DELETE to migration 000039: clear team-created agent_links
* fix(v3): tenant isolation for all agent_links queries + PromptBuilder Instructions
- DelegateTargets, GetLinkBetween, SearchDelegateTargets,
SearchDelegateTargetsByEmbedding, DeleteTeamLinksForAgent all now
scoped by tenant_id (fail-closed on missing tenant)
- BridgePromptBuilder now maps Instructions/InstructionContent to
AGENTS.md context file (was silently dropped)
* feat(v3): wire orchestration mode + evolution metrics into agent loop
- Orchestration mode: resolver resolves mode from team/links, tool filter
hides delegate/team_tasks based on mode, prompt builder injects delegation
targets section
- Evolution metrics: non-blocking goroutine records tool execution metrics
(name, success, duration) via EvolutionMetricsStore in both v2 loop and
v3 pipeline paths (sequential + parallel)
- Fix review findings: tenant ID propagated via store.WithTenantID in
background goroutine, 5s timeout prevents goroutine leak
* feat(v3): implement suggestion engine with pluggable analysis rules
- PG EvolutionSuggestionStore: CRUD for agent_evolution_suggestions table
- SuggestionEngine: aggregates 7-day metrics, runs rules, deduplicates
pending suggestions per type before creating new ones
- 3 initial rules: LowRetrievalUsage (usage_rate<0.2), ToolFailure
(success_rate<0.1), RepeatedTool (>100 calls/week → suggest skill)
- EventSuggestionCreated event type added to eventbus
- Cron wiring deferred to gateway startup integration pass
* feat(v3): implement auto-adapt guardrails with apply/rollback
- AdaptationGuardrails: max delta per cycle, min data points, locked
params, rollback-on-drop percentage
- ApplySuggestion: applies threshold suggestions to agent other_config
JSONB, stores baseline for rollback
- RollbackSuggestion: restores baseline values from suggestion params
- EvaluateApplied: compares post-apply metrics to baseline, auto-rolls
back when quality drops beyond threshold
- Scope limited to retrieval params only (never security settings)
* feat(v3): wire evolution stores + daily/weekly cron for suggestions
- Add EvolutionMetrics + EvolutionSuggestions to Stores struct + PG factory
- Wire EvolutionMetricsStore into ResolverDeps (cmd/gateway_managed.go)
- Add gateway_evolution_cron.go: daily suggestion analysis + weekly
evaluation/rollback for applied suggestions
- Cron runs as background goroutine with 5-min timeout per cycle
* fix(v3): address code review findings on evolution engine
- C1: persist baseline parameters before marking suggestion as applied
(was building map but never saving — rollback would always fail)
- H1: add tenant_id isolation to UpdateSuggestionStatus, GetSuggestion,
and new UpdateSuggestionParameters method
* test(v3): add unit tests for orchestration, suggestions, guardrails, prompt
- orchestration_mode_test: orchModeDenyTools (4 modes) + ResolveOrchestrationMode
(4 scenarios with mock stores)
- suggestion_rules_test: LowRetrievalUsage, ToolFailure, RepeatedTool with
threshold boundary tests (at/below/above min data points)
- evolution_guardrails_test: DefaultGuardrails values + CheckGuardrails
(insufficient data, locked params, zero-min fallback)
- prompt_builder_orchestration_test: BridgePromptBuilder orchestration section
presence/absence across 4 scenarios + target content verification
* test(v3): add integration tests for evolution metrics + suggestions
- Test helper: shared PG connection with sync.Once migration, per-test
tenant+agent seed with cleanup
- Evolution metrics: RecordMetric, AggregateToolMetrics (success rate),
Cleanup (TTL deletion)
- Evolution suggestions: full CRUD, UpdateSuggestionParameters (baseline
persist), tenant isolation (cross-tenant read blocked)
- Pipeline E2E: seed 25 failed tools + 55 low-usage retrievals, verify
SuggestionEngine creates suggestions, verify dedup on second run
- Fix: migration 039 de-duped (episodic_summaries already in 037)
- Fix: NULL reviewed_by scan via sql.NullString
* feat(v3): add HTTP API handlers for evolution, vault, episodic, orchestration, v3-flags
5 new handler files exposing v3 backend stores as REST endpoints:
- evolution_handlers.go: metrics query/aggregate + suggestions CRUD
- vault_handlers.go: cross-agent document listing + search + links
- episodic_handlers.go: episodic summaries list + hybrid search
- orchestration_handlers.go: computed mode + delegate targets (read-only)
- v3_flags_handlers.go: per-agent v3 feature flag get/toggle
Store fixes from code review:
- episodic FTS: use inline to_tsvector (no stored tsv column)
- episodic: conditional user_id filter in List + Search (admin view)
- episodic: add tenant_id to ExistsBySourceID + PruneExpired
- evolution: require tenant_id in context (no struct fallback)
- evolution: check RowsAffected on suggestion updates
- vault: optional agent_id filter in ListDocuments (cross-agent)
* feat(v3): add web UI for evolution tab, v3 settings, vault page, episodic memory
Agent Detail enhancements:
- V3 Settings section: pipeline/memory/retrieval flag toggles
- Orchestration section: mode badge + delegate targets display
- Evolution section: added metrics + suggestions v3 flag toggles
- Evolution tab: Recharts metrics charts + suggestion review table
with approve/reject/rollback actions + guardrails card
New pages:
- /vault: Knowledge Vault document registry with cross-agent listing,
hybrid search dialog, document detail with wikilinks
- Memory page: added Episodic Memory tab with summary cards,
expandable details, key topic badges, and hybrid search
Infrastructure:
- HttpClient: added patch() method
- Query keys: v3Flags, orchestration, evolution namespaces
- 4 new hooks: use-v3-flags, use-orchestration, use-evolution-metrics,
use-evolution-suggestions, use-vault, use-episodic
- i18n: vault namespace (en/vi/zh), agents + memory keys updated
- Reused formatRelativeTime from lib/format.ts (eliminated 3 duplicates)
* refactor(http): add bindJSON helper and migrate all decode call sites
Replace 36 json.NewDecoder(r.Body).Decode + error blocks with bindJSON
across 20 HTTP handler files. Standardizes decode error responses to
structured writeError format. Fixes unchecked decode in handleIndexAll.
* refactor(store): adopt sqlx for PG scan operations (Phase 1+2)
Add jmoiron/sqlx v1.4.0 with camelToSnake json tag mapper.
Migrate scan-heavy PG store methods to sqlx Get/Select:
- tracing.go: GetTrace, ListTraces, ListChildTraces, GetTraceSpans, GetCostSummary
- heartbeat.go: Get, ListDue, ListLogs
- providers.go: GetProvider, GetProviderByName, ListProviders, ListAllProviders
- mcp_servers.go: GetServer, GetServerByName, ListServers
- pairing.go: ListPending, ListPaired
- agents_export_queries.go: 5 export functions
- agents_export_team_queries.go: exportTeamMembers, ExportAgentLinks
All writes (INSERT/UPDATE/DELETE), execMapUpdate, and dynamic WHERE
builders remain raw SQL. Zero behavior change.
* refactor(store): adopt sqlx for SQLite scan operations (Phase 3)
Migrate SQLite store scan methods to sqlx Get/Select:
- providers.go: GetProvider, GetProviderByName, ListProviders, ListAllProviders
- tenants.go: GetTenant, GetTenantBySlug, ListTenants, GetTenantUser, ListUsers, ListUserTenants
- mcp_servers.go: GetServer, GetServerByName, ListServers
Create sqlx_scan_structs.go with sqliteTime-aware scan structs
(providerRow, tenantRow, tenantUserRow, mcpServerRow) to handle
SQLite TEXT timestamp parsing via StructScan.
* refactor(store): migrate PG bulk scan operations to sqlx (Phase 4)
Migrate scan-heavy methods across 6 PG store files:
- tenant_store.go: GetTenant, GetTenantBySlug, ListTenants, GetTenantUser,
ListUsers, ListUserTenants — removed 3 scan helpers
- teams.go: ListTeams, GetTeam, ListMembers, ListMembersByTenant
- teams_tasks_activity.go: ListComments, ListEvents, ListFollowUps
- pending_message_store.go: ListPending, ListByHistoryKey
- skills_grants.go: ListAgentGrants
- config_permissions.go: CheckPermission
~20 scan ops converted. Files with encryption post-processing,
pq.Array, pgvector, or dynamic SQL kept raw.
* refactor(store): extract shared CamelToSnake mapper, add UUIDArray usage note
- Move camelToSnake to internal/store/column_mapper.go (DRY)
- Both pg and sqlitestore packages now import shared CamelToSnake
- Add planned-use comment on UUIDArray type
* refactor(cli): migrate commands from config.json to HTTP API, add providers/setup/TUI
- Add unified HTTP client (gateway_http_client.go) with auth, error parsing, typed generics
- Rewrite agent list/add/delete to use gateway HTTP API instead of config.json
- Rewrite channels list to HTTP API, add channels add/delete subcommands
- Replace models command with full providers CRUD (list/add/update/delete/verify)
- Add setup wizard command (provider → agent → channel post-onboard flow)
- Add Bubble Tea TUI behind build tag (tui/!tui with noop fallback)
- Update onboard next-steps to mention goclaw setup
- Add build-tui Makefile target
- Fix URL path injection (url.PathEscape on all user-supplied path segments)
- Fix UTF-8 truncation in skills description display
* refactor(store): add explicit db struct tags, fix sqlx mapper for heartbeat scan error
Switch sqlx mapper from NewMapperFunc (which only applies CamelToSnake to
field names, not tag values) to NewMapperFunc("db", CamelToSnake) with
explicit db:"column_name" tags on all store structs.
Root cause: NewMapperFunc("json", fn) sets mapFunc but not tagMapFunc,
so camelCase json tags like "agentId" were used as-is instead of being
converted to "agent_id", causing "missing destination name" scan errors.
Fix: use db struct tags as the source of truth for column mapping.
Every DB entity field gets db:"column_name", nested JSON configs and
runtime-only structs get db:"-".
* test(store): add integration tests for 13 store interfaces (70 tests)
Cover Tier 1 (critical) + Tier 2 (security) stores with integration tests
running against pgvector pg18. Coverage from 2.4% to ~54%.
Stores tested: Session, Agent, Team/Task, Memory, KnowledgeGraph, Vault,
MCP Server, API Key, ConfigPermission, Contact.
Infrastructure: fixture builders (seedTeam, seedMCPServer, etc.),
mock EmbeddingProvider, multi-tenant helpers, expanded cleanup.
* fix(store): resolve NULL scan bugs in MCP server and task metadata
- mcp_servers: COALESCE nullable TEXT columns (display_name, command,
url, api_key, tool_prefix) to prevent sqlx scan failures
- mcp_servers_access: COALESCE nullable JSONB columns in ListAgentGrants
(tool_allow, tool_deny, config_overrides) to prevent silent row drops
- teams_tasks: default task metadata to '{}' instead of nil to satisfy
NOT NULL constraint on CreateTask
- sqlx_helpers: export InitSqlx for integration test setup
* feat(pipeline): fix v3 pipeline context injection, tracing, KG temporal filters
- Pipeline context: add InjectContext + LoadSessionHistory callbacks to
ContextStage, propagate enriched ctx via state.Ctx for iteration stages
- Pipeline tracing: wrap makeCallLLM with emitLLMSpanStart/End, wrap
makeExecuteToolCall/Raw with emitToolSpanStart/End
- Token counter: switch pipeline from FallbackCounter to TiktokenCounter
- KG temporal: add valid_until IS NULL filter to all entity/relation
queries (list, search, vector, FTS, traversal CTE, stats)
- Skills: add SkillEmbedder interface for future hybrid BM25+vector search
- Cache: remove unused tenantResolve dead code from PermissionCache
- Store: fix NULL scan bugs in tracing metadata and agent skill_nudge
- Test: add TestStoreKG_TemporalFilter integration test
- UI: add v3 version badge, evolution section, memory/traces improvements
* refactor(store): migrate KG store from raw sql.Rows to sqlx StructScan
Migrate 6 knowledge graph store files from manual rows.Scan() to
pkgSqlxDB.GetContext/SelectContext with intermediate scan row structs.
- Add entityRow, relationRow, traversalRow, dedupCandidateRow structs
with json.RawMessage for jsonb and time.Time for timestamptz columns
- Add toEntity()/toRelation() converters (UnixMilli + json.Unmarshal)
- Add sqlxTx() helper for wrapping *sql.Tx with sqlx mapper
- Fix ScanDuplicates passing time.Now().Unix() to TIMESTAMPTZ column
- Fix ListEntitiesTemporal missing tenant scope (scopeClause)
- Fix SupersedeEntity missing tenant scope and tenant_id on INSERT
- Fix DedupCandidate.CreatedAt using Unix() instead of UnixMilli()
- Update agents_export_queries.go to reuse new scan row structs
- Net -160 lines of manual scan boilerplate removed
* refactor(store): migrate memory, skills, agents, sessions, mcp, cron, vault stores to sqlx
Batch migration of 19 store files from raw rows.Scan() to
pkgSqlxDB.GetContext/SelectContext with intermediate scan row structs.
Groups migrated:
- Memory: memory_docs, memory_admin, memory_search, memory_embedding_cache
- Episodic: episodic_search, episodic_summaries
- Skills: skills, skills_admin, skills_embedding, skills_export_queries
- Agents: agents (backfill+shares), agents_context, agents_export_team_standalone
- Sessions: sessions_list (List, ListPaged, ListPagedRich)
- MCP: mcp_servers_access, mcp_export_queries
- Cron: cron_exec (GetRunLog)
- Vault: vault_documents (ListDocuments, ftsSearch, vectorSearch)
- Tenant: tenant_configs (ListDisabled, ListAll)
7 new scan row files created. Net -510 lines of manual scan boilerplate.
INSERT/UPDATE/DELETE and scalar COUNT queries kept as raw SQL.
* fix(store): fix 3 sqlx scan struct db tag issues found by audit
- Fix vault FTS alias mismatch: `AS rank` → `AS score` (critical: runtime scan error)
- Fix episodic key_topics type: json.RawMessage → pq.StringArray (TEXT[] column)
- Fix agentShareRow.CreatedAt: string → time.Time, wire to output struct
* feat(providers): implement Wave 2 provider resilience and intelligence
9-phase implementation covering:
- Request middleware chain with composable body transformers
- OpenAI prompt caching, service tier, and fast mode middlewares
- Error classification (9 categories) with two-tier failover
- Model registry with forward-compat resolvers (Anthropic + OpenAI)
- Embedding providers (OpenAI + Voyage) with 1536-dim validation
- Cooldown/probe system with per-provider:model state tracking
- Markdown-aware chunking shared across 5 channels
- Session recall via FTS + pgvector on episodic summaries
- Dreaming/promotion pipeline for long-term memory consolidation
Migrations: 000040 (episodic search index), 000041 (promoted_at column)
Schema version: 39 → 41
* feat(providers): wire model registry into gateway provider construction
Create InMemoryRegistry with Anthropic + OpenAI forward-compat resolvers
at gateway startup. Pass to all Anthropic and OpenAI providers created
from both config and DB sources.
* feat(consolidation): wire DomainEventBus and consolidation pipeline
Create DomainEventBus at gateway startup, thread through resolver →
LoopConfig → Loop → PipelineDeps. Emit session.completed event after
each run finalization. Register consolidation pipeline (episodic →
semantic → KG dedup → dreaming) with event bus subscriptions.
* fix(store): fix episodic key_topics pq.Array, ON CONFLICT, and migration 040 immutability
- episodic_summaries.go Create: json.Marshal(KeyTopics) → pq.Array (text[] column)
- episodic_search.go scanEpisodic/scanEpisodicRow: json.RawMessage → pq.StringArray
- episodic_summaries.go Create: ON CONFLICT add WHERE source_id IS NOT NULL for partial index
- migration 040: add immutable_array_to_string wrapper (array_to_string is STABLE in PG)
* test(store): add 17 integration tests for skills, cron, episodic, tenant configs
- Skills store: 6 tests (CRUD, grants, tenant isolation)
- Cron store: 4 tests (job CRUD, run log sqlx scan, pagination, tenant isolation)
- Episodic store: 4 tests (summary CRUD, list, FTS search, tenant isolation)
- Tenant configs: 3 tests (tool/skill disable, list, tenant isolation)
- Test helper: add cleanup for skills, cron, episodic tables
* fix(permissions): use cron-specific permission check for cron tool (#725)
* fix(security): harden exec path exemption matching (#721)
- Add absolute path exemption for dataDir/skills-store/ (fixes skill
scripts using absolute paths like /app/data/skills-store/ being denied)
- Strip surrounding quotes before prefix matching (LLMs often quote paths)
- Reject path traversal ("..") in exempt fields to prevent escape
- Switch from "any field exempt → skip" to per-field matching: only exempt
if ALL fields that match the deny pattern are individually exempt
- Closes pipe/comment bypass vectors where an exempt path in one argument
would exempt the entire command including non-exempt paths
Includes 27 test cases covering: legitimate access, quoted paths,
path traversal, unicode bypass, pipe/comment bypass, mixed args.
* fix(permissions): use cron-specific permission check for cron tool
Cron tool was hardcoded to check `file_writer` configType via
CheckFileWriterPermission(), ignoring the `cron` configType that
the UI actually saves when granting cron permissions. This caused
agents in group chats to be denied cron access even with correct
permission configured.
Add ConfigTypeCron constant and CheckCronPermission() that checks
`cron` configType first, falling back to `file_writer`.
---------
Co-authored-by: Viet Tran <viettranx@gmail.com>
* fix(chat): load message history on first conversation click (#730)
* fix(chat): load message history when selecting existing conversation from clean state
The skipNextHistoryRef was unconditionally set when sessionKey transitioned
from empty to non-empty. This prevented loadHistory() from running when
clicking an existing conversation from the initial /chat page. The skip
was only intended for the new-chat send flow where the optimistic message
is already displayed.
Guard the skip with expectingRunRef so it only activates when a message
send is in flight.
Closes #729
* docs: add UI diff evidence for PR #730
Before/after screenshots and HTML comparison report showing
first conversation click behavior fix.
* feat(whatsapp): port native WhatsApp channel with whatsmeow from dev
Cherry-pick 0db1e93a with manual conflict resolution:
- cmd/channels_cmd.go: kept dev-v3 HTTP API approach (not config-based)
- go.mod/go.sum: merged deps, ran go mod tidy
* feat(ui): v3 web UI enhancement — branded loading, rich markdown, vault graph, sidebar polish
- Branded loading: HTML pre-loader with logo pulse/shimmer, fade-out on app ready, PageLoader logo swap
- Rich markdown: wikilinks, mermaid (lazy-loaded), math/KaTeX, callouts/admonitions plugins
- Vault graph: force-directed document graph view with table/graph toggle
- Agent filters: type filter (open/predefined) on agents page
- Sidebar: tenant name + role badge in footer
- Query keys: add vault + episodic entries
* fix(ui): address code review — mermaid XSS, Safari compat, cache key
- Change mermaid securityLevel from "loose" to "strict" (XSS prevention)
- Add requestIdleCallback fallback for Safari < 17
- Fix vault all-links query key to use sorted doc IDs (stale cache fix)
- Remove dead abortRef from MermaidBlock
- Add prefers-reduced-motion to loader animation
* docs: update CLAUDE.md with v3 architecture + complete changelog
- Add 10 new v3 internal modules to project structure (pipeline, eventbus, consolidation, tokencount, vault, workspace, etc.)
- Add native WhatsApp channel, edition system, providerresolve, updater to structure
- Update Key Patterns with 8 v3-specific patterns (pipeline, eventbus, 3-tier memory, vault, evolution, orchestration, middleware)
- Add comprehensive V3 Redesign section to changelog covering:
- 8-stage pipeline with dual-mode gate
- DomainEventBus + consolidation workers
- 3-tier memory (working/episodic/semantic)
- Knowledge Vault with wikilinks
- Self-evolution engine
- Orchestration + delegate tool
- WorkspaceContext resolver
- ModelRegistry + provider adapter
- Feature flags
- Request middleware
- sqlx migration + 70+ integration tests
* fix(security): harden file path validation, tenant isolation, and tool access
- handleSign: validate path within workspace/dataDir before signing HMAC token
- handleSign: enforce tenant-scoped restriction for RBAC-enabled editions
- handleServe: add workspace/dataDir boundary check for ft= signed requests
- handleServe: remove cross-tenant findInWorkspace fallback for ft= requests
- TenantDataDir/TenantWorkspace: guard against empty slug resolving to parent
- exec tool: add tenants/ to AllowPathExemptions for tenant skill execution
- list_files: add AllowPaths support and wire skills directory access
* docs(v3): update 16 docs + add 2 new docs for v3 architecture
Update all docs/ to reflect v3 implementation (64 commits, 31K insertions):
- 00: architecture overview with 7 new packages
- 01: agent loop with pipeline, orchestration, evolution
- 02: providers with Wave 2 resilience (middleware, failover, registry)
- 03: tools with delegate, vault_search, vault_link, memory_expand
- 04,08,10,11,14,23: targeted v3 additions
- 06: store with 6 new tables, promoted columns, sqlx
- 07: memory with 3-tier architecture, consolidation pipeline
- 18,19: HTTP/WS API with v3 endpoints and methods
- 21: evolution system (metrics, suggestions, auto-adapt)
- model-steering: model registry relationship
New docs:
- 22-v3-http-endpoints.md: 12 v3 HTTP endpoints
- 24-knowledge-vault.md: vault architecture, wikilinks, search
* feat(v3): wire delegate tool, fix pipeline callbacks, clean dead code
Wire delegate tool end-to-end:
- Register DelegateTool in gateway with DelegateRunFunc
- Implement runFn: resolve target agent, build session key, propagate tracing
- Add async announce via msgBus (reuses subagent announce handler)
- Populate context fields (TenantID, Channel, ChatID) for routing
- Set DelegationID + ParentAgentID on RunRequest for event correlation
Fix pipeline callbacks:
- BreakLoop now completes remaining stages in current iteration
- EnrichMedia signature updated to use RunState for message buffer access
- Add non-streaming event emission for channel compatibility
- Fix user message flush tracking in v3 pipeline
Clean dead code (510 LOC removed):
- Delete memory/l1_cache.go, unified_search.go (superseded by vault search)
- Delete vault/auto_injector_impl.go, retriever_impl.go (never wired)
- Delete vault/sync_worker.go (never started)
- Remove orphaned EventMemoryLint, EventSuggestionCreated constants
* feat(v3): finalize stage — emit session.completed, NO_REPLY, strip directives
- Emit session.completed event for consolidation pipeline (episodic → semantic → dreaming)
- Detect NO_REPLY before flush so silent content is persisted for context
- Strip [[...]] message directives from user-facing content (v2 parity)
- Wire StripMessageDirectives, IsSilentReply, EmitSessionCompleted callbacks
* feat(vault): full CRUD — backend endpoints, UI dialogs, content preview
Backend (5 new HTTP endpoints):
- POST/PUT/DELETE /v1/agents/{id}/vault/documents — create, update, delete
- POST/DELETE /v1/agents/{id}/vault/links — create, delete
- Server-side validation for doc_type and scope enums
- Agent ownership verification on link creation
- FK cascade handles link cleanup on document delete
Frontend (React):
- Create document dialog (title, path, type, scope)
- Edit mode in detail dialog (inline title/type/scope editing)
- Delete document with confirmation
- Create link dialog (from/to doc, link type, context)
- Delete link with inline confirmation on badges
- Content preview (collapsible, lazy-loads via /v1/storage/files/)
- Mutation hooks with query invalidation
- i18n keys for en/vi/zh
* fix(v3): critical pipeline parity fixes — ChatRequest, reasoning, passback, media
- Enrich ChatRequest with all provider options (temperature, sessionKey,
agentID, userID, channel, workspace, tenantID) matching v2
- Add ResolveReasoningDecision for thinking models (o3, DeepSeek-R1, Kimi)
- Wire uniquifyToolCallIDs to prevent OpenAI 400 on duplicate IDs
- Add assistant message passback (Phase, RawAssistantContent) for Anthropic
- Emit block.reply for intermediate content (non-streaming channels)
- Add ContentSuffix append + ForwardMedia merge in FinalizeStage
- Build final assistant message with MediaRefs for session persistence
- Use effectiveMaxTokens() + OptMaxTokens constant
- Guard truncation retry on len(ToolCalls) > 0 + parseError check
- Accumulate ThinkingTokens in usage
- Deduplicate emitRun closure (shared from callbackSet)
- Fix EnrichMedia to receive RunState with actual messages
- Persist user message in makeFlushMessages (matching v2)
* fix(store): coerce NOT NULL JSONB columns to empty object on agent update
When switching provider away from ChatGPT OAuth, the UI sends
chatgpt_oauth_routing: null which violates the NOT NULL constraint.
Coerce null → '{}' for all NOT NULL JSONB promoted columns:
chatgpt_oauth_routing, reasoning_config, workspace_sharing,
shell_deny_groups, kg_dedup_config.
* feat(v3): v3 info modal redesign, agent links CRUD tab, sidebar rename
- Rewrite v3 info modal with 8 feature cards (pipeline, memory,
retrieval, vault, evolution, orchestration, resilience, registry)
with v2→v3 comparisons, stat badges, full i18n (en/vi/zh)
- Add tabbed layout to teams page: Agent Teams | Agent Links tabs
- Agent Links tab with full CRUD via existing WS RPC methods
(list/create/edit/delete) with Radix Select, Combobox, mutual
agent exclusion in create dialog
- Sidebar menu renamed to "Agent Link & Team"
- Backend: add source_display_name, source_emoji, target_emoji
to agent link joined queries for consistent display
* fix(store): include personal chats in cron delivery targets
Add 'user' contact_type to ListDeliveryTargets SQL filter in both
PG and SQLite stores. Previously only group/topic contacts appeared
in cron channel/chat dropdowns.
* fix(v3): duplicate messages, missing thinking, span numbering
- ThinkStage: skip AppendPending for final answer (no tool calls),
let FinalizeStage build the definitive message with sanitization
and MediaRefs — fixes duplicate assistant messages in session history
- RunResult: add Thinking field, propagate through v2 finalizeRun,
v3 convertRunResult, run.completed event, and chat.send response
- UI: capture thinkingRef before clearing on run.completed, include
in final message object so thinking renders without page refresh
- Span numbering: pass Iteration+1 in v3 callback to match v2's
1-based iteration display in trace span names
* fix(v3): wire delegation targets into BuildSystemPrompt
buildOrchestrationSection() was implemented and tested but never
wired into the actual BuildSystemPrompt() flow. Only the unused
BridgePromptBuilder had it. Add DelegateTargets + OrchMode fields
to SystemPromptConfig and inject "## Delegation Targets" section
so agents with agent_links see their delegation targets in prompt.
* fix(v3): sync mediaResults from bridgeRS to pipeline state
syncBridgeToState copied loopKilled, asyncToolCalls, deliverables
from the v2 bridgeRS but missed mediaResults. Tool results with
MEDIA: prefix were extracted by processToolResult into bridgeRS
but never propagated to state.Tool.MediaResults — causing
FinalizeStage to produce empty MediaRefs and RunResult.Media.
* fix(v3): populate SessionCompletedPayload in session.completed events
Both v2 loop and v3 pipeline emitted session.completed events with
nil Payload, causing episodicWorker type assertion to fail silently.
Episodic summaries were never created.
- Expand EmitSessionCompleted callback to pass msgCount, tokensUsed,
compactionCount from pipeline state
- V3 path: build payload from state.Messages.TotalLen(),
state.Think.TotalUsage, state.Compact.CompactionCount
- V2 path: build payload from history + rs.totalUsage +
sessions.GetCompactionCount()
* fix(v3): remaining pipeline parity gaps — skill postscript, team task count
- Add SkillPostscript callback to PipelineDeps + FinalizeStage,
matching v2's skill evolution nudge after complex tool runs
- Wire makeSkillPostscript() in adapter with same logic as v2
(skillEvolve + skillNudgeInterval + totalToolCalls threshold)
- Sync teamTaskCreates from bridgeRS to pipeline EvolutionState
(was already syncing teamTaskSpawns but missed creates)
* fix(evolution): add JSON struct tags to metric aggregates
ToolAggregate and RetrievalAggregate had no JSON tags, causing
Go to marshal field names as PascalCase (ToolName, CallCount)
while the UI expects snake_case (tool_name, call_count). Charts
rendered empty containers with no data bars.
Also change AvgDuration (time.Duration) to AvgDurationMs (float64)
for JSON-friendly serialization — time.Duration marshals as
nanoseconds which is unusable in frontend.
* fix(v3): add debug logging to episodic worker for consolidation pipeline
Add INFO/WARN logs at entry, summary decision, and creation points
to diagnose why episodic_summaries table stays empty in production.
* chore: silence noisy tenant_cache debug logs
* refactor(ui): replace v3 settings section with engine version picker + tabbed info modal
Replace flat toggle list with radio-style version cards (v2/v3) and
feature mini-cards. Redesign v3 info modal from single scroll to 3-tab
layout (Core Engine, Memory & Knowledge, Orchestration) with Lucide
icons and v2→v3 comparison cards.
- Add batchUpdate to use-v3-flags hook for atomic v2 switch
- Create engine-version-section with VersionCard + FeatureMiniCard
- Create v3-info-modal/ with 5 modular components (<45 LOC each)
- Add i18n keys (detail.engine + v3Info.tabs) for en/vi/zh
- Wire into agent-overview-tab, agent-header, agent-card
- Delete v3-settings-section.tsx + agent-v3-info-modal.tsx
* feat(v3): pass media files through delegate tool results
Delegate tool now carries media files (images, audio, etc.) produced
by the delegatee back to the parent agent. DelegateRunFunc returns
DelegateResult{Content, Media} instead of plain string.
- Add DelegateResult struct with Content + Media fields
- Convert agent.MediaResult to bus.MediaFile in gateway wire
- Attach media to sync result and async announce message
- Add MediaCount to DelegateCompletedPayload for observability
- Set MetaParentAgent in async announce metadata
* merge: bring main bug fixes into dev-v3
Cherry-pick 8 commits from origin/main:
- fix(telegram): handle group-to-supergroup migration (#698)
- feat(providers): add OpenRouter identification headers (#705)
- fix: deterministic prompt ordering for LLM cache hit (#719)
- fix(security): harden exec path exemption matching (#721)
- fix: invalidate storage size cache on delete and move (#726)
- fix: use errors.Is() for sentinel comparisons (#727)
- fix(desktop): add defaultValues to form dialogs (#737)
- Release: credential resolver, WhatsApp native, exec hardening (#754)
Conflict resolution:
- store/pg exports: accept main's errors.Is() additions
- shell.go: accept main's extracted matchesAnyPathExemption helper
- gateway_providers: merge both WithAnthropicName + WithAnthropicRegistry
- loop_types + resolver: merge v3 fields (OrchMode, DelegateTargets,
EvolutionMetricsStore) with main's new UserResolver/ContactStore
- gateway_setup: keep dev-v3's tenant-scoped path exemptions
- channels_cmd: keep dev-v3's HTTP API approach
* feat(v3): add foundation packages for architecture refactor (Phase 1)
Purely additive — zero changes to existing files. Creates shared types
and helpers that Phase 2-4 will migrate callers to:
- internal/store/base/: Dialect interface, BuildMapUpdate, nullable/JSON
helpers, scope clause builder, table metadata (44 tests)
- internal/orchestration/: ChildResult capture from v2/v3, media type
conversion with round-trip tests (10 tests)
- internal/providers/sse_reader.go: Shared SSE scanner replacing inline
bufio.Scanner boilerplate in 3 providers (8 tests)
* refactor(store): unify pg/ and sqlitestore/ helpers via base/ package (Phase 2)
- Create PG and SQLite dialect implementations (base.Dialect interface)
- Replace 15 duplicate helpers in pg/helpers.go with aliases to base.*
- Replace 13 duplicate helpers in sqlitestore/helpers.go with aliases
- Rewrite execMapUpdate and execMapUpdateWhereTenant to use
base.BuildMapUpdate with dialect-specific placeholders
- Rewrite scopeClause/scopeClauseAlias as thin wrappers around
base.BuildScopeClause
- Remove duplicate execMapUpdateWhereTenant from pg/agents.go
pg/helpers.go: 267→175 LOC, sqlitestore/helpers.go: 226→130 LOC
* refactor(orch): extract BatchQueue[T] generic for announce queues (Phase 3)
- Create internal/orchestration/batch_queue.go: generic producer-consumer
queue replacing duplicated sync.Map+mutex pattern (10 tests, race-safe)
- Simplify cmd/gateway_announce_queue.go: team queue uses BatchQueue,
removes announceQueueState/getOrCreate/drain/tryFinish (~40 LOC saved)
- Simplify cmd/gateway_subagent_announce_queue.go: subagent queue uses
BatchQueue, same pattern reduction (~40 LOC saved)
- Update cmd/gateway_consumer_handlers.go: callers use new signatures
* fix: remove defer accumulation in announce loop, clean comment tombstone
- Remove `defer ptd.ReleaseTeamLock()` inside for loop that accumulated
deferred calls per iteration (explicit ReleaseTeamLock already called)
- Remove dead comment tombstone in pg/agents.go
* fix: scope defer in announce loop via closure for panic safety
Wrap announce processing in closure so defer ptd.ReleaseTeamLock()
runs once per iteration instead of accumulating. Explicit release
still called for normal path; defer catches panics.
* refactor(providers): wire shared SSEScanner into 3 providers (Phase 4b)
Replace inline bufio.Scanner+SSE parsing boilerplate with shared
SSEScanner from sse_reader.go in:
- openai.go: data-only SSE (OpenAI, DashScope, Kimi)
- codex.go: data-only SSE (OpenAI Codex)
- anthropic_stream.go: event+data SSE (uses EventType() for switch)
Removes ~24 LOC of duplicated scanner setup + manual line parsing.
* refactor(agent): force v3 pipeline, remove v2 runLoop (Phase 4A)
- Delete runLoop() from loop.go (~745 LOC removed), keep shared helpers
(resolveToolCallName, hasParseErrors, truncateToolArgs)
- Remove v2/v3 gate in loop_run.go: always call runViaPipeline()
- Remove v3PipelineEnabled field from Loop struct + LoopConfig + resolver
- Always resolve workspace in loop_context.go (was behind v3 gate)
- Deprecate PipelineEnabled in V3Flags (kept for backward compat parsing)
All agents now always use v3 pipeline. No behavioral change for agents
that already had v3_pipeline_enabled=true (which was all production agents).
* refactor(gateway): decompose gateway.go from 1295 to 476 LOC (Phase 4B)
Extract sections of runGateway() into focused files:
- gateway_deps.go: gatewayDeps struct for shared state
- gateway_http_wiring.go: wireHTTPHandlersOnServer (~207 LOC)
- gateway_events.go: event subscribers + teamTaskEventType (~367 LOC)
- gateway_lifecycle.go: signal handling, shutdown, server start (~222 LOC)
- gateway_tools_wiring.go: cron/heartbeat/session tool wiring (~116 LOC)
Also extracted: startCronAndHeartbeat, makeDelegateAnnounceCallback.
Pure structural refactoring — no behavior change.
* test(agent): add v3 force migration guard tests
Verify v2 runLoop is deleted, V3PipelineEnabled field removed from
LoopConfig, and V3Flags backward compat parsing still works.
Compile-time guards prevent accidental re-introduction of v2 code.
* feat(delegate): wire ChildResult + fix media passthrough (Phase 3 gap)
- Use orchestration.CaptureFromRunResult in delegate run callback
to standardize result capture via ChildResult
- Use MediaResultToBusFiles in CaptureFromRunResult (DRY)
- Fix delegate_tool.go metadata key: delegate_id → delegation_id
* refactor(ui): remove v2/v3 pipeline toggle, always show V3 (Phase 4E)
- engine-version-section.tsx: remove pipeline toggle, show V3 read-only
- use-agent-version.ts: always return "v3" (no flag check)
- use-v3-flags.ts: remove v3_pipeline_enabled from toggleable flags
- Update i18n strings (en/vi/zh): remove v2-specific tooltip text
* test: add Phase 5 test infrastructure for v3 architecture refactor
- pg/pg_dialect_test.go: 4 tests for PG Dialect (placeholder, transform,
returning, interface compliance)
- sse_reader_test.go: 4 edge case tests (empty data, scanner error,
event type persistence, no data after [DONE])
- gateway_announce_format_test.go: 10 tests for team + subagent
announce formatting (single/batch, success/failure, snapshot)
Coverage: base/ 96%, orchestration/ 100%, providers/ 57%
* chore: remove stale runLoop references + apply go fix (Phase 6)
- Update comments referencing deleted runLoop in pipeline callbacks,
loop_types, stage.go
- go fix: reflect.TypeFor, range over int, strings.Builder
* docs: update architecture docs for v3 refactor completion (Phase 6)
- CLAUDE.md: add store/base/, orchestration/ to project structure;
remove v2 runLoop and dual-mode gate references; rename Pipeline (v3)
to Pipeline; add SSEScanner and BatchQueue to key patterns
- docs/00-architecture-overview.md: update module map with new packages,
remove [V3] markers (now standard)
- docs/17-changelog.md: add V3 Architecture Refactor entry (6 phases)
* feat(evolution): skill draft auto-generation + go fix cleanup
- Add skill draft template generation from evolution suggestions
- Wire skill apply endpoint in evolution HTTP handlers
- Apply go fix across codebase (range-over-int, reflect.TypeFor, etc.)
- Minor refactors: simplify switch/case, reduce string builder allocs
- Gateway deps: add skills loader field
* perf(prompt): deterministic tool order + Anthropic cache boundary split
Sort tool names in buildToolingSection for cache-stable output.
Insert GOCLAW_CACHE_BOUNDARY marker before Time section; Anthropic
provider splits system prompt into 2 blocks (stable cached, dynamic not).
Backward compat: no marker → single cached block.
* perf(prompt): optimize cache boundary position + add Execution Bias
Move Memory Recall and stable context files (AGENTS.md, TOOLS.md,
USER_PREDEFINED.md) above cache boundary. Dynamic per-user files
(USER.md, BOOTSTRAP.md) stay below. Add Execution Bias section
(full mode only) forcing action-oriented tool use. Fix duplicate
header when Project Context split across boundary.
* feat(prompt): add PromptMode task/none + 3-layer resolution
Expand PromptMode from full|minimal to full|task|minimal|none.
Task mode = enterprise automation: keeps Tooling, Execution Bias,
Safety-slim, Persona-slim, Skills-search, MCP-search, Memory-slim,
Workspace, Runtime, Delegation. Drops verbose sections (Tool Call
Style, Self-Evolution, Spawning, Recency, etc.).
None mode returns identity line only. 3-layer mode resolution:
runtime override > auto-detect (subagent/cron) > agent config
(other_config.prompt_mode) > default (full).
* feat(prompt): provider prompt contributions (stable/dynamic/overrides)
Add PromptContribution struct + PromptContributor interface for
provider-specific prompt customizations. Providers can inject
StablePrefix (before cache boundary), DynamicSuffix (after boundary),
or override sections by ID (e.g. execution_bias). Nil-safe: providers
that don't implement the interface get default behavior.
* feat(prompt): pinned skills with hybrid inline+search mode
Add per-agent pinned_skills config (max 10, from other_config JSONB).
Pinned skills always inline in prompt via BuildPinnedSummary.
Non-pinned discovered via skill_search. Hybrid section shows both
pinned XML and search instructions. Works in full and task modes.
* fix(prompt): validate prompt_mode from DB before cast
Reject unknown prompt_mode values from OtherConfig JSONB
(e.g. typos like "taks") by checking against validPromptModes set.
Invalid values default to "" (full mode). Prevents broken prompts
where no mode flags match.
* fix(prompt): wire SectionIDToolCallStyle for provider override
Tool Call Style section now uses sectionContent() like Execution Bias,
allowing providers to override it via PromptContribution.SectionOverrides.
* feat(store): implement 9 SQLite store backends for v3 parity
Close feature gap between PostgreSQL and SQLite (desktop/lite) editions.
96 methods across 9 stores: AgentLinks, SubagentTasks, SecureCLI,
SecureCLIGrants, EvolutionMetrics, EvolutionSuggestions, Episodic,
KnowledgeGraph, Vault. Schema v8→v9 adds 4 tables.
Key design decisions:
- LIKE-based search replaces tsvector/FTS5 (unavailable in modernc.org/sqlite)
- Go-side StringSimilarity for KG dedup (replaces pgvector cosine)
- Recursive CTE traversal with comma-delimited path cycle detection
- AES-256-GCM encryption on all SecureCLI read/write paths
- F15: SecureCLI disabled when EncryptionKey empty
- ON CONFLICT DO UPDATE (never INSERT OR REPLACE) to preserve FK cascades
* docs(store): add SQLite parity section to store data model docs
Document 9 new SQLite store implementations, schema v9, and
feature parity gaps (LIKE vs FTS, Jaro-Winkler vs vector dedup).
* fix(docker): skip web-builder stage when ENABLE_EMBEDUI=false
Use BuildKit conditional stage pattern so web-builder is not executed
when embedding is disabled. Also update pnpm-lock.yaml for 6 new deps
that were missing from the lockfile (markdown/math/mermaid packages).
* feat(vault): embed metadata.summary for richer vector search
Include summary from metadata JSONB in embedding text (title + path +
summary) for better semantic search. Update tsvector generated column
to include summary in FTS index. Zero new columns — summary stored in
existing metadata JSONB field. Backward compat: docs without summary
still embed title+path only.
* fix(vault-ui): wider detail dialog, markdown rendering, create tooltip
- Detail dialog: sm:max-w-lg → sm:max-w-2xl, content area 200→300px
- Content preview: render markdown via MarkdownRenderer instead of <pre>
- Create button: add tooltip explaining "select agent first" when disabled
* feat(ui): prompt_mode dropdown + pinned_skills multi-select
Add PromptSettingsSection to agent overview tab:
- prompt_mode: Select dropdown (full/task/minimal/none)
- pinned_skills: Tag input with max 10, click to remove
Both save to agent other_config JSONB via existing onUpdate flow.
Self-contained save button appears only when values change.
* fix(vault-ui): enlarge dialog to max-w-4xl, constrain markdown heading sizes
Dialog sm:max-w-2xl → sm:max-w-4xl. Markdown headings capped at
text-base/text-sm via Tailwind child selectors to prevent oversized
h1/h2 in content preview. Content area raised to 400px.
* refactor(ui): move pinned skills to dedicated section with skill select
Split pinned_skills out of PromptSettingsSection into PinnedSkillsSection.
Uses useAgentSkills hook for proper dropdown with granted skills list.
Placed right below SkillsSection in agent overview tab. Badge chips
with X to remove, Select dropdown to add. Max 10 enforced.
* fix(ui): add border wrap to capabilities section for visual consistency
* feat(ui): redesign prompt mode as compact cards, replace v3 badge
- Rewrite prompt settings from select dropdown to 2×2 compact cards
with lucide icons (Zap/Wrench/Package/CircleOff) and ring selection
- Move prompt settings to top of agent overview tab
- Replace v3 badge with prompt mode badge in header, card, and list row
- Add i18n keys for prompt mode labels/descriptions (en/vi/zh)
- Extract readPromptMode() to shared agent-display-utils
- Remove dead useAgentVersion hook and v3Tooltip/v2Tooltip i18n keys
- Remove v3 badge from EngineVersionSection (keep feature toggles)
- Use cn() for badge class composition with twMerge safety
* feat(ui): add section tags and token estimates to prompt mode cards
Each card now shows which system prompt sections are included
(Persona, Tools, Safety, Skills, MCP, Memory, etc.) as compact
tags, plus estimated base token range (~2-4K for full, ~10 for none).
Helps users understand the impact of each mode at a glance.
* fix(ui): correct token estimates with actual tiktoken measurements
Measured via tiktoken (cl100k) on realistic config:
full=~1.7K+, task=~1.1K+, minimal=~660, none=~6 base tokens.
The "+" suffix indicates context files/skills/MCP add more.
* fix(ui): use tiktoken-measured token ranges for prompt mode cards
Measured across 3 scenarios (bare/typical/heavy) via tiktoken cl100k:
full=~500-2.9K, task=~350-1.2K, minimal=~350-820, none=~6 tokens.
Ranges reflect real configs from minimal (3 tools) to heavy
(10 tools, long persona, MCP, sandbox, pinned skills).
* fix(ui): use production-measured token counts for prompt mode cards
Measured via tiktoken on real production agent (tieu-ho) with
4 context files (AGENTS.md, SOUL.md, IDENTITY.md, USER_PREDEFINED.md),
14 tools, memory, KG, skills, Telegram channel:
full=~3.1K, task=~2.2K, minimal=~1.9K, none=~6 tokens.
* refactor(ui): accurate section tags per prompt mode from systemprompt.go
Section tags now match exact gating logic in BuildSystemPrompt():
- full: persona, tools, exec bias, call style, safety, skills,
MCP, memory, sandbox, evolution, channel hints (11 sections)
- task: style echo, tools, exec bias, safety (slim), skills (search),
MCP (search), memory (slim) (7 sections)
- minimal: tools, safety (2 sections + shared context files)
- none: no tags shown, no token count (trivially ~6 tokens)
Removed workspace/identityOnly tags (shared across all modes).
* feat(ui): replace engine version toggles with V3 capabilities modal
- Remove 3 toggle switches (Pipeline, Memory, Retrieval) from agent
detail — all features are now always-on for v3 agents
- Replace with compact static badges layout
- New V3 Capabilities modal with 4 tabs:
Pipeline (8-stage flow), Memory (L0/L1/L2 tiers),
Knowledge (KG, Vault, Dreaming), Orchestration (Delegate, Evolution)
- Each tab has Lucide icons and technical descriptions of how
features actually work
- Separate i18n namespace v3-capabilities in en/vi/zh locales
* refactor(ui): move V3 badge to agent header, remove engine version section
- Add clickable V3 badge next to agent name in header
- Clicking opens V3 Capabilities modal
- Remove Engine Version section from overview tab entirely
- Remove unused EngineVersionSection import
* feat: v3 prompt engine overhaul — 7-phase restructuring
Phase 1: Fix mode resolution — subagent/cron cap at task (not minimal),
heartbeat stays minimal, pinned skills injected in all modes, USER_PREDEFINED
added to minimal allowlist.
Phase 2: Context file restructuring — new CAPABILITIES.md (domain expertise,
separated from SOUL.md), AGENTS_MINIMAL.md for heartbeat sessions, both added
to stable context files and minimal allowlist.
Phase 3: Summoner update — all 4 prompt builders generate CAPABILITIES.md,
fallback 2-call stores capabilities alongside SOUL.md.
Phase 4: Open agent deprecation — creation silently upgrades open→predefined
in both HTTP and WS endpoints.
Phase 5: Bootstrap auto-contact — sender name from channel metadata injected
into bootstrap context for 1-turn onboarding (DM only).
Phase 6: System prompt preview — GET /v1/agents/{id}/system-prompt-preview
endpoint with mode param, token counting, section parsing.
Phase 7: Agent creation UX — removed open agent type toggle, schema simplified,
description always required, prompt mode cards updated with v3 token estimates.
* fix(vault-ui): redesign detail dialog and link dialog UX
- Vault detail: show content preview by default (not collapsed),
move type/scope to header badges, hash to subtle footer
- Link dialog: replace native <select> with searchable Combobox
for target document and link type (5 presets + custom)
- Fix Vietnamese i18n: add proper diacritics to v3-capabilities
* fix(i18n): keep prompt section badges in English across all locales
Technical terms like Persona, Tools, Safety, Memory, Skills, MCP,
Sandbox, Evolution should not be translated — they are UI labels
matching system prompt section names.
* feat(ui): system prompt preview in Files tab + README restructure
Files tab: add "System Prompt" item in sidebar below context files.
When selected, shows readonly preview with mode selector (full/task/
minimal/none), token count badge, and cache boundary highlighting.
Fetches from GET /v1/agents/{id}/system-prompt-preview.
README: remove Claw Ecosystem comparison tables, remove OpenClaw port
reference, rename "What Makes It Different" to "Core Features" with v3
additions (8-stage pipeline, 4-mode prompt, 3-tier memory, knowledge
vault, self-evolution), slim built-in tools to category summary table.
* feat: replace architecture images with v3 sketchnotes
Add 9 new architecture sketchnote images generated for v3:
- 8-Stage Agent Pipeline
- 4-Mode Prompt System
- 3-Tier Memory Architecture
- Multi-Tenant Architecture
- Agent Orchestration
- Knowledge Vault
- Provider Adapter System
- Self-Evolution System
- DomainEventBus
Remove old architecture images (architecture.jpg, goclaw_multi_tenant.png,
agent-delegation.jpg, agent-teams.jpg).
Update README to reference new images in Architecture and Orchestration
sections. Consolidate orchestration section (remove separate delegation
and teams subsections).
* feat(readme): add remaining 4 architecture sketchnotes with sections
Add Knowledge Vault, Self-Evolution, Provider Adapters, and Event-Driven
Architecture sections with corresponding sketchnote images and concise
descriptions. All 9 v3 architecture diagrams now in README.
* feat: reorder README architecture images, evolution guardrails fix, memory tools enhancement
README: reorder architecture sketchnotes (Multi-Tenant first), remove
pinnedSkills from task mode sections badge.
Backend: evolution guardrails fix, memory auto-injector and tools
enhancement, gateway HTTP wiring update.
* feat(desktop): add 12 v3 feature sections to agent detail panel
Add evolution expansion (skill learning, v3 flags), prompt mode selector,
evolution dashboard tab (CSS bar charts, suggestions, guardrails),
thinking/reasoning config, orchestration display, context pruning,
compaction, subagents (lite-limited), tool policy, sandbox config,
and pinned skills management.
Extract agent detail state into use-agent-detail-state hook.
Add getWithParams to ApiClient. Add i18n keys to en/vi/zh locales.
* fix(vault): team-scope security — prevent cross-team data corruption and leaks
- Add team_id UUID + custom_scope to vault_documents (PG migration 043, SQLite migration 10)
- COALESCE-based UNIQUE prevents silent cross-team data overwrite on ON CONFLICT
- PG trigger auto-corrects scope to 'personal' on team deletion (ON DELETE SET NULL)
- Store layer: TeamID filter on all query methods, RunContext-based team scoping
- CreateLink validates same-tenant + same-team boundary (defense in depth)
- VaultInterceptor: infer scope from RunContext, add AfterWriteMedia for binary files
- Wire VaultInterceptor into 5 tools (create_image, create_video, create_audio, tts, edit)
- HTTP handlers: team membership validation via HasTeamAccess, non-owner defaults to personal
- GetBacklinks: single JOIN + LIMIT 100 replaces N+1, VaultBacklink struct with team_id
- vault_search/vault_link/vault_backlinks tools read TeamID from RunContext
- Backlinks filtered by team boundary to prevent title exfiltration
Addresses 7 original + 13 red-team findings (1 CRITICAL, 5 HIGH, 3 MEDIUM).
* feat(evolution): allow CAPABILITIES.md self-evolution, backfill existing agents, cleanup v3 dead flags
- Allow self-evolving predefined agents to read/write CAPABILITIES.md
(domain expertise) in addition to SOUL.md (style/tone)
- Add CAPABILITIES.md to contextFileSet (DB routing) and protectedFileSet
(group chat permission check)
- Update buildSelfEvolveSection() system prompt to mention CAPABILITIES.md
- Merge ensureUserPredefined + ensureCapabilities into single
ensureBackfillFiles() — one DB query instead of two, with error logging
- Remove dead V3MemoryEnabled/V3RetrievalEnabled from PipelineConfig,
Loop, LoopConfig, resolver, and adapter (always true at runtime)
- Keep fields in V3Flags struct for JSONB backward compat with
deprecation comments
- Add 10 new tests covering interceptor read/write, prompt section,
and backfill logic
* feat(export): add v3 sections to agent import/export pipeline
- Phase 1: KG temporal fields (valid_from/valid_until) in export/import
- Phase 2: Episodic summaries section (episodic/summaries.jsonl)
- Phase 3: Evolution metrics + suggestions (evolution/*.jsonl)
- Phase 4: Vault documents + links (vault/*.jsonl, two-pass link resolution)
- Phase 5: ImportSummary expanded to 17 fields, cron/overrides UPSERT dedup
- Add backup/pgpass.go: secure .pgpass credential handling for pg_dump
* feat(backup): system and tenant backup/restore with S3 support
Phase 6: System backup — pg_dump + filesystem tar.gz, .pgpass security,
preflight check, CLI (goclaw backup) + HTTP API with SSE progress
Phase 7: System restore — psql restore, path traversal protection,
active connection check, --force/--dry-run/--skip-db/--skip-files
Phase 8: S3 integration — upload/download via AWS SDK v2, credentials
encrypted in config_secrets (AES-256-GCM), custom endpoint support
Phase 9: Tenant backup/restore — per-table JSONL export (43 tables,
5-tier FK ordering), 3 restore modes (upsert/replace/new-tenant),
tenant admin permission checks, CLI + HTTP endpoints
* feat(backup): add table registry validation + gate tenant backup for PG-only
- DiscoverTenantTables() queries information_schema for tables with tenant_id
- ValidateTableRegistry() cross-checks hardcoded registry vs actual schema,
warns about unregistered tables to prevent silent data loss
- TenantBackup() runs validation before export
- Tenant backup/restore gated for PG-only — SQLite Lite edition has only
master tenant, returns clear error directing to system backup instead
* fix(backup): address code review security + correctness findings
- C1: Remove backup_path from S3 upload — prevent file exfiltration,
require backup_token only
- H1: createNewTenant fails explicitly on slug conflict instead of
silent NOOP that orphans imported data
- H2: Validate JSONL column names against safe regex before SQL
interpolation — prevent SQL injection from crafted archives
- H3: Replace EOF string comparison with io.Copy in addFileToTar
- H6: Add atomic concurrency guard — reject concurrent backup/restore
* feat(web): add backup & restore admin page with 4 tabs
New admin-only page at /backup-restore with System Backup, System
Restore, S3 Config, and Tenant Backup tabs. Reuses existing
useSseProgress hook and OperationProgress component for real-time
SSE streaming. Includes full i18n support (en/vi/zh).
* feat(web): system prompt preview modal + CAPABILITIES.md backfill + pipeline parity
- Add Eye button in agent header → opens wide modal with markdown-rendered system prompt preview
- CAPABILITIES.md one-time startup backfill for pre-v3 agents (single SQL INSERT WHERE NOT EXISTS)
- Add CAPABILITIES.md to allowedAgentFiles so it shows in Files tab
- Refactor preview API to reuse pipeline's BuildSystemPrompt via BuildPreviewPrompt()
- Resolve actual tool names from registry, provider contributions, sandbox config, shell deny groups
- Resolve team context (TEAM.md virtual file, members, workspace, delegation targets) from DB
- Add IsMinimalAllowed() for mode-aware context file filtering
- Support ?user_id= query param for per-user context file preview
* feat(prompt): redesign 4 system prompt modes with tiered context files
- Add AGENTS_CORE.md (minimal) and AGENTS_TASK.md (task) templates
- Implement ModeAllowlist() for per-mode context file filtering
- Wire filtering into pipeline (loop_history) and preview API
- Upgrade none mode from single-line to functional tool-call prompt
with slim safety, pinned skills, MCP search, workspace, runtime
- Task mode now gets full persona (SOUL.md + IDENTITY.md)
- Add prompt_mode selector to agent creation dialog
- Mode upgrade warning toast on save
- Skip summoning modal for none/minimal mode agents
- SQL migration 000044: seed AGENTS_CORE/TASK, remove AGENTS_MINIMAL
- SQLite: fix migration v9 duplicate column, v10 COALESCE in UNIQUE
- Add ModeAllowlist tests, none mode tests, SQLite schema tests
- Update i18n (en/vi/zh) with new section keys and mode descriptions
Token targets: full ~4.8K, task ~1.3K, minimal ~570, none ~640
* refactor(web): extract PromptModeCards shared component
Reuse same card layout (icon + name + desc + tokens + section tags)
in both agent creation dialog and agent settings page.
Create dialog uses compact=true to hide section tags.
* fix(backup): align preflight API contract + add package guidance UX
Backend preflight endpoint now returns flat JSON matching frontend
interface (pg_dump_available, disk_space_ok, size metrics) instead of
internal checks array. Adds DirSize/FormatBytes helpers.
Frontend: PageHeader component for consistency, amber alert banner
with link to /packages when pg_dump missing, mobile grid fix.
* feat(web): system prompt preview modal + CAPABILITIES.md backfill + pipeline parity
* feat(vault): async enrich worker for auto summary + semantic linking
Add EventBus-driven worker that generates LLM summaries for vault
documents, embeds them via pgvector, and auto-creates semantic links
between related docs using cosine similarity search.
- Skip embedding in UpsertDocument when summary is empty
- Add UpdateSummaryAndReembed + FindSimilarDocs to VaultStore interface
- Wire enrichment events in AfterWrite/AfterWriteMedia interceptors
- BatchQueue batching for burst writes, bounded dedup (10K cap)
- 5-minute LLM timeout, 0.7 similarity threshold, top-5 neighbors
- SQLite: summary-only (no vector ops, graceful noop)
* refactor(web): phase 1 quick wins — grid breakpoints, silent catches, useQuery migration
- Fix 6 grid-cols-2 without mobile breakpoint → grid-cols-1 sm:grid-cols-2
- Replace 9 silent .catch(() => {}) with console.error for debuggability
- Migrate system-prompt-preview.tsx from useEffect+useState to useQuery
- Add agent column + path tail truncation to vault documents table
* refactor(web): phase 2 split god components into sub-components
- cron-overview-tab: extract schedule, delivery, lifecycle sections
- channel-detail: extract timeline hook + dialogs component
- agent-advanced: extract state utils (deriveState, buildPayload)
- heartbeat-config: add deriveFormDefaults helper
- tenant-backup: split into backup + restore sections
- board-container: extract useBoardTasks hook
- provider-form: extract standard form fields
- contacts: extract contacts table component
* refactor(web): phase 3 form standardization — RHF+Zod migration + field errors
- Migrate 6 forms from useState to React Hook Form + Zod validation
- Create 4 new schema files (api-key, vault, login, s3-config)
- Add inline field error display to memory, mcp, heartbeat, agent-create forms
- Replace raw <input> with <Input> in login forms for consistent styling
* refactor(web): phase 4 lazy-load 20 dialog components + memory co-location
- Convert 20 heavy dialogs from eager to React.lazy + Suspense
- All dialogs use named exports with .then(m => ({ default: m.X })) wrapper
- Suspense fallback={null} per-dialog (invisible during chunk load)
- Move memory page components into documents/, knowledge-graph/, episodic/ subdirs
* refactor(web): phase 5 data fetching polish — staleTime tiers + optimistic updates
- Apply 3-tier staleTime policy: static 5min, standard 60s, realtime 5-15s
- Update 31 hooks with explicit staleTime (37 useQuery call sites)
- Add optimistic updates to builtin-tools, v3-flags, cron, mcp toggles
* refactor(web): phase 6 styling standardization — CVA, design tokens, font utility
- Convert input, textarea, select trigger to CVA with size variants
- Add font-mono-code utility class, replace 4 inline fontFamily styles
- Add text-2xs (10px) and text-xs-plus (11px) design tokens
- Replace 241 arbitrary text-[10px]/text-[11px] with token classes
- Co-locate memory page knowledge-graph files into subdirectory
* feat(vault): pagination, team filter, graph upgrade, and link_type param
- Add CountDocuments to VaultStore interface (PG + SQLite)
- Wrap vault list response as {documents, total} for pagination
- Add optional link_type param to vault_link tool (wikilink/reference)
- Fix resolveOrRegister to use inferVaultDocType instead of hardcoded "note"
- Add team filter dropdown and pagination UI (100/page) to vault page
- Rewrite vault graph with KG-level features: zoom controls, node limit
selector, click highlight/dim, double-click detail, link labels, stats bar
- Decouple graph data fetch from table (independent limit 500)
- Update VaultDocument type with team_id, summary, custom_scope, media
* fix(vault): graph not rendering due to containerRef timing issue
The early return for loading state prevented containerRef from mounting,
so useLayoutEffect and ResizeObserver never captured dimensions. Moved
loading/empty states inside the container div so the ref always exists.
* fix(vault): eliminate graph flicker on zoom by using ref instead of state
onZoom fired setZoomLevel on every frame → React re-render → new inline
callback references → ForceGraph2D flickered. Now zoom level is stored
in a ref and the display is updated via direct DOM mutation, avoiding
re-renders during continuous zoom interactions.
* refactor(backend): comprehensive audit — safety, god files, interfaces, BaseChannel, tests, benchmarks
Phase 1: ExportTokenStore with lifecycle management (replaces leaked globals)
Phase 2: Split 4 god files (agents_export, agents_import, openai, loop_history) into 17 focused files
Phase 3: Add testability interfaces (consolidation EntityExtractor, heartbeat ProviderResolver/EventPublisher/ActiveSessionChecker)
Phase 4: Consolidate 6 duplicated fields + policy/pairing logic into BaseChannel, migrate all 8 channels
Phase 5: 57 new unit tests (i18n, edition, channels/policy, consolidation, heartbeat) + CI coverage
Phase 6: 30 benchmark tests (tokencount, skills BM25, tool registry, agent loop)
Phase 7: Context propagation fix + 6 metadata key constants
69 files changed, +7361/-3828 (net -3050 lines removed)
* fix(vault): fetch graph links per-agent so all-agents mode shows links
useVaultAllLinks required a single agentId, so links never loaded in
all-agents mode (agentId=""). Replaced with inline per-agent fetching
that groups documents by agent_id and fetches links for each agent.
* docs: add vault enhancement changelog entry
* feat(agent): add displayName to Loop and SystemPrompt for runtime context
Pass agent display name through LoopConfig and SystemPromptConfig so
the runtime section can show a human-readable agent name.
* fix(vault): truncate path from head and hash from middle in detail dialog
Path now uses dir=rtl so ellipsis appears at the start, keeping the
meaningful filename visible. SHA-256 hash shows first 8 + last 8 chars
with ellipsis in the middle instead of truncating the tail.
---------
Co-authored-by: Plateau Nguyen <nguyennlt.ncc@gmail.com>
Co-authored-by: Kai (Tam Nhu) Tran <61256810+kaitranntt@users.noreply.github.com>
This commit is contained in:
866 files changed
+73165
-11578
No files matched your search
@@ -97,16 +97,18 @@ flowchart TD
|
||||
|
||||
| Module | Description |
|
||||
|--------|-------------|
|
||||
| `internal/gateway/` | WebSocket + HTTP server, client handling, method router |
|
||||
| `internal/gateway/` | WebSocket + HTTP server, client handling, method router. Decomposed: gateway_deps, gateway_http_wiring, gateway_events, gateway_lifecycle, gateway_tools_wiring |
|
||||
| `internal/gateway/methods/` | RPC method handlers: chat, agents, teams, delegations, sessions, config, skills, cron, pairing, exec approval, usage, send |
|
||||
| `internal/agent/` | Agent loop (think, act, observe), router, resolver, system prompt builder, sanitization, pruning, tracing, memory flush, DELEGATION.md + TEAM.md injection |
|
||||
| `internal/providers/` | LLM providers: Anthropic (native HTTP + SSE), OpenAI-compatible (HTTP + SSE, 12+ providers), DashScope (Qwen), ACP (JSON-RPC 2.0 subprocess), Claude CLI, Codex, extended thinking support, retry logic |
|
||||
| `internal/providers/` | LLM providers: Anthropic (native HTTP + SSE), OpenAI-compatible (HTTP + SSE, 12+ providers), DashScope (Qwen), ACP (JSON-RPC 2.0 subprocess), Claude CLI, Codex, extended thinking support, retry logic. Shared SSEScanner in providers/sse_reader.go |
|
||||
| `internal/providers/acp/` | ACP protocol implementation: ProcessPool (subprocess lifecycle), ToolBridge (fs/terminal), session management |
|
||||
| `internal/tools/` | Tool registry, filesystem ops, exec/shell, policy engine, subagent, delegation manager, team tools, context file + memory interceptors, credential scrubbing, rate limiting, PathDenyable |
|
||||
| `internal/tools/dynamic_loader.go` | Custom tool loader: LoadGlobal (startup), LoadForAgent (per-agent clone), ReloadGlobal (cache invalidation) |
|
||||
| `internal/tools/dynamic_tool.go` | Custom tool executor: command template rendering, shell escaping, encrypted env vars |
|
||||
| `internal/store/` | Store interfaces: SessionStore, AgentStore, ProviderStore, SkillStore, MemoryStore, CronStore, PairingStore, TracingStore, MCPServerStore, TeamStore, ChannelInstanceStore, ConfigSecretsStore |
|
||||
| `internal/store/` | Store interfaces: SessionStore, AgentStore, ProviderStore, SkillStore, MemoryStore, CronStore, PairingStore, TracingStore, MCPServerStore, TeamStore, ChannelInstanceStore, ConfigSecretsStore. Dual-DB support via Dialect pattern |
|
||||
| `internal/store/base/` | Shared store abstractions: Dialect interface, NilStr, BuildMapUpdate, BuildScopeClause, and other common helpers for both PostgreSQL and SQLite |
|
||||
| `internal/store/pg/` | PostgreSQL implementations (`database/sql` + `pgx/v5`) |
|
||||
| `internal/store/sqlitestore/` | SQLite implementations (`modernc.org/sqlite`) for desktop edition |
|
||||
| `internal/bootstrap/` | System prompt files (AGENTS.md, SOUL.md, TOOLS.md, IDENTITY.md, USER.md, BOOTSTRAP.md) + seeding + truncation |
|
||||
| `internal/config/` | Config loading (JSON5) + env var overlay |
|
||||
| `internal/skills/` | SKILL.md loader (5-tier hierarchy) + BM25 search + hot-reload via fsnotify |
|
||||
@@ -132,6 +134,14 @@ flowchart TD
|
||||
| `internal/sessions/` | Session management and lifecycle |
|
||||
| `internal/tasks/` | Task management system |
|
||||
| `internal/upgrade/` | Database schema version tracking and migrations |
|
||||
| `internal/pipeline/` | 8-stage pluggable agent pipeline (context → history → prompt → think → act → observe → memory → summarize) |
|
||||
| `internal/orchestration/` | Orchestration primitives: BatchQueue[T] generic for result aggregation, ChildResult capture, media conversion helpers |
|
||||
| `internal/eventbus/` | DomainEventBus: typed event publishing, worker pool, dedup, retry, used by consolidation workers |
|
||||
| `internal/consolidation/` | Memory consolidation workers: episodic (recent facts), semantic (embeddings), dreaming (synthesis), dedup |
|
||||
| `internal/tokencount/` | Token counting: tiktoken BPE counter with fallback, used by pipeline for context tracking |
|
||||
| `internal/workspace/` | Workspace context resolver: 6 scenarios (agent default, team lead, team member, dispatch, subagent, cron) |
|
||||
| `internal/vault/` | Knowledge Vault: wikilinks (semantic mesh), hybrid search (BM25+vector), filesystem sync, L0 auto-injection |
|
||||
| `internal/channels/whatsapp/` | Native WhatsApp channel via whatsmeow (replaces WhatsApp API), QR auth, media handling |
|
||||
|
||||
---
|
||||
|
||||
@@ -409,11 +419,68 @@ flowchart TD
|
||||
|
||||
---
|
||||
|
||||
## V3 Architecture (Wave 1 & Wave 2 - dev-v3 branch)
|
||||
|
||||
### Overview
|
||||
|
||||
V3 introduces a **pluggable 8-stage pipeline** (replacing the monolithic `runLoop`), an event-driven architecture via `DomainEventBus`, and advanced memory consolidation. The system maintains backward compatibility via a **dual-mode gate** at the loop level: agents can opt into v3 pipeline or stay on v2 monolithic loop per-agent via `other_config` JSONB.
|
||||
|
||||
### 8-Stage Pipeline
|
||||
|
||||
| Stage | Phase | Responsibility |
|
||||
|-------|-------|-----------------|
|
||||
| **ContextStage** | Setup (once) | Inject agent/user/workspace context, compute per-user files |
|
||||
| **ThinkStage** | Iteration | Build system prompt, filter tools by policy, call LLM |
|
||||
| **PruneStage** | Iteration | Context pruning (2-pass: soft trim → hard clear), run memory flush if compaction triggered |
|
||||
| **ToolStage** | Iteration | Execute tool calls (parallel goroutines for multiple calls) |
|
||||
| **ObserveStage** | Iteration | Process tool results, append to messages |
|
||||
| **CheckpointStage** | Iteration | Track iteration state, check for loop exit conditions |
|
||||
| **FinalizeStage** | Finalize (once) | Sanitize output, flush messages, update session metadata |
|
||||
|
||||
### Feature Flags (in `agents.other_config` JSONB)
|
||||
|
||||
| Flag | Key | Type | Default | Purpose |
|
||||
|------|-----|------|---------|---------|
|
||||
| Pipeline | `v3_pipeline_enabled` | bool | false | Use v3 pipeline instead of v2 monolithic loop |
|
||||
| Memory | `v3_memory_enabled` | bool | false | Enable episodic/semantic consolidation workers via DomainEventBus |
|
||||
| Retrieval | `v3_retrieval_enabled` | bool | false | Enable Knowledge Vault with wikilinks + hybrid search, L0 auto-injection |
|
||||
| Evolution Metrics | `self_evolution_metrics` | bool | false | Track agent metrics for evolution suggestions (tool usage, retrieval patterns) |
|
||||
| Evolution Suggestions | `self_evolution_suggestions` | bool | false | Generate and apply evolution suggestions (auto-adapt prompt/tools) |
|
||||
|
||||
### Memory Consolidation System
|
||||
|
||||
**DomainEventBus** drives asynchronous consolidation:
|
||||
|
||||
- **Episodic Worker** — Extracts facts from recent runs, clusters by topic, stores in `episodic_memory` table with embeddings
|
||||
- **Semantic Worker** — Reprocesses episodic clusters, generates abstracted summaries, produces `semantic_memory` entries
|
||||
- **Dreaming Worker** — Synthesizes novel insights from memory clusters, cross-links related memories, drives self-evolution
|
||||
- **Dedup Worker** — Prevents duplicate memory entries, maintains consistency across consolidation cycles
|
||||
|
||||
### Workspace Context Resolver
|
||||
|
||||
Six distinct workspace scenarios:
|
||||
|
||||
1. **Agent default** — Agent workspace from config, sandbox environment
|
||||
2. **Team lead** — Team workspace as default (agent coordinates tasks)
|
||||
3. **Team member** — Agent workspace with team workspace accessible via `WithToolTeamWorkspace()`
|
||||
4. **Dispatch** — Temporary workspace from `req.TeamWorkspace` (one-off delegated task)
|
||||
5. **Subagent** — Inherited workspace from parent agent via context propagation
|
||||
6. **Cron** — Workspace resolved from agent + timezone context at cron trigger time
|
||||
|
||||
### Knowledge Vault (Wikilinks + Hybrid Search)
|
||||
|
||||
- **Wikilinks**: Bidirectional semantic links (`[[related-concept]]`) automatically extracted from memories
|
||||
- **Hybrid Search**: BM25 keyword search + vector similarity (pgvector) combined via RRF (reciprocal rank fusion)
|
||||
- **L0 Auto-Injection**: Top-K vault entries injected into system prompt as "relevant context from vault"
|
||||
- **Filesystem Sync**: Vault entries exported as `.md` files for manual editing, re-imported with change tracking
|
||||
|
||||
---
|
||||
|
||||
## Cross-References
|
||||
|
||||
| Document | Content |
|
||||
|----------|---------|
|
||||
| [01-agent-loop.md](./01-agent-loop.md) | Agent loop detail, sanitization pipeline, history management |
|
||||
| [01-agent-loop.md](./01-agent-loop.md) | Agent loop detail, v3 pipeline stages, sanitization pipeline, history management, orchestration modes, self-evolution |
|
||||
| [02-providers.md](./02-providers.md) | LLM providers, retry logic, schema cleaning |
|
||||
| [03-tools-system.md](./03-tools-system.md) | Tool registry, policy engine, interceptors, custom tools, MCP grants |
|
||||
| [04-gateway-protocol.md](./04-gateway-protocol.md) | WebSocket protocol v3, HTTP API, RBAC, identity propagation |
|
||||
|
||||
+205
-4
@@ -2,11 +2,161 @@
|
||||
|
||||
## Overview
|
||||
|
||||
The Agent Loop implements a **Think --> Act --> Observe** cycle. Each agent owns a `Loop` instance configured with a provider, model, tools, workspace, and agent type. A user message enters as a `RunRequest`, passes through `runLoop`, and exits as a `RunResult`. The loop iterates up to 20 times: the LLM thinks, optionally calls tools, observes results, and repeats until it produces a final text response.
|
||||
The Agent Loop implements a **Think --> Act --> Observe** cycle. Each agent owns a `Loop` instance configured with a provider, model, tools, workspace, and agent type. A user message enters as a `RunRequest`, passes through the loop, and exits as a `RunResult`.
|
||||
|
||||
**V3 Dual Mode**: The loop supports two execution paths:
|
||||
- **V2 (monolithic)**: Original `runLoop()` function (default for backward compatibility)
|
||||
- **V3 (pipeline)**: Pluggable 8-stage pipeline (`internal/pipeline/`, enabled via feature flag)
|
||||
|
||||
Both paths implement the same external behavior; the difference is internal architecture. The loop iterates up to 20 times: the LLM thinks, optionally calls tools, observes results, and repeats until it produces a final text response.
|
||||
|
||||
---
|
||||
|
||||
## 1. RunRequest Flow
|
||||
## V3 Pipeline Architecture
|
||||
|
||||
When `pipeline_enabled` is true, `Loop.Run()` delegates to `runViaPipeline()`, which orchestrates the v3 pipeline:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
RUN["Loop.Run<br/>runRequest"] --> GATE{pipeline_enabled?}
|
||||
GATE -->|false| V2["runLoop<br/>v2 monolithic"]
|
||||
GATE -->|true| V3["runViaPipeline<br/>v3 pipeline"]
|
||||
|
||||
V3 --> NEWSTATE["NewRunState<br/>input, nil, model, provider"]
|
||||
NEWSTATE --> NEWPIPE["NewDefaultPipeline<br/>8 stages"]
|
||||
NEWPIPE --> PIPE_RUN["Pipeline.Run<br/>setup → iteration loop → finalize"]
|
||||
|
||||
PIPE_RUN --> CONVERT["convertRunResult<br/>pResult → RunResult"]
|
||||
CONVERT --> RESULT["RunResult"]
|
||||
```
|
||||
|
||||
### Stage Execution Order
|
||||
|
||||
```
|
||||
Setup (runs once)
|
||||
├─ ContextStage: Inject context, compute workspace, ensure per-user files
|
||||
│
|
||||
Iteration Loop (max 20 iterations)
|
||||
├─ ThinkStage: Build system prompt, filter tools, call LLM
|
||||
├─ PruneStage: Soft/hard trim context, run memory flush if needed
|
||||
├─ ToolStage: Execute tool calls (parallel)
|
||||
├─ ObserveStage: Process tool results, append messages
|
||||
└─ CheckpointStage: Check iteration state, conditionally break
|
||||
|
||||
Finalize (runs once, uses background context if cancelled)
|
||||
└─ FinalizeStage: Sanitize output, flush messages, update metadata
|
||||
```
|
||||
|
||||
### Stage Details
|
||||
|
||||
**ContextStage**
|
||||
- Inject context: `WithAgentID()`, `WithUserID()`, `WithAgentType()`, `WithLocale()`
|
||||
- Resolve per-user workspace (base + sanitized userID)
|
||||
- Ensure per-user files exist (idempotent via `sync.Map` cache)
|
||||
- Persist agent/user IDs on session
|
||||
|
||||
**ThinkStage**
|
||||
- Resolve workspace + context files dynamically
|
||||
- Build system prompt (15+ sections)
|
||||
- Inject conversation summary if exists
|
||||
- Run history pipeline (limitHistoryTurns → pruneContextMessages → sanitizeHistory)
|
||||
- Filter tools through PolicyEngine (RBAC)
|
||||
- Call LLM, record span with token counts
|
||||
- Emit `chunk` events (streaming) or single response
|
||||
|
||||
**PruneStage**
|
||||
- Estimate token ratio vs context window
|
||||
- If >= 30%, run soft trim pass (keep first/last 1500 chars, replace middle with "...")
|
||||
- If >= 50%, run hard clear pass (replace with placeholder)
|
||||
- Trigger memory flush (synchronous) if compaction threshold exceeded
|
||||
|
||||
**ToolStage**
|
||||
- Execute single tool sequentially (no goroutine overhead)
|
||||
- Execute multiple tools in parallel via goroutines, sort results by index
|
||||
- Emit `tool.call` before, `tool.result` after
|
||||
- Record tool span
|
||||
- Append tool messages to buffer
|
||||
|
||||
**ObserveStage**
|
||||
- Process tool result stream
|
||||
- Handle `NO_REPLY` convention (silent completion)
|
||||
- Append assistant message with tool call info
|
||||
|
||||
**CheckpointStage**
|
||||
- Increment iteration counter
|
||||
- Check if max iterations reached → `BreakLoop`
|
||||
- Check if context cancelled → `AbortRun`
|
||||
|
||||
**FinalizeStage**
|
||||
- Run 7-step output sanitization pipeline
|
||||
- Flush buffered messages atomically
|
||||
- Update session metadata (model, provider, token counts)
|
||||
- Emit `run.completed` or `run.failed` event
|
||||
|
||||
---
|
||||
|
||||
## Orchestration Modes
|
||||
|
||||
Agents support three orchestration modes that determine which inter-agent tools are available:
|
||||
|
||||
### ModeSpawn (Default)
|
||||
- **Use case**: Single independent agent
|
||||
- **Tools available**: `spawn` (self-clone child agents)
|
||||
- **Tools hidden**: `delegate`, `team_tasks`
|
||||
- **Resolution**: Default when no team or delegate links
|
||||
|
||||
### ModeDelegate
|
||||
- **Use case**: Agent with linked delegate targets
|
||||
- **Tools available**: `spawn`, `delegate` (dispatch to linked agents)
|
||||
- **Tools hidden**: `team_tasks`
|
||||
- **Resolution**: When `agent_links` table has rows with source = this agent
|
||||
|
||||
### ModeTeam
|
||||
- **Use case**: Agent in a team (multiple agents collaborating)
|
||||
- **Tools available**: `spawn`, `delegate`, `team_tasks` (full team workspace)
|
||||
- **Tools hidden**: None
|
||||
- **Resolution**: When `teams` table has a row with agent_id = this agent
|
||||
|
||||
**Mode Resolution Priority**: Team > Delegate > Spawn
|
||||
|
||||
The system prompt includes relevant details for each mode (delegate targets, team context, shared workspace paths).
|
||||
|
||||
---
|
||||
|
||||
## Self-Evolution System
|
||||
|
||||
Agents can auto-adapt their behavior based on metrics and admin-approved suggestions.
|
||||
|
||||
### Evolution Suggestion Engine
|
||||
|
||||
Analyzes agent metrics on a periodic schedule (cron job):
|
||||
|
||||
1. **LowRetrievalUsageRule** — Detects if `memory_search` or `knowledge_graph_search` is underutilized; suggests enabling vault
|
||||
2. **ToolFailureRule** — Identifies frequently failing tools; suggests limiting tool set or retraining
|
||||
3. **RepeatedToolRule** — Detects repetitive tool calls (loop detection); suggests prompt adjustment
|
||||
|
||||
### Adaptation Guardrails
|
||||
|
||||
**AdaptationGuardrails** struct controls safety limits (stored in `agents.other_config.evolution_guardrails`):
|
||||
|
||||
| Field | Default | Purpose |
|
||||
|-------|---------|---------|
|
||||
| `max_delta_per_cycle` | 0.1 | Max parameter change per cycle (prevents wild swings) |
|
||||
| `min_data_points` | 100 | Require at least N metrics before applying suggestion |
|
||||
| `rollback_on_drop_pct` | 20.0 | Revert if quality drops >20% after applying |
|
||||
| `locked_params` | [] | Parameter names that cannot auto-change (e.g., "temperature") |
|
||||
|
||||
### Suggestion Workflow
|
||||
|
||||
1. **SuggestionEngine.Analyze()** evaluates rules against 7-day metrics window
|
||||
2. Generates `EvolutionSuggestion` records (status="pending")
|
||||
3. Admin reviews in dashboard, approves/rejects
|
||||
4. On approval, auto-adapt worker applies suggestion + records baseline metrics
|
||||
5. Next cycle detects quality regression and auto-rolls back if threshold exceeded
|
||||
|
||||
---
|
||||
|
||||
## 1. RunRequest Flow (V2 Monolithic - Original)
|
||||
|
||||
The full lifecycle of a single agent run is broken into seven phases.
|
||||
|
||||
@@ -562,10 +712,13 @@ Enabled via the `GOCLAW_TRACE_VERBOSE=1` environment variable.
|
||||
|
||||
## 14. File Reference
|
||||
|
||||
### Agent Loop (V2 & V3)
|
||||
|
||||
| File | Responsibility |
|
||||
|------|---------------|
|
||||
| `internal/agent/loop_run.go` | Run() entry point: trace creation, span management, event emission wrapper |
|
||||
| `internal/agent/loop.go` | runLoop() core loop: LLM iteration, tool execution, message buffering, event emission |
|
||||
| `internal/agent/loop_run.go` | Run() entry point: dual-mode gate (v2 vs v3), trace creation, span management |
|
||||
| `internal/agent/loop_pipeline_adapter.go` | Bridge v2 Loop to v3 Pipeline: state conversion, dependency injection, callback wiring |
|
||||
| `internal/agent/loop.go` | runLoop() core loop: LLM iteration, tool execution, message buffering (v2 path) |
|
||||
| `internal/agent/loop_history.go` | History pipeline: limitHistoryTurns, pruneContextMessages, sanitizeHistory, summary injection |
|
||||
| `internal/agent/pruning.go` | Context pruning: 2-pass soft trim and hard clear algorithm |
|
||||
| `internal/agent/loop_compact.go` | Mid-loop compaction: in-memory message summarization during iterations |
|
||||
@@ -577,4 +730,52 @@ Enabled via the `GOCLAW_TRACE_VERBOSE=1` environment variable.
|
||||
| `internal/agent/sanitize.go` | 7-step output sanitization pipeline |
|
||||
| `internal/agent/memoryflush.go` | Pre-compaction memory flush: embedded agent turn with write_file tool |
|
||||
| `internal/agent/toolloop.go` | Tool execution and loop detection (no-progress warnings) |
|
||||
| `internal/agent/orchestration_mode.go` | OrchestrationMode enum: spawn/delegate/team, mode resolution logic, prompt section data |
|
||||
| `internal/agent/suggestion_engine.go` | SuggestionEngine: metrics analysis, rule evaluation, evolution suggestion generation |
|
||||
| `internal/agent/evolution_guardrails.go` | AdaptationGuardrails: safety checks for auto-adaptation, delta constraints, rollback logic |
|
||||
| `internal/bootstrap/files.go` | Bootstrap file loading and context file preparation |
|
||||
|
||||
### V3 Pipeline
|
||||
|
||||
| File | Responsibility |
|
||||
|------|---------------|
|
||||
| `internal/pipeline/pipeline.go` | Pipeline orchestrator: setup → iteration → finalize stage execution |
|
||||
| `internal/pipeline/stage.go` | Stage interface: Execute(ctx, state), StageResult (Continue/BreakLoop/AbortRun) |
|
||||
| `internal/pipeline/context_stage.go` | ContextStage: context injection, workspace resolution, per-user file setup |
|
||||
| `internal/pipeline/think_stage.go` | ThinkStage: system prompt building, tool filtering, LLM call |
|
||||
| `internal/pipeline/prune_stage.go` | PruneStage: context pruning (2-pass), memory flush trigger |
|
||||
| `internal/pipeline/tool_stage.go` | ToolStage: tool execution (serial/parallel), result processing |
|
||||
| `internal/pipeline/observe_stage.go` | ObserveStage: tool result stream handling, NO_REPLY detection |
|
||||
| `internal/pipeline/checkpoint_stage.go` | CheckpointStage: iteration tracking, exit conditions |
|
||||
| `internal/pipeline/finalize_stage.go` | FinalizeStage: output sanitization, message flush, metadata update |
|
||||
| `internal/pipeline/memory_flush_stage.go` | MemoryFlushStage: pre-compaction memory persistence |
|
||||
| `internal/pipeline/run_state.go` | RunState: mutable pipeline state, iteration tracking, exit codes |
|
||||
| `internal/pipeline/substates.go` | Sub-state structures (messages, tool results, context) |
|
||||
| `internal/pipeline/message_buffer.go` | MessageBuffer: deferred message persistence |
|
||||
|
||||
### V3 Memory & Knowledge
|
||||
|
||||
| File | Responsibility |
|
||||
|------|---------------|
|
||||
| `internal/consolidation/episodic_worker.go` | Episodic memory: extract facts from runs, cluster by topic, embed |
|
||||
| `internal/consolidation/semantic_worker.go` | Semantic memory: reprocess episodic clusters, generate abstractions |
|
||||
| `internal/consolidation/dreaming_worker.go` | Dreaming worker: synthesize insights, cross-link memories, drive evolution |
|
||||
| `internal/consolidation/dedup_worker.go` | Dedup worker: prevent duplicate entries, maintain consistency |
|
||||
| `internal/consolidation/workers.go` | Worker pool startup and lifecycle |
|
||||
| `internal/vault/retriever_impl.go` | Vault retrieval: hybrid search (BM25+vector), RRF ranking |
|
||||
| `internal/vault/auto_injector_impl.go` | L0 auto-injection: top-K vault entries into system prompt |
|
||||
| `internal/vault/links.go` | Wikilink parsing and semantic mesh construction |
|
||||
| `internal/vault/sync_worker.go` | Filesystem sync: vault → .md files, .md → vault re-import |
|
||||
|
||||
### V3 Infrastructure
|
||||
|
||||
| File | Responsibility |
|
||||
|------|---------------|
|
||||
| `internal/eventbus/domain_event_bus.go` | DomainEventBus interface: Publish, Subscribe, Start, Drain |
|
||||
| `internal/eventbus/bus_impl.go` | BusImpl: worker pool, event dedup, retry with backoff |
|
||||
| `internal/eventbus/event_types.go` | DomainEvent type definitions, EventType enums |
|
||||
| `internal/tokencount/tiktoken_counter.go` | Tiktoken BPE token counter (cl100k_base for OpenAI models) |
|
||||
| `internal/tokencount/token_counter.go` | TokenCounter interface and factory |
|
||||
| `internal/tokencount/fallback_counter.go` | Fallback counter (linear estimation) if tiktoken unavailable |
|
||||
| `internal/workspace/resolver_impl.go` | WorkspaceContext resolver: 6 scenarios, context variables |
|
||||
| `internal/workspace/workspace_context.go` | WorkspaceContext data structure and context injection |
|
||||
+27
-15
@@ -702,10 +702,37 @@ Reasoning behavior:
|
||||
|
||||
---
|
||||
|
||||
## 13. Wave 2: Provider Resilience (v3)
|
||||
|
||||
GoClaw v3 Wave 2 adds composable request middleware, error classification, per-model cooldown, and 2-tier failover for production resilience.
|
||||
|
||||
**Request Middleware** — Transforms provider requests in composable pipeline. Built-in: `CacheMiddleware` (prompt caching), `ServiceTierMiddleware` (routing hints), `RateLimitMiddleware` (quota management). Zero-alloc fast path: `ComposeMiddlewares` returns nil if all inputs nil.
|
||||
|
||||
**Error Classification** — Maps provider errors to 9 canonical reasons: `FailoverAuth`, `FailoverAuthPermanent`, `FailoverRateLimit`, `FailoverOverloaded`, `FailoverBilling`, `FailoverFormat`, `FailoverModelNotFound`, `FailoverTimeout`, `FailoverUnknown`. `DefaultClassifier` pattern-matches body strings (OpenAI, Anthropic pre-registered). Detects context overflow (triggers auto-compaction).
|
||||
|
||||
**Cooldown Tracking** — `CooldownTracker` in-memory state machine. Per-reason durations: 30s (rate limit), 60s→120s escalated (overloaded), 10m (auth), 1h (permanent auth/model not found), 15s (timeout), 5m (billing). Auto-decay 24h TTL; probe interval ≥30s.
|
||||
|
||||
**2-Tier Failover** — `RunWithFailover[T]`: Tier 1 rotates API profiles for transient errors (≤5 rotations); Tier 2 falls back to next model for permanent errors. Returns all attempts with classifications. Exhausted → `FailoverSummaryError`.
|
||||
|
||||
**Model Registry** — Thread-safe forward-compat resolver. Seeds Claude, GPT, Qwen models. Each spec: context window, max tokens, reasoning/vision flags, per-1M cost. Unknown models → provider's `ForwardCompatResolver` (caches hit). Template cloning with patch overrides.
|
||||
|
||||
**Embedding Providers** — OpenAI (text-embedding-3-small, 1536 dims, batch 2048) and Voyage AI (1024 dims, batch 1024) via `store.EmbeddingProvider`. Used by vault and episodic memory. All vectors normalized to 1536 for pgvector column.
|
||||
|
||||
---
|
||||
|
||||
## 14. File Reference
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `internal/providers/middleware.go` | RequestMiddleware type, ComposeMiddlewares, ApplyMiddlewares (zero-alloc fast path) |
|
||||
| `internal/providers/middleware_cache.go` | CacheMiddleware for prompt caching |
|
||||
| `internal/providers/middleware_service_tier.go` | ServiceTierMiddleware for routing hints |
|
||||
| `internal/providers/error_classify.go` | ErrorClassifier, DefaultClassifier, 9 failover reasons, context overflow detection |
|
||||
| `internal/providers/cooldown.go` | CooldownTracker: per-model:provider failure state, reason-dependent durations, probe intervals |
|
||||
| `internal/providers/failover.go` | RunWithFailover[T]: 2-tier logic, profile rotation, model fallback, candidate exhaustion |
|
||||
| `internal/providers/model_registry.go` | ModelRegistry, ModelSpec, InMemoryRegistry, forward-compat resolver, seeded defaults |
|
||||
| `internal/providers/embedding_openai.go` | OpenAI embedding provider (text-embedding-3-small, 1536 dims, batch 2048) |
|
||||
| `internal/providers/embedding_voyage.go` | Voyage AI embedding provider |
|
||||
| `internal/providers/types.go` | Provider interface, ChatRequest, ChatResponse, Message, ToolCall, Usage types |
|
||||
| `internal/providers/anthropic.go` | Anthropic provider: native HTTP + SSE, request/response marshaling |
|
||||
| `internal/providers/anthropic_request.go` | Anthropic request builder: message formatting, tool schemas, system blocks |
|
||||
@@ -717,29 +744,14 @@ Reasoning behavior:
|
||||
| `internal/providers/claude_cli_chat.go` | Chat/ChatStream implementation for CLI provider |
|
||||
| `internal/providers/claude_cli_session.go` | Session management: per-session state, history, workspace |
|
||||
| `internal/providers/claude_cli_mcp.go` | MCP configuration and server bridge for CLI provider |
|
||||
| `internal/providers/claude_cli_auth.go` | Authentication and token handling for CLI |
|
||||
| `internal/providers/claude_cli_parse.go` | Response parsing and message extraction from CLI output |
|
||||
| `internal/providers/claude_cli_deny_patterns.go` | Path validation and deny pattern enforcement |
|
||||
| `internal/providers/claude_cli_hooks.go` | Security hooks configuration for CLI tool execution |
|
||||
| `internal/providers/claude_cli_types.go` | Internal types for CLI provider (session, config, options) |
|
||||
| `internal/providers/codex.go` | CodexProvider: OAuth-based ChatGPT Responses API |
|
||||
| `internal/providers/codex_build.go` | Codex request builder: message formatting, phase handling |
|
||||
| `internal/providers/codex_types.go` | Codex request/response types and OAuth token management |
|
||||
| `internal/providers/chatgpt_oauth_router.go` | Agent-side routing across multiple authenticated OpenAI Codex OAuth providers |
|
||||
| `internal/providers/dashscope.go` | DashScope provider: OpenAI-compat wrapper with thinking budget, tools+streaming fallback |
|
||||
| `internal/providers/acp_provider.go` | ACPProvider: orchestrates ACP-compatible agent subprocesses |
|
||||
| `internal/providers/acp/types.go` | ACP protocol types: InitializeRequest, SessionUpdate, ContentBlock, etc. |
|
||||
| `internal/providers/acp/process.go` | ProcessPool: subprocess lifecycle, idle TTL reaping, crash recovery |
|
||||
| `internal/providers/acp/jsonrpc.go` | JSON-RPC 2.0 request/response marshaling over stdio |
|
||||
| `internal/providers/acp/tool_bridge.go` | ToolBridge: handles fs and terminal requests, workspace sandboxing |
|
||||
| `internal/providers/acp/terminal.go` | Terminal lifecycle: create, output, exit, release, kill |
|
||||
| `internal/providers/acp/session.go` | Session state tracking per ACP agent |
|
||||
| `internal/providers/retry.go` | RetryDo[T] generic function, RetryConfig, IsRetryableError, backoff computation |
|
||||
| `internal/providers/schema_cleaner.go` | CleanSchemaForProvider, CleanToolSchemas, recursive schema field removal |
|
||||
| `internal/providers/registry.go` | Provider registry: registration, lookup, lifecycle management |
|
||||
| `cmd/gateway_providers.go` | Provider registration from config and database during gateway startup |
|
||||
| `internal/tools/create_image_byteplus.go` | BytePlus Seedream async image generation (async polling) |
|
||||
| `internal/tools/create_video_byteplus.go` | BytePlus Seedance async video generation (async polling, 2K resolution) |
|
||||
|
||||
---
|
||||
|
||||
|
||||
+78
-45
@@ -76,8 +76,14 @@ Context keys ensure each tool call receives the correct per-call values without
|
||||
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `memory_search` | Search memory documents (BM25 + vector) |
|
||||
| `memory_get` | Retrieve a specific memory document |
|
||||
| `memory_search` | Search memory documents (BM25 + vector hybrid search) |
|
||||
| `memory_get` | Retrieve a specific memory document (L1 summary) |
|
||||
| `memory_expand` | Load full episodic memory content by ID (L2 deep retrieval) |
|
||||
|
||||
**Memory Layers:**
|
||||
- **L1 (Search)**: `memory_search` returns abstracts + vector scores for ranking
|
||||
- **L2 (Expand)**: `memory_expand` retrieves full summary for a given episodic ID
|
||||
- **Vault**: `vault_search` unified discovery across memory + vault docs + knowledge graph
|
||||
|
||||
### Sessions (group: `sessions`)
|
||||
|
||||
@@ -89,10 +95,11 @@ Context keys ensure each tool call receives the correct per-call values without
|
||||
| `spawn` | Spawn subagent or delegate to another agent |
|
||||
| `session_status` | Get current session status |
|
||||
|
||||
### Knowledge & Search (group: `knowledge`)
|
||||
### Knowledge & Vault (group: `knowledge`)
|
||||
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `vault_search` | Primary discovery: unified search across vault docs, memory, knowledge graph (hybrid FTS + vector) |
|
||||
| `knowledge_graph_search` | Search knowledge graph for entities and relationships |
|
||||
| `skill_search` | Search available skills (BM25) |
|
||||
|
||||
@@ -112,7 +119,9 @@ Context keys ensure each tool call receives the correct per-call values without
|
||||
|
||||
### Delegation (group: `delegation`)
|
||||
|
||||
> The `delegate` tool has been removed. Delegation is now handled via agent teams using `team_tasks` and `team_message`.
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `delegate` | Inter-agent task delegation via agent_links (async/sync modes with timeout) |
|
||||
|
||||
### Teams (group: `teams`)
|
||||
|
||||
@@ -321,6 +330,38 @@ When a sandbox manager is configured and a `sandboxKey` exists in context, comma
|
||||
|
||||
---
|
||||
|
||||
## 4a. Tool Capabilities & Metadata (v3)
|
||||
|
||||
Each tool is annotated with structured metadata describing capabilities, group membership, and requirements:
|
||||
|
||||
```go
|
||||
type ToolCapability string
|
||||
|
||||
const (
|
||||
CapReadOnly ToolCapability = "read-only" // no side effects
|
||||
CapMutating ToolCapability = "mutating" // modifies state
|
||||
CapAsync ToolCapability = "async" // returns immediately
|
||||
CapMCPBridged ToolCapability = "mcp-bridged" // proxied to external MCP
|
||||
)
|
||||
|
||||
type ToolMetadata struct {
|
||||
Name string
|
||||
Capabilities []ToolCapability
|
||||
Group string // "fs", "web", "runtime", "memory", "team", etc.
|
||||
RequiresWorkspace bool
|
||||
ProviderHints map[string]any
|
||||
}
|
||||
```
|
||||
|
||||
**Default capability inference** (based on tool name):
|
||||
- **Read-only**: `read_file`, `list_files`, `memory_search`, `memory_expand`, `web_fetch`, `skill_search`, `knowledge_graph_search`, `sessions_history`, `datetime`, `web_search`, `read_image`, `read_audio`, `read_video`, `read_document`
|
||||
- **Async**: `spawn` (subagent spawning)
|
||||
- **Mutating**: All other tools (write, exec, message, team tasks, etc.)
|
||||
|
||||
Metadata enables capability-aware tool filtering (e.g., restrict agents to read-only tools, gate async operations).
|
||||
|
||||
---
|
||||
|
||||
## 5. Policy Engine
|
||||
|
||||
The policy engine determines which tools the LLM can use through a 7-step allow pipeline followed by deny subtraction and additive alsoAllow.
|
||||
@@ -433,60 +474,44 @@ Results are announced back to the parent agent via the message bus, optionally b
|
||||
|
||||
---
|
||||
|
||||
## 7. Delegation System
|
||||
## 7. Delegation System (v3)
|
||||
|
||||
> **Note:** The `delegate` tool has been removed. The `DelegateManager` described below is deprecated/removed. Delegation is now handled via agent teams: leads create tasks on the shared board (`team_tasks`) and spawn member agents explicitly. See [11-agent-teams.md](11-agent-teams.md) for the current model.
|
||||
The `delegate` tool enables inter-agent task delegation using agent_links for permission management. Unlike subagents (anonymous clones), delegation crosses agent boundaries to fully independent agents with distinct identities, tools, providers, and context files.
|
||||
|
||||
Delegation allows named agents to delegate tasks to other fully independent agents (each with its own identity, tools, provider, model, and context files). Unlike subagents (anonymous clones), delegation crosses agent boundaries via explicit permission links.
|
||||
### Delegate Tool
|
||||
|
||||
### DelegateManager (Removed)
|
||||
Invoked with agent_key, task, mode (async/sync), and optional timeout:
|
||||
|
||||
The `delegate` tool and its `DelegateManager` in `internal/tools/subagent_spawn_tool.go` have been removed. Previously supported actions:
|
||||
```json
|
||||
{
|
||||
"agent_key": "data-analyst",
|
||||
"task": "Analyze Q3 sales trends",
|
||||
"mode": "sync",
|
||||
"timeout": 300
|
||||
}
|
||||
```
|
||||
|
||||
| Action | Mode | Behavior |
|
||||
|--------|------|----------|
|
||||
| `delegate` | `sync` | Caller waits for result (quick lookups, fact checks) |
|
||||
| `delegate` | `async` | Caller moves on; result announced later via message bus (`delegate:{id}`) |
|
||||
| `cancel` | -- | Cancel a running async delegation by ID |
|
||||
| `list` | -- | List active delegations |
|
||||
| `history` | -- | Query past delegations from `delegation_history` table |
|
||||
**Modes:**
|
||||
- **async** (default) — Fire-and-forget; result announced via message bus as `delegate:{delegationID}`. No blocking.
|
||||
- **sync** — Block up to timeout seconds; return result directly. Max timeout: 600s.
|
||||
|
||||
### Callback Pattern
|
||||
### Permission Model
|
||||
|
||||
The `tools` package cannot import `agent` (import cycle). A callback function bridges the gap:
|
||||
Delegation requires an `agent_link` from caller → target. Link status checked at runtime:
|
||||
|
||||
```go
|
||||
type AgentRunFunc func(ctx context.Context, agentKey string, req DelegateRunRequest) (*DelegateRunResult, error)
|
||||
allowed, err := links.CanDelegate(ctx, fromAgentID, toAgentID)
|
||||
```
|
||||
|
||||
The `cmd` layer provides the implementation at wiring time. The `tools` package never knows `agent` exists.
|
||||
If link missing or disabled, returns *"no delegation link from current agent to '{targetKey}'"*.
|
||||
|
||||
### Concurrency Control
|
||||
### Event Emission
|
||||
|
||||
Delegation concurrency is controlled at the agent level to prevent overload:
|
||||
Emits `delegate.sent` event with delegation ID, from/to agents, task description, and mode. Enables audit trails and async result routing.
|
||||
|
||||
| Layer | Config | Scope |
|
||||
|-------|--------|-------|
|
||||
| Per-agent | `other_config.max_delegation_load` | B from all sources |
|
||||
### Coordination with Teams
|
||||
|
||||
When limits hit, the error message is written for LLM reasoning: *"Agent at capacity (5/5). Try a different agent or handle it yourself."*
|
||||
|
||||
### DELEGATION.md Auto-Injection
|
||||
|
||||
During agent resolution, `DELEGATION.md` is auto-generated and injected into the system prompt:
|
||||
|
||||
- **≤15 targets**: Full inline list with agent keys, names, and frontmatter
|
||||
- **>15 targets**: Brief description-only list (LLM reads available delegation targets via resolver)
|
||||
|
||||
### Context File Merging (Open Agents)
|
||||
|
||||
For open agents, per-user context files merge with resolver-injected base files. Per-user files override same-name base files, but base-only files like `DELEGATION.md` are preserved:
|
||||
|
||||
```
|
||||
Base files (resolver): DELEGATION.md
|
||||
Per-user files (DB): AGENTS.md, SOUL.md, TOOLS.md, USER.md, ...
|
||||
Merged result: AGENTS.md, SOUL.md, TOOLS.md, USER.md, ..., DELEGATION.md ✓
|
||||
```
|
||||
Delegation is independent of teams. However, team leads often delegate to team members rather than spawning. For structured workflows with shared task board, use agent teams instead (see [11-agent-teams.md](11-agent-teams.md)).
|
||||
|
||||
---
|
||||
|
||||
@@ -679,6 +704,7 @@ The tool registry supports per-session rate limiting via `ToolRateLimiter`. When
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `internal/tools/{registry,types,policy,result}.go` | Registry, interfaces, PolicyEngine (7-step pipeline), result types |
|
||||
| `internal/tools/capability.go` | Tool metadata: capabilities (read-only, mutating, async, mcp-bridged), groups, hints |
|
||||
| `internal/tools/{context_keys,rate_limiter}.go` | Context key definitions, per-session rate limiting |
|
||||
| `internal/tools/{scrub,scrub_server}.go` | Credential scrubbing and dynamic value registration |
|
||||
|
||||
@@ -705,10 +731,17 @@ The tool registry supports per-session rate limiting via `ToolRateLimiter`. When
|
||||
| `internal/tools/web_fetch{,_convert,_convert_handlers,_convert_utils,_hidden}.go` | web_fetch tool: fetch, HTML→Markdown, element handlers |
|
||||
| `internal/tools/web_shared.go` | Shared web utilities |
|
||||
|
||||
### Memory, Knowledge & Sessions
|
||||
### Memory, Vault & Knowledge
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `internal/tools/{memory,knowledge_graph,skill_search}.go` | Memory search, KG queries, skill BM25 search |
|
||||
| `internal/tools/memory{,_expand}.go` | Memory search (L1) + expand (L2 deep retrieval) |
|
||||
| `internal/tools/vault_search.go` | Vault search: unified hybrid FTS + vector search across vault/memory/KG |
|
||||
| `internal/tools/{knowledge_graph,skill_search}.go` | Knowledge graph + skill BM25 search |
|
||||
|
||||
### Delegation & Sessions
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `internal/tools/delegate_tool.go` | Delegate tool: inter-agent task delegation via agent_links (async/sync) |
|
||||
| `internal/tools/sessions{,_history,_send}.go` | Session list, history, send tools |
|
||||
| `internal/tools/subagent{,_spawn_tool,_config,_exec,_control,_tracing}.go` | SubagentManager: spawn, cancel, steer, tracing |
|
||||
|
||||
|
||||
@@ -144,6 +144,14 @@ flowchart TD
|
||||
| `status` | Gateway status (connected clients, agents, channels) |
|
||||
| `providers.models` | List available models from all providers |
|
||||
|
||||
### Agent Evolution (v3)
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `agent.evolution.suggestions` | Get evolution suggestions for an agent (requires metrics enabled) |
|
||||
| `agent.evolution.apply` | Apply a suggested evolution to an agent configuration |
|
||||
| `agent.evolution.rollback` | Roll back applied evolution with quality guardrails |
|
||||
|
||||
### Chat
|
||||
|
||||
| Method | Description |
|
||||
@@ -588,6 +596,7 @@ Error responses include `retryable` (boolean) and `retryAfterMs` (integer) field
|
||||
| `internal/gateway/methods/usage.go` | usage.get/summary handlers |
|
||||
| `internal/gateway/methods/api_keys.go` | api_keys.list/create/revoke handlers |
|
||||
| `internal/gateway/methods/send.go` | send handler (direct message to channel) |
|
||||
| `internal/gateway/methods/agent_links.go` | agent_links.* handlers (v3 delegation links) |
|
||||
| `internal/http/chat_completions.go` | POST /v1/chat/completions (OpenAI-compatible) |
|
||||
| `internal/http/responses.go` | POST /v1/responses (OpenResponses protocol) |
|
||||
| `internal/http/tools_invoke.go` | POST /v1/tools/invoke (direct tool execution) |
|
||||
|
||||
+178
-5
@@ -1,6 +1,6 @@
|
||||
# 06 - Store Layer and Data Model
|
||||
|
||||
The store layer abstracts all persistence behind Go interfaces backed by PostgreSQL. Each store interface has a PostgreSQL implementation wired at startup.
|
||||
The store layer abstracts all persistence behind Go interfaces. Each store interface has a PostgreSQL implementation (standard edition) or SQLite implementation (Lite desktop edition). Implementations are wired at startup based on `//go:build` tags and edition configuration.
|
||||
|
||||
---
|
||||
|
||||
@@ -8,9 +8,14 @@ The store layer abstracts all persistence behind Go interfaces backed by Postgre
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
START["Gateway Startup"] --> PG["PostgreSQL Backend"]
|
||||
START["Gateway Startup"] --> CHOOSE{"Edition<br/>& Build Tag"}
|
||||
|
||||
CHOOSE -->|Standard<br/>(PostgreSQL)| PG["PostgreSQL Backend"]
|
||||
CHOOSE -->|Lite<br/>(-tags sqliteonly)| SQLite["SQLite Backend"]
|
||||
|
||||
PG --> PG_STORES["PGSessionStore<br/>PGMemoryStore<br/>PGCronStore<br/>PGPairingStore<br/>PGSkillStore<br/>PGAgentStore<br/>PGProviderStore<br/>PGTracingStore<br/>PGMCPServerStore<br/>PGCustomToolStore<br/>PGChannelInstanceStore<br/>PGConfigSecretsStore<br/>PGTeamStore<br/>PGBuiltinToolStore<br/>PGPendingMessageStore<br/>PGKnowledgeGraphStore<br/>PGContactStore<br/>PGActivityStore<br/>PGSnapshotStore<br/>PGSecureCLIStore<br/>PGAPIKeyStore"]
|
||||
|
||||
SQLite --> SQLITE_STORES["SQLiteActivityStore<br/>SQLiteEpisodicStore<br/>SQLiteEvolutionMetrics<br/>SQLiteEvolutionSuggestions<br/>SQLiteKnowledgeGraph<br/>SQLiteVaultStore<br/>SQLiteAgentLinks<br/>SQLiteSubagentTasks<br/>SQLiteSecureCLIStore"]
|
||||
```
|
||||
|
||||
---
|
||||
@@ -43,6 +48,22 @@ The `Stores` struct is the top-level container holding all PostgreSQL-backed sto
|
||||
| SecureCLIStore | `PGSecureCLIStore` | CLI binary configs with encrypted credential injection |
|
||||
| APIKeyStore | `PGAPIKeyStore` | Gateway API keys, scopes, expiration, revocation |
|
||||
|
||||
### SQLite Parity (Lite Edition)
|
||||
|
||||
**New in v3:** SQLite backend supports 9 additional stores for Lite desktop edition (`-tags sqliteonly`). Schema v9 adds 4 new tables. Text search uses LIKE (no FTS5). Vector features omitted.
|
||||
|
||||
| Interface | Implementation | PostgreSQL vs SQLite |
|
||||
|-----------|---|---|
|
||||
| ActivityStore | `SQLiteActivityStore` | ✓ Parity |
|
||||
| EpisodicStore | `SQLiteEpisodicStore` | LIKE search (no tsvector), no vector embedding |
|
||||
| EvolutionMetrics | `SQLiteEvolutionMetrics` | ✓ Parity (json_extract instead of JSONB operator) |
|
||||
| EvolutionSuggestions | `SQLiteEvolutionSuggestions` | ✓ Parity |
|
||||
| KnowledgeGraphStore | `SQLiteKnowledgeGraph` | LIKE search, Go-side dedup (Jaro-Winkler), no vector embedding, recursive CTE for traversal, depth cap 5 |
|
||||
| VaultStore | `SQLiteVaultStore` | LIKE search (no tsvector), no vector embedding |
|
||||
| AgentLinksStore | `SQLiteAgentLinks` | LIKE search, no vector |
|
||||
| SubagentTasksStore | `SQLiteSubagentTasks` | ✓ Parity (json_set for metadata merge) |
|
||||
| SecureCLIStore | `SQLiteSecureCLIStore` | ✓ Parity + AES-256-GCM encryption mandatory (GOCLAW_KEY env var required) |
|
||||
|
||||
---
|
||||
|
||||
## 3. Session Caching
|
||||
@@ -601,13 +622,161 @@ All "create or update" operations use `INSERT ... ON CONFLICT DO UPDATE`, ensuri
|
||||
|
||||
---
|
||||
|
||||
## 17. File Reference
|
||||
## 17. V3 Memory & Evolution System (New in v3)
|
||||
|
||||
GoClaw v3 introduces a 3-tier memory architecture with event-driven consolidation.
|
||||
|
||||
### 3-Tier Memory Model
|
||||
|
||||
```
|
||||
L0 (Working Memory) L1 (Episodic Memory) L2 (Semantic Memory)
|
||||
┌─────────────────────────┐ ┌──────────────────────┐ ┌──────────────────────┐
|
||||
│ Current conversation │ │ Session summaries │ │ Knowledge graph │
|
||||
│ messages in session │ │ w/ embeddings │ │ entities & relations │
|
||||
│ High context window │ │ Auto-injected via │ │ Temporal validity │
|
||||
└─────────────────────────┘ │ memory search tool │ │ Long-term recall │
|
||||
│ 90-day retention │ └──────────────────────┘
|
||||
│ Query via hybrid │
|
||||
│ search (FTS + vec) │
|
||||
└──────────────────────┘
|
||||
```
|
||||
|
||||
**L0 (Working Memory):** Current session messages stored in `sessions` table. Auto-compacted via summarization at context window threshold.
|
||||
|
||||
**L1 (Episodic Memory):** Session summaries extracted after `run.completed` events. Stored in `episodic_summaries` with L0 abstracts (~50 tokens each) for fast auto-inject. Hybrid search returns top results as context for memory_search/memory_expand tools.
|
||||
|
||||
**L2 (Semantic Memory):** Knowledge Graph with temporal validity windows (`valid_from`, `valid_until`). Supports long-term facts, relationships, and inference. Queried via kg_entities/kg_relations with current-only filters.
|
||||
|
||||
### New Store Interfaces
|
||||
|
||||
| Interface | Purpose | Key Methods |
|
||||
|-----------|---------|-------------|
|
||||
| `EpisodicStore` | Tier 1.5 memory CRUD + hybrid search | `Create`, `Search`, `ExistsBySourceID`, `ListUnpromoted`, `MarkPromoted` |
|
||||
| `EvolutionMetricsStore` | Stage 1: record metrics (retrieval, tool, feedback) | `RecordMetric`, `AggregateToolMetrics`, `AggregateRetrievalMetrics` |
|
||||
| `EvolutionSuggestionStore` | Stage 2: generate & track improvement suggestions | `CreateSuggestion`, `ListSuggestions`, `UpdateSuggestionStatus` |
|
||||
| `VaultStore` | Knowledge Vault: document registry + links | `UpsertDocument`, `Search`, `CreateLink`, `GetOutLinks`, `GetBacklinks` |
|
||||
| `AgentLinkStore` | Inter-agent delegation links (replaces v2 `agent_links` in teams context) | `CreateLink`, `CanDelegate`, `DelegateTargets`, `SearchDelegateTargets` |
|
||||
|
||||
### New Tables
|
||||
|
||||
| Table | Purpose | Key Columns |
|
||||
|-------|---------|-------------|
|
||||
| `episodic_summaries` | Session conversation summaries | `agent_id`, `user_id`, `session_key`, `summary`, `l0_abstract`, `key_topics` (TEXT[]), `embedding` (vector), `source_id` (dedup), `expires_at` |
|
||||
| `agent_evolution_metrics` | Self-evolution performance data | `agent_id`, `session_key`, `metric_type` (retrieval/tool/feedback), `metric_key`, `value` (JSONB) |
|
||||
| `agent_evolution_suggestions` | Data-driven improvement suggestions | `agent_id`, `suggestion_type`, `suggestion`, `rationale`, `parameters` (JSONB), `status` (pending/approved/rejected/applied) |
|
||||
| `vault_documents` | Knowledge Vault document registry | `agent_id`, `scope` (personal/team/shared), `path`, `title`, `doc_type`, `content_hash`, `embedding` (vector), `metadata` (JSONB) |
|
||||
| `vault_links` | Wikilinks between vault documents | `from_doc_id`, `to_doc_id`, `link_type`, `context` (snippet) |
|
||||
| `vault_versions` | Document version history (prepared for v3.1) | `doc_id`, `version`, `content`, `changed_by`, `created_at` |
|
||||
| `kg_entities` | Extended with temporal columns | `valid_from` (TIMESTAMPTZ), `valid_until` (TIMESTAMPTZ) for temporal facts |
|
||||
| `kg_relations` | Extended with temporal columns | `valid_from` (TIMESTAMPTZ), `valid_until` (TIMESTAMPTZ) for temporal edges |
|
||||
|
||||
### 12 Promoted Agent Columns
|
||||
|
||||
Migration 000037 moves 12 config fields from `agents.other_config` JSONB to dedicated columns:
|
||||
|
||||
**Scalar columns:**
|
||||
- `emoji` (VARCHAR) — agent emoji/icon
|
||||
- `agent_description` (VARCHAR) — human-friendly description
|
||||
- `thinking_level` (VARCHAR) — extended thinking depth
|
||||
- `max_tokens` (INT) — context window limit
|
||||
- `self_evolve` (BOOLEAN) — enable self-evolution metrics
|
||||
- `skill_evolve` (BOOLEAN) — enable skill evolution
|
||||
- `skill_nudge_interval` (INT) — suggestion frequency (days)
|
||||
|
||||
**JSONB columns (structures stay JSON-shaped):**
|
||||
- `reasoning_config` (JSONB) — reasoning model settings
|
||||
- `workspace_sharing` (JSONB) — workspace access config
|
||||
- `chatgpt_oauth_routing` (JSONB) — ChatGPT OAuth fallback rules
|
||||
- `shell_deny_groups` (JSONB) — shell command deny patterns
|
||||
- `kg_dedup_config` (JSONB) — KG deduplication thresholds
|
||||
|
||||
---
|
||||
|
||||
## 18. Progressive Memory Loading (L0/L1/L2)
|
||||
|
||||
Three-stage memory loading strategy minimizes token cost while maximizing relevance.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
MSG["User message arrives"] --> INJECT["L0: AutoInjector"]
|
||||
INJECT -->|"Not relevant"| SKIP["Skip injection"]
|
||||
INJECT -->|"Relevant"| L0OUT["Inject L0 summaries<br/>to system prompt"]
|
||||
L0OUT --> TOOL1["Tool available: memory_search"]
|
||||
TOOL1 -->|"Agent uses tool"| L1["L1: Unified search<br/>BM25 + vector hybrid<br/>across episodic + KG"]
|
||||
L1 --> L1RES["Return top K results"]
|
||||
TOOL1 -->|"Agent needs details"| TOOL2["Tool: memory_expand"]
|
||||
TOOL2 --> L2["L2: Deep retrieval<br/>Load full summary +<br/>linked KG edges"]
|
||||
L2 --> L2RES["Return full context"]
|
||||
```
|
||||
|
||||
### L0: Auto-Injection
|
||||
|
||||
Runs in ContextStage (once per turn). Checks user message relevance against episodic summaries and KG. Returns formatted section (~200 tokens max) for system prompt. Disabled if agent has `auto_inject_enabled: false`.
|
||||
|
||||
| Parameter | Default |
|
||||
|-----------|---------|
|
||||
| `MaxEntries` | 5 |
|
||||
| `MaxTokens` | 200 |
|
||||
| `Threshold` | 0.3 (relevance) |
|
||||
|
||||
### L1: Unified Search
|
||||
|
||||
Agent calls `memory_search(query)` tool. Hybrid search across:
|
||||
- **Episodic (L0 abstracts)** — fast (~50 token summaries) with FTS + vector
|
||||
- **Knowledge Graph** — current entities/relations (temporal `valid_until IS NULL`)
|
||||
|
||||
Weights: FTS 0.3, vector 0.7. Returns top K results within score threshold.
|
||||
|
||||
### L2: Memory Expansion
|
||||
|
||||
Agent calls `memory_expand(episodic_id)` for deep retrieval. Returns full summary + linked KG edges. Used when agent needs comprehensive context from a specific episodic entry.
|
||||
|
||||
---
|
||||
|
||||
## 19. Consolidation Pipeline (Event-Driven)
|
||||
|
||||
Event bus fires workers asynchronously to extract and build long-term memory.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
RUN["run.completed event"]
|
||||
RUN --> EP["EpisodicWorker"]
|
||||
EP -->|"Extract summary + L0"| ES["Create episodic_summary"]
|
||||
ES -->|"episodic.created event"| SW["SemanticWorker"]
|
||||
SW -->|"Extract entities/relations<br/>from summary"| KG["Create KG entities<br/>& relations"]
|
||||
KG -->|"entity.upserted event"| DW["DedupWorker"]
|
||||
DW -->|"Merge duplicates<br/>via embeddings"| DEDUP["Consolidate nodes"]
|
||||
ES -->|"episodic.created event"| DREAM["DreamingWorker<br/>(10m debounce)"]
|
||||
DREAM -->|"Batch synthesis"| SYNTH["LLM synthesis pass<br/>→ long-term memory"]
|
||||
```
|
||||
|
||||
### Workers
|
||||
|
||||
| Worker | Triggers | Responsibility |
|
||||
|--------|----------|-----------------|
|
||||
| **EpisodicWorker** | `run.completed` | Extract session summary via LLM or compaction summary. Generate L0 abstract. Store in `episodic_summaries`. Emit `episodic.created` |
|
||||
| **SemanticWorker** | `episodic.created` | Parse summary for entity mentions and relationships. Extract via regex/NER. Insert into KG tables (`kg_entities`, `kg_relations`). Emit `entity.upserted` |
|
||||
| **DedupWorker** | `entity.upserted` | Check for duplicate entities via embedding similarity. Merge duplicate nodes by redirecting relations. Update timestamps to reflect consolidation |
|
||||
| **DreamingWorker** | `episodic.created` (debounced 10m) | Batch collect unpromoted episodic summaries. Call LLM for synthesis/insight pass. Write results to long-term memory (update KG, write to vault, etc.) |
|
||||
|
||||
### Configuration
|
||||
|
||||
| Parameter | Default |
|
||||
|-----------|---------|
|
||||
| `ConsolidationEnabled` | true |
|
||||
| `EpisodicTTLDays` | 90 |
|
||||
|
||||
Workers subscribe on startup via `consolidation.Register()`.
|
||||
|
||||
---
|
||||
|
||||
## 18. File Reference
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `internal/store/stores.go` | `Stores` container struct (all 22 store interfaces) |
|
||||
| `internal/store/types.go` | `BaseModel`, `StoreConfig`, `GenNewID()` |
|
||||
| `internal/store/context.go` | Context propagation: `WithUserID`, `WithAgentID`, `WithAgentType`, `WithSenderID` |
|
||||
| `internal/store/context.go` | Context propagation: `WithUserID`, `WithAgentID`, `WithAgentType`, `WithSenderID`, `WithTenantID` |
|
||||
| `internal/store/session_store.go` | `SessionStore` interface, `SessionData`, `SessionInfo` |
|
||||
| `internal/store/memory_store.go` | `MemoryStore` interface, `MemorySearchResult`, `EmbeddingProvider` |
|
||||
| `internal/store/skill_store.go` | `SkillStore` interface |
|
||||
@@ -629,6 +798,10 @@ All "create or update" operations use `INSERT ... ON CONFLICT DO UPDATE`, ensuri
|
||||
| `internal/store/snapshot_store.go` | `SnapshotStore` interface, usage aggregation |
|
||||
| `internal/store/secure_cli_store.go` | `SecureCLIStore` interface, CLI credential injection |
|
||||
| `internal/store/api_key_store.go` | `APIKeyStore` interface, gateway API keys |
|
||||
| `internal/store/episodic_store.go` | `EpisodicStore` interface, episodic summary CRUD & hybrid search (v3 new) |
|
||||
| `internal/store/evolution_store.go` | `EvolutionMetricsStore`, `EvolutionSuggestionStore` interfaces (v3 new) |
|
||||
| `internal/store/vault_store.go` | `VaultStore` interface, document registry & links (v3 new) |
|
||||
| `internal/store/agent_link_store.go` | `AgentLinkStore` interface, delegation links (v3 new) |
|
||||
| `internal/store/pg/factory.go` | PG store factory: creates all PG store instances from a connection pool |
|
||||
| `internal/store/pg/sessions.go` | `PGSessionStore`: session cache, Save, GetOrCreate |
|
||||
| `internal/store/pg/agents.go` | `PGAgentStore`: CRUD, soft delete, access control |
|
||||
@@ -645,6 +818,6 @@ All "create or update" operations use `INSERT ... ON CONFLICT DO UPDATE`, ensuri
|
||||
| `internal/store/pg/providers.go` | `PGProviderStore`: provider CRUD with encrypted keys |
|
||||
| `internal/store/pg/tracing.go` | `PGTracingStore`: traces and spans with batch insert |
|
||||
| `internal/store/pg/pool.go` | Connection pool management |
|
||||
| `internal/store/pg/helpers.go` | Nullable helpers, JSON helpers, `execMapUpdate()` |
|
||||
| `internal/store/pg/helpers.go` | Nullable helpers, JSON helpers, `execMapUpdate()`, `StructScan` |
|
||||
| `internal/store/validate.go` | Input validation utilities |
|
||||
| `internal/tools/context_keys.go` | Tool context keys including `WithToolWorkspace` |
|
||||
@@ -497,6 +497,164 @@ The flush is idempotent per compaction cycle -- it will not run again until the
|
||||
|
||||
---
|
||||
|
||||
## 17. V3 Three-Tier Memory & Auto-Injection (New in v3)
|
||||
|
||||
V3 introduces a comprehensive 3-tier memory system with event-driven consolidation and intelligent auto-injection.
|
||||
|
||||
### Architecture Overview
|
||||
|
||||
**Working Memory (L0):** Current conversation in `sessions.messages`. Auto-compacted via summarization at context threshold.
|
||||
|
||||
**Episodic Memory (L1):** Session summaries stored in `episodic_summaries` table with:
|
||||
- Full summary + ~50-token L0 abstract (pre-computed)
|
||||
- Embedding vector for hybrid search
|
||||
- Key topics array for quick filtering
|
||||
- 90-day retention by default
|
||||
|
||||
**Semantic Memory (L2):** Knowledge Graph in `kg_entities` + `kg_relations` with temporal validity (`valid_from`, `valid_until`). Long-term structured knowledge.
|
||||
|
||||
### Auto-Injection (L0 Loading)
|
||||
|
||||
Runs in ContextStage once per turn. Checks user message against episodic index. If relevant matches found, injects L0 abstracts into system prompt.
|
||||
|
||||
**Config** (stored in agent settings):
|
||||
```json
|
||||
{
|
||||
"auto_inject_enabled": true,
|
||||
"auto_inject_threshold": 0.3,
|
||||
"auto_inject_max_tokens": 200,
|
||||
"episodic_ttl_days": 90,
|
||||
"consolidation_enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
**Return value:** Formatted section (~200 tokens max) with top K summaries, or empty string if no relevant matches.
|
||||
|
||||
### Progressive Tool Access
|
||||
|
||||
Three tool-based memory interactions:
|
||||
|
||||
| Tool | Purpose | Tier | Example |
|
||||
|------|---------|------|---------|
|
||||
| (auto-inject) | Automatic context injection | L0 | System prompt includes 3 relevant past sessions |
|
||||
| `memory_search(query)` | Hybrid search L1 + L2 | L1 | "Find past discussions about billing" |
|
||||
| `memory_expand(id)` | Deep retrieval from episodic | L2 | "Show me full summary + linked facts from session XYZ" |
|
||||
|
||||
---
|
||||
|
||||
## 18. Consolidation Pipeline (Event-Driven Workers)
|
||||
|
||||
After a session ends (`run.completed` event), async workers extract and consolidate memory into long-term storage.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
RUN["run.completed<br/>event"] --> EP["EpisodicWorker<br/>extract summary<br/>+ L0 abstract"]
|
||||
EP --> ES["episodic_summaries<br/>table"]
|
||||
ES --> EPEV["episodic.created<br/>event"]
|
||||
EPEV --> SW["SemanticWorker<br/>extract entities<br/>& relations"]
|
||||
SW --> KG["kg_entities<br/>kg_relations"]
|
||||
KG --> ENT["entity.upserted<br/>event"]
|
||||
ENT --> DW["DedupWorker<br/>merge duplicates<br/>via embeddings"]
|
||||
DW --> CONSOLIDATE["Consolidate<br/>duplicate nodes"]
|
||||
EPEV -->|"10m debounce"| DREAM["DreamingWorker<br/>batch synthesis<br/>via LLM"]
|
||||
DREAM --> SYNTH["Long-term<br/>memory output"]
|
||||
```
|
||||
|
||||
### Worker Responsibilities
|
||||
|
||||
**EpisodicWorker** (`internal/consolidation/episodic_worker.go`):
|
||||
1. Listens to `run.completed` events
|
||||
2. Checks for duplicate via `source_id` = `session_key:compaction_count`
|
||||
3. Uses compaction summary if available, else calls LLM to summarize
|
||||
4. Generates L0 abstract via `generateL0Abstract()` (~50 tokens)
|
||||
5. Extracts entity names via `extractEntityNames()`
|
||||
6. Sets 90-day expiry
|
||||
7. Stores in `episodic_summaries`
|
||||
8. Publishes `episodic.created` for downstream workers
|
||||
|
||||
**SemanticWorker** (`internal/consolidation/semantic_worker.go`):
|
||||
1. Listens to `episodic.created` events
|
||||
2. Parses summary for entity mentions + relationships
|
||||
3. Inserts entities into `kg_entities` with confidence score
|
||||
4. Inserts relations into `kg_relations`
|
||||
5. Publishes `entity.upserted` for dedup
|
||||
|
||||
**DedupWorker** (`internal/consolidation/dedup_worker.go`):
|
||||
1. Listens to `entity.upserted` events
|
||||
2. Searches for similar entities via embedding cosine distance
|
||||
3. Merges duplicates by redirecting relations
|
||||
4. Updates consolidation timestamps
|
||||
|
||||
**DreamingWorker** (`internal/consolidation/dreaming_worker.go`):
|
||||
1. Listens to `episodic.created` events with 10-minute debounce
|
||||
2. Collects unpromoted episodic summaries (limit: configurable, default 10)
|
||||
3. Calls LLM for batch synthesis/insight pass
|
||||
4. Writes results to long-term storage (vault, KG expansion, etc.)
|
||||
5. Marks summaries as promoted via `MarkPromoted()`
|
||||
|
||||
### Consolidation Flow
|
||||
|
||||
| Stage | Event | Worker | Output |
|
||||
|-------|-------|--------|--------|
|
||||
| 1 | `run.completed` | EpisodicWorker | `episodic_summaries` row + `episodic.created` |
|
||||
| 2 | `episodic.created` | SemanticWorker | `kg_entities` + `kg_relations` rows + `entity.upserted` |
|
||||
| 3 | `entity.upserted` | DedupWorker | Merged KG nodes |
|
||||
| 4 | `episodic.created` (debounced) | DreamingWorker | Promoted episodic + synthetic memory |
|
||||
|
||||
---
|
||||
|
||||
## 19. Episodic Summaries Table Schema
|
||||
|
||||
| Column | Type | Purpose |
|
||||
|--------|------|---------|
|
||||
| `id` | UUID | Primary key |
|
||||
| `tenant_id` | UUID | Multi-tenant scope |
|
||||
| `agent_id` | UUID | Agent owner |
|
||||
| `user_id` | VARCHAR(255) | Chat participant (empty for team) |
|
||||
| `session_key` | TEXT | Reference to original session |
|
||||
| `summary` | TEXT | Full conversation summary (2-4 paragraphs) |
|
||||
| `l0_abstract` | TEXT | Short abstract (~50 tokens) for auto-inject |
|
||||
| `key_topics` | TEXT[] | Extracted entity names for filtering |
|
||||
| `embedding` | vector(1536) | Vector embedding of full summary |
|
||||
| `source_type` | TEXT | "session", "v2_daily", "manual" |
|
||||
| `source_id` | TEXT | Dedup key (unique per source) |
|
||||
| `turn_count` | INT | Message count in session |
|
||||
| `token_count` | INT | Total tokens used |
|
||||
| `created_at` | TIMESTAMPTZ | Creation timestamp |
|
||||
| `expires_at` | TIMESTAMPTZ | Auto-expiry (90 days default) |
|
||||
|
||||
**Indexes:** GIN on `to_tsvector`, HNSW on embedding, unique on `(agent_id, user_id, source_id)`, on `(agent_id, user_id)` for scoped queries.
|
||||
|
||||
---
|
||||
|
||||
## 20. Knowledge Graph Temporal Validity
|
||||
|
||||
Migration 000037 adds temporal columns to KG tables for time-bounded facts.
|
||||
|
||||
**Added columns:**
|
||||
- `valid_from` (TIMESTAMPTZ, default NOW()) — when fact becomes true
|
||||
- `valid_until` (TIMESTAMPTZ, nullable) — when fact expires (NULL = current)
|
||||
|
||||
**Usage pattern:**
|
||||
```sql
|
||||
-- Query only current facts
|
||||
SELECT * FROM kg_entities
|
||||
WHERE agent_id = $1 AND valid_until IS NULL;
|
||||
|
||||
-- Query facts valid at point in time
|
||||
SELECT * FROM kg_entities
|
||||
WHERE agent_id = $1
|
||||
AND valid_from <= $2
|
||||
AND (valid_until IS NULL OR valid_until > $2);
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- Track fact lifecycle (learned → updated → deprecated)
|
||||
- Support temporal reasoning ("what did we know in January?")
|
||||
- Auto-expire outdated information via DedupWorker consolidation
|
||||
|
||||
---
|
||||
|
||||
## File Reference
|
||||
|
||||
### Bootstrap Files & Constants
|
||||
@@ -529,9 +687,27 @@ The flush is idempotent per compaction cycle -- it will not run again until the
|
||||
| `internal/store/pg/skills.go` | Managed skill store (embedding search, auto-backfill) |
|
||||
| `internal/store/pg/skills_grants.go` | Skill grants (agent/user visibility, version pinning, RBAC) |
|
||||
|
||||
### Memory System
|
||||
### V3 Memory System (New)
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `internal/memory/auto_injector.go` | AutoInjector interface for L0 auto-injection into system prompt |
|
||||
| `internal/memory/auto_injector_impl.go` | AutoInjector implementation (episodic search + relevance filtering) |
|
||||
| `internal/memory/unified_search.go` | Hybrid search across episodic summaries + KG |
|
||||
| `internal/memory/l1_cache.go` | L1 cache for fast episodic lookups |
|
||||
| `internal/consolidation/workers.go` | Worker registration + event subscriptions |
|
||||
| `internal/consolidation/episodic_worker.go` | Extract summaries from sessions → episodic_summaries |
|
||||
| `internal/consolidation/semantic_worker.go` | Extract entities/relations from episodic → KG |
|
||||
| `internal/consolidation/dedup_worker.go` | Merge duplicate entities via embeddings |
|
||||
| `internal/consolidation/dreaming_worker.go` | Batch synthesis of episodic → long-term memory (10m debounce) |
|
||||
| `internal/consolidation/l0_abstract.go` | L0 abstract generation (~50 tokens) |
|
||||
|
||||
### Memory Store
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `internal/store/episodic_store.go` | EpisodicStore interface (CRUD, search, promotion lifecycle) |
|
||||
| `internal/store/evolution_store.go` | EvolutionMetricsStore, EvolutionSuggestionStore interfaces |
|
||||
| `internal/store/vault_store.go` | VaultStore interface (document registry, links, search) |
|
||||
| `internal/store/pg/episodic*.go` | PG implementation of episodic store |
|
||||
| `internal/store/pg/memory_docs.go` | Memory document store (chunking, indexing, embedding, scoping) |
|
||||
| `internal/store/pg/memory_search.go` | Hybrid search (FTS + vector merge, weighted scoring, scope filtering) |
|
||||
|
||||
@@ -541,7 +717,7 @@ The flush is idempotent per compaction cycle -- it will not run again until the
|
||||
|
||||
| Document | Relevant Content |
|
||||
|----------|-----------------|
|
||||
| [00-architecture-overview.md](./00-architecture-overview.md) | Startup sequence, database wiring |
|
||||
| [01-agent-loop.md](./01-agent-loop.md) | Agent loop calls BuildSystemPrompt, compaction flow |
|
||||
| [03-tools-system.md](./03-tools-system.md) | ContextFileInterceptor routing read_file/write_file to DB |
|
||||
| [06-store-data-model.md](./06-store-data-model.md) | memory_documents, memory_chunks tables |
|
||||
| [00-architecture-overview.md](./00-architecture-overview.md) | Startup sequence, event bus setup, consolidation worker registration |
|
||||
| [01-agent-loop.md](./01-agent-loop.md) | Agent loop calls BuildSystemPrompt, auto-injection point, compaction flow |
|
||||
| [03-tools-system.md](./03-tools-system.md) | ContextFileInterceptor routing, memory_search + memory_expand tools |
|
||||
| [06-store-data-model.md](./06-store-data-model.md) | episodic_summaries, evolution, vault, KG temporal tables; EpisodicStore, EvolutionStore, VaultStore interfaces |
|
||||
@@ -188,6 +188,17 @@ Example retry sequence: fail → wait 2s → retry → fail → wait 4s → retr
|
||||
|
||||
Retries are transparent to the user; final run status (ok or error) is logged to the `cron_run_logs` table.
|
||||
|
||||
### v3 Agent Evolution Cron Jobs
|
||||
|
||||
Two background cron jobs manage agent evolution (v3):
|
||||
|
||||
| Job | Frequency | Purpose |
|
||||
|-----|-----------|---------|
|
||||
| **Suggestion Analysis** | Daily (1 min after startup, then every 24h) | Analyzes agents with `evolution_metrics` enabled, generates improvement suggestions |
|
||||
| **Evaluation & Rollback** | Weekly (every 7 days) | Checks applied suggestions against quality guardrails, auto-rolls back degraded evolutions |
|
||||
|
||||
Both jobs run with 5-minute timeout and tenant-scoped context. Failed analyses log at debug level and continue gracefully.
|
||||
|
||||
---
|
||||
|
||||
## File Reference
|
||||
@@ -223,6 +234,7 @@ Retries are transparent to the user; final run status (ok or error) is logged to
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `cmd/gateway_cron.go` | makeCronJobHandler (routes cron execution to scheduler) |
|
||||
| `cmd/gateway_evolution_cron.go` | Evolution daily/weekly background jobs (v3 suggestion analysis + rollback evaluation) |
|
||||
| `cmd/gateway_agents.go` | Agent initialization and run loop setup |
|
||||
| `internal/gateway/methods/cron.go` | RPC method handlers (list, create, update, delete, toggle, run, runs) |
|
||||
|
||||
|
||||
@@ -224,6 +224,7 @@ Delegation history is automatically recorded by `DelegateManager.saveDelegationH
|
||||
| `internal/agent/loop_tracing.go` | Span emission from agent loop (LLM, tool, agent spans) |
|
||||
| `internal/http/delegations.go` | Delegation history HTTP API handler |
|
||||
| `internal/gateway/methods/delegations.go` | Delegation history RPC handlers |
|
||||
| `internal/pipeline/` | v3 agent loop pipeline (stages route to agent loop for span emission) |
|
||||
|
||||
---
|
||||
|
||||
|
||||
+18
-2
@@ -613,7 +613,20 @@ When limits are hit, the error message is written for LLM reasoning: "Agent at c
|
||||
|
||||
---
|
||||
|
||||
## 11. Delegation Context
|
||||
## 11. Agent Links & Delegation Graph (v3)
|
||||
|
||||
Agent links define directed delegation relationships between agents, separate from team membership. Each link tracks:
|
||||
- **Source agent** (can delegate to)
|
||||
- **Target agent** (can receive delegations from)
|
||||
- **Direction** (outbound from source, inbound to target)
|
||||
- **Team context** (optional `team_id` if created by team setup)
|
||||
- **Concurrency limit** (max simultaneous delegations for this link)
|
||||
|
||||
**Team-created links** are automatically created when a team is set up (lead → each member). Links remain even if the team is deleted or members are removed, allowing manual cleanup.
|
||||
|
||||
**Delegation graph visibility** — agents can query available delegation targets via the delegation system. The graph determines which agents appear in the `spawn` tool's available targets.
|
||||
|
||||
## 12. Delegation Context
|
||||
|
||||
### SenderID Clearing
|
||||
|
||||
@@ -642,7 +655,7 @@ Context keys injected:
|
||||
|
||||
---
|
||||
|
||||
## 12. Events
|
||||
## 13. Events
|
||||
|
||||
Teams emit events for real-time UI updates and observability.
|
||||
|
||||
@@ -680,6 +693,9 @@ Teams emit events for real-time UI updates and observability.
|
||||
| `internal/tools/subagent_exec.go` | Delegation execution, artifact accumulation, session cleanup |
|
||||
| `internal/tools/subagent_config.go` | Delegation configuration and concurrency control |
|
||||
| `internal/tools/subagent_tracing.go` | Delegation tracing and event broadcasting |
|
||||
| `internal/store/agent_link_store.go` | AgentLinkStore interface: CRUD for delegation links |
|
||||
| `internal/store/pg/agent_links.go` | PostgreSQL agent link persistence and querying |
|
||||
| `internal/gateway/methods/agent_links.go` | agent_links.* RPC handlers (v3 delegation graph management) |
|
||||
| `internal/tools/workspace_dir.go` | WorkspaceDir helper, shared/isolated mode detection, file limits |
|
||||
| `internal/tools/context_keys.go` | Tool context injection: team_id, team_workspace, team_task_id, workspace channel/chatid |
|
||||
| `internal/agent/resolver.go` | TEAM.md generation (buildTeamMD), injection during agent resolution |
|
||||
|
||||
@@ -188,3 +188,9 @@ To add a new package to the Docker image:
|
||||
5. **Rebuild**: `docker compose ... up -d --build`
|
||||
|
||||
For packages only needed by specific skills, prefer runtime installation (Option B) to keep the image lean.
|
||||
|
||||
---
|
||||
|
||||
## 8. Skill Search (v3)
|
||||
|
||||
Skills are searchable via BM25 keyword + semantic similarity matching (in `internal/skills/search.go`). The skill loader indexes all available skills from workspace/project/global/builtin sources. Skill discovery combines keyword matching with embeddings for improved recall of relevant tools to agent tasks.
|
||||
@@ -32,8 +32,59 @@ All notable changes to GoClaw Gateway are documented here. Format follows [Keep
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Refactored
|
||||
|
||||
#### V3 Architecture Refactor — Phase 6 Completion (2026-04-08)
|
||||
- **Store unification**: Created `internal/store/base/` with shared Dialect interface, common helpers (NilStr, BuildMapUpdate, BuildScopeClause, execMapUpdate, etc.). PostgreSQL (`pg/`) and SQLite (`sqlitestore/`) now use base/ abstractions via type aliases, eliminating code duplication
|
||||
- **Orchestration module**: New `internal/orchestration/` with orchestration primitives: BatchQueue[T] generic for result aggregation, ChildResult structure for capturing child agent outputs, media conversion helpers
|
||||
- **Forced V3 pipeline**: Deleted legacy v2 `runLoop()` (~745 LOC). Removed `v3PipelineEnabled` conditional flag — all agents now always execute the unified 8-stage pipeline (context→history→prompt→think→act→observe→memory→summarize)
|
||||
- **Gateway decomposition**: Split monolithic gateway.go (1295 LOC → 476 LOC) into focused modules: gateway_deps.go, gateway_http_wiring.go, gateway_events.go, gateway_lifecycle.go, gateway_tools_wiring.go for better maintainability
|
||||
- **SSE extraction**: Created shared SSEScanner in `providers/sse_reader.go` — unified streaming implementation used by OpenAI, Codex, and Anthropic streaming providers, eliminating provider-level duplication
|
||||
- **UI cleanup**: Removed v2/v3 toggle from web UI settings since v3 is now the only execution path
|
||||
- **Build compatibility**: All builds (PostgreSQL standard + SQLite desktop) compile cleanly. Dual-DB store pattern enables seamless database backend switching
|
||||
|
||||
### Added
|
||||
|
||||
#### Knowledge Vault UI/Backend Enhancements (2026-04-09)
|
||||
- **Doc type inference**: `vault_link` tool now infers document type from file path instead of hardcoding "note"
|
||||
- **Link type parameter**: `vault_link` accepts optional `link_type` param (wikilink or reference, default wikilink)
|
||||
- **Pagination support**: `/v1/vault/documents` and `/v1/agents/{id}/vault/documents` return `{documents: [...], total: N}` for pagination
|
||||
- **CountDocuments store method**: Added to VaultStore interface with PostgreSQL and SQLite implementations
|
||||
- **Frontend pagination UI**: Vault documents table shows 100 items per page with Previous/Next navigation, "Showing X-Y of Z" indicator
|
||||
- **Team filter dropdown**: Vault page has team selector alongside agent selector for multi-team document filtering
|
||||
- **Graph view upgrade**: Independent graph data fetching (limit 500) with KG-level features:
|
||||
- Node click highlight + neighbor emphasis + dim non-neighbors
|
||||
- Double-click opens document detail dialog
|
||||
- Zoom controls (ZoomIn/ZoomOut buttons + percentage display)
|
||||
- Node limit selector (100/200/300/500 by degree centrality)
|
||||
- Link labels on highlighted links + directional particles
|
||||
- Stats bar showing doc/link counts
|
||||
- Fit-to-view button to auto-center graph
|
||||
- Background click clears selection
|
||||
- Works in all-agents mode (shows nodes without agent-specific links)
|
||||
- **VaultDocument type updates**: Added team_id, summary, custom_scope, media type fields for richer metadata
|
||||
- **Files modified**:
|
||||
- `internal/tools/vault_link.go` — doc type inference + link_type param
|
||||
- `internal/http/vault_handlers.go` — pagination response wrapper
|
||||
- `internal/store/vault_store.go`, `pg/vault_documents.go`, `sqlitestore/vault_documents.go` — CountDocuments
|
||||
- `ui/web/src/pages/vault/*` — pagination, team filter, graph upgrade
|
||||
- `ui/web/src/adapters/vault-graph-adapter.ts` — degree centrality limiting
|
||||
- `ui/web/src/i18n/locales/{en,vi,zh}/*` — pagination + vault strings
|
||||
|
||||
#### Vault Enrich Worker — Auto Summary + Semantic Linking (2026-04-09)
|
||||
- **Async document enrichment**: EventBus-driven worker auto-summarizes new/updated vault documents via LLM
|
||||
- **Vector embeddings**: Document summaries automatically embedded and indexed for semantic search
|
||||
- **Auto-linking**: Vector similarity search (0.7 threshold, top-5 neighbors) auto-creates bidirectional vault links
|
||||
- **Efficient batching**: BatchQueue[T] batches documents by tenantID:agentID, bounded dedup map (10K cap) prevents memory leaks
|
||||
- **Provider independence**: Separate provider resolution from consolidation pipeline, reuses master tenant provider
|
||||
- **Dual-DB support**: PostgreSQL includes full embed+link workflow; SQLite (desktop) summarizes only (no vector ops)
|
||||
- **Files added**:
|
||||
- `internal/vault/enrich_worker.go` — BatchQueue-driven worker with bounded dedup
|
||||
- `internal/eventbus/event_types.go` — EventVaultDocUpserted event type
|
||||
- Updated `internal/store/vault_store.go` with UpdateSummaryAndReembed, FindSimilarDocs methods
|
||||
- Updated PostgreSQL and SQLite vault document stores
|
||||
|
||||
|
||||
#### WhatsApp Native Protocol Integration (2026-04-06)
|
||||
- **Direct protocol migration**: Replaced Node.js Baileys bridge with direct in-process WhatsApp connectivity
|
||||
- **Database auth persistence**: Auth state, device keys, and client metadata stored in PostgreSQL (standard) or SQLite (desktop)
|
||||
|
||||
@@ -1070,6 +1070,12 @@ These endpoints require an active WebSocket connection to the `/ws` endpoint wit
|
||||
|
||||
---
|
||||
|
||||
## Notes on V3 Endpoints
|
||||
|
||||
GoClaw v3 introduces new HTTP endpoints for agent evolution metrics, episodic memory, knowledge vault, and orchestration. These are documented separately in [22 — V3 HTTP Endpoints](22-v3-http-endpoints.md) to keep this document focused on the core REST API. V3 endpoints follow the same authentication, error handling, and header conventions as documented above.
|
||||
|
||||
---
|
||||
|
||||
## File Reference
|
||||
|
||||
| File | Purpose |
|
||||
@@ -1119,3 +1125,8 @@ These endpoints require an active WebSocket connection to the `/ws` endpoint wit
|
||||
| `internal/http/contact_merge_handlers.go` | Contact merge/unmerge |
|
||||
| `internal/http/user_search.go` | User search |
|
||||
| `internal/http/secure_cli_user_credentials.go` | CLI per-user credentials |
|
||||
| `internal/http/evolution_handlers.go` | V3: Evolution metrics + suggestions endpoints |
|
||||
| `internal/http/episodic_handlers.go` | V3: Episodic memory list + search endpoints |
|
||||
| `internal/http/vault_handlers.go` | V3: Vault document + link endpoints |
|
||||
| `internal/http/orchestration_handlers.go` | V3: Orchestration mode info endpoint |
|
||||
| `internal/http/v3_flags_handlers.go` | V3: Feature flag get/toggle endpoints |
|
||||
@@ -520,6 +520,178 @@ Multi-tenant management (admin only).
|
||||
|
||||
---
|
||||
|
||||
## 19. V3 Methods (Evolution, Episodic, Vault, Orchestration)
|
||||
|
||||
### Evolution Metrics
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `agent.evolution.metrics` | Get aggregated or raw metrics for agent |
|
||||
| `agent.evolution.suggestions` | List evolution suggestions with filtering |
|
||||
| `agent.evolution.apply` | Apply an approved suggestion (auto-adapt) |
|
||||
| `agent.evolution.rollback` | Rollback a previously applied suggestion |
|
||||
|
||||
**`agent.evolution.metrics` request:**
|
||||
|
||||
```json
|
||||
{
|
||||
"agentId": "uuid",
|
||||
"type": "tool|retrieval|feedback",
|
||||
"aggregate": true,
|
||||
"since": "2026-03-30T00:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:** Same as HTTP `GET /v1/agents/{agentID}/evolution/metrics`.
|
||||
|
||||
**`agent.evolution.suggestions` request:**
|
||||
|
||||
```json
|
||||
{
|
||||
"agentId": "uuid",
|
||||
"status": "pending|approved|applied|rejected|rolled_back",
|
||||
"limit": 50
|
||||
}
|
||||
```
|
||||
|
||||
**`agent.evolution.apply` request:**
|
||||
|
||||
```json
|
||||
{
|
||||
"agentId": "uuid",
|
||||
"suggestionId": "uuid"
|
||||
}
|
||||
```
|
||||
|
||||
### Episodic Memory
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `agent.episodic.list` | List episodic summaries for agent |
|
||||
| `agent.episodic.search` | Hybrid search episodic summaries |
|
||||
|
||||
**`agent.episodic.list` request:**
|
||||
|
||||
```json
|
||||
{
|
||||
"agentId": "uuid",
|
||||
"userId": "optional-user-id",
|
||||
"limit": 20,
|
||||
"offset": 0
|
||||
}
|
||||
```
|
||||
|
||||
**`agent.episodic.search` request:**
|
||||
|
||||
```json
|
||||
{
|
||||
"agentId": "uuid",
|
||||
"query": "search terms",
|
||||
"userId": "optional",
|
||||
"maxResults": 10,
|
||||
"minScore": 0.5
|
||||
}
|
||||
```
|
||||
|
||||
### Knowledge Vault
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `agent.vault.documents` | List vault documents for agent |
|
||||
| `agent.vault.get` | Get single vault document |
|
||||
| `agent.vault.search` | Hybrid search vault documents |
|
||||
| `agent.vault.links` | Get outgoing + backlinks for document |
|
||||
|
||||
**`agent.vault.documents` request:**
|
||||
|
||||
```json
|
||||
{
|
||||
"agentId": "uuid",
|
||||
"scope": "team|user|global",
|
||||
"docTypes": ["guide", "reference"],
|
||||
"limit": 20,
|
||||
"offset": 0
|
||||
}
|
||||
```
|
||||
|
||||
**`agent.vault.search` request:**
|
||||
|
||||
```json
|
||||
{
|
||||
"agentId": "uuid",
|
||||
"query": "search terms",
|
||||
"scope": "team",
|
||||
"docTypes": ["guide"],
|
||||
"maxResults": 10
|
||||
}
|
||||
```
|
||||
|
||||
### Orchestration
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `agent.orchestration.mode` | Get agent's orchestration mode + delegation targets |
|
||||
|
||||
**`agent.orchestration.mode` request:**
|
||||
|
||||
```json
|
||||
{
|
||||
"agentId": "uuid"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"mode": "standalone|delegate|team",
|
||||
"delegateTargets": [
|
||||
{"agentKey": "research-agent", "displayName": "Research Specialist"}
|
||||
],
|
||||
"team": null
|
||||
}
|
||||
```
|
||||
|
||||
### V3 Feature Flags
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `agent.v3flags.get` | Get v3 feature flags for agent |
|
||||
| `agent.v3flags.update` | Update v3 feature flags |
|
||||
|
||||
**`agent.v3flags.get` request:**
|
||||
|
||||
```json
|
||||
{
|
||||
"agentId": "uuid"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"evolutionEnabled": true,
|
||||
"episodicEnabled": true,
|
||||
"vaultEnabled": true,
|
||||
"orchestrationEnabled": false
|
||||
}
|
||||
```
|
||||
|
||||
**`agent.v3flags.update` request:**
|
||||
|
||||
```json
|
||||
{
|
||||
"agentId": "uuid",
|
||||
"flags": {
|
||||
"evolutionEnabled": true,
|
||||
"episodicEnabled": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 20. Permission Matrix
|
||||
|
||||
Methods are gated by role. The role is determined at `connect` time from the token type and scopes.
|
||||
@@ -561,6 +733,18 @@ The server pushes events to connected clients via event frames. Key event types:
|
||||
| `team.task.*` | Team task lifecycle events |
|
||||
| `exec.approval.pending` | Command awaiting approval |
|
||||
|
||||
### V3 Events
|
||||
|
||||
| Event | Description | Payload |
|
||||
|-------|-------------|---------|
|
||||
| `evolution.metrics.updated` | New evolution metrics recorded | `{agentId, metricType, toolName, value}` |
|
||||
| `evolution.suggestion` | New evolution suggestion generated | `{agentId, suggestionId, type, title}` |
|
||||
| `episodic.summary` | New episodic summary created/updated | `{agentId, summaryId, userId}` |
|
||||
| `vault.document.created` | New vault document created | `{agentId, docId, title, docType}` |
|
||||
| `vault.document.updated` | Vault document updated | `{agentId, docId, title}` |
|
||||
| `orchestration.mode.changed` | Agent orchestration mode changed | `{agentId, newMode}` |
|
||||
| `v3flags.changed` | V3 feature flags updated | `{agentId, flags}` |
|
||||
|
||||
---
|
||||
|
||||
## File Reference
|
||||
@@ -597,5 +781,11 @@ The server pushes events to connected clients via event frames. Key event types:
|
||||
| `internal/gateway/methods/api_keys.go` | API key management |
|
||||
| `internal/gateway/methods/send.go` | Outbound messaging |
|
||||
| `internal/gateway/methods/logs.go` | Log tailing |
|
||||
| `internal/gateway/methods/agent_evolution.go` | Evolution metrics + suggestions + apply + rollback |
|
||||
| `internal/gateway/methods/agent_episodic.go` | Episodic memory list + search |
|
||||
| `internal/gateway/methods/agent_vault.go` | Knowledge vault documents + search + links |
|
||||
| `internal/gateway/methods/agent_orchestration.go` | Orchestration mode info |
|
||||
| `internal/gateway/methods/agent_v3flags.go` | V3 feature flags get/update |
|
||||
| `internal/permissions/policy.go` | RBAC policy engine |
|
||||
| `pkg/protocol/methods.go` | Method name constants |
|
||||
| `pkg/protocol/events.go` | Event type constants (incl. v3 events) |
|
||||
@@ -526,11 +526,152 @@ When both features are disabled (default), zero token overhead.
|
||||
| `internal/i18n/catalog_vi.go` | Vietnamese nudge translations |
|
||||
| `internal/i18n/catalog_zh.go` | Chinese nudge translations |
|
||||
| `cmd/gateway_builtin_tools.go` | `skill_manage` builtin tool seed entry |
|
||||
| `internal/agent/suggestion_engine.go` | SuggestionEngine + pluggable rules interface |
|
||||
| `internal/agent/suggestion_rules.go` | Concrete rules: LowRetrievalUsageRule, ToolFailureRule, RepeatedToolRule |
|
||||
| `internal/agent/evolution_guardrails.go` | Guardrail validation, apply/rollback logic |
|
||||
| `internal/store/evolution_store.go` | Store interfaces: EvolutionMetricsStore, EvolutionSuggestionStore |
|
||||
| `internal/store/pg/evolution_metrics.go` | PostgreSQL evolution metrics CRUD + aggregation |
|
||||
| `internal/store/pg/evolution_suggestions.go` | PostgreSQL evolution suggestions CRUD + status updates |
|
||||
| `cmd/gateway_evolution_cron.go` | Daily cron job scheduler for suggestion generation |
|
||||
|
||||
---
|
||||
|
||||
## 8. Cross-References
|
||||
## 8. Agent Evolution Metrics System (V3)
|
||||
|
||||
V3 introduces automated agent improvement via metrics-driven suggestions. Agents track tool usage, retrieval performance, and user feedback to generate actionable evolution recommendations.
|
||||
|
||||
### 8.1 Metrics Collection
|
||||
|
||||
Metrics are recorded during agent execution and stored per-agent in the database. Three metric types:
|
||||
|
||||
| Type | Description | Examples |
|
||||
|------|-------------|----------|
|
||||
| **tool** | Tool invocation performance | invocation_count, success_rate, failure_count, avg_duration_ms |
|
||||
| **retrieval** | Knowledge retrieval quality | recall_rate, precision, relevance_score, query_count |
|
||||
| **feedback** | User satisfaction signals | rating, sentiment, effectiveness_score |
|
||||
|
||||
**Collection points:**
|
||||
- Tool execution: name, status (success/failure), duration recorded in agent loop
|
||||
- Retrieval queries: recall metrics computed from vector search results
|
||||
- User feedback: optional post-run feedback API or implicit signals (abort/rephrase patterns)
|
||||
|
||||
Metrics aggregate over 7-day rolling windows for suggestion analysis.
|
||||
|
||||
### 8.2 Suggestion Engine
|
||||
|
||||
`SuggestionEngine` analyzes aggregated metrics and generates suggestions via pluggable rules.
|
||||
|
||||
**Architecture:**
|
||||
|
||||
```
|
||||
Metrics Aggregation (7-day window)
|
||||
↓
|
||||
Rule Evaluation (run daily/weekly)
|
||||
├─ LowRetrievalUsageRule
|
||||
├─ ToolFailureRule
|
||||
└─ RepeatedToolRule
|
||||
↓
|
||||
Suggestion Creation (pending status)
|
||||
↓
|
||||
Admin Review → Approve/Reject/Rollback
|
||||
```
|
||||
|
||||
**Suggestion Types:**
|
||||
|
||||
| Type | Trigger | Recommendation | Parameters |
|
||||
|------|---------|-----------------|------------|
|
||||
| `low_retrieval_usage` | Avg recall < threshold for 7 days | Lower `retrieval_threshold` parameter | `{current_threshold, proposed_threshold, confidence}` |
|
||||
| `tool_failure` | Single tool failure rate > 20% | Review tool config or fallback | `{tool_name, failure_count, success_count}` |
|
||||
| `repeated_tool` | Tool called 5+ consecutive times without context change | Consider extracting as skill | `{tool_name, occurrence_count, pattern_score}` |
|
||||
|
||||
**Duplicate Prevention:** Only one pending suggestion per type per agent. New analyses skip rules that already have pending suggestions.
|
||||
|
||||
### 8.3 Auto-Adapt Guardrails
|
||||
|
||||
Suggestions can be auto-applied with safety constraints (optional admin-enabled). Guardrails prevent runaway adaptation.
|
||||
|
||||
**Constraints:**
|
||||
|
||||
| Name | Default | Purpose |
|
||||
|------|---------|---------|
|
||||
| `max_delta_per_cycle` | 0.1 | Max parameter change per apply cycle (prevents aggressive swings) |
|
||||
| `min_data_points` | 100 | Minimum metrics before applying (avoid overfitting on small sample) |
|
||||
| `rollback_on_drop_pct` | 20.0 | Auto-rollback if quality metric drops >20% after apply |
|
||||
| `locked_params` | `[]` | Parameters that cannot be auto-changed (e.g., security settings) |
|
||||
|
||||
**Apply Flow:**
|
||||
|
||||
1. Admin approves `low_retrieval_usage` suggestion (raises `retrieval_threshold` by +0.05)
|
||||
2. System checks: min_data_points met? parameter not locked? delta ≤ 0.1? → OK
|
||||
3. Baseline values saved for rollback
|
||||
4. Suggestion status → `applied`
|
||||
5. Monitor: if recall drops >20%, auto-rollback and set status → `rolled_back`
|
||||
|
||||
**Baseline Storage:**
|
||||
|
||||
Previous parameter values stored in suggestion `parameters._baseline` for rollback:
|
||||
|
||||
```json
|
||||
{
|
||||
"current_threshold": 0.5,
|
||||
"proposed_threshold": 0.55,
|
||||
"_baseline": {
|
||||
"retrieval_threshold": 0.5
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 8.4 Cron Scheduling
|
||||
|
||||
Evolution analysis runs as a periodic cron job (default: daily).
|
||||
|
||||
**Schedule:** Configurable via `evolution_cron_schedule` in agent config. Examples: `every day at 02:00`, `every 7 days at sunday 02:00`.
|
||||
|
||||
**Execution:**
|
||||
1. Load all agents in tenant
|
||||
2. For each agent: `engine.Analyze(ctx, agentID)`
|
||||
3. Create pending suggestions (if new findings detected)
|
||||
4. Log results: created suggestions count, skipped rules, errors
|
||||
|
||||
**Cron Event:** `evolution.analysis.completed` event emitted on completion.
|
||||
|
||||
### 8.5 API & WebSocket
|
||||
|
||||
**HTTP Endpoints** (see [22 — V3 HTTP Endpoints](22-v3-http-endpoints.md)):
|
||||
- `GET /v1/agents/{agentID}/evolution/metrics` — Query/aggregate metrics
|
||||
- `GET /v1/agents/{agentID}/evolution/suggestions` — List suggestions
|
||||
- `PATCH /v1/agents/{agentID}/evolution/suggestions/{suggestionID}` — Approve/reject/rollback
|
||||
|
||||
**WebSocket Methods** (see [19 — WebSocket RPC](19-websocket-rpc.md)):
|
||||
- `agent.evolution.metrics` — Get metrics
|
||||
- `agent.evolution.suggestions` — List suggestions
|
||||
- `agent.evolution.apply` — Apply suggestion
|
||||
- `agent.evolution.rollback` — Rollback applied suggestion
|
||||
|
||||
### 8.6 Configuration
|
||||
|
||||
Per-agent evolution settings stored in `agents.other_config` JSONB:
|
||||
|
||||
```json
|
||||
{
|
||||
"evolution_enabled": true,
|
||||
"evolution_guardrails": {
|
||||
"max_delta_per_cycle": 0.1,
|
||||
"min_data_points": 100,
|
||||
"rollback_on_drop_pct": 20.0,
|
||||
"locked_params": ["security_level"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Defaults used if keys absent. Set `evolution_enabled: false` to disable metrics collection entirely.
|
||||
|
||||
---
|
||||
|
||||
## 9. Cross-References
|
||||
|
||||
- [14 - Skills Runtime](./14-skills-runtime.md) — Python/Node runtime environment for skill scripts
|
||||
- [15 - Core Skills System](./15-core-skills-system.md) — Bundled system skills, startup seeding, dependency checking
|
||||
- [16 - Skill Publishing System](./16-skill-publishing.md) — `publish_skill` tool and `skill-creator` core skill
|
||||
- [19 - WebSocket RPC Methods](./19-websocket-rpc.md) — V3 WebSocket methods for evolution, episodic, vault
|
||||
- [22 - V3 HTTP Endpoints](./22-v3-http-endpoints.md) — HTTP REST endpoints for evolution metrics, suggestions, episodic memory, vault documents
|
||||
@@ -0,0 +1,497 @@
|
||||
# 22 — V3 HTTP Endpoints
|
||||
|
||||
GoClaw v3 introduces new HTTP endpoints for agent evolution, episodic memory, knowledge vault, and orchestration capabilities. All endpoints follow the standard authentication and error response patterns from [18 — HTTP REST API](18-http-api.md).
|
||||
|
||||
---
|
||||
|
||||
## 1. Evolution Metrics & Suggestions
|
||||
|
||||
Per-agent evolution metrics track tool usage, retrieval performance, and user feedback to drive automated agent improvements.
|
||||
|
||||
### Get Evolution Metrics
|
||||
|
||||
```
|
||||
GET /v1/agents/{agentID}/evolution/metrics
|
||||
```
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `type` | string | no | Filter by metric type: `tool`, `retrieval`, `feedback`. Omit for all types. |
|
||||
| `aggregate` | boolean | no | Return aggregated metrics (grouped by tool/metric). Default: `false` (raw metrics). |
|
||||
| `since` | ISO 8601 | no | Start timestamp (default: 7 days ago). Example: `2026-04-01T00:00:00Z` |
|
||||
| `limit` | integer | no | Max results (default: 100, max: 500). |
|
||||
|
||||
**Response (raw metrics):**
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "uuid",
|
||||
"agent_id": "uuid",
|
||||
"metric_type": "tool",
|
||||
"tool_name": "web_fetch",
|
||||
"metric_key": "invocation_count",
|
||||
"metric_value": 15,
|
||||
"metadata": {"status": "success"},
|
||||
"recorded_at": "2026-04-06T10:30:00Z"
|
||||
},
|
||||
{
|
||||
"id": "uuid",
|
||||
"agent_id": "uuid",
|
||||
"metric_type": "retrieval",
|
||||
"metric_key": "recall_rate",
|
||||
"metric_value": 0.78,
|
||||
"metadata": {"query_count": 42},
|
||||
"recorded_at": "2026-04-06T11:00:00Z"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**Response (aggregated metrics):**
|
||||
|
||||
```json
|
||||
{
|
||||
"tool_aggregates": [
|
||||
{
|
||||
"tool_name": "web_fetch",
|
||||
"invocation_count": 15,
|
||||
"success_count": 14,
|
||||
"failure_count": 1,
|
||||
"avg_duration_ms": 2340
|
||||
},
|
||||
{
|
||||
"tool_name": "exec",
|
||||
"invocation_count": 8,
|
||||
"success_count": 8,
|
||||
"failure_count": 0,
|
||||
"avg_duration_ms": 1200
|
||||
}
|
||||
],
|
||||
"retrieval_aggregates": [
|
||||
{
|
||||
"query_count": 42,
|
||||
"avg_recall": 0.78,
|
||||
"avg_precision": 0.85,
|
||||
"avg_relevance_score": 0.81
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Status codes:** `200` OK, `400` bad request, `404` agent not found, `500` server error.
|
||||
|
||||
### List Evolution Suggestions
|
||||
|
||||
```
|
||||
GET /v1/agents/{agentID}/evolution/suggestions
|
||||
```
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `status` | string | Filter: `pending`, `approved`, `applied`, `rejected`, `rolled_back`. Omit for all. |
|
||||
| `limit` | integer | Max results (default: 50, max: 500). |
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "uuid",
|
||||
"agent_id": "uuid",
|
||||
"suggestion_type": "low_retrieval_usage",
|
||||
"status": "pending",
|
||||
"title": "Improve retrieval threshold",
|
||||
"description": "Recent queries show low recall. Consider lowering retrieval_threshold from 0.5 to 0.4.",
|
||||
"parameters": {
|
||||
"current_threshold": 0.5,
|
||||
"proposed_threshold": 0.4,
|
||||
"confidence": 0.85
|
||||
},
|
||||
"created_at": "2026-04-06T09:00:00Z",
|
||||
"reviewed_by": null,
|
||||
"reviewed_at": null
|
||||
},
|
||||
{
|
||||
"id": "uuid",
|
||||
"agent_id": "uuid",
|
||||
"suggestion_type": "repeated_tool",
|
||||
"status": "pending",
|
||||
"title": "Tool usage pattern detected",
|
||||
"description": "Tool 'exec' called 5+ times in a row without context change. Consider skill creation.",
|
||||
"parameters": {
|
||||
"tool_name": "exec",
|
||||
"occurrence_count": 12,
|
||||
"pattern_score": 0.92
|
||||
},
|
||||
"created_at": "2026-04-05T14:30:00Z",
|
||||
"reviewed_by": null,
|
||||
"reviewed_at": null
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
**Suggestion Types:**
|
||||
- `low_retrieval_usage` — Retrieval recall is below threshold for recent queries.
|
||||
- `tool_failure` — High failure rate detected for a tool.
|
||||
- `repeated_tool` — Tool called repeatedly without context change; candidate for skill.
|
||||
|
||||
### Update Suggestion Status
|
||||
|
||||
```
|
||||
PATCH /v1/agents/{agentID}/evolution/suggestions/{suggestionID}
|
||||
```
|
||||
|
||||
**Request:**
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "approved",
|
||||
"reviewed_by": "optional-user-id"
|
||||
}
|
||||
```
|
||||
|
||||
**Valid status transitions:** `pending` → `approved`, `rejected`, `rolled_back`.
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Episodic Memory Summaries
|
||||
|
||||
Episodic memory captures conversation summaries per user session for long-term context continuity.
|
||||
|
||||
### List Episodic Summaries
|
||||
|
||||
```
|
||||
GET /v1/agents/{agentID}/episodic
|
||||
```
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `user_id` | string | Filter by user ID (optional). |
|
||||
| `limit` | integer | Max results (default: 20, max: 500). |
|
||||
| `offset` | integer | Pagination offset (default: 0). |
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "uuid",
|
||||
"agent_id": "uuid",
|
||||
"user_id": "user-123",
|
||||
"summary": "User asked about deployment pipeline optimization. Discussed GitHub Actions, Docker layers, caching strategies. User implemented multi-stage builds.",
|
||||
"key_entities": ["GitHub Actions", "Docker", "CI/CD"],
|
||||
"sentiment": "positive",
|
||||
"interaction_count": 5,
|
||||
"tokens_exchanged": 4200,
|
||||
"created_at": "2026-04-05T10:00:00Z",
|
||||
"updated_at": "2026-04-05T11:30:00Z"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Search Episodic Summaries
|
||||
|
||||
```
|
||||
POST /v1/agents/{agentID}/episodic/search
|
||||
```
|
||||
|
||||
**Request:**
|
||||
|
||||
```json
|
||||
{
|
||||
"query": "Docker optimization strategies",
|
||||
"user_id": "optional-user-id",
|
||||
"max_results": 10,
|
||||
"min_score": 0.5
|
||||
}
|
||||
```
|
||||
|
||||
Runs hybrid search combining BM25 (keyword) and vector (semantic) matching.
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "uuid",
|
||||
"agent_id": "uuid",
|
||||
"user_id": "user-123",
|
||||
"summary": "User asked about deployment pipeline optimization...",
|
||||
"similarity_score": 0.92,
|
||||
"created_at": "2026-04-05T10:00:00Z"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Knowledge Vault
|
||||
|
||||
Persistent knowledge vault stores documents with vector embeddings and outbound/backlink graph connections.
|
||||
|
||||
### List Vault Documents
|
||||
|
||||
Cross-agent listing:
|
||||
```
|
||||
GET /v1/vault/documents
|
||||
```
|
||||
|
||||
Per-agent listing:
|
||||
```
|
||||
GET /v1/agents/{agentID}/vault/documents
|
||||
```
|
||||
|
||||
**Query Parameters:**
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `scope` | string | Filter by scope (e.g., `team`, `user`, `global`). |
|
||||
| `doc_type` | string | Comma-separated doc types (e.g., `guide,reference,note`). |
|
||||
| `limit` | integer | Max results (default: 20, max: 500). |
|
||||
| `offset` | integer | Pagination offset. |
|
||||
| `agent_id` | string | (Cross-agent only) Filter by specific agent. |
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "uuid",
|
||||
"agent_id": "uuid",
|
||||
"title": "Database Indexing Best Practices",
|
||||
"doc_type": "guide",
|
||||
"scope": "team",
|
||||
"content_preview": "Indexes are crucial for query performance. Types include...",
|
||||
"created_at": "2026-04-01T09:00:00Z",
|
||||
"updated_at": "2026-04-05T14:20:00Z",
|
||||
"outlink_count": 3,
|
||||
"backlink_count": 2
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Get Single Document
|
||||
|
||||
```
|
||||
GET /v1/agents/{agentID}/vault/documents/{docID}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "uuid",
|
||||
"agent_id": "uuid",
|
||||
"title": "Database Indexing Best Practices",
|
||||
"doc_type": "guide",
|
||||
"scope": "team",
|
||||
"content": "Indexes are crucial for query performance. Types include: B-tree, Hash, Bitmap...",
|
||||
"metadata": {"version": 2, "author": "team-lead"},
|
||||
"created_at": "2026-04-01T09:00:00Z",
|
||||
"updated_at": "2026-04-05T14:20:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### Search Vault Documents
|
||||
|
||||
```
|
||||
POST /v1/agents/{agentID}/vault/search
|
||||
```
|
||||
|
||||
**Request:**
|
||||
|
||||
```json
|
||||
{
|
||||
"query": "index performance optimization",
|
||||
"scope": "team",
|
||||
"doc_types": ["guide", "reference"],
|
||||
"max_results": 10
|
||||
}
|
||||
```
|
||||
|
||||
Hybrid FTS+vector search combining keyword and semantic matching.
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "uuid",
|
||||
"title": "Database Indexing Best Practices",
|
||||
"doc_type": "guide",
|
||||
"relevance_score": 0.94,
|
||||
"snippet": "...Indexes are crucial for query performance. Types include B-tree, Hash, Bitmap...",
|
||||
"created_at": "2026-04-01T09:00:00Z"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Get Document Links
|
||||
|
||||
```
|
||||
GET /v1/agents/{agentID}/vault/documents/{docID}/links
|
||||
```
|
||||
|
||||
Returns outgoing links and backlinks for a document.
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"outlinks": [
|
||||
{
|
||||
"target_doc_id": "uuid",
|
||||
"target_title": "Query Optimization Techniques",
|
||||
"link_type": "references"
|
||||
}
|
||||
],
|
||||
"backlinks": [
|
||||
{
|
||||
"source_doc_id": "uuid",
|
||||
"source_title": "Performance Tuning Guide",
|
||||
"link_type": "referenced_by"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Orchestration Mode
|
||||
|
||||
Determines how an agent routes requests (standalone, delegation, team-based).
|
||||
|
||||
### Get Agent Orchestration Mode
|
||||
|
||||
```
|
||||
GET /v1/agents/{agentID}/orchestration
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"mode": "delegate",
|
||||
"delegate_targets": [
|
||||
{
|
||||
"agent_key": "research-agent",
|
||||
"display_name": "Research Specialist"
|
||||
},
|
||||
{
|
||||
"agent_key": "doc-agent",
|
||||
"display_name": "Documentation Expert"
|
||||
}
|
||||
],
|
||||
"team": null
|
||||
}
|
||||
```
|
||||
|
||||
Or in team mode:
|
||||
|
||||
```json
|
||||
{
|
||||
"mode": "team",
|
||||
"delegate_targets": [],
|
||||
"team": {
|
||||
"id": "uuid",
|
||||
"name": "Platform Team"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Mode values:**
|
||||
- `standalone` — No delegation. Agent handles all requests directly.
|
||||
- `delegate` — Routes complex requests to specialized agents (via agent links).
|
||||
- `team` — Routes to team members via task system.
|
||||
|
||||
---
|
||||
|
||||
## 5. V3 Feature Flags
|
||||
|
||||
Per-agent feature flags control v3 system capabilities (evolution, episodic memory, vault, etc.).
|
||||
|
||||
### Get V3 Flags
|
||||
|
||||
```
|
||||
GET /v1/agents/{agentID}/v3-flags
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"evolution_enabled": true,
|
||||
"episodic_enabled": true,
|
||||
"vault_enabled": true,
|
||||
"orchestration_enabled": false,
|
||||
"skill_evolve": true,
|
||||
"self_evolve": false
|
||||
}
|
||||
```
|
||||
|
||||
### Update V3 Flags
|
||||
|
||||
```
|
||||
PATCH /v1/agents/{agentID}/v3-flags
|
||||
```
|
||||
|
||||
Accepts partial updates. Flag keys are validated against recognized v3 flags.
|
||||
|
||||
**Request:**
|
||||
|
||||
```json
|
||||
{
|
||||
"evolution_enabled": true,
|
||||
"episodic_enabled": false,
|
||||
"vault_enabled": true
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## File Reference
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `internal/http/evolution_handlers.go` | Metrics + suggestions endpoints |
|
||||
| `internal/http/episodic_handlers.go` | Episodic memory list + search endpoints |
|
||||
| `internal/http/vault_handlers.go` | Knowledge vault document + link endpoints |
|
||||
| `internal/http/orchestration_handlers.go` | Orchestration mode info endpoint |
|
||||
| `internal/http/v3_flags_handlers.go` | V3 feature flag get/toggle endpoints |
|
||||
| `internal/store/evolution_store.go` | Evolution metrics/suggestions store interface |
|
||||
| `internal/store/episodic_store.go` | Episodic memory store interface |
|
||||
| `internal/store/vault_store.go` | Knowledge vault store interface |
|
||||
| `internal/store/pg/evolution_metrics.go` | PostgreSQL evolution metrics impl |
|
||||
| `internal/store/pg/evolution_suggestions.go` | PostgreSQL evolution suggestions impl |
|
||||
| `internal/store/pg/episodic.go` | PostgreSQL episodic memory impl |
|
||||
| `internal/store/pg/vault.go` | PostgreSQL vault documents/links impl |
|
||||
|
||||
---
|
||||
|
||||
## Cross-References
|
||||
|
||||
- [18 — HTTP REST API](18-http-api.md) — Standard authentication, error responses, common headers
|
||||
- [19 — WebSocket RPC Methods](19-websocket-rpc.md) — V3 WebSocket methods (evolution, orchestration)
|
||||
- [21 — Agent Evolution & Skill Management](21-agent-evolution-and-skill-management.md) — Evolution system architecture and suggestion engine
|
||||
@@ -374,6 +374,20 @@ erDiagram
|
||||
|
||||
40+ tables carry `tenant_id` with NOT NULL constraint. Exception: `api_keys.tenant_id` is nullable — NULL means system-level cross-tenant key.
|
||||
|
||||
### v3 Tenant-Scoped Stores
|
||||
|
||||
New v3 stores (`evolution`, `vault`, `episodic`, `agent_links`) all enforce tenant isolation:
|
||||
|
||||
| Store | Purpose | Tenant Scoping |
|
||||
|-------|---------|----------------|
|
||||
| `EvolutionMetrics` | Track agent improvement suggestions | `WHERE tenant_id = $N` |
|
||||
| `EvolutionSuggestions` | Store LLM-generated optimizations | `WHERE tenant_id = $N` |
|
||||
| `Vault` | Persistent data storage for agents | `WHERE tenant_id = $N` |
|
||||
| `Episodic` | Episodic memory for agents | `WHERE tenant_id = $N` |
|
||||
| `AgentLink` | Delegation links between agents | `WHERE tenant_id = $N` |
|
||||
|
||||
All v3 stores follow the same tenant isolation pattern — all queries include `WHERE tenant_id = $N` at the SQL level.
|
||||
|
||||
**Master tenant** (UUID `0193a5b0-7000-7000-8000-000000000001`): All legacy/default data. Single-tenant deployments use this exclusively.
|
||||
|
||||
---
|
||||
|
||||
@@ -0,0 +1,533 @@
|
||||
# 24 - Knowledge Vault
|
||||
|
||||
The Knowledge Vault is a query layer above existing memory systems (episodic memory, knowledge graph, memory files). It provides document registration, bidirectional wikilinks, filesystem sync, and unified search across all knowledge sources.
|
||||
|
||||
**Not a replacement** — extends capability. The vault sits between agents and episodic/KG stores, enabling agents to curate workspace documents with explicit relationships.
|
||||
|
||||
---
|
||||
|
||||
## 1. Architecture Overview
|
||||
|
||||
### Components
|
||||
|
||||
| Component | Role |
|
||||
|-----------|------|
|
||||
| **VaultStore** | Interface: document CRUD, link management, hybrid search (FTS+vector) |
|
||||
| **VaultService** | Search coordinator: fan-out across vault, episodic, KG; parallel queries with weighted ranking |
|
||||
| **VaultSyncWorker** | Filesystem watcher: detects file changes (create/write/delete), syncs content hashes back to registry |
|
||||
| **VaultRetriever** | Retrieval adapter: bridges vault search into agent L0 memory system |
|
||||
| **HTTP Handlers** | REST endpoints: list, get, search, links |
|
||||
|
||||
### Data Flow
|
||||
|
||||
```
|
||||
Agent writes document → Workspace FS
|
||||
↓
|
||||
VaultSyncWorker detects change
|
||||
↓
|
||||
Update vault_documents (hash, metadata)
|
||||
↓
|
||||
On agent query: vault_search tool
|
||||
↓
|
||||
VaultSearchService (parallel fan-out)
|
||||
↙ ↓ ↘
|
||||
Vault Episodic Knowledge Graph
|
||||
↘ ↓ ↙
|
||||
Normalize & Weight Scores
|
||||
↓
|
||||
Return Top Results
|
||||
```
|
||||
|
||||
### Tenant & Scope Isolation
|
||||
|
||||
Documents are scoped by **tenant** (isolation), **agent** (per-agent namespace), and **document scope** (personal/team/shared):
|
||||
|
||||
- **personal**: Agent-specific documents (agent_context_files, per-user work)
|
||||
- **team**: Team workspace documents (team_context_files)
|
||||
- **shared**: Cross-tenant shared knowledge (future)
|
||||
|
||||
---
|
||||
|
||||
## 2. Data Model
|
||||
|
||||
### vault_documents
|
||||
|
||||
Document registry: metadata pointers. Content lives on filesystem; registry holds path, hash, embeddings, and links.
|
||||
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | UUID | Primary key |
|
||||
| `tenant_id` | UUID | Multi-tenant isolation |
|
||||
| `agent_id` | UUID | Per-agent namespace |
|
||||
| `scope` | TEXT | personal \| team \| shared |
|
||||
| `path` | TEXT | Workspace-relative path (workspace/notes/foo.md) |
|
||||
| `title` | TEXT | Display name |
|
||||
| `doc_type` | TEXT | context, memory, note, skill, episodic |
|
||||
| `content_hash` | TEXT | SHA-256 of file content (detects changes) |
|
||||
| `embedding` | vector(1536) | pgvector: semantic similarity |
|
||||
| `tsv` | tsvector | Generated: FTS index on title+path |
|
||||
| `metadata` | JSONB | Optional custom fields |
|
||||
| `created_at`, `updated_at` | TIMESTAMPTZ | Timestamps |
|
||||
| **Unique constraint** | (agent_id, scope, path) | One doc per path per scope |
|
||||
|
||||
**Indices:**
|
||||
- `idx_vault_docs_tenant` — tenant_id (multi-tenant queries)
|
||||
- `idx_vault_docs_agent_scope` — agent_id, scope (agent-scoped filters)
|
||||
- `idx_vault_docs_type` — agent_id, doc_type (type filters)
|
||||
- `idx_vault_docs_hash` — content_hash (change detection)
|
||||
- `idx_vault_docs_embedding` — HNSW vector (semantic search)
|
||||
- `idx_vault_docs_tsv` — GIN FTS index (keyword search)
|
||||
|
||||
### vault_links
|
||||
|
||||
Bidirectional links between documents (wikilinks, explicit references).
|
||||
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | UUID | Primary key |
|
||||
| `from_doc_id` | UUID | Source document |
|
||||
| `to_doc_id` | UUID | Target document |
|
||||
| `link_type` | TEXT | wikilink, reference, etc. |
|
||||
| `context` | TEXT | ~50 chars: surrounding text snippet |
|
||||
| `created_at` | TIMESTAMPTZ | Creation timestamp |
|
||||
| **Unique constraint** | (from_doc_id, to_doc_id, link_type) | No duplicate links |
|
||||
|
||||
**Indices:**
|
||||
- `idx_vault_links_from` — from_doc_id (outgoing links)
|
||||
- `idx_vault_links_to` — to_doc_id (backlinks)
|
||||
|
||||
### vault_versions
|
||||
|
||||
Version history (v3.1+ preparation; empty in v3.0).
|
||||
|
||||
| Column | Type | Notes |
|
||||
|--------|------|-------|
|
||||
| `id` | UUID | Primary key |
|
||||
| `doc_id` | UUID | Document reference |
|
||||
| `version` | INT | Version number |
|
||||
| `content` | TEXT | Snapshot of document content |
|
||||
| `changed_by` | TEXT | User/agent identifier |
|
||||
| `created_at` | TIMESTAMPTZ | Snapshot timestamp |
|
||||
|
||||
---
|
||||
|
||||
## 3. Wikilinks
|
||||
|
||||
Bidirectional markdown links in the `[[target]]` format.
|
||||
|
||||
### Parsing & Extraction
|
||||
|
||||
`ExtractWikilinks(content string)` parses all `[[...]]` patterns:
|
||||
|
||||
- **Format:** `[[path/to/file.md]]` or `[[name|display text]]` (display text ignored)
|
||||
- **Returns:** `[]WikilinkMatch` with target, context (~50 chars), and byte offset
|
||||
|
||||
Examples:
|
||||
```markdown
|
||||
See [[architecture/components]] for details.
|
||||
Reference [[SOUL.md|agent persona]] here.
|
||||
Link [[../parent-project]] up.
|
||||
```
|
||||
|
||||
**Edge cases:**
|
||||
- Empty target `[[]]` → skipped
|
||||
- Whitespace-only targets → trimmed and skipped
|
||||
- Paths with spaces: `[[foo bar]]` → preserved
|
||||
- File extensions: `.md` auto-appended if missing
|
||||
|
||||
### Resolution Strategy
|
||||
|
||||
`ResolveWikilinkTarget()` finds the document matching a wikilink target:
|
||||
|
||||
1. **Exact path match** — `GetDocument(tenantID, agentID, "path/to/file.md")`
|
||||
2. **With .md suffix** — If target lacks `.md`, retry with suffix
|
||||
3. **Basename search** — Linear search through all agent docs; match by basename (case-insensitive)
|
||||
4. **Unresolved** — Return nil (not an error; backlinks can be incomplete)
|
||||
|
||||
Example: `[[SOUL.md]]` → lookup SOUL.md → fallback to lookup SOUL → scan for any doc with basename SOUL
|
||||
|
||||
### Link Sync
|
||||
|
||||
`SyncDocLinks(ctx, vaultStore, doc, content, tenantID, agentID)` keeps wikilinks in vault_links in sync with document content:
|
||||
|
||||
1. Extract `[[...]]` patterns from content
|
||||
2. Delete all outgoing links for the document (replace strategy)
|
||||
3. For each match:
|
||||
- Resolve target via strategy above
|
||||
- Create `vault_link` row if resolved
|
||||
4. Return (errors logged, not thrown)
|
||||
|
||||
Called during:
|
||||
- Document upsert (agent writing to workspace)
|
||||
- VaultSyncWorker processing file changes
|
||||
|
||||
---
|
||||
|
||||
## 4. Search
|
||||
|
||||
Hybrid search integrates vault FTS, vector embeddings, episodic memory, and knowledge graph.
|
||||
|
||||
### Vault Search (Store-Level)
|
||||
|
||||
`VaultStore.Search(ctx, opts VaultSearchOptions)` on single vault:
|
||||
|
||||
- **FTS**: PostgreSQL `plainto_tsquery()` on tsv (title+path keywords)
|
||||
- **Vector**: pgvector cosine similarity on embedding (semantic)
|
||||
- **Combined scoring**: Normalize each method's scores (0–1), then apply query-time weights
|
||||
- **Results:** Top N documents with score
|
||||
|
||||
### Unified Search (Cross-Store)
|
||||
|
||||
`VaultSearchService.Search(ctx, opts UnifiedSearchOptions)`:
|
||||
|
||||
**Parallel fan-out:**
|
||||
```
|
||||
Query → ├─ VaultStore.Search() [0.4 weight]
|
||||
├─ EpisodicStore.Search() [0.3 weight]
|
||||
└─ KGStore.SearchEntities() [0.3 weight]
|
||||
↓
|
||||
Normalize each source
|
||||
(max score = 1.0, then * weight)
|
||||
↓
|
||||
Merge & deduplicate by ID
|
||||
↓
|
||||
Sort by final score DESC
|
||||
↓
|
||||
Return top N
|
||||
```
|
||||
|
||||
**Score normalization:** Each source's scores scaled to 0–1 (max_score / weight), then weighted:
|
||||
- Vault: 0.4 (keyword + semantic)
|
||||
- Episodic: 0.3 (session summaries)
|
||||
- KG: 0.3 (entity relationships)
|
||||
|
||||
Default max results per source: `maxResults * 2` (then deduplicate + cap to maxResults).
|
||||
|
||||
### Parameters
|
||||
|
||||
| Param | Type | Default | Notes |
|
||||
|-------|------|---------|-------|
|
||||
| `Query` | string | — | Required: natural language |
|
||||
| `AgentID` | string | — | Scope to agent |
|
||||
| `TenantID` | string | — | Scope to tenant |
|
||||
| `Scope` | string | all | personal, team, shared |
|
||||
| `DocTypes` | []string | all | context, memory, note, skill, episodic |
|
||||
| `MaxResults` | int | 10 | Results per final result set |
|
||||
| `MinScore` | float64 | 0.0 | Filter by minimum score |
|
||||
|
||||
---
|
||||
|
||||
## 5. Filesystem Sync
|
||||
|
||||
`VaultSyncWorker` watches workspace directories for changes and keeps vault_documents hashes in sync.
|
||||
|
||||
### Watcher Loop
|
||||
|
||||
Uses `fsnotify` to detect Write, Create, Remove events:
|
||||
|
||||
1. Debounce: 500ms (multiple rapid changes → one batch)
|
||||
2. For each file change:
|
||||
- Compute SHA-256 hash of file content
|
||||
- Compare to `vault_documents.content_hash`
|
||||
- If different: update hash in DB
|
||||
- If file deleted: mark `metadata["deleted"] = true`
|
||||
|
||||
### Constraints
|
||||
|
||||
- Only syncs **registered** documents (files already in vault_documents)
|
||||
- New files must be registered by agent (via agent write) first
|
||||
- Unreadable files → marked as deleted
|
||||
|
||||
### Setup
|
||||
|
||||
```go
|
||||
syncer := vault.NewVaultSyncWorker(vaultStore)
|
||||
go syncer.Watch(ctx, workspaceDir, tenantID, agentID)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. HTTP API
|
||||
|
||||
RESTful endpoints for vault operations.
|
||||
|
||||
### List Documents
|
||||
|
||||
**Endpoint:** `GET /v1/agents/{agentID}/vault/documents`
|
||||
|
||||
**Query params:**
|
||||
- `scope` — personal, team, or shared (optional)
|
||||
- `doc_type` — comma-separated types (optional)
|
||||
- `limit` — default 20, max 500
|
||||
- `offset` — pagination
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "uuid",
|
||||
"agent_id": "uuid",
|
||||
"path": "workspace/notes/foo.md",
|
||||
"title": "Foo Notes",
|
||||
"doc_type": "note",
|
||||
"content_hash": "sha256hex",
|
||||
"created_at": "2026-04-07T00:00:00Z"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Get Document
|
||||
|
||||
**Endpoint:** `GET /v1/agents/{agentID}/vault/documents/{docID}`
|
||||
|
||||
**Response:** Single VaultDocument object
|
||||
|
||||
### Search
|
||||
|
||||
**Endpoint:** `POST /v1/agents/{agentID}/vault/search`
|
||||
|
||||
**Request body:**
|
||||
```json
|
||||
{
|
||||
"query": "authentication flow",
|
||||
"scope": "team",
|
||||
"doc_types": ["context", "note"],
|
||||
"max_results": 10
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
[
|
||||
{
|
||||
"document": { /* VaultDocument */ },
|
||||
"score": 0.87,
|
||||
"source": "vault"
|
||||
},
|
||||
{
|
||||
"document": { /* episodic record */ },
|
||||
"score": 0.65,
|
||||
"source": "episodic"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Get Links
|
||||
|
||||
**Endpoint:** `GET /v1/agents/{agentID}/vault/documents/{docID}/links`
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"outlinks": [
|
||||
{
|
||||
"id": "uuid",
|
||||
"to_doc_id": "uuid",
|
||||
"link_type": "wikilink",
|
||||
"context": "See [[target]] for details."
|
||||
}
|
||||
],
|
||||
"backlinks": [
|
||||
{
|
||||
"id": "uuid",
|
||||
"from_doc_id": "uuid",
|
||||
"link_type": "wikilink",
|
||||
"context": "Reference [[SOUL.md]] here."
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### List (Cross-Agent)
|
||||
|
||||
**Endpoint:** `GET /v1/vault/documents`
|
||||
|
||||
**Query params:**
|
||||
- `agent_id` — optional filter to specific agent (otherwise all tenant agents)
|
||||
- `scope`, `doc_type`, `limit`, `offset` — same as per-agent endpoint
|
||||
|
||||
---
|
||||
|
||||
## 7. Tools
|
||||
|
||||
Agents access vault via two tools.
|
||||
|
||||
### vault_search
|
||||
|
||||
**Primary discovery tool:** Search across all knowledge sources (vault, episodic, KG) with unified ranking.
|
||||
|
||||
**Parameters:**
|
||||
```json
|
||||
{
|
||||
"query": "string (required)",
|
||||
"scope": "string (optional: personal|team|shared)",
|
||||
"types": "string (optional: comma-separated doc types)",
|
||||
"maxResults": "number (optional: default 10)"
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```
|
||||
Agent: "Find documents about authentication"
|
||||
Tool call: vault_search(query="authentication", types="context,note")
|
||||
Result: Top 10 results from vault + episodic + KG
|
||||
```
|
||||
|
||||
### vault_link
|
||||
|
||||
**Create explicit link:** Connect two documents (similar to [[wikilink]]).
|
||||
|
||||
**Parameters:**
|
||||
```json
|
||||
{
|
||||
"from": "string (source document path, required)",
|
||||
"to": "string (target document path, required)",
|
||||
"context": "string (optional: relationship description)"
|
||||
}
|
||||
```
|
||||
|
||||
**Example:**
|
||||
```
|
||||
Agent: "Link the authentication guide to the SOUL file"
|
||||
Tool call: vault_link(from="docs/auth.md", to="SOUL.md", context="Persona reference")
|
||||
Result: Explicit link created in vault_links
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. Retriever Integration
|
||||
|
||||
`VaultRetriever` bridges vault search into agent L0 memory system.
|
||||
|
||||
### Usage
|
||||
|
||||
```go
|
||||
retriever := vault.NewVaultRetriever(searchService)
|
||||
summaries, err := retriever.RetrieveL0(ctx, agentID, userID, query, config)
|
||||
// Returns []memory.L0Summary with highest-ranked results
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
```go
|
||||
type RetrieverConfig struct {
|
||||
RelevanceThreshold float64 // default 0.3
|
||||
MaxL0Items int // default 5
|
||||
TenantID string
|
||||
}
|
||||
```
|
||||
|
||||
Used by agent to retrieve relevant documents during think phase (before plan/act).
|
||||
|
||||
---
|
||||
|
||||
## 9. Web UI (v3)
|
||||
|
||||
Vault page in dashboard displays:
|
||||
|
||||
- **Document list** — workspace documents filtered by scope/type
|
||||
- **Graph visualization** — nodes = documents, edges = wikilinks, interactive pan/zoom
|
||||
- **Search interface** — unified cross-store search with source badges
|
||||
- **Link editor** — create/remove links between documents
|
||||
|
||||
---
|
||||
|
||||
## 10. Feature Flags & Configuration
|
||||
|
||||
Knowledge Vault is **v3-only** feature.
|
||||
|
||||
- **Edition:** Standard and Lite (full support)
|
||||
- **Prerequisite:** PostgreSQL with pgvector extension
|
||||
- **Storage:** vault_* tables created by migration 000038
|
||||
- **Workspace:** Documents organized in agent-level workspace directory (e.g., `~/.goclaw/workspace/agent_name/`)
|
||||
|
||||
No explicit feature flag; vault is enabled if:
|
||||
1. Migration 000038 ran successfully
|
||||
2. VaultStore initialized during gateway startup
|
||||
3. VaultSyncWorker started
|
||||
|
||||
---
|
||||
|
||||
## 11. Examples
|
||||
|
||||
### Adding a Document
|
||||
|
||||
Agent writes to workspace:
|
||||
```
|
||||
~/.goclaw/workspace/myagent/notes/architecture.md
|
||||
```
|
||||
|
||||
On next sync (or immediate write):
|
||||
1. VaultSyncWorker detects file creation
|
||||
2. Computes SHA-256 hash
|
||||
3. Vault document auto-registered with metadata
|
||||
|
||||
### Creating a Wikilink
|
||||
|
||||
Markdown content:
|
||||
```markdown
|
||||
See [[architecture.md]] for system design.
|
||||
See [[SOUL.md|persona]] for agent personality.
|
||||
```
|
||||
|
||||
Agent calls `vault_search("system design")`:
|
||||
1. ExtractWikilinks finds `[[architecture.md]]` and `[[SOUL.md]]`
|
||||
2. ResolveWikilinkTarget matches to registered documents
|
||||
3. SyncDocLinks creates vault_link rows
|
||||
4. Backlinks available via `/links` endpoint
|
||||
|
||||
### Search Example
|
||||
|
||||
Agent: "Find notes about authentication"
|
||||
|
||||
Request:
|
||||
```json
|
||||
POST /v1/agents/agent-123/vault/search
|
||||
{
|
||||
"query": "authentication flow",
|
||||
"scope": "personal",
|
||||
"max_results": 5
|
||||
}
|
||||
```
|
||||
|
||||
Response (parallel results from vault + episodic + KG):
|
||||
```json
|
||||
[
|
||||
{
|
||||
"document": {
|
||||
"id": "doc-456",
|
||||
"path": "notes/auth.md",
|
||||
"title": "Authentication Flow",
|
||||
"doc_type": "note"
|
||||
},
|
||||
"score": 0.92,
|
||||
"source": "vault"
|
||||
},
|
||||
{
|
||||
"id": "episodic-789",
|
||||
"title": "Session-2026-04-06",
|
||||
"source": "episodic",
|
||||
"score": 0.68
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12. Limitations
|
||||
|
||||
- **Vault docs do not auto-embed in system prompt** — Must be retrieved via agent tools
|
||||
- **No full-text indexing on document content** — Only title+path FTS; content requires embeddings
|
||||
- **Sync is one-way** — Filesystem changes sync to vault; vault does not write back to FS
|
||||
- **No conflict resolution** — Concurrent edits not detected; last write wins
|
||||
- **Version history empty** — vault_versions table prepared for v3.1
|
||||
|
||||
---
|
||||
|
||||
## File References
|
||||
|
||||
- **Vault service:** `internal/vault/*.go`
|
||||
- **Store interface:** `internal/store/vault_store.go`
|
||||
- **HTTP handlers:** `internal/http/vault_handlers.go`
|
||||
- **Tools:** `internal/tools/vault_*.go`
|
||||
- **Migration:** `migrations/000038_vault_tables.up.sql`
|
||||
@@ -411,6 +411,34 @@ When using small models (MiniMax, Qwen, Gemini Flash): all 3 layers are critical
|
||||
|
||||
---
|
||||
|
||||
## 6. Model Registry (v3)
|
||||
|
||||
The **model registry** (v3) provides structured metadata about LLM models used by the steering system and cost calculation:
|
||||
|
||||
```
|
||||
ModelRegistry interface
|
||||
├── Resolve(provider, modelID) → ModelSpec
|
||||
├── Register(spec ModelSpec)
|
||||
├── Catalog(provider) → []ModelSpec
|
||||
└── RegisterResolver(provider, ForwardCompatResolver)
|
||||
```
|
||||
|
||||
Each `ModelSpec` includes:
|
||||
- **ID, Provider**: Model identifier
|
||||
- **ContextWindow, MaxTokens**: Capacity bounds
|
||||
- **Reasoning, Vision**: Capability flags
|
||||
- **TokenizerID**: For token estimation
|
||||
- **Cost**: Per-1M-token pricing (input, output, cache-read)
|
||||
|
||||
**Forward-compatibility resolver** — providers can implement custom resolution for unknown models, allowing dynamic pricing updates without code changes. The registry caches resolved specs for performance.
|
||||
|
||||
**Model steering uses the registry for:**
|
||||
- Context window checks (adaptive throttle at 60%)
|
||||
- Token-based iteration budgets and hints (75%, 90% warnings)
|
||||
- Cost calculation in traces
|
||||
|
||||
---
|
||||
|
||||
## Reference Keywords
|
||||
|
||||
| Keyword | File/Package |
|
||||
@@ -426,3 +454,5 @@ When using small models (MiniMax, Qwen, Gemini Flash): all 3 layers are critical
|
||||
| `IsSilentReply()` (NO_REPLY) | `internal/agent/sanitize.go` |
|
||||
| `i18n.MsgSkillNudge70Pct` / `90Pct` | `internal/i18n/` |
|
||||
| `WithIterationProgress()` | `internal/tools/` |
|
||||
| `ModelRegistry`, `ModelSpec`, `InMemoryRegistry` | `internal/providers/model_registry.go` |
|
||||
| `SeedDefaultModels()`, `ForwardCompatResolver` | `internal/providers/model_registry.go` |
|
||||
Reference in new issue
Block a user