Commit Graph

250 Commits

Author SHA1 Message Date
Carlos Florêncio 77ef32c86c feat: add simplified resource metrics display (#1904) 2026-07-23 21:24:10 +01:00
Carlos Florêncio 1f905be98f fix: support long Git worktree paths on Windows (#1878) 2026-07-23 20:21:23 +01:00
Carlos Florêncio ca63861207 chore: consolidate harness delegation guidance (#1906) 2026-07-23 19:58:43 +01:00
Carlos Florêncio 8c010ebeca fix: inherit service temporary environment (#1894) 2026-07-23 17:24:54 +01:00
Carlos Florêncio 9cd0c27be0 feat: mark selected task repositories (#1881) 2026-07-23 16:32:25 +01:00
Carlos Florêncio d475cdf597 fix: scope Office agent tools and task mutations (#1867) 2026-07-23 16:31:37 +01:00
Carlos Florêncio a225fbac3e ci: harden trusted PR review evidence (#1896) 2026-07-23 16:22:50 +01:00
Carlos Florêncio 5167bb9832 feat: add external vcs file links (#1883) 2026-07-23 15:18:10 +01:00
Carlos Florêncio f6007de1cb feat: move GitLab MR linking to task menus (#1868) 2026-07-23 14:46:07 +01:00
Carlos Florêncio c041dbee0e fix(orchestrator): separate office task session ownership (#1893) 2026-07-23 14:12:58 +01:00
Ahmad Ragab e1008737c4 fix(web): retain enhanced prompt results (#1854) 2026-07-23 14:01:44 +01:00
João Salavisa ac6d8eb3be feat: resolve @prompt references in workflow prompts (#1865)
* feat: resolve @prompt_name saved-prompt references in workflow step prompts

Workflow step prompts, the GitHub review-watch task prompt, and the CI
auto-fix prompt now resolve "@name" references to saved custom prompts
(Settings > Prompts) at invocation time, appending the referenced
content as hidden context while keeping the mention visible - matching
how chat messages and message_task already resolve @mentions. Editing
a saved prompt now propagates to every workflow step that references
it without needing to update each step individually.

Adds a Monaco "@" completion provider to the step-prompt and
review-watch editors, alongside the existing "{{placeholder}}"
provider.

* fix: address PR review findings on prompt-reference expansion

- Make AppendReferenceExpansions idempotent: a prompt that already
  contains a previously-appended expansion block is returned unchanged,
  since the block's own "### @name" headers would otherwise be
  re-scanned as fresh mentions on a second call.
- Skip "@name" expansion entirely for CLI-passthrough sessions: their
  prompts are written raw to a PTY with no <kandev-system> stripping,
  so the hidden block would otherwise leak into the terminal verbatim.

* fix: fail safe on passthrough lookup error in startTask

A session-lookup error while resolving isPassthrough for the Kandev
MCP wrap and prompt-reference expansion previously defaulted to
false (non-passthrough), risking a hidden <kandev-system> block
leaking into a real passthrough session's PTY on a transient error.
Extracted resolveIsPassthroughForLaunch, which now fails safe by
defaulting to true (skip injection) and logs a warning.

* fix: scope expansion idempotency marker to system blocks

The idempotency guard added in a prior commit checked for the
expansion marker anywhere in the prompt, which could falsely
suppress a legitimate first-time expansion if caller-controlled
text happened to contain that literal phrase. Add
sysprompt.HasSystemMarker, mirroring the existing HasKandevContext
pattern, so the marker is only recognized inside a formatter-
produced <kandev-system> block. Also strengthens a passthrough test
assertion that didn't actually distinguish the snapshot path from
the fail-safe default.

* fix: match exact expansion-block prefix in idempotency guard

The prior scoping fix accepted the marker anywhere inside any
<kandev-system> block, which could still false-positive against an
unrelated system block (e.g. a plan-mode prefix or MCP context
wrap) that happens to mention the marker phrase without it being
the block's own content. Check for the exact literal prefix this
package's formatter produces instead: the opening tag immediately
followed by the marker, with nothing in between.

---------

Co-authored-by: Kandev Agent <agent@kandev.dev>
2026-07-23 13:14:54 +01:00
José Almeida 9c16d337a2 feat(web): add global app status surface (#1869)
* feat(web): add app status bar

* feat(web): add app status bar visibility toggle

* feat(web): persist status bar item order

* fix(web): refine app status bar behavior

* fix(web): polish app status bar presentation

* test(web): split plugin slot coverage groups

* fix(web): address status surface review feedback

* fix(web): stabilize status bar overlay tests
2026-07-23 12:28:54 +01:00
José Almeida 7de788ceed feat: add cross-integration entity references (#1862)
* feat: add cross-integration entity references

* fix(web): anchor entity reference menu to composer

* fix: keep Kandev tasks under @ mentions

* fix: address entity reference review findings

* test(e2e): use option role for prompt menu

* test(e2e): update passthrough mention roles

* fix: preserve queued references and paginate Azure PRs
2026-07-23 09:03:56 +01:00
José Almeida 67507c9273 fix: prevent blank mobile task views (#1877) (#1889) 2026-07-23 08:58:05 +01:00
José Almeida d5da5d0022 fix(web): select pull request on review surface (#1857)
* fix(web): select pull request on review surface

* fix(web): address review feedback

* test(web): stabilize tablet review target assertion

* fix(web): address late review feedback

* test(web): cover PR review boundary branches
2026-07-22 17:52:58 +01:00
João Salavisa 6d5fc4794b fix(plugins): select utility agents per plugin (#1855)
* fix(plugins): select utility agents per plugin

* fix(plugins): address utility agent review feedback

---------

Co-authored-by: Kandev Agent <agent@kandev.dev>
2026-07-22 01:50:40 +01:00
João Salavisa 78cae96bb1 feat(plugins): host-side conversation reads + utility-agent invoke (#1852)
* 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>
2026-07-22 00:20:09 +01:00
Carlos Florêncio 7fd99c3f33 feat: complete GitLab integration parity (#1832) 2026-07-21 23:24:22 +01:00
Carlos Florêncio 5499eadf19 fix(github): keep draft pull requests out of merge-ready state (#1851) 2026-07-21 23:22:49 +01:00
João Salavisa 330afb7901 feat(plugins): add main-top-bar slot for the default app top bar (#1841)
* feat(plugins): add main-top-bar slot for the default app top bar

Plugins could only contribute to the session/chat top bar via the
chat-top-bar slot. Add a main-top-bar slot, mounted on the default
app top bar (Home / Kanban / Tasks) across desktop, tablet, and
mobile headers, so plugins can surface workspace-scoped status or
actions outside of a task session.

slotProps: { workspaceId, workspaceLabel, currentPage } — no task or
session ids, since the bar isn't scoped to a task.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: narrow main-top-bar currentPage slot prop to a literal union

Addresses Claude Code Review feedback on PR #1841: MainTopBarSlotProps.currentPage
was typed as a plain string even though every caller only ever passes
"kanban" | "tasks" (matching KanbanHeaderProps.currentPage). Narrow the
plugin-facing contract to match so plugin authors get real type safety.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* test(e2e): cover the main-top-bar plugin slot

Extend the kandev-plugin-e2e fixture bundle to also register a
main-top-bar slot component, and assert it renders on the default
app top bar (Home) alongside the existing task-sidebar coverage.

Verified locally: apps/web e2e plugins.spec.ts passes, plugin-fixture
Go tests pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 17:18:34 +01:00
João Salavisa 2921553d40 feat(plugins): render plugin-settings slot at top of settings page (#1838)
Move the plugin-settings slot above the schema-driven settings card so a
plugin's live status/health card is the first content under the header,
instead of sitting between the settings and manifest cards. Docs updated to
match.

Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 14:07:54 +01:00
José Almeida 8f2fe7a8e6 feat(web): add agent message comments (#1835)
* feat(web): add agent message comments

* fix(web): harden agent message comments

* fix(web): address message comment review findings

* fix(web): allow comments on legacy agent messages

* fix(web): address latest message comment review

* fix(web): address follow-up message comment review

* fix(web): address remaining message comment review

* fix(web): keep failed mobile comments open

* fix(web): preserve selections over message comments
2026-07-21 13:32:19 +01:00
João Salavisa 30e6cba810 feat(plugins): add owner-scoped plugin-settings slot for inline plugin UI (#1836)
* feat(plugins): add plugin-settings slot to per-plugin settings page

Add a named "plugin-settings" host slot so an installed plugin can render
its own inline UI (e.g. a live integration/health status card) on its
per-plugin settings page (Settings > Plugins > <plugin>), between the
schema-driven settings form and the manifest card.

The slot renders on every plugin's detail page, so the host forwards the
current plugin's identity via slotProps ({ pluginId, status }); registered
components must gate on slotProps.pluginId to avoid rendering on another
plugin's page. Documented in PLUGIN-API.md and mirrored in types.ts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(plugins): make plugin-settings slot owner-scoped in the host

Instead of forcing every plugin author to early-return unless
slotProps.pluginId matches its own id, the host now filters the
"plugin-settings" slot by the owning plugin. The registry already records
the owning pluginId per registration, so PluginSlot gains an optional
ownerPluginId prop backed by a new getSlotComponentsForPlugin() getter,
and the plugin-detail page passes the current plugin's id.

A plugin's settings card now renders only on its own settings page, with
no gating boilerplate in the plugin. Docs/types updated to match.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(plugins): add plugin-settings slot usage mockups

Add illustrative mockups of the plugin-settings slot rendered on a plugin's
settings page (Provider Usage health card) plus a strip of possible usages
(healthy, degraded, and a generic integration), referenced from PLUGIN-API.md.
Clearly labeled as mockups, not real product screenshots.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(plugins): reset error boundary per owner in owner-scoped slots

Address review feedback on PR #1836:

- The PluginErrorBoundary key was `${name}-${index}`, identical across
  plugins for an owner-scoped slot. Navigating /settings/plugins/A -> B in
  the same settings shell (without remounting PluginSlot) reused A's errored
  boundary and silently hid B's healthy card. Include ownerPluginId in the
  key so each owner gets an independent boundary; add a regression test.
- Trim the verbose multi-line slot comment in plugin-detail to one line per
  the repo's comment convention.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(plugins): document plugin-settings slot; drop mockup binaries

- Public docs: add the owner-scoped `plugin-settings` slot to the Named
  slots table and a "Plugin settings page" section in plugins-authoring.md,
  and mention inline status cards in plugins.md's per-plugin settings.
- Remove the illustrative mockup PNGs (and their PLUGIN-API.md embeds) that
  were committed to the branch — image binaries should not merge into main;
  PR screenshots belong on a throwaway media ref instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(plugins): condense owner-key boundary comment to one line

Address review nit — the repo prefers one short comment line.

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>
2026-07-21 11:53:43 +01:00
Carlos Florêncio c2a678b023 feat: add Azure DevOps integration (#1778) 2026-07-21 08:40:26 +01:00
Carlos Florêncio 8b8d51548b docs(plugins): add plugin authoring skill (#1828) 2026-07-20 21:41:44 +01:00
Carlos Florêncio 2130c28686 fix(repositories): allow explicit paths outside discovery roots (#1825) 2026-07-20 19:49:41 +01:00
João Salavisa b277e7f1d8 feat: add chat-top-bar plugin slot (#1827)
* feat: add chat-top-bar plugin slot

Adds a new named plugin UI slot, chat-top-bar, rendered in the session
top bar beside the CPU/DB metrics and document/editor/debug controls.
Mirrors the existing chat-input-actions slot pattern: a wrapper
component (TaskTopBarPluginActions) assembles slotProps from the store
and renders <PluginSlot name="chat-top-bar" />, forwarding
{ taskId, taskTitle, workspaceId, activeSessionId, sessionIds }.

Updates PLUGIN-API.md, the public authoring guide, and the
PluginSlotName JSDoc to document the new slot. No backend changes
needed since slot names are an open string type.

* fix: address PR review — mobile chat-top-bar slot, gate by archived state

- Wire the chat-top-bar plugin slot into the mobile session top bar
  (session-mobile-top-bar.tsx), matching desktop's slotProps shape.
- Gate the plugin cluster behind !isArchived in TopBarRight, matching
  every other interactive control in that group (port-forward, PR/MR
  buttons, layout preset, editors menu).
- Extract RemoteExecutorIndicator, ApproveButton, and MobileTopBarDialogs
  to keep the mobile top bar under lint complexity/length limits.

* fix: gate mobile chat-top-bar plugin slot by archived state

Threads isArchived through TaskLayout -> SessionMobileLayout ->
SessionMobileTopBar -> MobileTopBarActions so the mobile top bar
hides plugin content on archived tasks, matching the desktop
TopBarRight behavior fixed in the previous commit.

---------

Co-authored-by: Kandev Agent <agent@kandev.dev>
2026-07-20 18:52:56 +01:00
João Salavisa 91bdee3d3a feat(plugins): show source repo link on installed plugin list and settings (#1821)
* feat(plugins): show source repo link on installed plugin list and settings

Installed plugins carried no repository URL — a "Repo" link existed only
on marketplace catalog cards, whose repo_url is registry-derived from
plugins.yaml and never reaches an installed record. Let the manifest
declare an optional repo_url so sideloaded and directly-installed plugins
surface the same link.

- manifest: add optional RepoURL (repo_url); validate http(s)-only at
  registration so a javascript: scheme can't reach the operator's session.
- Record embeds the manifest, so repo_url flows through JSON to the
  frontend PluginRecord with no extra plumbing.
- frontend: extract the shared, guarded PluginRepoLink (+ isHttpUrl) from
  the marketplace row and render it on the installed plugin row and the
  plugin detail header.
- fixture + docs: exercise repo_url in the e2e fixture manifest and
  document the field in the public manifest reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(plugins): harden repo_url handling per review

- PluginRepoLink accepts string|undefined|null and isHttpUrl null-checks
  before trim, so it can't throw on an absent repo_url (CodeRabbit); drop
  the now-redundant truthiness guards at the call sites.
- validateRepoURL trims m.RepoURL in place so the stored/serialised value
  matches what was validated (Greptile).

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>
2026-07-20 14:03:52 +01:00
João Salavisa 948b3b62d0 docs: reference kandev-plugin-template starter repo in plugin guides (#1818)
* docs: reference kandev-plugin-template starter repo in plugin guides

Link the new kdlbs/kandev-plugin-template repo from the authoring guide as the
recommended "Use this template" starting point, and from the marketplace guide's
release step. Clarify the two release assets: the in-tar checksums.txt verified
on install versus the release-level checksums.txt (tarball digest) pinned as the
catalog's package_sha256.

* docs: describe package_sha256 as advisory, not yet index-pinned

* docs: mark release checksums.txt asset as optional for consistency

* docs: mark checksums.txt optional in marketplace submission step too

---------

Co-authored-by: Kandev Agent <agent@kandev.dev>
2026-07-20 13:20:29 +01:00
Carlos Florêncio ba75922fa1 feat: add configurable task layout profiles (#1799) 2026-07-20 12:09:06 +01:00
Carlos Florêncio f212258907 perf: scale workspace git status for large worktrees (#1814) 2026-07-20 11:04:59 +01:00
Carlos Florêncio 110c2efcb1 fix: report storage usage and reclaim archived workspaces (#1813) 2026-07-20 09:35:57 +01:00
Carlos Florêncio 33aa7af67e feat: configure profiles for MCP-created tasks (#1797) 2026-07-19 23:18:06 +01:00
Alassane 2c3bcd1ec2 docs: document cross-task agent communication and MCP tools (#1793) 2026-07-19 22:17:15 +01:00
José Almeida f7f1d10996 feat: add parent-controlled task stopping via MCP (#1803)
* feat: add parent-controlled task stopping via MCP

* fix: harden parent task stop lifecycle

* fix: preserve rejected task rollback state
2026-07-19 20:23:46 +01:00
João Salavisa 52946b9159 docs: document the plugin marketplace (#1805)
* 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>
2026-07-19 17:44:15 +01:00
João Salavisa bc2f310e73 feat(plugins): add plugin marketplace with curated registry and updates (#1798)
* feat(plugins): add plugin marketplace with curated registry and updates

Discover and install plugins from inside kandev instead of only by URL/upload.

- Backend marketplace subpackage: SQLite-backed sources, catalog fetch/merge
  (dedup, install_state, degraded-source handling), and the /api/plugins/
  marketplace[/sources] API. Install reuses the existing install-by-URL pipeline.
- Frontend: Settings > Plugins gains a Browse tab (search/category/star sort),
  source management, one-click install, and an Update button on the Installed
  tab when the catalog has a newer version. Plugins may ship an icon (manifest
  `icon` -> catalog icon_url) with a letter-tile fallback.
- Registry infra: plugin-registry/ (curated plugins.yaml + schema.json) and two
  GitHub Actions (zero-dependency Node build-index.mjs -> index.json on Pages,
  daily star refresh). Ships empty at launch; the empty catalog is valid.
- Spec: docs/specs/plugins/marketplace.md. No download telemetry; stars only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(plugins): address marketplace PR review feedback

- CodeQL: stub fetch routers keyed off a path segment, not a hostname substring.
- Nullable stars end to end: a failed star lookup stays unknown (not 0) and
  sorts last, instead of decoding null -> 0 and corrupting ranking.
- Fetch marketplace sources concurrently and dedupe identical in-flight
  downloads (singleflight), so a slow source can't serialize the Browse tab.
- Register /marketplace routes before the /:id wildcard (gin tree ordering).
- Duplicate source URL returns 409 ErrDuplicateSource, not a raw SQL error.
- Browse: filter category client-side so the category dropdown never collapses.
- marketplaceInstall swallows after toasting (no unhandled promise rejection).
- Source badge keys off source_id, not a name substring.
- build-index: read block-sequence manifest categories, not just inline.
- Registry index workflow validates on pull_request; Pages deploy gated to
  push/schedule/dispatch. Spec: align package_sha256 (advisory in v1) + sort enum.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(plugins): guard catalog repo_url against javascript: href injection

repo_url comes from an untrusted index.json, so a malicious/compromised source
could set a javascript: URL that runs in the operator's session when the Repo
link is clicked (rel/target don't block it). Only render the anchor for http(s)
schemes. Adds regression tests for the scheme guard and null-star rendering.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(plugins): require https for remote sources; test oversized download

- Reject remote plain-http marketplace sources (MITM catalog-poisoning risk);
  loopback http stays allowed for local dev/e2e fixtures.
- Add the missing oversized-response assertion to TestDownloadRejectsNon200AndOversized
  so the maxIndexBytes memory bound has coverage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(plugins): detach source download from singleflight leader's context

Two concurrent Browse opens after a cache Refresh share one singleflight
download; if the leader's request context is canceled (it navigates away), the
waiters must not inherit that cancellation. Use context.WithoutCancel so values
are preserved but the leader can't cancel everyone; the client's 20s timeout
still bounds the download.

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>
2026-07-19 17:10:53 +01:00
Carlos Florêncio 5fb1923a9a feat: add explicit save for settings (#1686) 2026-07-19 14:21:36 +01:00
José Almeida 1a42c07abe fix: keep queued messages parked after agent cancellation (#1796) 2026-07-19 13:50:03 +01:00
Carlos Florêncio 4fdd679269 fix: attribute model configuration to turns (#1784) 2026-07-19 12:46:40 +01:00
João Salavisa 4b1320ffb0 feat(plugins): add chat-input-actions slot for plugin toolbar icons (#1794)
* feat(plugins): add chat-input-actions slot for plugin toolbar icons

Let plugins render icon buttons into the chat composer toolbar, beside
the model picker, mic, and send button, via the existing named-slot
mechanism (registerComponent). The host mounts the slot in both the
desktop and mobile toolbars and forwards { taskId, taskTitle,
activeSessionId, sessionIds } as slotProps — the active session plus
every kandev session id on the task. Resolving a session id to an
agent/ACP transcript id is left to the plugin (server-side, via the Host
data API); the host only propagates ids.

Docs (PLUGIN-API.md, public plugins/plugins-authoring) document the new
slot and its props.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(plugins): read store optionally in chat-input-actions slot

ChatInputPluginActions read the app store via useAppStore, which throws
outside a StateProvider — breaking existing toolbar tests that render the
composer in isolation. Switch to useOptionalAppStore (fallback to no
sessions) so the slot never hard-requires a provider.

Also: gate the slot behind hideAgentControls to match the other
session-specific toolbar controls, drop a redundant String() coercion,
tidy a dangling docs reference, and cover the taskId=null path.

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>
2026-07-19 12:26:50 +01:00
João Salavisa 370cee8f7f docs: add plugin system guide, authoring tutorial, and manifest reference (#1774)
* 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>
2026-07-19 00:56:32 +01:00
Carlos Florêncio 8fae44fb12 feat: unify configuration chat sessions (#1695) 2026-07-18 22:58:18 +01:00
Carlos Florêncio e9634fa636 feat: allow detaching subtasks from parent tasks (#1781) 2026-07-18 21:19:05 +01:00
Carlos Florêncio 21f811d5ec feat: add idle storage maintenance (#1699) 2026-07-18 19:08:44 +01:00
zensi-dev 4684390bc6 feat(acp): add Grok CLI driver (#1745) 2026-07-18 17:37:07 +01:00
João Salavisa 1ba81a7cd0 refactor(plugins): remove the unused tools feature (#1776)
Plugin `tools[]` overlapped with MCP, kandev's established mechanism for
exposing tools to agents (`internal/mcp/`), and the `InvokeTool` path was
never wired into agent tool sets — the `listTools` handler was
listing-only. Rather than maintain a half-built parallel surface, remove
it. If plugins ever contribute agent tools, they should feed through MCP,
not a separate `InvokeTool` RPC.

Removes:
- proto `InvokeTool` RPC + `ToolRequest`/`ToolResponse`/`ToolContext`
  messages (regenerated `*.pb.go` / `*_grpc.pb.go`)
- pkg/pluginsdk `InvokeTool` from the `Plugin` interface and
  `UnimplementedPlugin`, the Tool* Go-native types, and the
  `RemotePlugin`/`GRPCServer` dispatch
- internal/plugins `Service.InvokeTool`, the `listTools` handler +
  `GET /api/plugins/tools` route, `PluginToolDTO`, and the manifest
  `Tools` field + tool-name uniqueness validation
- frontend `PluginTool`/`PluginToolDTO` types, `listPluginTools`, and the
  manifest-card tools list
- fixture/e2e `InvokeTool` echo impl and the `tools:` manifest block

Test doubles that used the "crash" tool to exercise crash-restart now use
a "crash" webhook key instead. Internal specs (GRPC-CONTRACT, spec) drop
the tool RPC/feature; docs/public was already updated in #1774.

Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 17:03:13 +01:00
Carlos Florêncio 547f312dbe feat(web): link mobile Kandev brand to workspace home (#1755) 2026-07-18 16:17:01 +01:00
José Almeida e2c076c71c feat(web): improve mobile task navigation (#1769)
* feat(web): improve mobile task and kanban navigation

* feat(web): refine mobile task navigation

* fix(web): address mobile navigation review feedback

* test(web): cover task title field modes

* fix(web): keep mobile board navigator open across workflows

* fix(web): address mobile workflow review feedback
2026-07-18 02:28:29 +01:00