feat(sdk): runtime skills and context-file management (#36)#37
Conversation
Let SDK consumers add, remove, and replace skills and AGENTS.md-style context files after Kit construction. Every mutation recomposes the system prompt and applies it to the agent so the next turn picks up the new instructions without restarting Kit. - AddSkill / LoadAndAddSkill / RemoveSkill / SetSkills on *kit.Kit - AddContextFile / AddContextFileContent / LoadAndAddContextFile / RemoveContextFile / SetContextFiles on *kit.Kit - RefreshSystemPrompt to force a manual recomposition - agent.SetSystemPrompt / GetSystemPrompt on the internal agent so the composed prompt rebuilds the fantasy agent on the next call - Per-instance runtimeMu guards skills/contextFiles; GetSkills and GetContextFiles return defensive snapshots safe for concurrent use - Capture the resolved basePrompt during New so recomposition keeps per-model overrides and --system-prompt file resolution intact - Skills dedupe by Name; context files dedupe by Path (opaque ID, not required to be a real filesystem path) Tests cover add/remove/set/replace semantics, validation errors, disk loading round-trips, prompt composition, and an 8-goroutine race-stress sweep (go test -race clean). Docs: pkg/kit/README, root README Go SDK section, www sdk/overview "Runtime skills and context files" section, www sdk/options callout cross-referencing the new API. Fixes #36
|
Connected to Huly®: KIT-38 |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds thread-safe runtime APIs to Kit for adding/removing/replacing skills and AGENTS.md-like context files, captures the base prompt at New(), composes and applies a recomposed system prompt on mutations, exposes agent-level Set/GetSystemPrompt, includes tests (including race checks), and updates documentation. ChangesRuntime Mutation of Skills and Context Files
Sequence Diagram(s)sequenceDiagram
participant SDK_User
participant Kit
participant runtimeMu
participant composeSystemPrompt
participant Agent
SDK_User->>Kit: AddSkill(newSkill)
Kit->>runtimeMu: Lock (write)
runtimeMu-->>Kit: locked
Kit->>Kit: Validate, deduplicate by name
Kit->>Kit: Update skills slice
Kit->>runtimeMu: Unlock
Kit->>composeSystemPrompt: Recompose prompt from base + snapshots
composeSystemPrompt->>runtimeMu: RLock
runtimeMu-->>composeSystemPrompt: locked
composeSystemPrompt->>composeSystemPrompt: Snapshot contextFiles & skills
composeSystemPrompt-->>Kit: Composed prompt with new skill
composeSystemPrompt->>runtimeMu: RUnlock
Kit->>Agent: SetSystemPrompt(composed)
Agent-->>Kit: Rebuilt with new prompt
Kit-->>SDK_User: Success
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning Review ran into problems🔥 ProblemsStopped waiting for pipeline failures after 30000ms. One of your pipelines takes longer than our 30000ms fetch window to run, so review may not consider pipeline-failure results for inline comments if any failures occurred after the fetch window. Increase the timeout if you want to wait longer or run a Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
pkg/kit/skills.go (1)
184-195: ⚡ Quick winSkill mutators store the caller's pointer; context-file mutators defensively copy.
AddContextFiledeliberately copies the input (stored := &ContextFile{...}) so "later mutations by the caller don't race with the agent reading the composed prompt."AddSkillstores the caller's*skilldirectly, so a caller that retains and later mutates the passedSkillraces withcomposeSystemPromptreadingName/Description/Content—runtimeMuonly guards the slice, not the pointee.SetSkills(Line 253-254) has the same gap (copyonly duplicates pointers).♻️ Mirror the context-file defensive copy
+ // Defensive copy so later caller-side mutations don't race with the agent + // reading the composed prompt (mirrors AddContextFile). + stored := *skill + m.runtimeMu.Lock() replaced := false for i, s := range m.skills { if s.Name == skill.Name { - m.skills[i] = skill + m.skills[i] = &stored replaced = true break } } if !replaced { - m.skills = append(m.skills, skill) + m.skills = append(m.skills, &stored) }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@pkg/kit/skills.go` around lines 184 - 195, AddSkill (and similarly SetSkills) stores the caller's *Skill directly into m.skills which lets external code mutate the pointee after the mutex is released and race with composeSystemPrompt; instead, create a defensive copy of the Skill data (allocate a new Skill value and copy Name, Description, Content, and any slice/map fields deeply as needed) and store the new pointer in m.skills (and when replacing in the loop that checks s.Name). Ensure SetSkills copies each incoming *Skill into fresh allocations too so runtimeMu continues to only guard the slice, not the caller-owned pointees accessed by composeSystemPrompt.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@pkg/kit/skills.go`:
- Around line 269-278: applyComposedSystemPrompt currently releases runtimeMu
before calling m.agent.SetSystemPrompt, causing races because
Agent.SetSystemPrompt/rebuildFantasyAgent lack synchronization; either make
Agent.SetSystemPrompt thread-safe by adding internal locking around
a.systemPrompt and rebuildFantasyAgent (preferable) or keep Kit.runtimeMu held
(use write lock) across composeSystemPrompt and m.agent.SetSystemPrompt to
serialize updates; update tests to instantiate a non-nil Agent and run the
concurrent mutation stress test under -race to verify the fix.
---
Nitpick comments:
In `@pkg/kit/skills.go`:
- Around line 184-195: AddSkill (and similarly SetSkills) stores the caller's
*Skill directly into m.skills which lets external code mutate the pointee after
the mutex is released and race with composeSystemPrompt; instead, create a
defensive copy of the Skill data (allocate a new Skill value and copy Name,
Description, Content, and any slice/map fields deeply as needed) and store the
new pointer in m.skills (and when replacing in the loop that checks s.Name).
Ensure SetSkills copies each incoming *Skill into fresh allocations too so
runtimeMu continues to only guard the slice, not the caller-owned pointees
accessed by composeSystemPrompt.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f94b09a6-66a4-4902-b065-81c830b5638f
📒 Files selected for processing (9)
README.mdinternal/agent/agent.gopkg/kit/README.mdpkg/kit/context_files.gopkg/kit/kit.gopkg/kit/runtime_skills_context_test.gopkg/kit/skills.gowww/pages/sdk/options.mdwww/pages/sdk/overview.md
- add promptMu to Agent guarding systemPrompt writes and the fantasy agent rebuild, fixing a data race when Kit.applyComposedSystemPrompt is invoked concurrently - read systemPrompt under the same lock in GetSystemPrompt - update the thread-safety stress test to use a non-nil agent so the SetSystemPrompt path is actually exercised under -race
Description
Adds a public Go SDK surface for runtime mutation of skills and
AGENTS.md-stylecontext files. Today,
pkg/kitauto-discovers skills and context files duringkit.New()and bakes them into the system prompt — there is no way to changethat set without restarting the Kit instance. This blocks multi-tenant hosts
(chatbots, per-user agents, web services) from swapping per-user instructions
on the fly, which is exactly the scenario the requester describes in #36.
The new methods on
*kit.Kitare:AddSkill(*Skill) errorAddContextFile(*ContextFile) errorLoadAndAddSkill(path) (*Skill, error)LoadAndAddContextFile(path) (*ContextFile, error)RemoveSkill(name) boolAddContextFileContent(path, content) (*ContextFile, error)SetSkills([]*Skill) errorRemoveContextFile(path) boolGetSkills() []*SkillSetContextFiles([]*ContextFile) errorGetContextFiles() []*ContextFilePlus
RefreshSystemPrompt()for callers that mutate state through other paths.Every mutator validates input, takes a per-instance
sync.RWMutex, thenrecomposes the system prompt from the captured base prompt + current skills +
current context files and pushes the result onto the underlying agent via a
new
agent.SetSystemPrompt→rebuildFantasyAgentpath (same mechanismSetModelalready uses). The next LLM call sees the updated prompt; norestart, no file shuffling.
Fixes #36
Type of Change
Design Notes
Name, context files dedupe byPath.Re-adding the same key replaces in place instead of appending a duplicate,
matching how SDK consumers would expect upsert to work.
ContextFile.Pathis not required to be a realfilesystem path — it's used purely for dedup and for the
Instructions from: <Path>header injected into the prompt. URIs likesession://user-123/AGENTS.mdwork fine, which is what the per-tenantuse case needs.
New()now captures the fully-resolved baseprompt (after
--system-promptfile resolution + per-model overridelookup) onto the
Kitstruct. Runtime recomposition reuses this capturedbase so model-specific prompts and file paths keep working after mutation.
runtimeMulock;GetSkills/GetContextFilesreturn defensive snapshot copies socallers can iterate without holding the lock. Verified clean under
go test -racewith an 8-goroutine × 50-iteration stress test.Options.Skills,Options.SkillsDir,Options.NoSkills, andOptions.NoContextFilesstill control thestarting set; the runtime API mutates from whatever state
New()produced. No backwards-incompatible change.
Checklist
pkg/kit/README.md, rootREADME.md,www/pages/sdk/overview.md,www/pages/sdk/options.md)go vet,golangci-lint runboth clean)pkg/kit/runtime_skills_context_test.go— 13 cases incl. race-stress)go test ./... -race)Files Changed
Code
internal/agent/agent.go—SetSystemPrompt/GetSystemPrompton the internal agent (rebuilds the fantasy agent so the next call uses the new prompt).pkg/kit/kit.go—runtimeMu,basePromptcapture duringNew, thread-safeGetSkills/GetContextFiles,composeSystemPromptsnapshots under read lock.pkg/kit/skills.go—AddSkill,LoadAndAddSkill,RemoveSkill,SetSkills,applyComposedSystemPrompt,RefreshSystemPrompt;ReloadSkillsnow hot-applies the recomposed prompt.pkg/kit/context_files.go(new) —AddContextFile,AddContextFileContent,LoadAndAddContextFile,RemoveContextFile,SetContextFiles.Tests
pkg/kit/runtime_skills_context_test.go(new) — add/remove/set/replace semantics, validation errors, on-disk loading round-trips, prompt composition verification, 8-goroutine race-stress sweep.Docs
pkg/kit/README.md— new "Runtime Skills and Context Files" section + Key Methods entries.README.md(root) — new "Runtime Skills & Context Files" subsection in the Go SDK area with a cross-link to the docs site.www/pages/sdk/overview.md— full "Runtime skills and context files" section (after In-process MCP servers) covering dedup, opaque paths, thread safety, and interplay with init-time options.www/pages/sdk/options.md— callout under Skills & configuration cross-referencing the runtime API.Backwards Compatibility
Fully additive. Existing callers see no behavioural change:
Options.Skills/SkillsDir/NoSkills/NoContextFilesstill control the startup set.composeSystemPromptproduces the same output it did before whenever no runtime mutations have happened.*kit.Kit; nothing was removed or renamed.Summary by CodeRabbit
New Features
Documentation
Tests