Skip to content

Proactive context linking: surface related past decisions when signals fire #15

Description

@tyejcoleman

Problem

When a signal fires, the system surfaces the signal in isolation. It doesn't say "this is related to the decision you made last week." The data exists in memory to make these connections, but the heartbeat pipeline doesn't search for related context.

For example: a new issue appears that's similar to one resolved last month. The heartbeat surfaces it as CheckPendingWork, but doesn't mention the previous resolution or the PR that fixed it.

The GraphEngine already has GetEntityContext() which returns an entity's relationships and associated memories. The memory store has similarity search. Both can find related past decisions.

Proposal

When to run

Only on ticks where ShouldAct = true and HeartbeatAnalysis is requested (cfg.llmProvider != nil). This is a premium feature that costs 1 LLM call per actionable tick.

Implementation

Add a context enrichment step after evaluateShouldAct() returns ShouldAct=true but before building the delivery message:

func (k *Keyoku) enrichWithRelatedContext(ctx context.Context, entityID string, result *HeartbeatResult) error {
    // 1. Collect the top 3 signal memories (by tier priority)
    topSignals := getTopSignalMemories(result, 3)
    
    // 2. For each, search for related past decisions and outcomes
    for _, mem := range topSignals {
        related, _ := k.store.SearchMemories(ctx, entityID, SimilarityOptions{
            Query:     mem.Content,
            Limit:     3,
            MinScore:  0.5,
            Types:     []MemoryType{TypeActivity, TypePlan, TypeEvent},
            States:    []MemoryState{StateActive, StateStale},
            ExcludeIDs: []string{mem.ID}, // don't match self
        })
        result.RelatedContext = append(result.RelatedContext, related...)
    }
    
    // 3. Also check graph connections
    for _, entityID := range result.TopicEntities {
        edges, _ := k.graphEngine.GetEntityNeighbors(ctx, entityID, entityID)
        // Filter for decision-relevant relationship types
        // Add to result.GraphContext
    }
    
    // 4. Pass to LLM analysis prompt:
    //    "Given these active signals AND this related past context,
    //     explain the connection and what the user should know."
    
    return nil
}

New field on HeartbeatResult

RelatedContext []*Memory  // Past decisions/activities related to current signals

LLM prompt addition

Extend the existing analysis prompt (used when analyze: true in heartbeatContext()) with:

Related past context:
- [2 days ago] "Resolved recursive import issue in PR #8 using depth-limited scanning"
- [5 days ago] "dan-and filed Issue #5 requesting recursive import support"

Consider these connections when writing your action brief.

Plugin side (separate PR in keyoku repo)

  • packages/openclaw/src/context.ts — format related_context in formatHeartbeatContext()
  • packages/types/src/memory.ts — add related_context?: SearchResult[] to HeartbeatContextResult

Files to modify (engine)

  • heartbeat.go — add RelatedContext []*Memory to HeartbeatResult
  • heartbeat_decide.go — add enrichWithRelatedContext(), call after evaluateShouldAct() when ShouldAct && llmProvider != nil
  • heartbeat_llm.go (or wherever LLM analysis prompt is built) — include related context in prompt

Constraints

  • Only runs when ShouldAct = true AND LLM analysis is enabled (not on suppressed/cooldown ticks)
  • Max 3 signal memories enriched, max 3 related memories per signal (9 total max)
  • Similarity threshold: 0.5 (looser than dedup's 0.85, we want thematic relevance)
  • Related memories must be from a different session than the signal memory (avoid self-referencing)
  • LLM cost: ~$0.01-0.05 per enriched tick (added to existing analysis call, not a separate call)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions