feat(kb): dynamic-kb-* tool family + <kb-block> subblock provider
Implements the HF side of Plexum DESIGN-DYNAMIC-BLOCK.md Phase 2 — the
HarborForge knowledge-base block agents cache facts into per session.
Cross-runtime aligned with the ClawSkills workflow text (same tool
names + input schemas + return shapes will land on openclaw side later).
manifest.json:
- 5 new dynamic-kb-* tool contracts (list-kbs / list-topics /
list-facts / cache / evict)
- dynamicSubblocks contract entry declaring this plugin owns
the "kb-block" <dynamic-block> subblock
internal/kbblock/ (new):
- Per-session storage at <PLEXUM_PROFILE_ROOT>/agents/<id>/sessions/
<sid>/plugins/harbor-forge/kb-block.json
- Entry carries ID (HF backend DB primary key) + KBCode + SourceTopic
+ Content + InsertSeq for §9 #4 cache-insertion-order rendering
- Render emits <kb-fact id=N kb=<code> source=topic:<slug>>...
</kb-fact> (no title/description per §9 #8; source attr omitted
when empty)
- Fade NOT applied in v1 — §9 #3 lock has fade params shared with
memory but implementation deferred until prod data informs whether
KB needs it; agent dynamic-kb-evict is the only eviction path
- 11 unit tests
internal/kbclient/ (new):
- Typed HTTP client for HarborForge.Backend KB routes verified
against app/api/routers/knowledge.py
- GET /knowledge-bases[?project=<code>] (list KBs)
- GET /knowledge-bases/{kb_code}/topics (list topics)
- GET /knowledge-bases/{kb_code}/tree (full hierarchy — ListFacts
flattens this client-side filtered by topic ids; backend has no
flat list-facts-in-topic route)
- GET /knowledge-facts/{id} per fact (GetFacts batch loop)
- Auth: plugin-level Bearer APIKey. Per-agent hf-token resolution
is a TODO when SDK exposes secret-mgr access.
internal/tools/kb.go (new) + tools.go:
- 5 tool functions hooked into Dispatch
- KBDeps struct bundles Client + ProfileRoot + SessionFor + Turn
- Cache/evict use SessionFor lookup populated by main.go's
RenderDynamicSubblock (called per turn by host; carries sessionID)
cmd/plexum-harborforge-plugin/main.go:
- kbClient field initialized when BackendURL + APIKey present
- profileRoot cached for kbblock path resolution
- agentSession sync.Map tracks agentID → sessionID; populated by
RenderDynamicSubblock so subsequent tool calls in the same turn
can resolve the per-session kb-block.json path
- Implements sdkplugin.DynamicBlockProvider.RenderDynamicSubblock:
opens kbblock for (agentID, sessionID) and returns its Render()
body; host wraps in <kb-block>...</kb-block>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -49,7 +49,35 @@
|
||||
"name": "harborforge_restart_status",
|
||||
"description": "Check whether a Plexum host restart is pending (backend-driven flag). Reports last poll time and pending flag.",
|
||||
"inputSchema": {"type": "object"}
|
||||
},
|
||||
{
|
||||
"name": "dynamic-kb-list-kbs",
|
||||
"description": "List HarborForge Knowledge Bases the agent can access. Optional project-code filter (equivalent to `hf knowledge-base list [--project <code>]`). Returns code/title/description per KB. Browse — follow up with dynamic-kb-list-topics to drill into one.",
|
||||
"inputSchema": {"type": "object", "properties": {"project-code": {"type": "string"}}}
|
||||
},
|
||||
{
|
||||
"name": "dynamic-kb-list-topics",
|
||||
"description": "List topics in one KB by code. Returns topic id/slug/description per row. Browse — follow up with dynamic-kb-list-facts.",
|
||||
"inputSchema": {"type": "object", "properties": {"kb-code": {"type": "string"}}, "required": ["kb-code"]}
|
||||
},
|
||||
{
|
||||
"name": "dynamic-kb-list-facts",
|
||||
"description": "List facts in given topics. Returns lightweight previews (id/topic/title/snippet). Browse — feed into dynamic-kb-cache to commit selected facts to your kb-block.",
|
||||
"inputSchema": {"type": "object", "properties": {"kb-code": {"type": "string"}, "topic-ids": {"type": "array", "items": {"type": "integer"}}}, "required": ["kb-code", "topic-ids"]}
|
||||
},
|
||||
{
|
||||
"name": "dynamic-kb-cache",
|
||||
"description": "Cache specific KB facts into your per-session kb-block (visible in System prompt next turn). Pass previous-dynamic-id to consume the matching list-facts browse output.",
|
||||
"inputSchema": {"type": "object", "properties": {"kb-code": {"type": "string"}, "fact-ids": {"type": "array", "items": {"type": "integer"}}, "previous-dynamic-id": {"type": "string"}}, "required": ["kb-code", "fact-ids"]}
|
||||
},
|
||||
{
|
||||
"name": "dynamic-kb-evict",
|
||||
"description": "Remove cached facts from your kb-block (free ctx). Takes effect starting your next turn.",
|
||||
"inputSchema": {"type": "object", "properties": {"fact-ids": {"type": "array", "items": {"type": "integer"}}}, "required": ["fact-ids"]}
|
||||
}
|
||||
],
|
||||
"dynamicSubblocks": [
|
||||
{"name": "kb-block"}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user