feat(agent-core-v2): let plugins contribute system prompt instructions via the manifest systemPrompt field - #2314
Open
7Sageer wants to merge 15 commits into
Open
feat(agent-core-v2): let plugins contribute system prompt instructions via the manifest systemPrompt field#23147Sageer wants to merge 15 commits into
7Sageer wants to merge 15 commits into
Conversation
…s via the manifest systemPrompt field
🦋 Changeset detectedLatest commit: 1262bd1 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
commit: |
…gin contributions at session scope - restore no longer re-renders or re-persists prompts: a resumed agent keeps its replayed profile binding (prompt and tool set) as persisted - a new Session-level convergence point reloads plugin skills into the session skill catalog before fanning out to every live agent prompt, and every catalog-kind plugin mutation awaits the whole pipeline; MCP-only toggles carry a distinct change kind and skip it - live refreshes after a restart re-resolve the bound profile by name and rebind the full slice (prompt, disallowed tools, active tools) atomically, warning and keeping the persisted state when the profile is gone; renders reuse the first-render timestamp and unchanged prompts are not re-persisted, so convergence never churns the wire - cap plugin system-prompt contributions (32 KB per field/file, 64 KB aggregate per prompt build) with manifest diagnostics and warnings - bump the changeset to minor: this is a new user-facing capability
…ssing-profile warning - add sessionPluginContribution to the domain-layer registry so lint:domain stays green - emit system-prompt-refresh-profile-missing once per profile name, matching the service's other deduped warnings - document the convergence timeout escape hatch and the klient exclusion of enabledSystemPrompts
…ion read failures - emit plugin-sections-oversized once per skipped-plugin signature - let enabledSystemPrompts failures propagate to the refresh catch (keeps the current prompt and warns) instead of silently rendering and persisting a prompt without plugin instructions - cover the convergence timeout cut-off with a fake-timers test - clarify that the first-render timestamp anchors per process
…load timing - run at most one convergence per session and bound each change's wait by the timeout, so a fan-out emitter never interleaves deliveries after a timed-out convergence - fire onDidReload as soon as the reload commits again, keeping hook reloads independent of prompt convergence - sign the plugin budget warning with an unambiguous key
…mantics - the timeout retry promise only holds once stalled work clears - note the per-session serial delivery cost model on the plugin change contract and the dual-queue invariant on the service
…ompt template
- place ${plugin_sections} on the same template line as
${skills_section} so prompts without either block render exactly as
before this feature
- note on the change contract that waitUntil work must not call back
into plugin mutations, and spell out the per-session convergence
order in the user docs
…bind it
- applyBindingSnapshot left the fork with no pinned profile, which
routed in-process forks into the post-restart catalog rebind and
could reset an inherited tool set; forks now inherit the source
agent's pinned profile object
- pin the first-render timestamp reuse with a ${now}-embedding test
and document the anchored ${now} semantics
- tighten the plugin docs budget and resume-refresh wording
- an agent created while a plugin convergence is in flight now waits for it, and a restored agent refreshes once after it, so a plugin mutation never straddles an agent's bootstrap - warn on a non-string systemPrompt field and strip a UTF-8 BOM from systemPromptPath files before trimming - correct the consumption-surface wording (every CLI surface on the experimental flag, not just kimi -p), the per-session queueing note, and the single-plugin combined budget clause
A permanently wedged convergence kept convergeTail pending forever, and the unconditional settled() wait in bindBootstrap would have blocked every later agent creation in that session; the join now races the shared convergence timeout and continues (a restored agent still refreshes once, which never touches the tail), and the timeout constant moves to the contract for reuse
…stores - a convergence fan-out could land while an agent's wire log is still replaying, dispatching a replay-visible config record whose effect the rest of the replay then overwrites; refreshSystemPrompt now skips while the wire restore is in progress - convergence completion is tracked by a generation counter; bootstrap compares it (after a bounded join) and refreshes a restored agent exactly once when a round completed after its creation began, replacing the wasConverging flag that could miss both windows
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Related Issue
No linked issue — the problem is explained below.
Problem
Plugins currently have no native way to contribute instructions to the agent's system prompt. Every existing manifest capability lands in the conversation context instead:
sessionStartbecomes a user-role<system-reminder>injection,skillInstructionsonly rides along when one of the plugin's skills is activated, and hooks append user/assistant messages. The only manifest field that reaches the system prompt at all isskills, and only as name/description entries in the skills listing. A plugin that wants to declare stable, always-on guidance (domain rules that should hold for the whole session) has no channel for it.What changed
Adds
systemPromptandsystemPromptPathfields to the plugin manifest (kimi.plugin.json):systemPrompttakes inline instruction text, whilesystemPromptPathloads it from a file inside the plugin (both may be set; inline content comes first). Content from enabled plugins is rendered into a new# Plugin Instructionssection at the tail of the system prompt, with per-plugin provenance annotations and the same precedence disclaimer used for AGENTS.md content.The implementation mirrors the existing
${skills_section}pipeline end to end:systemPromptstring;systemPromptPathis resolved to text at manifest parse time, so the wire contract stays unchanged.IPluginService.enabledSystemPrompts()exposes enabled plugins' declarations on the consumption plane (degrades to empty on plugin-state load failure, like the other consumption reads).AgentProfileServiceassembles the sections intoSystemPromptContext.pluginSectionsand participates in an awaitable plugin-change barrier to refresh the prompt.systemPromptVarscomposes the conditionalplugin_sectionsblock;system.mdplaces${plugin_sections}after${skills_section}, so agent files andSYSTEM.mdtemplates get the variable too.Lifecycle consistency
Successful plugin mutations — install, enable/disable, remove, and explicit reload — publish an awaitable
onDidChangeevent after the catalog state is committed, tagged with a changekind(catalogormcp). A Session-level convergence point first reloads plugin skills into the session skill catalog, then fans out to every live agent, which re-renders its prompt from the converged catalog and the current plugin sections; the mutation call returns only after the whole pipeline finishes, so prompts and skill listings never straddle a mutation. MCP-server toggles carry themcpkind and skip prompt convergence entirely.A restored agent keeps its replayed profile binding — prompt and tool set — exactly as persisted; restore never re-renders. A fork pins its source agent's profile object at fork time, so only a genuinely cold binding (a restore, or a fork of one) ever re-resolves the profile from the catalog. An agent created while a plugin convergence is in flight waits for it (a restored one refreshes once after it), so a mutation never straddles an agent's bootstrap. Live refreshes re-render from the pinned in-memory profile; after a restart, the first refresh re-resolves the profile by name and rebinds the full slice (prompt, disallowed tools, active tools) atomically, warning and keeping the persisted state when the profile no longer exists. Renders reuse the agent's first-render timestamp of the current process and unchanged prompts are not re-persisted, so steady-state convergence never appends replay-visible config records within a process lifetime. Contributions are capped at 32 KB per plugin field or file (oversized content is ignored with a diagnostic) and 64 KB in aggregate per prompt build (excess skipped with a warning).
Requests already in flight are not modified retroactively.
Scope note: this is implemented on the agent-core-v2 engine (which backs
kimi weband the server). The legacy v1 engine used by the default TUI ignores the field; porting it is a separate follow-up, and the user docs are intentionally left for that port.Checklist
gen-changesetsskill, or this PR needs no changeset.gen-docsskill, or this PR needs no doc update.