* feat(agent-core-v2): let plugins contribute system prompt instructions via the manifest systemPrompt field
* feat(agent-core-v2): add systemPromptPath to load plugin system prompt from a file
* docs: explain plugin system prompt templates
* fix(agent-core-v2): refresh plugin system prompts after changes
* fix(agent-core-v2): freeze restored profile bindings and converge plugin 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
* fix(agent-core-v2): register the new session domain and dedupe the missing-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
* fix(agent-core-v2): dedupe the plugin budget warning and surface section 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
* fix(agent-core-v2): serialize session convergence and restore onDidReload 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
* docs(agent-core-v2): align convergence wording with the serialized semantics
- 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
* fix(agent-core-v2): keep empty plugin sections byte-neutral in the prompt 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
* fix(agent-core-v2): pin a fork's profile so refresh triggers never rebind 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
* fix(agent-core-v2): join in-flight convergence during agent bootstrap
- 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
* fix(agent-core-v2): bound the bootstrap convergence join by the timeout
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
* fix(agent-core-v2): close the convergence race against in-progress restores
- 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
* fix(agent-core-v2): bound each convergence so a wedged participant cannot stop the pipeline
- the fan-out now races the convergence timeout, so convergeTail always
settles: a permanently hung refresh delays its round (blocked entries
drain oldest-first on later changes) instead of killing the session's
convergence for good
- warn when agent bootstrap stops waiting on a stalled convergence
- diagnose a blank systemPromptPath and pin the plugin-root escape
guard with traversal, absolute-path, and symlink tests
* fix(agent-core-v2): bound the skill reload, preserve user-tool overlays, roll the prompt clock daily
- the convergence's skill-reload segment now races the same timeout as
the fan-out, so no segment of the pipeline can wedge a session for
good; it continues with the previous catalog and retries next change
- a cold rebind that resets the tool set replays session-added user
tools onto the new base instead of dropping them for the rest of the
process
- the rendered timestamp re-anchors when the UTC date rolls over, so
long-lived processes keep a fresh clock while steady-state renders
stay byte-stable within a day
- the plugin budget warning dedupes per plugin id, and the docs note
that systemPromptPath content is frozen until the next reload
* feat(agent-core-v2): converge cold plugin changes on resume through a drift-free gate
- restore replays the persisted binding untouched, then bootstrap
refreshes only when drift-free inputs changed while the session was
cold: the catalog profile's tool set/denylist, or the plugin-sections
baseline persisted alongside the prompt on the existing bind/update
payloads; directory-listing and date drift wait for live triggers,
so quiet resumes append no replay-visible records
- the rendered timestamp is day-precision (UTC date at 00:00,
re-anchored on rollover), keeping steady-state renders byte-stable
across resumes and sessions on the same day
- consolidate both timeout helpers onto a shared raceOutcome, and drop
the generation counter the gate supersedes
- align the plugin-sections precedence prose with the AGENTS.md
disclaimer (no self-granted authority, system instructions win on
conflict)
* fix(agent-core-v2): bound the restored-prompt gate and land the sections baseline
- the gate's plugin-sections read now races the convergence timeout, so
agent creation never blocks behind an unrelated plugin mutation
- refreshes serialize per agent through a tail, so overlapping triggers
cannot write prompts out of order
- when plugin sections change but a plugin-free custom prompt does not,
the new baseline lands as a sections-only update instead of making
every later resume re-render in vain
- align the system prompt's Date and Time paragraph with the
day-precision anchored timestamp
* Update plugin system-prompt instructions in changeset
Live sessions pick up plugin changes, while the default TUI and `kimi -p` paths ignore these fields.
Signed-off-by: 7Sageer <sag77r@hotmail.com>
* refactor(agent-core-v2): keep plugin skill reload user-driven
Plugin mutations still converge live agent prompts, but the session
skill catalog goes back to refreshing only on explicit plugin reload,
as before: the prompt feature does not need skill convergence, and the
pre-existing manual-reload semantics stay uniform across all plugin
contributions. Removes the convergence-driven skill reload, the
reloadSource de-privatization, and their tests; restores the
PluginSkillSource onDidReload forwarding and its catalog tests.
* refactor(agent-core-v2): apply plugin system-prompt changes only on explicit reload
Drop the live convergence machinery (the plugin onDidChange barrier,
the sessionPluginContribution fan-out, the restored-prompt drift gate,
and the day-precision render clock) so plugin system-prompt sections
take effect at the same point as every other plugin contribution:
/plugins reload or a new session. The profile now refreshes when the
session skill catalog re-pulls its plugin source on reload, reading
both the skill list and the prompt sections fresh.
* feat(agent-core): let plugins contribute system prompt instructions via the manifest systemPrompt field
* chore(agent-core-v2): remove inline implementation comment
* docs: clarify plugin prompt refresh semantics
---------
Signed-off-by: 7Sageer <sag77r@hotmail.com>
* feat(agent-core): support per-hook cwd and env in HookEngine
* feat(agent-core): support hooks in plugin manifest and aggregate via PluginManager
* feat(agent-core): merge plugin hooks into session hook engine
* chore: add changeset for plugin hooks
* feat(plugin): install plugins from a GitHub repository URL
Allow `/plugins install <github-url>` (and marketplace `source` entries) to
take a GitHub repo URL directly. A new `github` source kind joins the
existing `local-path` and `zip-url` kinds.
Recognized URL forms (parsing in source.ts):
- `https://github.com/<o>/<r>` — bare; resolves to latest
release tag, falling back
to default branch HEAD.
- `https://github.com/<o>/<r>/tree/<ref>` — branch / tag / SHA;
value passed to codeload
in its short form so the
backend resolves either.
- `https://github.com/<o>/<r>/releases/tag/<tag>` — explicit tag, uses
refs/tags/<tag> to avoid
same-named-branch ambiguity.
- `https://github.com/<o>/<r>/commit/<sha>` — explicit commit SHA.
The resolver deliberately avoids `api.github.com`: its 60/hour anonymous
quota is shared with every other tool on the egress IP (browser, gh CLI,
IDE integrations) and a first-time install failing because some other tool
ate the budget is unacceptable UX. Instead we:
- GET `github.com/<o>/<r>/releases/latest` with manual redirect and parse
the `Location` header (302 → tag URL; 404 → no own release).
- Fall back to `codeload.github.com/<o>/<r>/zip/HEAD` for repos with no
releases (or for forks that inherit upstream tags but have no own release
page, which redirect to bare `/releases`).
- Only treat the explicit 404 from `/releases/latest` as "no release" — 5xx,
403, 429, and any other non-2xx status surface a hard error rather than
silently installing the default branch, so the user knows when transient
GitHub issues changed the install path.
UI changes in the TUI:
- `/plugins install` now shows a live Braille spinner while resolving and
downloading, then flips to a final status that distinguishes Installed
(fresh) vs Updated (same repo identity, new version) vs Migrated (source
changed, e.g. CDN zip-url → GitHub).
- `/plugins list`, the `/plugins` overview, and `/plugins info` show the
install provenance inline. `zip-url` installs now display the URL host
(e.g. `via code.kimi.com`, `via 127.0.0.1:port`) instead of the opaque
`zip-url` literal. GitHub installs show `github <owner>/<repo>@<ref>`.
- Three-tier trust badge driven by the marketplace context recorded at
install time: `official` (green) for `tier: official`, `curated` (blue) for
`tier: curated`, `third-party` (muted) for anything not installed through
the marketplace selector. CLI `/plugins install <url>` always records as
third-party; the marketplace selector passes the tier through. A
re-install replaces the marketplace context: switching to a third-party
source clears the badge, which matches the underlying trust change.
`installed.json` gains optional `github` and `marketplace` fields
(back-compatible). PluginSummary surfaces `source`, `originalSource`,
`github`, and `marketplace` so the TUI can label installs without an extra
round trip to PluginInfo. The SDK's `session.installPlugin(source)` gains
an optional `{ marketplace }` second argument so the marketplace selector
can forward `{ id, tier }` through RPC; the CLI install path omits it.
Tests: 112 plugin-suite tests (URL parser, resolver, store round-trip,
manager integration). The manager integration tests assert codeload URLs
shape (short form for `/tree/<ref>`, explicit `refs/tags/` for
`/releases/tag/`) and verify marketplace context is persisted across
reloads and cleared on a third-party re-install.
* chore(changeset): plugin install from GitHub
* docs(plugins): document GitHub install URLs and trust badges
* fix(plugin): preserve URL-encoded characters in GitHub ref names
Git permits ref characters that have special meaning in URLs — most
notably `#`, which is a valid tag character (e.g. `release#1`) but the URL
fragment delimiter. The resolver decoded the tag from GitHub's
`/releases/latest` 302 redirect Location header and then interpolated the
raw value into the codeload URL. The literal `#1` became a fragment and
the HTTP request reached the server as `…/refs/tags/release` — a wrong or
truncated ref, leading to install failure for a release whose URL was
otherwise valid.
Two symmetric changes:
- The codeload URL builder now splits the ref on `/` (so multi-segment
refs like `feat/foo` keep their path separators) and percent-encodes
each segment.
- The GitHub URL parser now percent-decodes each segment from the URL's
pathname when extracting `/tree/<ref>`, `/releases/tag/<tag>`, and
`/commit/<sha>`. Storage and display see the human-readable Git ref
name; the resolver re-encodes on the way out.
Malformed `%xx` sequences in user-typed URLs are tolerated: we keep the
raw segment so the caller surfaces a normal "ref not found" error
downstream instead of crashing during parse.
* fix: restrict plugin trust badges
* chore: remove fetch when show plugin list
---------
Co-authored-by: qer <Anna_Knapprfr@mail.com>