* feat(plugins): add capability-gated Host reader for conversation content
Plugins can now read historical user/agent message content over the Host
data API, gated by a new `api_read:messages` capability (ADR 0047). This
unblocks "summarize yesterday"-style plugins that the live `message.added`
bus event alone cannot serve.
- proto: `ListMessages` RPC + `Message`/`MessageFilter` on `service Host`.
- SDK: `Host.Messages() MessageReader`, Go-native `Message`/`MessageFilter`.
- host: `api_read:messages` accessor + denied reader; content sanitized via
`sysprompt.StripSystemContent` (no raw system prompts); RFC3339 `since`/
`until` → `InvalidArgument` on bad input.
- data: new `ListMessagesForPlugin` filtered query (session/task ids, type,
created_at range) through the task service, never the DB file.
- tests: in-process + wire capability gating, content stripping, filter/time
passthrough, pagination, and the SQLite query.
- docs: ADR 0047, spec, plugins-manifest, plugins-authoring.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(plugins): add Host.InvokeUtilityAgent for settings-selectable utility agent
Plugins can now delegate a one-shot LLM step to an operator-configured agent,
gated by a new `agent_invoke` capability (ADR 0048), so a plugin needs no
provider API key of its own.
- setting: new `utility_agent_profile_id` user setting (Settings > System >
Utility Agent), a profile dropdown cloned from the existing utility-agent
path; selectable in the UI.
- capability: boolean `agent_invoke`, enforced like `state`/`secrets`.
- proto/SDK: `InvokeUtilityAgent(prompt) -> text` RPC and
`Host.InvokeUtilityAgent(ctx, prompt)`.
- backend: reuses the sessionless host-utility inference tier (ADR 0002) via a
thin adapter — no agent loop, no task/session/workspace; resolves the
configured profile to (agent type, model). Unconfigured / deleted profile ->
gRPC FailedPrecondition.
- tests: capability gating, completion path (asserts the configured profile is
used and text returned), no-utility-agent and stale-profile error paths,
SDK/wire round-trips, and the settings apply/trim behavior.
- docs: ADR 0048, spec, plugins-manifest, plugins-authoring.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(plugins): resolve utility-agent deps live and bound message filter size
Address CI + review feedback on the host plugin capabilities:
- docs: register the new /settings/system/utility-agent route in
docs/public/coverage.json (fixes the "Validate public docs" CI failure).
- utility agent (ADR 0048): hostUtilityMgr is wired late in boot — after
StartActivePlugins has already spawned boot-active plugins — so snapshotting
the utility deps in hostForPlugin stranded those hosts with nil deps and made
InvokeUtilityAgent return Unimplemented for their lifetime. Read the deps live
from the Service (mutex-guarded) at call time instead; add a regression test
for late wiring reaching an already-spawned host.
- messages (ADR 0047): cap the combined session/task/type filter values at 400
so an oversized filter fails fast with InvalidArgument instead of hitting
SQLite's host-parameter limit at query execution; add a test.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs: document the plugin marketplace
Add a public Plugin Marketplace page covering both audiences: operators
using the in-app catalog (Browse/Installed tabs, one-click install,
Update to vX, the Sources dialog for team/corporate registries, plugin
icons, feature-flag gating, sideloading still works) and plugin authors
publishing into it (one-repo-per-plugin GitHub Release assets, the
optional manifest icon field, submitting a plugins.yaml PR to the
official catalog, GitHub-stars-only ranking, and hosting your own
index.json source).
Also cross-link the new page from plugins.md (replacing the now-false
"No plugin marketplace" security note), plugins-authoring.md, and
plugins-manifest.md (new icon field row); register it in meta.json and
coverage.json.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs: correct plugin-registry CI validation claims
The registry index-build workflow does not validate plugins.yaml against
schema.json, nor does build-index.mjs compare the entry id to the
manifest id — bad entries are skipped-and-logged, and CI only fails when
a non-empty list resolves to zero built entries. Reword the submission
steps (in both the marketplace docs page and plugin-registry/README.md)
to describe what the workflow actually does: it resolves each entry
against the GitHub API and requires a correctly named
<id>-<version>.tar.gz release asset, with maintainer approval as the real
gate. Also clarify that plugins.yaml `categories` are free-form curation
tags, not the manifest's category enum.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* ci: split plugin-registry Pages deploy into its own job
The single build-and-deploy job bound the `github-pages` environment at
the job level, so GitHub evaluated that environment's protection rules on
every PR run — failing the check even though the deploy steps were gated
to non-PR events. This broke every PR touching plugin-registry/**, which
is exactly what a plugin-submission PR does.
Split into a PR-safe `build` job (no environment) and a non-PR-gated
`deploy` job that owns the `github-pages` environment and runs only
`deploy-pages`. Behavior on push/schedule/workflow_call is unchanged; PRs
now run build+tests without tripping the environment gate. The star
refresh calls this workflow by file path, so the job rename is safe.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs: describe index-build asset resolution accurately
build-index.mjs picks the <id>-<version>.tar.gz release asset when
present but falls back to the release's first .tar.gz otherwise, and only
errors when a release has no .tar.gz at all — it does not hard-fail a
misnamed asset. Reword the submission steps to present the exact asset
name as an authoring instruction (so the index resolves the right asset)
rather than a builder-enforced gate.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Revert "ci: split plugin-registry Pages deploy into its own job"
This reverts commit 2c5f649146.
* Reapply "ci: split plugin-registry Pages deploy into its own job"
This reverts commit 3795cdb98a.
---------
Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs: add plugin system guide, authoring tutorial, and manifest reference
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs: update plugin docs for host data API, config, and nav additions
Cherry-picked plugin docs (d853b66b8) were written before four follow-up
PRs merged: typed capability-gated Host data API (#1753, ADR 0043),
richer frontend SDK (#1759), per-plugin settings pages (#1761), and
sidebar Integrations nav items (#1767). Re-verify against the merged
code and bring the docs current:
- plugins.md: per-plugin settings/config page, expanded Host RPC list,
updated capability vocabulary (api_read/api_write), sidebar nav note.
- plugins-authoring.md: GetConfig/GetSecret/SetSecret/DeleteSecret and
the six Host data readers, registerRoute options/PluginPageChrome,
NavItem icon/integrations section, host.api.baseUrl/navigate, and the
full host.ui component list.
- plugins-manifest.md: corrected api_read/api_write semantics (now
enforced, not reserved), config_schema validation/secret-masking
behavior, and new Host data API / config-schema sections.
- configuration.md, feature-status.md: document the KANDEV_FEATURES_PLUGINS
flag alongside the existing Office flag.
- coverage.json: new plugin-system coverage area so the public-docs
validator accounts for the three new pages.
* docs: fix plugin config route, signing claim, and data-dir layout
Address automated review findings on PR #1774:
- plugins-manifest.md: PATCH route for config is /api/plugins/{id}, not
/api/plugins/{id}/config (only GET uses the /config suffix).
- plugins-authoring.md: host.store is not read-only — setState is
genuinely exposed and writable; correct the misleading inline comment.
- plugins.md: signature verification is not currently wired up in
production (pkgtar.VerifySignature is nil, never assigned outside
tests), so every package installs and is reported as unsigned today —
correct the 'kandev verifies it' claim in both the install pipeline
step and the signed/unsigned section.
- plugins.md: KANDEV_PLUGIN_DATA_DIR is <id>/data, a sibling of the
<version>/ directory (shared across versions), not nested inside it —
fix the on-disk layout diagram.
* docs: mark plugin docs as experimental
The plugin system is feature-flagged and off by default in production
(KANDEV_FEATURES_PLUGINS). Mark all three plugin pages with
status: experimental frontmatter, matching the k8s.md convention for a
page that is wholly about an experimental feature.
* docs: add plugin UI screenshots and a capture spec
Embed four operator-facing screenshots in the plugin docs (the Plugins
list, the install dialog, a per-plugin settings page with its manifest
card, and a native plugin route). Captured from an isolated e2e backend
with the kandev-plugin-hello reference example installed.
Add e2e/tests/plugins/plugins-docs-screenshots.spec.ts to regenerate them:
it reuses the plugins e2e harness (isolated backend, plugins flag on) and
is skipped unless CAPTURE_DOCS_MEDIA=1, so it adds no CI cost. Defaults to
the repo's own e2e fixture package; override DOCS_PLUGIN_* to point at the
polished hello example (as the committed captures were taken).
* docs: fix plugin screenshot paths for the landing publisher
The Cloudflare preview build failed because markdown images under
docs/public/media/ compile to build-time module imports that the docs
site can't resolve — media/ is reserved for <DocsVideo> assets. The
landing publisher only rewrites and copies images referenced under
screenshots/ (README convention). Move the four plugin captures to
docs/screenshots/plugin-*.png and reference them as ../screenshots/*.png,
and update the capture spec to write there.
* docs: convert plugin how-it-works diagram to mermaid
The hand-drawn ASCII pipeline rendered with broken alignment and stray
syntax highlighting on the docs site. Replace it with a mermaid flowchart
(same convention as architecture.md), which renders as a clean SVG.
* docs: give plugins its own nav section
* docs: clarify plugin tools are declared/listed, not agent-invocable yet
Plugin tools[] are validated and exposed via GET /api/plugins/tools, and
the gRPC InvokeTool transport is implemented and tested, but nothing in
production calls it — no agent, MCP bridge, or HTTP route. The docs
overstated this as agent-invocable. Correct the Plugin interface comment,
the manifest tools[] rows, the how-it-works diagram, and the example
plugin description, and add a note in both the authoring and manifest
pages that InvokeTool stays unused until the agent-tool wiring ships.
* docs: clarify webhook relay in how-it-works diagram
The diagram collapsed two hops into one node, reading as if the plugin
receives the external HTTP POST directly. Split it: an external caller
POSTs to kandev's webhook route, which relays the request to the plugin
over the gRPC HandleWebhook method (the plugin serves no HTTP itself).
Also label the DeliverEvent/InvokeTool/HandleWebhook edges by their real
proto method names.
* docs: note the plugin backend is any go-plugin gRPC executable, Go SDK only
* docs: remove plugin tools from the docs
Plugin tools[] overlap with MCP (kandev's established agent-tool surface)
and the invocation path was never wired to agents. Rather than document a
half-feature that implies plugins are an alternate MCP, drop it entirely:
the Plugin interface (InvokeTool), the manifest tools[] block and field
rows, the how-it-works diagram branch, and the example-plugin mention.
Removal from the backend code is tracked separately.
* docs: replace how-it-works mermaid with a designed diagram image
The mermaid flowchart rendered poorly. Replace it with a hand-authored
HTML/CSS architecture figure (install pipeline + the runtime gRPC
interactions), rendered to docs/screenshots/plugin-architecture.png. The
diagram source lives in the docs-screenshot capture spec (skipped unless
CAPTURE_DOCS_MEDIA=1) so it regenerates deterministically at 2x.
* docs: make the how-it-works figure a real box-and-arrow diagram
The previous figure read as a list of rows. Replace it with a hand-drawn
SVG architecture diagram: the install pipeline feeds a spawned plugin
subprocess, then kandev and the plugin communicate over one gRPC
connection (DeliverEvent, HandleWebhook, and the plugin's Host API
callbacks), with the external webhook caller and the browser SPA as
edge nodes. Source lives in the capture spec's ARCH_HTML.
* docs: make the how-it-works diagram a clean monochrome flowchart
Drop the multi-color component diagram for a calm top-down flowchart
(mermaid's shape, drawn properly): install → verify → extract → spawn,
branching into event delivery and webhook relay, converging on the Host
API callbacks, with an optional native-UI step. Grayscale, no accent
colors. Source stays in the capture spec's ARCH_HTML.
* docs: dark plugin diagram + space out the two page images
- Make the how-it-works flowchart a dark rounded card (matches the docs
page in dark mode; reads as an embedded card in light). Rendered with
omitBackground so the corners are transparent.
- Move the Settings > Plugins list screenshot out of the intro (where it
sat right before the diagram) down into the "Installing a plugin"
section, so text separates the two images.
* docs: update install-dialog screenshot for aligned footer actions
Recaptured after fix(plugins): align install-dialog footer actions
(#1780) — Cancel and Install now sit together in one right-aligned
footer row instead of the Install button being stranded under the
dropzone.
* docs: account for /settings/system/storage in coverage.json
The storage settings route (added by #1699) was never recorded in the
public-docs coverage inventory — the docs validator is path-filtered to
docs/public/** so it didn't run for that PR. This docs PR is the first to
trip it. Add the route to system-health-and-maintenance.
* docs: review fixes — remove stale tools refs, tighten accuracy
Review pass against the now-merged main (tools removed in #1776):
- extending-kandev.md: drop "serves agent-invocable tools" from the plugin
overview (the feature no longer exists).
- plugins-manifest.md: the legacy endpoints block is health/events/webhooks
only (no tools path); and the `signed` field is always false today since
signature verification isn't wired.
- plugins.md: install validation covers the categories/UI-surface enums (not
a "capability vocabulary"); AutoMTLS secures the channel on all platforms,
not just Windows; and status vs signing are separate badges.
* docs: explain what Host state is (GetState/SetState)
The authoring guide listed the state RPCs but never said what 'state'
means. Add a plain-language explanation: a per-plugin, kandev-persisted
key/value store keyed by (scope, scopeID, key) holding JSON objects,
namespaced per plugin, scoped to instance/workspace/task/agent, and how it
differs from the writable data dir. Make the example a real increment.
* docs: note NavItem section:'settings' is not rendered
A NavItem with section:'settings' is accepted by the type but no sidebar
section renders it (plugin-nav-items renders main/default, the Integrations
sections render 'integrations'); annotate that settings pages use
registerSettingsRoute. Addresses repeated review feedback.
---------
Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>