feat(kbblock): fade-out per §9 #3 — seed + RenderFaded + Tick + Refresh
Phase 4b. Wires fade-out for HarborForge kb-block entries using the
new Plexum-sdk-go/fade primitives + RenderDynamicSubblockRequest's
CurrentTurn field.
internal/kbblock/kbblock.go:
- Entry gains Seed int64 (json:"seed") for per-entry RNG seed
- Add() generates a fresh Seed at insert time
- renderLocked() refactored — Render() unchanged behaviour;
RenderFaded(currentTurn, params) added — applies sdkfade.Fade
per entry given (currentTurn - LastRefreshAtTurn) elapsed
- Tick(currentTurn, params) drops entries whose underscore ratio
crossed the m% threshold, returns dropped IDs
- Refresh(ids, currentTurn) resets fade state (regenerates Seed
+ bumps LastRefreshAtTurn) — exposed for a future
dynamic-kb-refresh tool (not in this commit)
cmd/plexum-harborforge-plugin/main.go:
- RenderDynamicSubblock signature now sdkplugin.
RenderDynamicSubblockRequest (per SDK d6fdb9f)
- Each turn: Tick → drop crossed-threshold entries (logs IDs
dropped) → Save (only when something dropped) → RenderFaded
returned. Uses sdkfade.DefaultFadeParams() (5/10/70 per §9 #3).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
sdkfade "git.hangman-lab.top/hzhang/Plexum-sdk-go/fade"
|
||||
sdkplugin "git.hangman-lab.top/hzhang/Plexum-sdk-go/plugin"
|
||||
|
||||
"git.hangman-lab.top/zhi/HarborForge.PlexumPlugin/internal/calendar"
|
||||
@@ -215,35 +216,49 @@ func (p *harborForgePlugin) Init(ctx context.Context, host sdkplugin.HostAPI) er
|
||||
// RenderDynamicSubblock implements sdkplugin.DynamicBlockProvider. The
|
||||
// host calls this once per turn per declared subblock name from this
|
||||
// plugin's manifest (DESIGN-DYNAMIC-BLOCK.md §2 / §3.3). For
|
||||
// "kb-block" we open the per-session kb-block.json and return its
|
||||
// rendered body (host wraps in <kb-block>...</kb-block>). Empty block
|
||||
// → return "" → host omits the subblock for this turn.
|
||||
// "kb-block" we open the per-session kb-block.json, Tick fade-out
|
||||
// drops on entries that crossed the m% threshold (§9 #3), Save the
|
||||
// (possibly mutated) block, and return RenderFaded body for the host
|
||||
// to wrap in <kb-block>...</kb-block>. Empty block → return "" → host
|
||||
// omits the subblock for this turn.
|
||||
//
|
||||
// Side effect: stash agentID → sessionID into p.agentSession so the
|
||||
// dynamic-kb-cache + dynamic-kb-evict tools can resolve the same
|
||||
// kb-block file when they fire later in the same turn.
|
||||
func (p *harborForgePlugin) RenderDynamicSubblock(ctx context.Context, agentID, sessionID, name string) (string, error) {
|
||||
if agentID == "" || sessionID == "" {
|
||||
func (p *harborForgePlugin) RenderDynamicSubblock(ctx context.Context, req sdkplugin.RenderDynamicSubblockRequest) (string, error) {
|
||||
if req.AgentID == "" || req.SessionID == "" {
|
||||
return "", nil
|
||||
}
|
||||
// Cache the (agent, session) pair for tool dispatch this turn.
|
||||
p.agentSession.Store(agentID, sessionID)
|
||||
p.agentSession.Store(req.AgentID, req.SessionID)
|
||||
|
||||
if name != "kb-block" {
|
||||
// Host shouldn't request unknown subblock names per manifest
|
||||
// contract, but log defensively so operator notices wiring drift.
|
||||
if req.Name != "kb-block" {
|
||||
p.host.Log("warn", "dynamic-block render: unknown subblock name",
|
||||
map[string]any{"agent": agentID, "session": sessionID, "name": name})
|
||||
map[string]any{"agent": req.AgentID, "session": req.SessionID, "name": req.Name})
|
||||
return "", nil
|
||||
}
|
||||
block, err := kbblock.Open(p.profileRoot, agentID, sessionID)
|
||||
block, err := kbblock.Open(p.profileRoot, req.AgentID, req.SessionID)
|
||||
if err != nil {
|
||||
p.host.Log("warn", "open kb-block", map[string]any{
|
||||
"agent": agentID, "session": sessionID, "err": err.Error(),
|
||||
"agent": req.AgentID, "session": req.SessionID, "err": err.Error(),
|
||||
})
|
||||
return "", nil
|
||||
}
|
||||
return block.Render(), nil
|
||||
// Apply fade tick (drop entries that crossed the m% threshold).
|
||||
// Uses DESIGN-DYNAMIC-BLOCK.md §9 #3 spec defaults (5/10/70).
|
||||
fadeParams := sdkfade.DefaultFadeParams()
|
||||
if tick := block.Tick(req.CurrentTurn, fadeParams); len(tick.FadedOut) > 0 {
|
||||
p.host.Log("info", "kb-block fade tick", map[string]any{
|
||||
"agent": req.AgentID,
|
||||
"session": req.SessionID,
|
||||
"dropped": tick.FadedOut,
|
||||
})
|
||||
if err := block.Save(); err != nil {
|
||||
p.host.Log("warn", "save kb-block after tick",
|
||||
map[string]any{"err": err.Error()})
|
||||
}
|
||||
}
|
||||
return block.RenderFaded(req.CurrentTurn, fadeParams), nil
|
||||
}
|
||||
|
||||
func (p *harborForgePlugin) CallTool(ctx context.Context, name string, input json.RawMessage) (sdkplugin.ToolResult, error) {
|
||||
|
||||
Reference in New Issue
Block a user