From c633b8faa1ca88a61509ff3bb0f6a9e533010c07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Almeida?= <57680069+zeval@users.noreply.github.com> Date: Fri, 17 Jul 2026 11:30:37 +0100 Subject: [PATCH] docs: overhaul public product guides (#1749) * docs: overhaul public product guides * fix(docs): address review findings * docs: clarify validation contracts * docs: complete validator API contracts * docs: clarify validator existence check --- apps/web/e2e/README.md | 16 +- docs/public/README.md | 111 ++- docs/public/add-agent-cli.md | 291 +++--- docs/public/agents-and-profiles.md | 150 ++- docs/public/architecture.md | 113 ++- docs/public/automation-and-mcp.md | 243 +++-- docs/public/backend-development.md | 111 ++- docs/public/cli.md | 317 +++--- docs/public/configuration.md | 387 ++++++-- docs/public/contributing.md | 100 +- docs/public/coordination.md | 236 ++++- docs/public/coverage.json | 286 ++++++ docs/public/desktop-app.md | 232 +++-- docs/public/developer-tools.md | 196 +++- docs/public/docker.md | 363 ++++--- docs/public/executors.md | 226 ++++- docs/public/extending-kandev.md | 90 +- docs/public/feature-status.md | 136 ++- docs/public/git-operations.md | 261 ++--- docs/public/index.md | 60 +- docs/public/integrations.md | 211 +++- docs/public/k8s.md | 472 +++++---- docs/public/media/feature-guides/NOTES.txt | 63 ++ .../media/feature-guides/code-walkthrough.mp4 | Bin 0 -> 558064 bytes .../feature-guides/code-walkthrough.webm | Bin 0 -> 492774 bytes .../feature-guides/code-walkthrough.webp | Bin 0 -> 23902 bytes .../feature-guides/diff-line-feedback.mp4 | Bin 0 -> 574058 bytes .../feature-guides/diff-line-feedback.webm | Bin 0 -> 434285 bytes .../feature-guides/diff-line-feedback.webp | Bin 0 -> 23770 bytes .../public/media/feature-guides/manifest.json | 324 ++++++ .../parallel-named-sessions.mp4 | Bin 0 -> 256388 bytes .../parallel-named-sessions.webm | Bin 0 -> 241631 bytes .../parallel-named-sessions.webp | Bin 0 -> 11880 bytes .../feature-guides/plan-review-implement.mp4 | Bin 0 -> 433605 bytes .../feature-guides/plan-review-implement.webm | Bin 0 -> 403001 bytes .../feature-guides/plan-review-implement.webp | Bin 0 -> 19520 bytes .../profile-executor-selection.mp4 | Bin 0 -> 256392 bytes .../profile-executor-selection.webm | Bin 0 -> 193080 bytes .../profile-executor-selection.webp | Bin 0 -> 9104 bytes .../scheduled-workflow-automation.mp4 | Bin 0 -> 480537 bytes .../scheduled-workflow-automation.webm | Bin 0 -> 424609 bytes .../scheduled-workflow-automation.webp | Bin 0 -> 13312 bytes .../media/feature-guides/task-create.mp4 | Bin 0 -> 210157 bytes .../media/feature-guides/task-create.webm | Bin 0 -> 177911 bytes .../media/feature-guides/task-create.webp | Bin 0 -> 8824 bytes .../media/feature-guides/task-start-agent.mp4 | Bin 0 -> 196034 bytes .../feature-guides/task-start-agent.webm | Bin 0 -> 188571 bytes .../feature-guides/task-start-agent.webp | Bin 0 -> 10634 bytes docs/public/meta.json | 20 +- docs/public/operations.md | 267 +++-- docs/public/release-process.md | 101 +- docs/public/remote-cloud-environment.md | 259 ++++- docs/public/run-as-a-service.md | 359 +++---- docs/public/security.md | 123 +++ docs/public/sessions-and-review.md | 227 ++++- docs/public/tasks-and-workflows.md | 259 +++-- docs/public/testing.md | 145 +-- docs/public/use-kandev.md | 158 ++- docs/public/web-development.md | 100 +- docs/public/websocket-api.md | 936 ++++++++++++------ docs/public/windows-support.md | 280 ++++-- docs/public/workflow-import-export.md | 443 ++++----- docs/public/workflow-sync.md | 206 ++-- docs/public/workflow-tips.md | 186 ++-- scripts/validate-public-docs.mjs | 826 +++++++++++++++- scripts/validate-public-docs.test.mjs | 528 +++++++++- 66 files changed, 7322 insertions(+), 3096 deletions(-) create mode 100644 docs/public/coverage.json create mode 100644 docs/public/media/feature-guides/NOTES.txt create mode 100644 docs/public/media/feature-guides/code-walkthrough.mp4 create mode 100644 docs/public/media/feature-guides/code-walkthrough.webm create mode 100644 docs/public/media/feature-guides/code-walkthrough.webp create mode 100644 docs/public/media/feature-guides/diff-line-feedback.mp4 create mode 100644 docs/public/media/feature-guides/diff-line-feedback.webm create mode 100644 docs/public/media/feature-guides/diff-line-feedback.webp create mode 100644 docs/public/media/feature-guides/manifest.json create mode 100644 docs/public/media/feature-guides/parallel-named-sessions.mp4 create mode 100644 docs/public/media/feature-guides/parallel-named-sessions.webm create mode 100644 docs/public/media/feature-guides/parallel-named-sessions.webp create mode 100644 docs/public/media/feature-guides/plan-review-implement.mp4 create mode 100644 docs/public/media/feature-guides/plan-review-implement.webm create mode 100644 docs/public/media/feature-guides/plan-review-implement.webp create mode 100644 docs/public/media/feature-guides/profile-executor-selection.mp4 create mode 100644 docs/public/media/feature-guides/profile-executor-selection.webm create mode 100644 docs/public/media/feature-guides/profile-executor-selection.webp create mode 100644 docs/public/media/feature-guides/scheduled-workflow-automation.mp4 create mode 100644 docs/public/media/feature-guides/scheduled-workflow-automation.webm create mode 100644 docs/public/media/feature-guides/scheduled-workflow-automation.webp create mode 100644 docs/public/media/feature-guides/task-create.mp4 create mode 100644 docs/public/media/feature-guides/task-create.webm create mode 100644 docs/public/media/feature-guides/task-create.webp create mode 100644 docs/public/media/feature-guides/task-start-agent.mp4 create mode 100644 docs/public/media/feature-guides/task-start-agent.webm create mode 100644 docs/public/media/feature-guides/task-start-agent.webp create mode 100644 docs/public/security.md diff --git a/apps/web/e2e/README.md b/apps/web/e2e/README.md index cff842153d..4a79e78ce6 100644 --- a/apps/web/e2e/README.md +++ b/apps/web/e2e/README.md @@ -15,7 +15,20 @@ Playwright-based end-to-end tests. Each Playwright worker spawns its own real Go ## Playwright projects -The suite is split into three projects. Pick one with `--project=`. +The suite is split into four projects. Pick one with `--project=`. + +### `routing` + +Runs `office-routing-*.spec.ts` in an isolated desktop worker. Those specs restart +their backend with provider overrides that apply only to the restart/spec that +supplies them; the next restart rebuilds the environment from its clean baseline. +Keeping these specs separate also keeps their routing-specific provider and agent +fixtures away from tests that count agents or assert the active-agent label. Run +it directly with: + +```sh +pnpm e2e --project=routing +``` ### `chromium` (default) @@ -71,6 +84,7 @@ This project used to be named `docker`. It was renamed to `containers` once SSH | `pnpm e2e:headed` | Run headless project but with a visible browser. | | `pnpm e2e --project=containers` | Run container-backed tests (needs Docker). | | `pnpm e2e --project=mobile-chrome` | Run mobile responsive tests. | +| `pnpm e2e --project=routing` | Run provider-mutating Office routing tests. | | `E2E_DEBUG=1 pnpm e2e` | Surface Docker build output + extra logging. | Common flags: `--shard=1/4`, `-g "fragment of test name"`, `--repeat-each=3` (flake hunting). diff --git a/docs/public/README.md b/docs/public/README.md index e6ae4916ba..e693cefe6d 100644 --- a/docs/public/README.md +++ b/docs/public/README.md @@ -1,34 +1,27 @@ # Contributing to the Public Docs -Files in this directory are the source for [kandev.ai/docs](https://kandev.ai/docs). A merged change under `docs/public/**` triggers a Cloudflare Pages rebuild automatically. +`docs/public/` is the source for [kandev.ai/docs](https://kandev.ai/docs). Pull requests validate this tree. After a push to `main`, `.github/workflows/notify-docs.yml` validates it again and, when the deploy hook is configured, calls Landing; a validation failure prevents publication. -The docs have two audiences: +The published docs have two audiences: -- **Use Kandev** explains product tasks, configuration, security implications, limitations, and operations. -- **Contribute to Kandev** explains repository structure, code boundaries, testing, extension points, releases, and docs maintenance. +- **Use Kandev** covers product tasks, configuration, limits, security, and operations. +- **Contribute to Kandev** covers code ownership, development, testing, extension points, releases, and docs maintenance. -Keep a page focused on one audience. Link across the boundary when a user-facing explanation needs contributor detail, but do not mix internal implementation notes into a task guide. +Keep implementation detail in contributor pages. A user guide should explain the supported behavior and link here when internal context is useful. -## Update an Existing Page +## Update an existing page -Edit the relevant Markdown file directly. Verify commands, configuration keys, default values, API names, labels, and screenshots against the implementation in the same pull request. +Edit the Markdown source and verify every command, setting, label, default, platform claim, and screenshot against current source or tests. Preserve the filename when possible: it is the public slug. -Use relative links between published pages, for example `[CLI](cli.md)`. The website build rewrites those links to `/docs/...` routes. Links to repository files outside `docs/public` are rewritten to their GitHub source URLs. +Use source-relative links such as `[CLI](cli.md)`. Publication rewrites links between public pages to `/docs/...` and links to repository files outside `docs/public/` to GitHub. Do not use site-root links or local machine paths. -When behavior is conditional, say whether it is: +State whether behavior is default, optional, platform-dependent, experimental, feature-flagged, internal, or still in progress. An ADR, spec, hidden route, or source package is not evidence that a feature is generally available. -- supported by default; -- optional and dependent on an executor, integration, platform, or credential; -- experimental or feature-flagged; -- in progress or internal. +## Add a page -Do not describe an ADR, spec, plan, hidden route, or feature flag as generally available without confirming its runtime and UI path. - -## Add a Page - -1. Decide whether the page belongs under **Use Kandev** or **Contribute to Kandev**. Search existing pages first; prefer extending one task guide over creating overlapping reference pages. -2. Create a lowercase, kebab-case Markdown file in this directory, such as `custom-executors.md`. Its filename becomes its stable route: `/docs/custom-executors`. -3. Start the file with non-empty `title` and `description` frontmatter, followed by one level-one heading: +1. Search existing pages and choose **Use Kandev** or **Contribute to Kandev**. +2. Create a lowercase, kebab-case Markdown file in `docs/public/`. The filename becomes the stable route; `custom-executors.md` publishes as `/docs/custom-executors`. +3. Add non-empty `title` and `description` frontmatter, then one level-one heading: ```markdown --- @@ -37,50 +30,78 @@ Do not describe an ADR, spec, plan, hidden route, or feature flag as generally a --- # Custom Executors - - Page content starts here. ``` -4. Add the filename without `.md` to `meta.json` exactly once, below the correct audience heading. Its position controls sidebar order. Keep routes flat unless a concrete publication constraint requires a directory. -5. Link the new page from `index.md` and from closely related guides. Use relative Markdown links and repository-owned assets. -6. Include prerequisites, a concrete workflow, exact configuration, limitations or failure modes, and related pages where those sections apply. +4. Add the slug, without `.md`, exactly once to `docs/public/meta.json` under the correct audience heading. Its position controls sidebar order. `README.md` is the maintenance guide and is not a published page. +5. Add the page to at least one area in `docs/public/coverage.json`, with current implementation and test evidence. +6. Link the page from the relevant overview and neighboring guides. +7. Include prerequisites, a concrete workflow, exact configuration, failure modes or limits, and related pages where applicable. -Pages omitted from `meta.json`, duplicate entries, unknown entries, broken local Markdown links, and missing frontmatter fail validation. +The validator rejects missing frontmatter, pages absent from navigation or coverage, duplicate or unknown navigation entries, broken local files or heading fragments, missing assets, and site-root links. -## Assets and Diagrams +## Maintain feature coverage -- Keep user-facing screenshots under `docs/screenshots/` and reference them relatively. -- Do not link temporary capture output, local paths, or third-party hotlinked images. -- Mermaid code fences render on the website. Keep diagrams readable at docs-column width, use stable node labels, and provide surrounding prose that carries the same essential meaning. -- Prefer commands and text examples over screenshots when the UI changes frequently. +`docs/public/coverage.json` is the source-backed map from shipped product areas to their user, operator, contributor, or integrator documentation. Every area cites concrete source and test files. The validator also discovers every static Settings route and registered Kandev MCP tool, then requires each one to have exactly one coverage owner or an explicit exclusion with a reason. -## Validate +When behavior changes: -Run the focused dependency-free docs checks from the Kandev repository root: +1. Update the owning page and its support boundary. +2. Update the area's source or test evidence when ownership moved. +3. Assign a new Settings route or MCP tool to the appropriate area. +4. Add a new area only when the behavior has a distinct audience, stability, and documentation owner. + +Do not use the inventory to claim that a route, package, ADR, test harness, or feature flag is generally available. The page must still explain whether the user-facing behavior is stable, dependency-bound, limited, experimental, or unavailable. + +## Assets and diagrams + +- Store product screenshots under `docs/screenshots/` and reference them relatively. +- Do not hotlink mutable third-party images or commit temporary capture output. +- Mermaid diagrams are supported. Keep them readable at docs-column width and explain the essential result in prose. +- Prefer text and commands when the UI changes frequently. + +Use a short focused video only when motion or interaction order communicates something that prose and a still image cannot. Store reviewed deliverables under `docs/public/media/feature-guides/` as a WebM, an H.264 MP4 fallback, and a WebP poster. Keep raw captures, browser profiles, and temporary encoder output outside the repository. + +Embed the local sources with the publication component: + +```mdx + +``` + +Videos must show current isolated test data, avoid real credentials and production repositories, use native controls, and remain understandable from the poster and caption. Prefer a compact view that follows the relevant cursor or touch target over a wide application recording. Keep `manifest.json` and `NOTES.txt` beside the reviewed triplets. Source validation checks accepted QA status, the 960x600/25fps/no-audio delivery contract, page and section ownership, complete embeds, byte counts, SHA-256 hashes, and orphaned files. Do not add Markdown files under `docs/public/media/` because public Markdown is treated as a page. + +## Validate the source + +Run the dependency-free checks from the Kandev repository root: ```bash node --test scripts/validate-public-docs.test.mjs node scripts/validate-public-docs.mjs ``` -The broader repository script suite requires the pinned repository toolchain and installed dependencies: +`make test-scripts` runs the validator's unit tests among other repository script tests; it does not replace the live validation command above. -```bash -make test-scripts -``` +## Verify the Landing publication build -To build the complete docs site, clone `kdlbs/landing` beside this repository and run with the toolchain expected by Landing: +With `kdlbs/landing` checked out beside `kandev`, use Landing's documented Node 24.12.0 and pnpm 10.29.3 prerequisites and build the same root-plus-docs artifact deployed to Cloudflare Pages: ```bash cd ../landing pnpm install --frozen-lockfile -KANDEV_DOCS_SOURCE_PATH=../kandev/docs pnpm --filter @kandev/docs build +KANDEV_DOCS_SOURCE_PATH="$(cd ../kandev && pwd)/docs" pnpm build:pages ``` -Before opening a pull request, also check: +`KANDEV_DOCS_SOURCE_PATH` makes Landing fetch the local public pages instead of GitHub. Pass an absolute path because the docs package runs its prebuild from `landing/apps/docs`. The build copies only pages named by `meta.json`, rewrites links, and builds both the landing site and docs route. Do not commit generated Landing content, `.next`, or `out` directories. -- every new or renamed page appears once in `meta.json`; -- links resolve from the source file, not from the repository root; -- user claims are supported by current code or tests; -- feature status and platform limitations are explicit; -- generated files under the Landing docs app were not committed. +Before review, confirm: + +- each new or renamed page appears exactly once in `meta.json`; +- links resolve relative to their source file; +- claims are supported by current code, tests, or workflows; +- conditional support and platform limits are explicit; +- source validation and, for publication-sensitive changes, the Landing build pass. diff --git a/docs/public/add-agent-cli.md b/docs/public/add-agent-cli.md index 6f46a0dd18..9efe81f88c 100644 --- a/docs/public/add-agent-cli.md +++ b/docs/public/add-agent-cli.md @@ -1,239 +1,192 @@ --- title: "Add an Agent CLI" -description: "Add a new ACP adapter or agent CLI integration to Kandev." +description: "Register a local TUI agent or ship a tested built-in passthrough or ACP agent integration." --- -# Adding a New Agent CLI Integration +# Add an Agent CLI -This guide explains how to add support for a new coding agent CLI to Kandev. +Choose the smallest integration that matches the CLI: -Agents are defined entirely in Go. The backend discovers them through a registry, and the frontend picks them up automatically via the API - **no frontend changes are needed**. +| Need | Path | +|---|---| +| Use a local interactive CLI without changing Kandev source | Add a custom TUI agent in Settings | +| Ship a passthrough-only CLI as a built-in | Add a declarative `TUIAgent` and registry entry | +| Show structured chat, tool calls, modes, and resume | Implement a built-in ACP agent | -For a clean reference implementation, see [`gemini.go`](../../apps/backend/internal/agent/agents/gemini.go) (ACP protocol). For a CLI-only TUI agent, see the `TUIAgent` declarative type in [`tui_agent.go`](../../apps/backend/internal/agent/agents/tui_agent.go). +ACP, REST, and MCP are different boundaries. ACP is the only structured agent protocol accepted by the current agentctl adapter factory. REST/WebSocket control agentctl and Kandev. MCP supplies tools to an agent; it is not a runtime adapter. ---- +## Register a local TUI agent -## CLI-Only Agent (Passthrough Only) - Recommended for TUI tools +Open **Settings → Agents**, choose **Add TUI Agent**, and provide: -If an agent has a CLI but no structured protocol (no JSON-RPC, no streaming API), use the **`TUIAgent` declarative type**. The agent runs in a terminal session and input/output passes through a WebSocket - users interact with the full agent TUI directly. +- a display name; +- an optional model/profile label; +- a command available on the executor's `PATH`. -This is the simplest path. A complete agent definition is ~20 lines. +Use `{{model}}` in the command to insert the optional model value. Kandev derives a stable slug from the display name, persists the definition, registers it at startup, and creates a default passthrough profile. -### Step 1: Create the agent file +This path is terminal passthrough only. It does not provide structured chat messages, tool-call visibility, ACP capability probing, or Kandev MCP injection. The command is split into an executable and arguments on whitespace; it is not evaluated by a shell, and shell quoting/expansion is not supported. -Create `apps/backend/internal/agent/agents/{agent_id}.go`: +Use a source integration when the agent must ship for every installation, needs logos/discovery/auth/permissions, needs safe structured argument building, or supports ACP. + +## Ship a built-in passthrough agent + +Definitions live in `apps/backend/internal/agent/agents/`. The declarative `TUIAgent` implements `agents.Agent` plus `agents.PassthroughAgent`, but Kandev classifies it as passthrough-only. + +Create `apps/backend/internal/agent/agents/my_agent.go`: ```go package agents -func NewClaude() *TUIAgent { +func NewMyAgent() *TUIAgent { return NewTUIAgent(TUIAgentConfig{ - AgentID: "claude", - AgentName: "Claude", - Command: "claude", - Desc: "Claude Code - an agentic coding tool by Anthropic.", - - // Optional: let users select a model in profile settings. - // The {model} placeholder is replaced with the user's chosen model at launch. - // Omit this if the CLI doesn't accept a model flag. + AgentID: "my-agent", + AgentName: "My Agent", + Command: "my-agent", + Desc: "Run My Agent in terminal passthrough mode.", ModelFlag: NewParam("--model", "{model}"), + WaitForTerm: true, }) } ``` -Logos are optional - the UI shows a blank placeholder when none is provided. To add them, embed SVGs and pass `LogoLight` / `LogoDark`: +Only set `ModelFlag` when the CLI accepts that flag. `WaitForTerm` is useful for a full-screen TUI that needs an initial terminal resize before process start. -```go -package agents +The exact `TUIAgentConfig` contract is: -import _ "embed" +| Fields | Required | Meaning | +|---|---|---| +| `AgentID`, `AgentName`, `Command`, `Desc` | Yes | Stable lowercase ID, names, executable, and description | +| `Display`, `Order` | No | Alternate display name and list order; defaults are name and 99 | +| `LogoLight`, `LogoDark` | No | Embedded SVG bytes; the UI otherwise shows a placeholder | +| `IdleTimeout`, `BufferMax`, `WaitForTerm` | No | Passthrough buffering and terminal-start behavior | +| `ModelFlag`, `CommandArgs` | No | Typed model parameter and fixed argument tokens | +| `DetectOpts` | No | Installation detection; defaults to `WithCommand(Command)` | +| `Protocol` | No | Runtime metadata, default ACP; it does not turn `TUIAgent` into structured chat | -//go:embed logos/claude_light.svg -var claudeLogoLight []byte +Do not shell-concatenate user values. `CommandArgs` is a token slice and `ModelFlag` uses the typed parameter builder. Detection should inspect known commands/files without executing untrusted content. -//go:embed logos/claude_dark.svg -var claudeLogoDark []byte +`TUIAgentConfig` does not expose a passthrough MCP strategy. If this CLI needs Kandev/profile MCP servers, implement a full agent with an explicit `PassthroughConfig.MCPStrategy`. -func NewClaude() *TUIAgent { - return NewTUIAgent(TUIAgentConfig{ - AgentID: "claude", - AgentName: "Claude", - Command: "claude", - Desc: "Claude Code - an agentic coding tool by Anthropic.", - ModelFlag: NewParam("--model", "{model}"), - LogoLight: claudeLogoLight, - LogoDark: claudeLogoDark, - }) -} -``` +## Ship a structured ACP integration -`TUIAgentConfig` fields (all optional fields have sensible defaults): +Use [`gemini.go`](../../apps/backend/internal/agent/agents/gemini.go) as a current ACP example and [`agent.go`](../../apps/backend/internal/agent/agents/agent.go) as the contract. A full integration implements `agents.Agent`: -| Field | Required | Description | -|-------|----------|-------------| -| `AgentID` | Yes | Unique lowercase identifier (e.g. `"claude"`) | -| `AgentName` | Yes | Display name (e.g. `"Claude"`) | -| `Command` | Yes | Binary name to run (e.g. `"claude"`) | -| `Desc` | Yes | Description shown in the UI | -| `ModelFlag` | No | Model CLI flag, e.g. `NewParam("--model", "{model}")`. Lets users pick a model in profile settings. Omit if the CLI doesn't support model selection. | -| `CommandArgs` | No | Extra args appended after `Command` (e.g. `[]string{"--debug"}`) | -| `WaitForTerm` | No | When `true`, delays process start until the terminal WebSocket connects and sends its first resize event. Use this for TUI apps that need accurate terminal dimensions on startup (e.g. ncurses-based UIs). Most agents don't need this. Defaults to `false`. | -| `LogoLight` / `LogoDark` | No | Embedded SVG bytes for light/dark themes. The UI shows a blank placeholder if omitted. | -| `DetectOpts` | No | Custom detection options. Defaults to `WithCommand(Command)` which checks if the binary is in `$PATH`. | +- identity: `ID`, `Name`, `DisplayName`, `Description`, `Enabled`, `DisplayOrder`; +- assets and discovery: `Logo`, `IsInstalled`; +- execution: `BuildCommand`, `Runtime`; +- policy: `PermissionSettings`, `BillingType`, `RemoteAuth`, `InstallScript`. -### Step 2: Register the agent +Optional interfaces add specific capabilities: -Add your agent to `LoadDefaults()` in [`apps/backend/internal/agent/registry/registry.go`](../../apps/backend/internal/agent/registry/registry.go): +| Interface | Capability | +|---|---| +| `InferenceAgent` | One-shot inference through the host utility | +| `PassthroughAgent` | Optional direct terminal mode | +| `NativeBinaryAgent` | Prefer an installed native binary over a package launch | +| `LoginAgent` | Interactive PTY-backed authentication | -```go -func (r *Registry) LoadDefaults() { - all := []agents.Agent{ - // ... existing agents ... - agents.NewMyAgent(), // add here - } - // ... -} -``` +### Build the runtime command -### Step 3: Build and test +`BuildCommand(CommandOptions)` must return a tokenized command that launches an ACP-speaking process or bridge. Use `Cmd`, `Command`, and `Param` builders for model, session, permission, prompt, and configured CLI flags. Never interpolate them into a shell string. -```bash -make -C apps/backend build && make -C apps/backend test && make -C apps/backend lint -``` +`Runtime()` describes working directory, environment, resource limits, required/stripped variables, mounts, session recovery, and the command used outside the host. Set `RuntimeConfig.Protocol` to `agent.ProtocolACP`. The factory in [`internal/agentctl/server/adapter/factory.go`](../../apps/backend/internal/agentctl/server/adapter/factory.go) rejects other structured protocols. -Note: passthrough-only agents provide terminal interaction only. There are no structured chat messages, tool call visibility, or progress tracking in the chat UI. +If the upstream CLI has no ACP server, add or reuse a bridge that speaks ACP, or keep the integration passthrough-only. Adding a protocol constant alone does not create an adapter. ---- +### Detect installation and authentication -## Adding a New Agent (Full Integration) +`IsInstalled` returns a `DiscoveryResult` with availability, matched path, MCP support/paths, installation paths, and resume/shell/workspace capabilities. Use `Detect` with bounded `DetectOption` checks for known commands or files. -For agents with a structured protocol (JSON-RPC, streaming JSON, etc.), implement the full [`Agent`](../../apps/backend/internal/agent/agents/agent.go) interface. +Declare remote credential methods through `RemoteAuth`, required environment through `RuntimeConfig.RequiredEnv`, and variables that must never reach the child through `StripEnv`. `InstallScript` runs in remote environments; keep it deterministic, pinned where possible, and free of embedded secrets. -### Step 1: Create the agent definition file +Permission settings must map to actual CLI or agentctl behavior. Test supervised, autonomous, and plan-shaped policies when supported. Do not advertise a permission toggle that only changes the UI. -Create `apps/backend/internal/agent/agents/{agent_id}.go`. +### Discover models and modes -This file implements the [`Agent`](../../apps/backend/internal/agent/agents/agent.go) interface. See the interface definition in `agent.go` for the full contract. +Do not maintain a static model list in the agent definition. `internal/agent/hostutility/` probes the ACP server and caches models, modes, and capabilities learned during `session/new`. Test empty, changed, and failed capability responses. -Embed `StandardPassthrough` to get CLI passthrough support for free: +### Configure MCP -```go -type MyAgent struct { - StandardPassthrough -} +In structured mode, resolved MCP servers normally travel in ACP `session/new`. Set `RuntimeConfig.ProjectMCPStrategy` only when an adapter cannot forward those servers and the CLI requires a project file. -func NewMyAgent() *MyAgent { - return &MyAgent{ - StandardPassthrough: StandardPassthrough{ - PermSettings: myAgentPermSettings, - Cfg: PassthroughConfig{ - Supported: true, - Label: "CLI Passthrough", - Description: "Show terminal directly instead of chat interface", - PassthroughCmd: NewCommand("myagent"), - ModelFlag: NewParam("--model", "{model}"), - PromptFlag: NewParam("--prompt", "{prompt}"), - IdleTimeout: 3 * time.Second, - BufferMaxBytes: DefaultBufferMaxBytes, - }, - }, - } -} -``` +For optional passthrough mode, embed or implement `PassthroughAgent` and set `PassthroughConfig.MCPStrategy`. Existing strategies in [`internal/agent/mcpconfig/passthrough.go`](../../apps/backend/internal/agent/mcpconfig/passthrough.go) cover Claude, Codex, Cursor, Pi, and OpenCode configuration shapes. -Key implementation notes: +An MCP strategy must: -- **`BuildCommand()`** returns the CLI or bridge command for structured protocol mode. It must start an ACP-speaking process; include the CLI's ACP flag when one is required. -- **`Runtime()`** returns configuration used when running the agent in Docker or as a standalone process. Full integrations set `Protocol: agent.ProtocolACP`; the adapter factory only accepts ACP. -- **`IsInstalled()`** uses the `Detect()` helper with `WithFileExists()` to check for known installation paths. Set `SupportsMCP` and `MCPConfigPaths` on the result. -- **MCP servers in passthrough** — set `MCPStrategy` on `PassthroughConfig` so the agent receives kandev's own tools server (plus any profile-configured MCP servers) when run in passthrough mode. Omitting it means **no MCP servers are injected in passthrough**. Pick the strategy that matches how your CLI loads MCP servers: `mcpconfig.ClaudeStrategy{}` (temp JSON file + `--mcp-config` flag), `mcpconfig.CodexStrategy{}` (repeated `-c mcp_servers.…` overrides), `mcpconfig.CursorStrategy{}` (project-local `.cursor/mcp.json`), `mcpconfig.PiStrategy{}` (project-local `.pi/mcp.json`), or `mcpconfig.OpenCodeStrategy{}` (temp JSON + `OPENCODE_CONFIG` env var). To add a strategy for a CLI with a different mechanism, implement `mcpconfig.PassthroughMCPStrategy`. See ADR 0014, ADR 0020, and `internal/agent/mcpconfig/passthrough.go`. -- **MCP project files in protocol mode** — set `RuntimeConfig.ProjectMCPStrategy` only when the structured protocol adapter does not pass ACP `session/new` MCP servers through to the underlying CLI. Pi uses this to write `.pi/mcp.json` before the subprocess starts. -- **Models and modes** are discovered from the ACP server during host-utility probing. Agent definitions do not maintain a separate static model list. -- Use `Cmd()` builder for constructing commands fluently: `Cmd("myagent", "--flag").Model(...).Settings(...).Build()`. +- materialize only resolved, scoped servers; +- avoid modifying a user's global CLI configuration; +- pass arguments/environment as tokens; +- clean temporary or project files on normal and failed exits; +- avoid logging tokens, headers, or generated configuration. -### Step 2: Add logos (optional) +### Add assets and registration -Logos are optional - the UI shows a blank placeholder when none is provided. To add them, place SVGs in: +Optional logos live at: ```text -apps/backend/internal/agent/agents/logos/{agent_id}_light.svg -apps/backend/internal/agent/agents/logos/{agent_id}_dark.svg +apps/backend/internal/agent/agents/logos/my-agent_light.svg +apps/backend/internal/agent/agents/logos/my-agent_dark.svg ``` -Embed them in your agent file: +Embed them with `//go:embed`. Keep the `AgentID` stable and independent from the display name; persisted profiles and API data refer to it. + +Register every shipped agent in `LoadDefaults()` in [`internal/agent/registry/registry.go`](../../apps/backend/internal/agent/registry/registry.go): ```go -//go:embed logos/myagent_light.svg -var myagentLogoLight []byte - -//go:embed logos/myagent_dark.svg -var myagentLogoDark []byte -``` - -### Step 3: Register the agent - -Add your agent to `LoadDefaults()` in [`apps/backend/internal/agent/registry/registry.go`](../../apps/backend/internal/agent/registry/registry.go): - -```go -func (r *Registry) LoadDefaults() { - all := []agents.Agent{ - // ... existing agents ... - agents.NewMyAgent(), // add here - } - // ... +all := []agents.Agent{ + // Existing agents... + agents.NewMyAgent(), } ``` -That's it for registration. The frontend discovers agents through the API automatically. +Ordinary identity, availability, profiles, models, and modes reach the UI through the registry API. A new capability, setting, or wire field can still require web types/components and mandatory Playwright coverage. -### Step 4: Understand ACP, REST, and MCP +## Test the integration -These names describe separate boundaries; they are not interchangeable agent runtime options: - -| Boundary | Role in Kandev | -|----------|----------------| -| ACP | The only structured agent runtime protocol accepted by the agentctl adapter factory. Set `Runtime().Protocol` to `agent.ProtocolACP`. ACP adapters can wrap CLIs whose internals use other transports. | -| REST | Local HTTP control APIs exposed by agentctl for operations such as Git, processes, and workspace state. REST is not selected as an agent `Protocol`. | -| MCP | The tool protocol used to give agents Kandev and profile-configured tools. MCP servers are passed through ACP `session/new` when supported or materialized by `MCPStrategy` / `ProjectMCPStrategy`. MCP is not an agent runtime adapter. | - -To integrate a structured agent, provide or reuse an ACP server/bridge and configure `agent.ProtocolACP`. The adapter factory in [`factory.go`](../../apps/backend/internal/agentctl/server/adapter/factory.go) rejects non-ACP runtime protocols. If a CLI has no ACP server or bridge, integrate it as a passthrough-only `TUIAgent` instead of adding a protocol constant. - -### Step 5: Build and test +Start with package tests: ```bash -make -C apps/backend build && make -C apps/backend test && make -C apps/backend lint +cd apps/backend +CGO_ENABLED=1 go test -tags fts5 ./internal/agent/agents ./internal/agent/registry +CGO_ENABLED=1 go test -tags fts5 ./internal/agent/runtime/lifecycle \ + ./internal/agentctl/server/adapter/... +cd ../.. +make lint-backend ``` -Start the dev server and verify: -- The agent appears in the agent selector in the UI -- Agent execution works (start a task, send a prompt) -- Passthrough mode works (toggle to CLI passthrough in the session) -- Model listing works +Cover: ---- +- unique ID and deterministic registry order; +- installed, missing, and ambiguous discovery; +- every model/session/permission argument as exact tokens; +- required and stripped environment variables; +- ACP initialize, `session/new`, prompt, cancellation, resume, and shutdown; +- capability probe changes and failures; +- passthrough terminal sizing, buffering, cleanup, and MCP materialization when offered; +- local/worktree plus each declared container or remote runtime; +- missing login, missing credential, and interrupted installation paths. -## Updating an Existing Agent +Then run `make test-backend` and start `make dev`. Verify discovery, profile selection, start/prompt/resume, model/mode refresh, permissions, and passthrough if supported. -| Change | Where | -|--------|-------| -| Update CLI version | `BuildCommand()` and `Runtime().Cmd` in the agent file | -| Change model or mode discovery | Update the ACP server/bridge capability response; Kandev learns these during host-utility probing | -| Update permissions | `PermissionSettings()` variable and `PassthroughConfig` | -| Update Docker image | `Runtime()` - change `Image`/`Tag` fields | -| Change structured runtime | Keep `Runtime().Protocol` on ACP and update the ACP server/bridge command | +The real adapter suite is separate: ---- +```bash +make -C apps/backend test-e2e +``` -## File Reference +It may launch installed third-party agents, require login, and incur paid usage. Run only after reading the target tests. Root `make test-e2e` is the browser Playwright suite, not this real-agent suite. -| File | When to modify | -|------|---------------| -| `apps/backend/internal/agent/agents/{agent_id}.go` | Always - agent definition | -| `apps/backend/internal/agent/agents/logos/{agent_id}_{light,dark}.svg` | Optional - agent logos (UI shows blank placeholder if omitted) | -| `apps/backend/internal/agent/registry/registry.go` | Always - register in `LoadDefaults()` | -| `apps/backend/internal/agent/agents/tui_agent.go` | Never - provides `TUIAgent` declarative type (read-only reference) | -| `apps/backend/pkg/agent/protocol.go` | Read-only reference for the ACP runtime protocol value | -| `apps/backend/internal/agentctl/server/adapter/factory.go` | Read-only reference for ACP adapter construction | -| `apps/backend/internal/agentctl/server/adapter/transport/acp/adapter.go` | ACP transport implementation and bridge behavior | -| `apps/backend/internal/agent/agents/passthrough.go` | Never - provides `StandardPassthrough` (read-only reference) | -| `apps/backend/internal/agent/agents/agent.go` | Never - defines the `Agent` interface (read-only reference) | +## Update an existing agent + +Trace the behavior instead of assuming one file owns it: + +| Change | Inspect | +|---|---| +| Identity, command, image, auth, permissions, resume | Agent definition under `internal/agent/agents/` | +| Models, modes, structured capabilities | ACP server/bridge and host-utility probe | +| Passthrough MCP format | Agent `PassthroughConfig` and `internal/agent/mcpconfig/` | +| Structured MCP gap | `RuntimeConfig.ProjectMCPStrategy` and adapter behavior | +| Registry/default availability | `internal/agent/registry/` and settings reconciler | +| Packaged native binary | Runtime bundle matrices, npm runtime packages, desktop resources, release tests | +| New user-visible field | Backend DTO/API, web types/UI, Playwright, and public docs | diff --git a/docs/public/agents-and-profiles.md b/docs/public/agents-and-profiles.md index c1a1d50bac..d3bb6fc50c 100644 --- a/docs/public/agents-and-profiles.md +++ b/docs/public/agents-and-profiles.md @@ -1,78 +1,140 @@ --- title: "Agents and Profiles" -description: "Configure agent CLIs, models, modes, flags, environment variables, passthrough, MCP, and permission behavior." +description: "Install agent CLIs and create profiles for models, modes, flags, secrets, permissions, passthrough, and MCP." --- # Agents and Profiles -An **agent** identifies a CLI integration. An **agent profile** is the reusable launch policy for that integration. Profiles let one installed agent serve different repositories or trust levels without repeatedly entering models, flags, and environment configuration. +An **agent** is Kandev's integration with a coding-agent CLI. A **profile** is a reusable launch configuration for that agent. Profiles let the same installed CLI run with different models, modes, credentials, flags, or trust levels. -## Built-in and custom agents +Agent authentication is separate from repository credentials and from credentials saved under an integration. Installing a CLI does not sign it in, and connecting GitHub does not let an agent call its model provider. -Kandev discovers supported ACP-native CLIs and adapter commands, including Claude Code, Codex, GitHub Copilot, Gemini CLI, Amp, Auggie, OpenCode, Cursor, Devin, Qwen, Factory Droid, iFlow, Kilocode, Pi, Kimi, Kiro, Qoder, Trae, Oh My Pi, and Grok. +## Install or detect an agent -Availability depends on the current platform, executable/install command, and the agent's own authentication. The list in **Settings > Agents** is authoritative for the running Kandev version. +Open **Settings > Agents** (`/settings/agents`). Kandev scans the host on which its backend runs, not the browser computer. -Custom TUI agents can run an arbitrary CLI command in a PTY even when they do not speak ACP. Configure the executable, arguments, display name, optional model/description, and whether Kandev waits for the terminal. TUI mode does not manufacture ACP capabilities that the CLI lacks. +The production registry currently shows Auggie, Claude, Codex, Copilot, Gemini, OpenCode, Amp, Qwen, iFlow (beta), Droid, Kilocode, Pi, Cursor, Kimi, Kiro, Qoder, Trae, `omp`, Devin, and Grok. An entry is usable only when its executable is supported on the current platform and available to the Kandev process. Development and E2E profiles can add mock agents that are not product integrations. -## Profile fields +1. Select **Rescan** after installing or updating a CLI. +2. If the card offers an install action, review the command before running it. Installation runs on the Kandev host. +3. If the card reports that login is required, open its login terminal or authenticate the CLI as the same operating-system user that runs Kandev. +4. Open the seeded default profile and review it before selecting the agent in a workflow step or session. Discovery creates one default profile the first time it provisions an agent. If you deliberately delete every profile, later rescans do not recreate one; create a replacement manually. -Agent profiles can configure: +The status shown on this page is authoritative for the current host. A CLI that works in your interactive shell can still be absent from Kandev when the service has a different `PATH`, home directory, or operating-system user. -| Field | Effect | +### Add a custom terminal agent + +Use **Settings > Agents > Add TUI Agent** for a CLI that Kandev does not register. Enter a display name, command, and optional model label. `{{model}}` in the command is replaced by the selected model value, then the entire command is split on whitespace with Go's `strings.Fields`. + +That parser is not a shell and is not quote-aware: quotes and backslashes do not preserve a path or model containing spaces as one argument. Custom TUI agents always use terminal passthrough. They do not gain ACP features such as structured permission prompts, model discovery, modes, or session configuration merely by being added. Test the exact resulting argument split before assigning it to work. + +## Create and configure a profile + +Select an agent, create a profile, then open **Settings > Agents > _Agent_ > _Profile_**. The page shows the resolved command preview and only the settings supported by that agent. + +| Setting | Runtime behavior | |---|---| -| Name | Human-readable profile selection label. | -| Model | ACP model identifier applied at session start when the agent advertises model selection. | -| Mode | Optional ACP session mode applied with `session/set_mode`. | -| Config options | Dynamic ACP session options applied with `session/set_config_option`. | -| CLI flags | Tokenized command arguments; only enabled entries reach the subprocess. | -| Environment variables | Plain values or references to named Kandev secrets injected into the agent process. | -| CLI passthrough | Opens the native TUI execution style instead of relying only on structured ACP chat. | -| Auto approve | Lets Kandev agentctl approve ACP permission requests automatically. | -| MCP policy | Controls which Kandev/external MCP servers are made available in the session. | +| Name | Label shown in workflow, session, and automation selectors. | +| Model | Requested through ACP when the agent supports model selection. Leaving it unset uses the agent's default where the form allows that. | +| Mode | Requested with ACP `session/set_mode`. The choices come from the installed agent. | +| Configuration options | Dynamic ACP values requested with `session/set_config_option`. | +| CLI flags | Enabled entries are tokenized and appended to the launch command. | +| Environment | Literal values or references to Kandev secrets, resolved when the process starts. | +| CLI passthrough | Uses the CLI's native terminal interface instead of a structured ACP conversation. | +| Auto-approve all permissions | Answers automatically: the first `allow_once`/`allow_always` option, otherwise the first option supplied by the agent; no options cancels. It is off by default. | +| MCP servers | Adds profile-specific external MCP servers when the agent supports MCP. | -Some fields appear only when the agent advertises the related capability. A profile copied from another machine can reference a model or mode that the locally installed agent does not provide; inspect discovery/status before using it. +Model, mode, command, and configuration choices are probed from the locally installed CLI and cached. Refresh the profile if an agent update changes them. Probe status can report **auth required**, **not installed**, **not configured**, or **failed**; a saved model name does not prove that the current provider account can use it. -## CLI flags +### Monitor capability and subscription status -Each flag entry stores a raw string, description, and enabled state. At launch, Kandev shell-tokenizes the raw string, so one entry such as `--add-dir /shared` becomes two arguments. +Use the profile refresh control after installing, authenticating, or upgrading an agent. A manual refresh updates both the advertised models, modes, and commands and the visible capability status, so an old failure banner does not remain authoritative after the local CLI recovers. -- Use separate profiles for materially different permission or workspace flags. -- Do not paste shell pipelines into a flag field; it is an argument list, not a shell startup script. -- Agent updates can add, rename, or remove flags. Recheck user-modified profiles after upgrading a CLI. -- `allow_indexing` remains a compatibility field for older Auggie profiles; current launch behavior is represented by CLI flags. +**Settings > Agents** shows **Subscription Usage** only when a supported host agent is signed in through a subscription plan. The current section covers Codex and Claude Code, reports the provider's plan and rate-limit windows, and can be refreshed on demand. It is an operational signal from the installed CLI, not a billing ledger or a guarantee that the next request will be accepted; provider availability, account policy, and concurrent usage still apply. -## Environment and secrets +### CLI flags -Environment variables are visible to the agent subprocess and anything it launches. Prefer a named secret reference for tokens. Secret values are still available at runtime to that process; the reference avoids duplicating plaintext in profile configuration, not exposure to the selected agent. +Each flag entry has a raw value, description, enabled state, and an agent-specific default where applicable. Only enabled entries reach the process. Kandev tokenizes each raw value as command arguments: `--add-dir /shared` becomes two arguments. -Use the narrowest credential that completes the job. Separate read-only review profiles from write-capable implementation/release profiles where practical. +The field is not a shell script. Pipes, redirects, variable expansion, and command substitution do not run as shell syntax. Empty or malformed quoting is rejected. Keep separate profiles for materially different permission or workspace flags, and recheck customized flags after upgrading the CLI. -## Permission requests and auto approval +Some older profiles contain compatibility fields such as Auggie's `allow_indexing`; current launch behavior is represented by the active profile settings and flags. -ACP agents can ask the client to approve tool use. With **Auto approve** disabled, Kandev displays permission requests in the session so a user can choose an offered response. With it enabled, agentctl automatically selects an allowed response. +## Environment variables and secrets -Auto approval is high impact. It can permit shell commands, file changes, network calls, or other tools exposed by the agent. Combine it only with an executor, credentials, and repository scope that are safe for unattended work. +Create reusable secrets at **Settings > Secrets** (`/settings/general/secrets`), then select a secret reference in a profile environment entry. Secret names are 1–100 characters and values are 1–10,000 characters. Editing a secret with a blank value keeps the saved value. -Agent-specific flags that bypass permissions can be broader than ACP auto approval and may prevent Kandev from showing a request at all. Treat both as explicit security policy. +Kandev encrypts secret values at rest with AES-256-GCM. The encryption key is `/data/master.key` (by default `~/.kandev/data/master.key`) and is created with owner-only file permissions. `KANDEV_DATABASE_PATH` does not relocate this key. Protect and back it up with the Kandev database; losing it makes stored values unreadable. Anyone with access to the Secrets settings can reveal the plaintext. -## ACP versus passthrough +Profile environment rules are: -Structured ACP sessions provide capabilities such as model/mode selection, typed messages, permission requests, tool updates, todos, usage, and resume metadata when the agent supports them. +- at most 100 entries; +- key length at most 256 characters and value length at most 8,192 characters; +- keys cannot contain `=` or a NUL character, and values cannot contain NUL; +- duplicate keys are rejected; +- `TASK_DESCRIPTION` and every `KANDEV_*` key are reserved; +- an entry must use either a literal value or a secret reference, never both. -Passthrough preserves the agent's terminal UX. It is useful for CLIs whose native interface has features not exposed through ACP. Kandev can inject task MCP configuration for supported passthrough integrations, but the agent controls how it loads and uses that configuration. +Secret references are resolved at process launch. A deleted, missing, or unreadable secret causes that environment entry to be omitted; Kandev does not fall back to an old value. Empty resolved values are also omitted. Profile values fill missing environment keys but do not overwrite environment supplied by the executor or Kandev runtime. -## Usage and billing hints +Literal values remain in profile configuration. A secret reference avoids copying a token there, but the selected agent and its child processes still receive the plaintext at runtime. Use narrowly scoped credentials, and keep read-only review profiles separate from profiles allowed to publish, merge, deploy, or administer external systems. -Kandev can show subscription utilization for supported CLIs when local credential files expose that data. Profiles using API keys are represented separately. This is operational visibility, not a universal billing ledger; provider reports, reset windows, and availability vary. +## Permissions and unattended work + +In a structured ACP session, the agent can present a permission request and its available responses. With **Auto-approve all permissions** disabled, a person chooses a response in the session. With it enabled, the runtime selects the first allow-once or allow-always response without waiting. If the agent supplies no allow response, Kandev selects its first response even when that response is not approval; with no responses, it cancels. + +Auto approval can authorize shell commands, file changes, network calls, or any other capability exposed by that agent. Agent-specific flags that suppress permission prompts can be broader still. Use either only with a constrained executor, repository, environment, and credential set. + +Workspace automation selectors do not offer passthrough agent profiles or Local executor profiles. **Run**-mode automations also cannot wait for a permission response: an unanswered request is rejected and the run fails. Use **Task** mode when a person must approve agent actions, or use a profile whose safe work does not prompt. See [Automation and MCP](automation-and-mcp.md). + +## Structured ACP and terminal passthrough + +ACP sessions can expose typed messages, tool updates, permission requests, models, modes, dynamic configuration, todos, usage, and resume metadata. Each capability depends on the agent's actual ACP implementation. + +Passthrough preserves the CLI's native PTY interface. It is useful when the native terminal has features that ACP does not expose, but Kandev cannot manufacture structured capabilities that are absent. Custom TUI profiles are locked to passthrough. Profile-specific MCP injection also varies by CLI; verify the command preview and the MCP section before depending on it. + +## Add external MCP servers to a profile + +When an agent advertises MCP support, open its profile's **MCP** section. The editor accepts either a servers map or an object containing `mcpServers`. + +Supported server types are `stdio`, `http`, `sse`, and `streamable_http`. If `type` is absent, a `command` implies `stdio` and a `url` implies `http`. Connection mode can be: + +- `auto`: per-session for stdio and shared for a network transport; +- `per_session`: create a connection for each agent session; +- `shared`: reuse a network connection. Stdio cannot use shared mode. + +The built-in task-aware server is injected separately. A profile server named `kandev` is ignored so it cannot replace the task server. + +Executor policy can allow or deny transports or server names, rewrite URLs, and inject environment values. In the current launch wiring, profile MCP resolution starts from the standalone allow-all baseline for every executor and then overlays the selected executor profile's explicit MCP policy. A blank SSH, Sprites, or other remote policy therefore inherits allowed `stdio`, HTTP, SSE, and streamable HTTP transports; it does **not** receive the deny-all remote default defined elsewhere in the runtime. Set an explicit restrictive executor MCP policy before relying on remote isolation. + +MCP JSON, including `headers` and server `env`, is stored as profile configuration. The raw editor has no secret-reference field for those values, so do not paste long-lived credentials into it unless access to the profile store is an acceptable boundary. Prefer a server that can inherit a narrowly scoped profile environment secret. + +Passthrough injection adds CLI-specific exposure. Codex encodes MCP environment values and HTTP headers into `-c` process arguments, which another local user may read through process inspection. Cursor and Pi write project-local `.cursor/mcp.json` or `.pi/mcp.json`; when either file already exists, Kandev merges its entries and deliberately does not remove them at teardown because it does not own the user's file. Review and remove persisted entries or credentials yourself. + +Kandev does not validate a server-name syntax centrally, so blank or unusual names can fail or be transformed differently by each CLI. A missing command/URL, unsupported or denied transport, or server-name policy denial skips that server with a warning. Configuring `shared` mode for a stdio server is different: it aborts profile MCP resolution with an error. Review launch/session logs when an expected tool is missing. + +## Delete profiles and custom agents + +Deleting a profile is irreversible. Kandev checks references before deletion: + +- active sessions and watcher references are soft conflicts; force bypasses the active-session check and soft-deletes the profile, while disabling affected watchers is best effort rather than guaranteed cleanup; +- experimental Office routing-tier references are hard conflicts and cannot be forced; +- Kandev attempts to clean every ephemeral task with a session using the profile, including matching Quick Chat, Configuration Chat, and Run-mode automation work. A cleanup failure is logged and does not prevent profile deletion, so audit leftover resources afterward. + +Only custom TUI agents can be deleted from the agent list. Built-in definitions remain registered even when their CLI is not installed. + +## Office status + +Office team roles, reporting lines, budgets, concurrency, and governance add more fields to agent profiles only when the experimental Office feature is enabled. Office is disabled by default and remains in progress; its coordinator routing and approval policy are not a stable public workflow. Regular profiles, workflow approval steps, and task-mode automation provide the supported human-gated path today. ## Troubleshooting -- **Agent unavailable:** verify the command under the same user and PATH as Kandev, then refresh discovery. -- **Login required:** use the profile's login/terminal flow or authenticate the CLI outside Kandev under the service user. -- **Model rejected:** choose an advertised model and verify the provider account has access. -- **Flag fails at startup:** run the resolved command manually and remove stale/custom arguments. -- **Environment missing remotely:** configure the agent variable on the agent profile and executor prerequisites on the executor profile; local shell exports do not automatically cross SSH/container boundaries. -- **MCP tool absent:** inspect agent MCP support, profile MCP policy, executor policy, and task mode. +- **Agent unavailable after install:** confirm the executable as Kandev's service user, select **Rescan**, and compare the service `PATH` with your shell. +- **Login required:** use the agent card's login terminal or sign in under Kandev's operating-system user; signing in as another user does not help the service. +- **Model, mode, or command probe fails:** authenticate first, refresh discovery, and choose a value advertised by the installed version. +- **Launch fails after editing flags:** inspect the command preview, remove stale arguments, and correct unmatched quotes or trailing escapes. +- **Environment value is absent:** confirm the secret still exists, the key is not reserved, and an executor/runtime variable is not already taking precedence. +- **MCP server is absent:** confirm agent MCP support, valid JSON, transport mode, executor policy, and the session warning logs. +- **Automation cannot select a profile:** passthrough agent profiles and Local executor profiles are intentionally omitted from the automation selectors. -Contributor guide: [Adding a new agent CLI](add-agent-cli.md). +Related: [Executors](executors.md), [Automation and MCP](automation-and-mcp.md), and the contributor guide [Adding a new agent CLI](add-agent-cli.md). diff --git a/docs/public/architecture.md b/docs/public/architecture.md index 3bc873c68e..394e670ef8 100644 --- a/docs/public/architecture.md +++ b/docs/public/architecture.md @@ -1,90 +1,99 @@ --- title: "Architecture" -description: "Understand Kandev's unified backend, web client, task runtime, persistence, event, and deployment boundaries." +description: "Understand Kandev's process, code, runtime, persistence, event, protocol, and trust boundaries." --- # Architecture -Kandev is a server-first development workbench. A unified Go binary owns orchestration and persistent state, serves the Vite-built React application, and manages local or remote task environments through agentctl. +Kandev is a server-first development workbench. A Go backend owns durable product state and orchestration. The browser UI is a Vite-built React SPA. Agent processes run behind an `agentctl` sidecar in local, worktree, container, or remote task environments. ```mermaid flowchart LR - Browser["React web client"] -->|HTTP and WebSocket| Backend["Unified Go backend"] - Desktop["Tauri desktop shell"] --> Backend - Backend --> DB[("SQLite or PostgreSQL")] - Backend --> Providers["Git and provider integrations"] - Backend --> Local["Local or worktree runtime"] - Backend --> Container["Docker runtime"] - Backend --> Remote["SSH, remote Docker, or Sprites"] - Local --> Agentctl1["agentctl"] - Container --> Agentctl2["agentctl"] - Remote --> Agentctl3["agentctl"] - Agentctl1 --> Agent1["Agent CLI"] - Agentctl2 --> Agent2["Agent CLI"] - Agentctl3 --> Agent3["Agent CLI"] + UI["Browser or Tauri window"] -->|HTTP + WebSocket| BE["Go backend"] + BE --> DB[("SQLite or PostgreSQL")] + BE --> Providers["Git and provider APIs"] + BE -->|control channel| A1["agentctl: local/worktree"] + BE -->|control channel| A2["agentctl: Docker/SSH/Sprites"] + A1 --> Agent1["Agent CLI + task MCP"] + A2 --> Agent2["Agent CLI + task MCP"] ``` -## Process entry points +## Processes and launchers -`apps/backend/cmd/kandev/main.go` dispatches between the public launcher and the hidden `__backend` mode. `apps/backend/internal/launcher/` starts the installed runtime and browser. `apps/backend/internal/backendapp/` wires the production server. +`apps/backend/cmd/kandev/main.go` is the shipped entry point. Normal arguments enter the native launcher in `apps/backend/internal/launcher/`; the hidden `__backend` mode enters the composition root in `apps/backend/internal/backendapp/`. -At startup the backend loads configuration, creates logging and the event bus, opens persistence, constructs domain services, registers HTTP/WebSocket/MCP routes, starts schedulers and watchers, and flips readiness only after the listener and routes are ready. +These adjacent launch layers have different jobs: -The release binary embeds the built web assets. Development runs Vite separately and the backend proxies web traffic while retaining API and WebSocket ownership. +- `apps/cli/src/` supplies the TypeScript development supervisor used by `make dev`. +- The published `apps/cli/bin/` npm shim selects the matching `@kdlbs/runtime-*` package and starts its native `kandev` binary. +- `apps/backend/internal/launcher/` implements installed `run`, `start`, and service behavior. +- `apps/desktop/` is a Tauri shell. Rust starts its bundled `kandev --headless` on an owned loopback origin and owns native windows, external links, notifications, and updates. The product UI is still the backend-served SPA. -## Backend domains +The root build embeds generated web assets in the Go binary. `make dev` instead starts Vite and configures Go to proxy it, so HTTP, API, and WebSocket traffic still enters through Go. -Backend code is organized by domain under `apps/backend/internal/`: +## Backend ownership -- `task/`, `workflow/`, `orchestrator/`, and `runs/` own work state and dispatch; -- `agent/` and `agentctl/` own agent discovery, profiles, protocol adapters, process control, terminals, Git, and runtime communication; -- `worktree/`, `ssh/`, `sprites/`, and `agent/docker/` materialize executor environments; -- `github/`, `gitlab/`, `jira/`, `linear/`, `sentry/`, and `slack/` own provider adapters; -- `mcp/` exposes task/config coordination tools; -- `system/` owns status, database, backups, logs, disk, updates, and build information; -- `gateway/websocket/` broadcasts state changes to connected clients; -- `db/` and domain repositories provide SQLite/PostgreSQL persistence. +Backend code is under `apps/backend/internal/`. Common owners include: -Handlers translate HTTP or WebSocket messages. Services enforce domain behavior. Repositories own persistence. Keep provider SDK shapes and transport DTOs out of the core model where an existing adapter boundary exists. +- `task/`, `workflow/`, `orchestrator/`, and `runs/` for work state and dispatch; +- `agent/` for agent definitions, profiles, executor mapping, and lifecycle; +- `agentctl/` for the sidecar server, process/protocol adapters, Git, files, shell, terminal, and workspace operations; +- `worktree/` plus runtime implementations for local, Docker, remote Docker, SSH, and Sprites environments; +- provider domains such as `github/`, `gitlab/`, `jira/`, `linear/`, `sentry/`, and `slack/`; +- `mcp/` for Kandev tool schemas and backend handlers; +- `gateway/websocket/` for client broadcasts; +- `system/`, `persistence/`, and `db/` for status, storage setup, pools, and SQL dialect primitives. -## Web client +`internal/backendapp/` constructs these domains and registers routes, subscribers, schedulers, and startup recovery. -`apps/web/` is a React 19 application built with Vite. The backend serves its production output as a single-page application. Client routing, initial bootstrap data, HTTP domain clients, a WebSocket connection, and Zustand state keep the UI synchronized with backend state. +Layouts vary by domain. Handlers translate HTTP or WebSocket requests; some domains add controllers; services own behavior; repositories or stores own persistence; providers adapt external systems. Use the nearest established boundary instead of treating this as a mandatory global stack. -Page-level code lives under `apps/web/app/`; reusable UI and feature components live under `apps/web/components/`; API, routing, state, and shared utilities live under `apps/web/lib/`. Tests are colocated or under `apps/web/e2e/` depending on scope. +## Web ownership and state flow -The web application should display backend truth rather than reimplement workflow or lifecycle rules in components. +`apps/web/src/main.tsx` loads the Go boot payload, creates the Zustand state provider, and renders the shell and route dispatcher. Top-level routing lives in `src/spa-routes.tsx`; it is custom SPA routing, not Next.js filesystem routing. Next-shaped `app/**/page.tsx` files are page components. -## Task execution +HTTP clients in `lib/api/domains/` and domain hooks in `hooks/domains/` issue requests. `lib/state/` hydrates and composes store slices. `lib/ws/` routes live updates into that state. The backend remains authoritative; the client must recover from missed, duplicate, or stale events. -A task selects one or more repositories, an agent profile, and an executor profile. The executor materializes a task environment. The backend launches or reaches agentctl in that environment; agentctl then runs the agent CLI and exposes process, shell, Git, file, terminal, ACP, and task-MCP capabilities through its control channel. +See [Web development](web-development.md) for settings and workbench ownership. -The task environment can outlive one agent turn and can be shared by several sessions. A worktree separates Git files/branches, while a container or remote host establishes a stronger runtime boundary. None of them automatically narrow the credentials explicitly provided to the agent. +## Task runtime and agentctl -## Events and real-time state +A task selects repositories, an agent profile, and an executor profile. Product executor types are `local`, `worktree`, `local_docker`, `remote_docker`, `sprites`, `ssh`, and test-only `mock_remote`. They map to lifecycle backends `standalone`, `docker`, `remote_docker`, `sprites`, or `ssh`; local and worktree share a process backend but prepare different Git environments. -The backend event bus decouples state changes from WebSocket broadcasts, workflow reactions, integration watches, and schedulers. Unified mode uses an in-memory bus; deployments can configure NATS where supported. +`internal/agent/runtime/` is the intended high-level backend lifecycle facade, but it remains transitional and current callers also use `internal/agent/runtime/lifecycle/` directly. Backend-side clients in `internal/agent/runtime/agentctl/` reach the agentctl server implemented in `internal/agentctl/server/`. -Persist the durable transition before publishing an event. Consumers must tolerate retries, reconnects, and stale clients. The UI refetches authoritative state when an incremental event is insufficient. +Local and worktree execution share a host agentctl control server that manages multiple task instances. Container and remote backends deliver or start agentctl in the target environment. Its authenticated control and per-instance APIs own the agent subprocess, ACP adapter, workspace, Git, file, process, shell, terminal, port, and MCP relay operations. The backend, not the browser, coordinates that channel. -## Persistence +A worktree isolates concurrent Git state. It does not isolate processes, the host filesystem, or credentials. Containers and remote hosts add stronger runtime boundaries, but every credential explicitly delivered to an environment remains available there. -SQLite is the default. PostgreSQL support is selected through configuration. Domain repositories own schema and queries; dialect helpers isolate SQL differences. +## ACP, REST, and MCP -Do not put long-running network or agent work inside database transactions. Use explicit statuses and durable IDs so startup recovery, watchers, and queued work can reconcile partial execution. +These protocols are separate: + +- **ACP** is the structured agent-session protocol. The current agentctl adapter factory accepts ACP. +- **REST and WebSocket** are backend/client and agentctl control surfaces. +- **MCP** supplies Kandev and profile-configured tools to an agent. It is not an agent runtime adapter. + +agentctl hosts task MCP endpoints and relays tool calls over the agent stream to backend handlers. The backend also exposes external MCP routes. Kandev currently adds no user-auth middleware to those external routes; do not expose them on an untrusted network without binding, proxy authentication, and scoped credentials. See [Automation and MCP](automation-and-mcp.md). + +## Events and persistence + +The event bus provides live fan-out to WebSocket broadcasters and backend subscribers. It uses an in-memory bus by default and can use NATS when configured. It is not durable replay: persist a transition before publishing, make consumers tolerate duplicates and reconnects, and recover from the database. + +SQLite is the default database; PostgreSQL is supported. There is no central migration-file framework. Domain repositories/stores initialize fresh schemas and apply ordered, inline upgrades at startup. `internal/persistence/` opens the database, maintains boot metadata, and records the current binary version after repository initialization succeeds; `internal/db/` provides pools, rebinding, and dialect helpers. + +Keep long-running provider or agent work outside transactions. Persist stable IDs and explicit statuses so startup recovery can reconcile partial execution. See [Backend development](backend-development.md) for migration rules. ## Trust boundaries -Important boundaries are: +Review each crossing explicitly: -- browser/desktop client to backend; +- browser or desktop window to backend; - backend to provider APIs and Git remotes; -- backend to agentctl task environments; -- agentctl to the selected agent process and MCP servers; -- host to Docker/SSH/Sprites infrastructure. +- backend to agentctl and a task environment; +- agentctl to the selected agent and MCP servers; +- host to Docker, SSH, Sprites, or other remote infrastructure. -Worktrees are concurrency isolation, not OS security. External MCP currently has no Kandev user-auth boundary. Remote deployments must use network access controls and scoped credentials. See [Automation and MCP](automation-and-mcp.md) and [Operations](operations.md). +Validate scope and identity server-side. Treat provider text, repository content, agent output, URLs, archives, paths, and command arguments as untrusted. Never rely on a frontend-only permission check. -## Office source tree - -`apps/backend/internal/office/` contains feature-flagged, in-progress autonomy work. Its source packages are not a supported extension API for the regular task product. Keep public behavior and status explicit when touching it. +`apps/backend/internal/office/` contains feature-flagged, in-progress autonomy work. Its source tree is not a supported extension API for the regular task product. diff --git a/docs/public/automation-and-mcp.md b/docs/public/automation-and-mcp.md index 1c18f2dc89..ee71654ec9 100644 --- a/docs/public/automation-and-mcp.md +++ b/docs/public/automation-and-mcp.md @@ -1,106 +1,215 @@ --- title: "Automation and MCP" -description: "Automate task creation and let agents coordinate Kandev through task-scoped and external MCP endpoints." +description: "Create scheduled, GitHub, or webhook automations and connect agents through task, profile, and external MCP." --- # Automation and MCP -Kandev has two automation layers and two MCP contexts. Keep their scopes distinct: +Kandev has several mechanisms that can act without repeated manual setup. Their scopes and trust boundaries differ: -- workflow events act on a task already moving through a workflow; -- workspace automations create work from schedules, provider events, or webhooks; -- task MCP lets an active coding agent coordinate its task and related work; -- external MCP lets a separate client control Kandev through the backend. - -## Workflow events - -Workflow-step events can react when a task enters a step, a user sends a message, or an agent turn completes. Depending on the workflow, actions can start an agent, send a configured prompt, stop work, or move the task. - -Use them for predictable task-local transitions such as Plan to human approval to Implement to Review. Inspect all events on both source and destination steps before enabling auto-start behavior so a move does not create an accidental loop. - -## Workspace automations - -**Settings > Automations** and the workspace automation editor define rules that create task-backed or run-only executions. An automation selects a workspace, workflow and step, repository, agent profile, executor profile, prompt, title template, concurrency limit, and one or more triggers. - -Current trigger types are: - -| Trigger | Filters or configuration | +| Mechanism | Purpose | |---|---| -| Schedule | Cron expression and optional timezone. | -| GitHub pull request | Events, repositories, base branches, authors, labels, and draft exclusion. | -| GitHub push | Repositories and branch globs. | -| GitHub CI | Repositories, conclusions, and optional check names. | -| Webhook | Signed endpoint with an optional filter expression. | +| Workflow events and actions | React to an existing task entering a step, receiving a message, or completing an agent turn. | +| Workspace automations | Create task-backed work from a schedule, GitHub pull request, webhook, or manual trigger. | +| Task MCP | Give an active Kandev session task, plan, conversation, and coordination tools. | +| Profile MCP | Add third-party MCP servers to one agent profile, subject to executor policy. | +| External MCP | Let a client outside a task configure Kandev and create or manage work through the backend. | -Execution mode **Task** creates visible kanban work. **Run** uses the same session pipeline for a lighter, hidden execution whose result appears in automation run history. Concurrency and deduplication reduce duplicate work, but they do not make external actions idempotent by themselves. +Use workflow events for predictable transitions on existing work. Use a workspace automation when an external signal must create new work. MCP is a tool interface, not a scheduler. -Start with a narrow repository and read-only prompt. Inspect run history and generated tasks before enabling write credentials or widening filters. +## Workflow events and human gates + +Regular workflow entry actions can enable plan mode, reset agent context, or auto-start an agent; auto-start can use the step prompt or a stored prompt override. Turn-start and turn-complete events can move the task, while turn-complete and step-exit actions can disable plan mode. There is no regular standalone **stop agent** or **send prompt** workflow action. Approval/review steps and steps without automatic start remain the supported human gates. Inspect events on both the source and destination step before enabling a move or automatic start; otherwise two steps can form a loop. + +See [Tasks and workflows](tasks-and-workflows.md) for event configuration and defaults. + +## Create a workspace automation + +Open **Settings > Workspaces > _Workspace_ > Automations** (`/settings/workspace/{workspaceId}/automations`) and select **New Automation**. The top-level `/settings/automations` route redirects to, or asks you to select, a workspace. + +1. Enter a required name and optional description. +2. Choose **Task** or **Run** execution mode. +3. Select an agent profile and a non-local executor profile. Passthrough agent profiles are not offered. +4. For Task mode, select a workflow and starting step. +5. Select a registered repository, a discovered local repository, or **None**. A discovered repository is registered in the workspace when the automation is saved. +6. Enter a prompt and optional task-title template. +7. Keep the default maximum concurrency of 1 unless parallel work is safe. +8. Choose a schedule and optional GitHub condition, or switch to webhook mode. +9. Save, trigger a small test, then inspect **Runs** before widening credentials or scope. + +The form can save an empty agent, executor, or repository selection, but launch still needs a usable agent/executor and a repository. For scheduled, webhook, and manual work, an empty repository falls back to the workspace's first repository. If the workspace has none, the run fails with `no repository available — add a repository to the workspace`. A GitHub pull-request run instead checks out that PR's head branch and uses its base branch. + +### Task mode + +Task is the default. It creates a visible kanban task in the chosen workflow step. The task starts automatically only when that step has an enabled `auto_start_agent` action on entry; otherwise it waits for a person to start it or move it through the workflow. + +Use Task mode for work that needs discussion, permission responses, review, or approval before merge/release. The task remains available like other tracked work. + +### Run mode + +Run creates an ephemeral task hidden from the kanban, starts it immediately, and exposes the result through automation run history. After the turn finishes, Kandev reaps an associated Worktree executor's worktree/branch when a worktree manager and runtime worktree ID are present. Docker, Sprites, and SSH runs do not have that Worktree cleanup record and follow their own runtime lifecycle. Workflow and step selections do not apply to this mode. + +Run mode cannot wait for a permission response. Kandev rejects the request and marks the run failed. Use only a profile whose intended, constrained actions can complete without a prompt. Prefer Task mode when human approval is required. + +## Trigger behavior + +The editor has two exclusive layouts: + +- **Scheduled**: one schedule plus at most one GitHub condition; +- **Webhook**: one authenticated webhook trigger. Switching to webhook deletes the schedule and condition, and switching back deletes the webhook trigger. + +In the current backend, the schedule and GitHub PR condition are independent triggers. A non-empty schedule creates generic scheduled runs, while the PR trigger separately polls GitHub. Adding a PR condition does not constrain the scheduled run. Clear the schedule expression if the automation should run only for matching PRs. + +### Schedule + +The scheduler checks every 30 seconds and treats expressions as elapsed intervals, not calendar cron times. If a new enabled schedule has never been evaluated, its first check fires immediately; later runs are spaced from the last evaluation. + + + +Use the supplied presets: every 5, 15, or 30 minutes; hourly; every 6 hours; daily; or weekly. The backend also accepts `@every` followed by a Go duration, `@hourly`, `@daily`, `@weekly`, and step forms such as `*/10 * * * *` or `0 */6 * * *`. + +The editor currently accepts arbitrary five-field cron text, but the backend cannot execute fixed calendar forms such as `30 8 * * *`. A saved timezone defaults to UTC but is not used in schedule evaluation. Until calendar scheduling is implemented, use a preset or `@every` and interpret it as an interval. Scheduled runs are deduplicated per trigger per minute. + +### GitHub pull requests + +The GitHub evaluator polls every 60 seconds and requires a working GitHub integration. It searches open PRs and supports: + +- an explicit list of repositories; +- base-branch glob filters, including `*` and a trailing wildcard such as `release/*`; +- exact author-login filters; +- draft exclusion. + +Select at least one repository. Although the UI offers **All repos**, an empty repository list is not evaluated, so it produces no PR runs. The editor exposes only the **Opened** event, but the evaluator currently ignores the saved event list: clearing that checkbox does not stop polling or firing. Disable the automation/trigger instead. The first evaluation considers every currently open matching PR rather than only PRs opened after the automation was enabled. Each matching PR is then deduplicated once per automation by repository and PR number. + +The current evaluator does not apply label filters, and the current form does not offer them. GitHub push and generic CI-result conditions are shown as **Coming soon**; their backend evaluators do not create runs. Task-specific PR check remediation is a separate review feature, not the generic CI automation trigger. + +### Webhook + +After creating a webhook automation, copy its URL and one-time displayed secret. The edit page can reveal the secret again. + +Send: + +```http +POST /api/v1/automations/webhook/{automationId} +X-Webhook-Secret: +Content-Type: application/json +``` + +Kandev silently reads only the first 1 MiB of the request body; it does not reject an oversized body. If that retained prefix is valid JSON, it becomes trigger data. Empty or invalid JSON is wrapped as `{"body":""}`. The endpoint returns 401 for a wrong secret, 404 for an unknown automation, and 409 when the automation or its webhook trigger is disabled. + +Webhook delivery has no event deduplication or filter-expression evaluator. Make downstream actions idempotent when the sender retries. The secret is stored with the automation rather than in Kandev's encrypted provider-secret store, and anyone with Kandev settings access can reveal it. Treat it as a credential, use TLS, keep it out of URLs/logs, and replace the automation if rotation is required. + +### Manual trigger + +The play action in the automations table fires a run with trigger type `manual` and no deduplication. Use it on an enabled automation to test repository/profile resolution and inspect the resulting history. The UI currently leaves **Play** enabled for a disabled automation: the HTTP request reports that it triggered, but the orchestrator silently discards the event and creates no run-history row. Re-enable the automation before testing. + +## Prompt and title placeholders + +Every trigger supports `{{trigger.type}}`, `{{trigger.timestamp}}`, and `{{data.}}`. + +GitHub PR runs additionally support `{{pr.number}}`, `{{pr.title}}`, `{{pr.url}}`, `{{pr.author}}`, `{{pr.repo}}`, `{{pr.branch}}`, `{{pr.base_branch}}`, and `{{pr.body}}`. + +Webhook runs support `{{webhook.body}}` and `{{webhook.}}`. Dot segments traverse nested objects, and a numeric segment indexes an array, for example `{{webhook.commits.0.message}}`. Scalar values are converted to text; objects and arrays become JSON. Missing or unresolved placeholders are removed rather than sent literally. + +Trigger payloads are untrusted input. Do not let a PR body or webhook field silently choose credentials, repositories, shell commands, or a production target. + +## Concurrency, history, and cleanup + +Maximum concurrent runs defaults to 1 and cannot be less than 1. A run counts as active while its status is `task_created`. When the cap is reached, Kandev records a `skipped` run and advances the schedule's evaluation time rather than retrying every 30 seconds. + +Run history shows the latest 50 entries by default and can report `triggered`, `task_created`, `succeeded`, `failed`, `skipped`, or `cancelled`. A missing or archived pending task is reconciled as cancelled. Task-mode entries link to their kanban task; Run-mode entries remain in history only. + +Deleting one run also deletes its associated task. **Delete all runs** deletes all associated tasks and history for that automation and is irreversible. ## Task MCP -Kandev injects a task-aware MCP server into compatible ACP and passthrough sessions. It gives the agent structured coordination tools instead of requiring it to infer board state from prose. +Kandev automatically injects a task-aware MCP server into supported agent sessions. You do not need to add it to the profile. It lets the active agent use current IDs and structured operations instead of inferring board state from text. -Depending on the current task and policy, tools cover: +Task mode currently registers these tool groups: -- listing workspaces, workflows, steps, profiles, executors, and related tasks; -- creating and updating tasks or subtasks; -- moving, archiving, or deleting tasks; -- attaching sibling repositories or additional branches; -- adding dependencies and selecting a diff base; -- sending cross-task messages and reading conversations; -- creating and updating task plans/documents; -- asking the user a structured clarification question; -- creating a code walkthrough; -- inspecting associated pull requests and signalling step completion. +| Group | Available operations | +|---|---| +| Board lookups and task lifecycle | List workspaces, workflows, workflow steps, tasks, agents, and executor profiles; create, update, move, archive, or delete tasks. This mode does not mutate workflows, profiles, or executors. | +| Coordination | Message a task or targeted session, spawn a named session on the current or another same-workspace task, and read task conversation. | +| User interaction | Ask a structured question when the current agent/session supports it. | +| Plans | Create, get, update, and delete the current task plan. | +| Walkthroughs | Show, get, and delete the task's code walkthrough. | +| Relationships and branches | List related tasks, add a branch/worktree to a task, and change a repository's diff base. | +| Workflow signal | Signal step completion when an auto-advance step explicitly requires that signal. | -The server injects calling-task identity for operations that require it. Tools still enforce task relationships, workspace rules, executor constraints, and normal provider permissions. An MCP call does not bypass GitHub branch protection or human approval. +Task identity is injected for operations that require it. Workspace, parent/subtask, executor, and task-state rules still apply. -## Profile and executor MCP policy +`spawn_session_kandev` creates a named sibling session on the current task by default and can target another task in the same workspace. `message_task_kandev` can address a task's primary session or an explicit session ID: a running agent receives queued input, an idle/created session can be started, and a failed or cancelled session rejects the message. -An agent profile can add external MCP servers. An executor profile can restrict allowed transports and destinations. Supported transport forms include stdio, SSE, HTTP, and streamable HTTP where the selected agent supports them. +A same-task message requires the sibling's session ID. Normal messages can cross workspaces when the sender knows the full task ID. Delivery to a running session is queued by default; `delivery_mode: "interrupt"` is allowed only from a direct parent task to its child, and another sender receives a hard error rather than a silent downgrade. -Passthrough agents load MCP differently. Kandev may pass command-line configuration or merge a project-local MCP file for the specific CLI. Inspect the profile UI before assuming that an arbitrary TUI receives task tools. +`add_branch_to_task_kandev` works only with the Worktree executor. It can add another branch of the same repository or a second repository entirely. Select at most one of `repository_id`, `repository_url`, or `local_path`; `repository_url` accepts a GitHub repository URL, while the URL/path forms find or create that repository in the task's workspace. A locator is optional for a single-repository task and required to disambiguate a multi-repository task. The new repository/branch receives its own worktree. `update_repository_base_branch_kandev` changes the base used for Kandev's diff, not a pull request's target branch. + +`step_complete_kandev` matters only for an auto-advance action configured to require an explicit signal. A user message arriving before transition can cancel that automatic move. + +The task server runs inside agentctl's local runtime boundary. Its MCP routes do not use a separate bearer token. Do not expose agentctl ports; rely on the executor's process/network isolation and Kandev's session scoping. + +## Profile and executor MCP + +An agent profile can add `stdio`, `http`, `sse`, or `streamable_http` servers when that agent supports MCP. The built-in Kandev task server is injected separately and cannot be replaced by a profile entry named `kandev`. + +Stdio normally starts per session and cannot be shared. Network servers can be shared or per-session. The executor's MCP policy can deny transports/server names, rewrite URLs, or inject environment. See [Agents and profiles](agents-and-profiles.md) for configuration, secret handling, and failure behavior. ## External MCP -For a local backend bound to loopback, use: +Open **Settings > External MCP** (`/settings/external-mcp`) for client-specific snippets for Claude Code, Cursor, Codex, Auggie, OpenCode, and GitHub Copilot CLI. + +The recommended Streamable HTTP endpoint is: ```text http://127.0.0.1:/mcp ``` -For a remote deployment, terminate TLS and authentication at a secure reverse proxy and use: +SSE compatibility uses `/mcp/sse` with messages sent to `/mcp/message`. A reverse proxy must support long-lived streaming connections. -```text -https:///mcp -``` +External MCP exposes 32 tools in these groups: -SSE compatibility endpoints are available at `/mcp/sse` and `/mcp/message`. **Settings > External MCP** provides client-specific configuration snippets. +- workspace/workflow configuration: list workspaces, workflows, repositories, and workflow steps; create, update, delete, or import workflows; create, update, delete, or reorder steps; +- agents and profiles: list/update agents; create/delete profiles; list/update profiles; get/update profile MCP configuration; +- executors: list executors and profiles; create, update, or delete executor profiles; +- tasks: list, create, move, delete, archive, or update task state, and read task conversation. -External MCP is useful when an agent outside a Kandev task needs to inspect configuration or create and coordinate Kandev work. It runs against the backend's own reachability and credentials, not a task worktree. +The settings page's static **Available tools** preview currently counts 29 and omits `list_repositories_kandev`, `import_workflow_kandev`, and `get_task_conversation_kandev`. Treat the client's live `tools/list` response from the endpoint—not that preview—as authoritative. -## Security boundary +In external mode, `create_task_kandev` has no current task and does not accept the `parent_id: "self"` shorthand. Its registered top-level contract asks for a repository ID, GitHub URL, or local path; workspace and workflow resolve automatically only when unambiguous. The current handler can nevertheless accept an omitted repository and create repo-less work, which is a contract/implementation mismatch rather than a supported equivalent of the regular UI's **None** option. Supply an explicit repository locator for portable clients. A resolvable agent profile is required even with `start_agent: false`; otherwise `start_agent` defaults to true. To create a subtask, pass the full ID of an existing parent. -The external MCP endpoint currently has no Kandev user-authentication boundary. Anything that can reach the backend endpoint can attempt to use its exposed tools. +External mode has no live Kandev session, so it does not expose task-scoped questions, plans, walkthroughs, sibling-session spawning, targeted session messages, branch operations, or step-completion signals. Some external tools can delete or materially reconfigure data; review the client's tool approvals. -- Bind Kandev to loopback for a single-user local install. -- For remote access, place it behind a VPN, firewall, or authenticated reverse proxy that also supports long-lived streaming requests. -- Do not publish `/mcp`, `/mcp/sse`, or the backend port directly to the public internet. -- Use a separate network segment and scoped provider credentials for unattended automation. -- Review reverse-proxy logs and automation run history for unexpected clients or task creation. +### External MCP security boundary -The agentctl-local MCP server used inside a task is a different endpoint and is protected by the task runtime's control channel. Do not expose agentctl ports either. +The backend's `/mcp`, `/mcp/sse`, and `/mcp/message` routes currently have no Kandev authentication. They are reachable on every interface to which the backend is bound. Anyone who can reach them can attempt the exposed configuration and task mutations. -## Operating checklist +- Bind the backend to loopback for a local single-user install. +- For remote use, place the whole backend behind a VPN, firewall, or authenticated TLS reverse proxy. +- Do not publish the MCP routes or backend port directly to the internet. +- Ensure the proxy protects both Streamable HTTP and SSE/message paths and permits long-lived requests. +- Scope integration, Git, and agent credentials for the damage an unattended client could cause. -Before enabling unattended automation: +## Office status -1. Limit trigger repositories, branches, authors, labels, or issue query. -2. Use a dedicated agent and executor profile with minimal credentials. -3. Set a conservative concurrency limit. -4. Keep a human gate before merge, release, or production actions. -5. Test duplicate events and failed retries. -6. Confirm logs and run history contain enough context to audit an execution. -7. Verify the external MCP endpoint is unreachable outside the intended network. +Office has separate coordinator agents, routines, governance approvals, and an Office MCP/agentctl interface only when its experimental feature flag is enabled. Office is disabled by default and remains in progress; do not treat its coordinator-led automation as a stable public contract. Workspace automations plus workflow approval/review steps are the supported human-gated mechanism today. + +## Troubleshooting + +- **No scheduled run:** use a preset or `@every`; fixed calendar cron and timezone scheduling are not implemented. +- **Scheduled run happened as well as a PR run:** clear the schedule expression. The two stored triggers fire independently. +- **No GitHub PR runs:** connect GitHub and select explicit repositories; **All repos** currently evaluates none. +- **Run fails before a task starts:** select valid non-passthrough agent and non-local executor profiles, and add/select a repository. +- **Run fails on permission:** Run mode cannot answer prompts. Use Task mode or a safely constrained profile that does not require one. +- **Webhook rejected or data is incomplete:** check the exact automation ID, `X-Webhook-Secret` header, and enabled automation/trigger. Bodies over 1 MiB are not rejected; the suffix is silently discarded, so inspect the retained trigger data. +- **Missing template data:** inspect run trigger data and the dot path; unresolved placeholders are intentionally removed. +- **Task MCP tool missing:** confirm this is a Kandev task session, the agent supports the injection strategy, and the operation belongs to task rather than external mode. +- **External client cannot stream:** verify the base backend URL and configure the reverse proxy for both the selected MCP transport and long-lived requests. Related: [Tasks and workflows](tasks-and-workflows.md), [Coordination](coordination.md), [Agents and profiles](agents-and-profiles.md), and [Integrations](integrations.md). diff --git a/docs/public/backend-development.md b/docs/public/backend-development.md index 71e5bbd44c..193cce2ce2 100644 --- a/docs/public/backend-development.md +++ b/docs/public/backend-development.md @@ -1,18 +1,19 @@ --- title: "Backend Development" -description: "Work in Kandev's Go backend using its handler, service, repository, event, and runtime patterns." +description: "Change Kandev's Go backend across domain, API, event, persistence, migration, and agent-runtime boundaries." --- # Backend Development -The backend is a Go module rooted at `apps/backend`. It builds the unified `kandev` binary and the `agentctl` helper used inside local, container, and remote task environments. +The Go module in `apps/backend/` builds both the unified `kandev` binary and the `agentctl` helper installed in task environments. -## Run and test the backend +## Run focused loops + +Use `make dev` for normal full-stack work: it isolates state, starts Vite, and configures the backend proxy. A raw `make dev-backend` uses the normal application home unless you set an isolated `KANDEV_HOME_DIR`; see [Contributing](contributing.md). From the repository root: ```bash -make dev-backend make test-backend make lint-backend ``` @@ -21,76 +22,88 @@ For a package-sized loop: ```bash cd apps/backend -go test -tags fts5 ./internal/task/... -go test -tags fts5 ./internal/mcp/server -run TestName +CGO_ENABLED=1 go test -tags fts5 ./internal/task/service/... +CGO_ENABLED=1 go test -tags fts5 ./internal/agent/runtime/lifecycle -run TestName ``` -The `fts5` build tag and CGO settings used by the Makefile matter for SQLite search behavior. Reproduce CI through the root targets before concluding that a direct `go test` result is sufficient. +The supported local target enables CGO and SQLite FTS5. CI also has race/coverage and PostgreSQL jobs, so a direct package result is not the complete matrix. -## Follow domain boundaries +## Follow the owning domain -A normal backend feature flows through: +Backend domains live under `apps/backend/internal/`. A persistence-backed HTTP feature often changes: -1. domain model or request DTO; -2. repository interface and implementation when persistence changes; -3. service method that enforces behavior; -4. HTTP or WebSocket handler; -5. event publication and gateway notification when clients need real-time state; -6. startup wiring in `internal/backendapp/`; -7. unit/integration tests at each changed boundary. +1. a domain model and transport DTO; +2. a repository or store contract and implementation; +3. a service or controller method that enforces behavior; +4. an HTTP or WebSocket handler; +5. an event publisher and client broadcaster; +6. construction or route registration in `internal/backendapp/`; +7. tests at each changed boundary. -Use the nearest domain's naming and error conventions. Avoid reaching directly into another domain's SQLite repository when a service or interface exists. +This is a tracing guide, not a required package template. For example, task code uses `models/`, `dto/`, `repository/`, `service/`, and `handlers/`; other domains use `store/` or add a controller/provider/poller. Follow the nearest domain's errors, transactions, and ownership. Do not call another domain's concrete store when its service or interface owns the rule. -## HTTP and WebSocket APIs +## HTTP, WebSocket, and events -HTTP routes generally live with a domain's handlers/controllers under `/api/v1`. Real-time task and agent behavior also uses typed actions from `apps/backend/pkg/websocket` and gateway broadcasters under `internal/gateway/websocket`. +HTTP routes are generally registered under `/api/v1` by domain handlers. WebSocket action constants live in `apps/backend/pkg/websocket/`; dispatch and broadcasting are wired through registered handlers and `internal/gateway/websocket/`. -When adding or changing a wire field: +For a wire change: -- update the Go DTO and web TypeScript type/client together; -- preserve JSON compatibility unless the change is intentionally breaking; -- test malformed input and authorization/workspace scope; -- publish an event only after persistence succeeds; -- consider a reconnecting client that missed the event. +- update Go DTOs, JSON compatibility, TypeScript types, domain clients, and WebSocket handlers together; +- validate malformed input, workspace/task reachability, and identity server-side; +- preserve additive compatibility when old and new clients can overlap; +- update [WebSocket API](websocket-api.md) manually when its public contract changes; +- test a reconnecting client that missed the incremental event. -The public WebSocket protocol reference is generated from maintained behavior in [WebSocket API](websocket-api.md). +The event bus in `internal/events/` is live fan-out, not durable storage. In-memory delivery is the default; NATS is selected when configured. Write and commit durable state before publishing. Do not publish on rollback. Subscribers must tolerate duplicates, reconnects, and startup recovery from the database. -## Persistence and migrations +## Persistence and inline migrations -Domain repositories under `internal/*/repository` and `internal/db` own storage. SQLite is the primary local path; PostgreSQL compatibility uses dialect helpers and CI coverage. +SQLite is the default. PostgreSQL uses the same domain repositories where supported, with `sqlx.Rebind` and helpers in `internal/db/dialect/`. A package name such as `internal/task/repository/sqlite` does not prove SQLite-only behavior. -- Make migrations forward-safe and deterministic. -- Add repository tests for both new writes and old-row reads. -- Keep transaction scope small. -- Never log secret values. -- Consider startup recovery for statuses that represent in-flight work. +Kandev has no central migration directory or external migration runner. Each repository/store creates its fresh schema and applies ordered upgrade steps during backend startup. A schema change must therefore: -## Agent runtime boundary +1. update the fresh-schema definition; +2. add or update the ordered upgrade/replay path; +3. preserve legacy rows, null/default semantics, indexes, and foreign keys; +4. remain safe when initialization or a compatible step runs again; +5. use the writer connection, dialect helpers, and rebinding; +6. return an error for a failure that must block startup. -The backend does not run every agent command directly. `internal/agent/runtime/lifecycle` coordinates execution, while agentctl owns the process in the task environment. Control DTOs and streams cross that boundary. +`db.MigrateLogger.Apply` exists for tolerant legacy changes and does not abort on every unexpected error. Do not use it for a critical rebuild while assuming startup will fail. -Changes to agent launch, MCP injection, terminals, Git operations, or remote behavior often require tests on both sides. Check local, worktree, Docker, and at least one remote-shaped path rather than assuming local process behavior generalizes. +SQLite table rebuilds need explicit copy order, index/trigger recreation, and interruption tests. Before a different binary version boots an existing SQLite database, the persistence provider takes a snapshot and retains recent backups. PostgreSQL does not receive that automatic snapshot; operators remain responsible for `pg_dump`. -## Events and background work +Test fresh initialization, replay, and upgrade from representative old rows. Useful patterns include `internal/task/repository/sqlite/schema_replay_test.go` and PostgreSQL tests gated by `KANDEV_TEST_POSTGRES_DSN`. Add PostgreSQL coverage when shared SQL or startup ordering changes. -Schedulers, integration watches, automations, workflow reactions, and run queues can execute after the initiating request returns. Make handlers return durable IDs/status, use cancellation-aware contexts, and define deduplication or idempotency where external events can repeat. +## Agent runtime and agentctl -Background failures must be observable in persisted status or logs. A goroutine that only logs an error is usually insufficient for user-facing work. +`internal/agent/runtime/` defines launch, resume, stop, and observation seams. `internal/agent/runtime/lifecycle/` supplies executor backends and environment preparers. Product executor names in `internal/task/models/` map through `internal/agent/executor/`; startup registration is in `internal/backendapp/agents.go`. -## Configuration and secrets +The backend does not directly own every agent subprocess. Backend clients in `internal/agent/runtime/agentctl/` reach the sidecar built from `cmd/agentctl/` and implemented in `internal/agentctl/server/`. The sidecar owns the process/ACP adapter, workspace, Git, files, shell, terminal, ports, and MCP relay inside the environment. -`internal/common/config` loads server configuration. User-managed credentials go through the secrets domain or provider-specific secret adapter rather than plaintext config rows or logs. +Launch or executor changes usually cross both sides. Test command construction, prepare failure, agentctl delivery/readiness, resume/reconnect, cancellation, process-group cleanup, and at least one relevant container or remote-shaped path. Do not infer remote filesystem, signal, credential, or network behavior from a local process test. -Validate URLs, filesystem paths, shell input, and provider payloads at the boundary. Pay particular attention to prepare scripts, Git refs, archive extraction, MCP destinations, and remote command construction. +## Background work and recovery + +Schedulers, integration pollers, automation rules, workflow reactions, and run queues continue after the initiating request. Return durable IDs and status, pass cancellation-aware contexts, and define timeout, retry, and deduplication behavior. User-visible failure needs persisted state or a queryable run record; a goroutine that only logs is not recoverable. + +Keep network and agent work outside database transactions. Startup must be able to distinguish queued, preparing, running, failed, and abandoned work without relying on an event replay. + +## Configuration, secrets, and input + +`internal/common/config/` owns server configuration and `profiles.yaml` owns prod/dev/E2E runtime-profile environment defaults. User credentials belong in the secrets domain or a provider-specific secret adapter, never plaintext rows or logs. + +Validate provider hosts and URLs against SSRF, keep archive extraction within its target, pass commands as argument vectors, and constrain Git refs and filesystem paths. Treat repository files, provider payloads, agent output, and MCP arguments as untrusted. ## Review checklist -- Domain rule lives in a service, not only a handler. -- Repository behavior and migration are tested. -- Events follow durable writes. -- Cancellation, timeout, retry, and duplicate-event behavior are explicit. -- No secrets or agent content leak into logs unexpectedly. -- Web types and public docs match the API. -- Relevant focused tests plus `make test-backend` and `make lint-backend` pass. +- Domain behavior is enforced below the handler and has focused tests. +- Transactions are short; events follow successful durable writes. +- Fresh schema, upgrade/replay, legacy rows, and PostgreSQL impact are covered. +- Cancellation, retry, duplicates, recovery, and cleanup are explicit. +- Runtime changes cover backend and agentctl ownership. +- Secrets and untrusted content do not leak through logs, shell construction, or errors. +- Go/TypeScript wire types and public protocol docs agree. +- Focused tests, `make test-backend`, and `make lint-backend` pass. Related: [Architecture](architecture.md), [Testing](testing.md), and [Extending Kandev](extending-kandev.md). diff --git a/docs/public/cli.md b/docs/public/cli.md index c366aaee6a..5ad94fce21 100644 --- a/docs/public/cli.md +++ b/docs/public/cli.md @@ -5,222 +5,247 @@ description: "Install, start, and operate Kandev from the command line." # Kandev CLI -## Architecture +The `kandev` command starts a local Kandev backend, which serves the web UI, HTTP API, WebSocket API, and MCP endpoint. Use it when you want a browser-based installation or a headless/service process. For a packaged system WebView and desktop updates, use the [desktop app](./desktop-app.md) instead. -```mermaid -flowchart TB - subgraph CLI["kandev"] - run["run"] - dev["dev"] - start["start"] - end +## Supported release targets - run --> resolve["Resolve installed runtime"] - dev --> makedev["make dev"] - start --> binary["Local binary
(embedded Vite assets)"] +| OS | Architectures | Install channels | +|---|---|---| +| macOS | Apple silicon (`arm64`), Intel (`x64`) | Homebrew, npm/npx | +| Linux | `arm64`, `x64` | Homebrew, npm/npx | +| Windows | `x64` | npm/npx | - resolve --> envvar["KANDEV_BUNDLE_DIR
(Homebrew, tests)"] - resolve --> npmpkg["@kdlbs/runtime-{platform}
(npm/npx)"] +The npm package is a small Node.js shim. It selects an exact, same-version native runtime package for `process.platform` and `process.arch`, then starts its `kandev` binary. npm 7 or later is required because the native packages are platform-specific optional dependencies. There is no native Windows ARM64 npm package; running the x64 package under Windows emulation is OS-dependent and is not a tested release target. - envvar --> supervisor - npmpkg --> supervisor - makedev --> supervisor - binary --> supervisor +Every runtime bundle contains the backend, `agentctl`, the embedded web application, and Linux/macOS `agentctl` helpers used by supported remote executors. Node.js is used only to select the npm runtime; the application itself is native. - subgraph supervisor["Process Supervisor"] - direction LR - s1["Manages backend process"] - s2["Handles graceful shutdown"] - s3["Forwards signals"] - end -``` +## Install -## Overview +### Homebrew -The Kandev CLI (`kandev`) is the primary way to run the Kandev application. It launches the backend from an installed runtime bundle; the backend serves the web assets, API, and WebSocket gateway. - -## Installation - -### Homebrew (macOS, Linux) +Homebrew is available on macOS and Linux: ```bash brew install kdlbs/kandev/kandev +kandev --version ``` -### NPX (requires npm 7+) +### npm or npx -```bash -npx kandev@latest -``` - -`npx` installs the `kandev` CLI plus the matching `@kdlbs/runtime-{platform}` package via npm optional dependencies. Older npm versions silently skip optional deps and won't work — npm 7 is the floor. - -### NPM (global) +Use a global install for a persistent command: ```bash npm install -g kandev@latest +kandev --version ``` -## Quick Start +Or run the release selected by npm's `latest` tag without a global install: ```bash -# Run the installed runtime -kandev - -# Opens the app at http://localhost:38429 (or next available port) +npx -y kandev@latest ``` -## Updates +If an npm policy such as `--omit=optional` prevents optional dependencies from being installed, Kandev cannot find its native runtime. -The package manager controls the runtime version: +## Start and stop -```bash -brew upgrade kandev # Homebrew users -npm install -g kandev@latest # global npm users -npx kandev@latest # npx users (always latest) -``` - -## Commands - -### `kandev` / `kandev run` - -Runs the installed runtime bundle. This is the default command. +`run` is the default command. These are equivalent: ```bash kandev kandev run ``` -**What happens:** -1. Resolves the runtime bundle (KANDEV_BUNDLE_DIR → npm package). -2. Starts the backend server. -3. Waits for backend health check. -4. Serves the embedded web UI from the backend. -5. Opens browser when ready. +On a normal start, the launcher: -### `kandev start` +1. validates the installed runtime bundle; +2. creates the Kandev data directory with owner-only permissions where the platform supports Unix modes; +3. selects backend and `agentctl` ports; +4. starts and supervises the backend; +5. waits up to 45 seconds for `/health`; and +6. opens the printed local URL in the default browser. -Runs the application using the local production backend binary. Requires running -`make build` first so the Vite SPA is embedded into that binary. +The launcher remains in the foreground. Press `Ctrl+C` or terminate it to stop the backend and its managed children cleanly. A force-kill can leave worktree processes or containers running; inspect them before deleting data. + +Use headless mode for SSH sessions, containers, or an external reverse proxy: + +```bash +kandev --headless +# Alias: +kandev --no-browser +``` + +The URL is still printed. `KANDEV_NO_BROWSER=1` also suppresses browser launch. + +## Commands and options + +```text +kandev [run] [options] +kandev start [options] +kandev service [service options] +``` + +| Option | Meaning | +|---|---| +| `--port <1-65535>` | Request an exact backend port. `--backend-port` is an alias; `--port=` forms also work. | +| `--headless`, `--no-browser` | Do not open a browser. | +| `--verbose`, `-v` | Show backend info output. | +| `--debug` | Show debug output and enable diagnostic endpoints and ACP frame logs. See the security warning below. | +| `--version`, `-V` | Print the native runtime version. | +| `--help`, `-h`, `help` | Print help. | + +These commands and options describe the installed native launcher. Unknown arguments fail with exit status 2. In particular, the npm and Homebrew release entrypoints currently invoke that native launcher, which does **not** support `dev`, `--dev`, `--runtime-version`, `--web-internal-port`, or the removed `--web-port` spelling. The source-checkout development launcher has a separate contract described below. + +### `dev` and the internal web port are source-checkout options + +The repository's TypeScript launcher supports hot-reload development with this logical CLI syntax: + +```text +kandev dev [--port ] [--web-internal-port ] +kandev --dev [--port ] [--web-internal-port ] +``` + +Run its normal setup path from the repository root with `make dev`. To pass the internal-port override directly, invoke the same package script used by that Make target: + +```bash +cd apps +pnpm -C cli dev -- dev --web-internal-port 37430 +``` + +`--web-internal-port` accepts an integer from `1` through `65535`, including the `--web-internal-port=` form. It controls the Vite development server that the Go backend reverse-proxies to; `--port` continues to control the backend URL. The flag is valid only with `dev` or `--dev`. `KANDEV_WEB_PORT` is its environment equivalent in dev mode and is ignored by `run` and `start`. Without either override, the source launcher prefers web port `37429` and selects a fallback if that port is unavailable. + +The old `--web-port` spelling has been removed, not retained as an alias. The TypeScript launcher rejects both `--web-port ` and `--web-port=` and directs callers to `--web-internal-port`; the native release launcher rejects both web-port spellings because it serves embedded web assets and has no separate web process. + +### `start` is for a source build + +`kandev start` makes the executable invoke its own embedded backend instead of resolving an installed bundle. It is a contributor/local-production-build path, not a second installation channel. From a checkout, use the Make targets so the correct binary and embedded web assets are built: ```bash make build -kandev start +make start ``` -## Options - -| Option | Description | Example | -|--------|-------------|---------| -| `--version`, `-V` | Print CLI version and exit | `kandev --version` | -| `--port ` | Backend port (alias: `--backend-port`) | `--port 3000` | -| `--verbose`, `-v` | Show info logs from backend | `--verbose` | -| `--debug` | Show debug logs + agent message dumps | `--debug` | -| `--help`, `-h` | Show help | `--help` | - -### Examples +Development hot reload is also checkout-only: ```bash -# Print CLI version -kandev --version - -# Custom port -kandev --port 18080 - +make dev ``` -## Port Selection +`make dev` builds the remote `agentctl` helpers and invokes the repository's TypeScript development launcher. The `kandev dev` syntax above belongs to that source launcher; installing the npm or Homebrew release does not install it as a second runtime mode. -By default, the CLI automatically finds available ports: +### OS service commands -| Service | Default Port | Fallback | -|---------|--------------|----------| -| Backend | 38429 | Auto-selects from 10000–60000 | -| Web | 37429 | Auto-selects from 10000–60000 | -| AgentCtl | 39429 | Auto-selects from 10000–60000 | - -If the default port is in use, the CLI finds the next available port automatically. - -## Environment Variables - -| Variable | Description | -|----------|-------------| -| `KANDEV_BUNDLE_DIR` | Force the runtime bundle location (set by Homebrew wrapper) | -| `KANDEV_PORT` / `KANDEV_BACKEND_PORT` | Backend port (CLI flag wins) | -| `KANDEV_WEB_PORT` | Internal web app port | -| `KANDEV_HEALTH_TIMEOUT_MS` | Override health check timeout (ms) | - -## Makefile Integration - -The repo includes a Makefile that wraps the CLI for common operations: +The native CLI can install and manage a systemd user/system unit on Linux or a launchd agent/daemon on macOS: ```bash -make start # Production mode (after make build) -make dev # Development mode -make build # Build everything +kandev service install +kandev service status +kandev service logs --follow ``` -See `make help` for all available commands. +Supported actions are `install`, `uninstall`, `start`, `stop`, `restart`, `status`, `logs`, and `config`. Installation accepts `--system`, `--port`, `--home-dir`, and `--no-boot-start`. In the current native installer, `--port` is written as `KANDEV_SERVER_PORT`, but the supervising launcher overwrites that value with its own automatic port selection; the option therefore does not reliably pin a service listener today. The service normally prefers `38429` and falls back when it is busy. Windows service installation is not implemented. The published native service path also does not write the legacy update metadata consumed by the Settings-page package updater: upgrade with Homebrew/npm, rerun `kandev service install` with the same flags so versioned executable/bundle paths are refreshed, then restart the service. See [Run as a service](./run-as-a-service.md) for privileges, paths, upgrades, and recovery. -## Comparison: run vs dev vs start +## Ports and network exposure -| Feature | `run` | `dev` | `start` | -|---------|-------|-------|---------| -| Source | Installed runtime | Local repo | Local build | -| Hot-reload | No | Yes | No | -| Requires repo | No | Yes | Yes | -| Requires build | No | No | Yes | -| Use case | End users | Development | Testing production | +| Process | Preferred port | Automatic behavior | +|---|---:|---| +| Backend (UI, HTTP, WebSocket, MCP) | `38429` | If no port was requested and this port cannot bind on loopback, try up to 10 random ports in `10000`-`60000`. | +| Core `agentctl` | `39429` | Uses the same automatic fallback strategy and never shares the backend port. | + +There is no separate web-server port in an installed release: the backend serves embedded assets. If `--port`, `KANDEV_BACKEND_PORT`, or `KANDEV_PORT` specifies a port, the launcher does not substitute another one; backend startup fails if the configured listen address cannot bind it. + +The launcher prints `http://localhost:`, but the backend's default `server.host` is `0.0.0.0`. That can expose Kandev to other machines on the network, and the current local product path is not an authenticated multi-user boundary. Bind it to loopback unless remote access is deliberately protected: + +```bash +KANDEV_SERVER_HOST=127.0.0.1 kandev +``` + +See [Configuration](./configuration.md) before putting Kandev behind a reverse proxy or publishing the port. + +## Launcher environment + +Flags take precedence over the equivalent port variables. `KANDEV_BACKEND_PORT` takes precedence over `KANDEV_PORT`. + +| Variable | Default | Behavior | +|---|---|---| +| `KANDEV_BACKEND_PORT` | unset | Backend port when `--port` is absent. | +| `KANDEV_PORT` | unset | Compatibility backend-port alias. | +| `KANDEV_HOME_DIR` | `~/.kandev` | Root for application data, tasks, repositories, logs, and launcher state. | +| `KANDEV_DATABASE_PATH` | `/data/kandev.db` | Advanced SQLite path override. See the backup caveat in [Configuration](./configuration.md). | +| `KANDEV_LOG_LEVEL` | `warn` from the launcher | Explicit backend log level; overrides `--verbose` and `--debug` log-level selection. | +| `KANDEV_HEALTH_TIMEOUT_MS` | `45000` | Positive integer startup-health timeout. Invalid or non-positive values fall back to 45 seconds. | +| `KANDEV_NO_BROWSER` | unset | The exact value `1` suppresses browser opening. | +| `KANDEV_BUNDLE_DIR` | selected by installer | Advanced packaging override. The npm shim and Homebrew wrapper set this; a bad path is a fatal runtime validation error. | +| `KANDEV_VERSION` | unset | Optional installer-supplied display metadata (the Homebrew wrapper sets it). | +| `KANDEV_SHUTDOWN_DEBUG` | unset | The exact value `1` prints launcher process IDs, commands, paths, signals, and graceful/forced shutdown decisions. Use temporarily for shutdown diagnosis. | + +The launcher also sets the selected server and `agentctl` ports for the backend. Treat its supervisor socket and manifest under `/supervisor/` as private implementation state, not a control API. + +`--debug` sets `KANDEV_DEBUG_AGENT_MESSAGES=true` and `KANDEV_DEBUG_PPROF_ENABLED=true`. ACP logs can contain full prompts, file content, and tool calls, while diagnostic endpoints expose process details. Use debug mode only on a trusted machine and remove retained debug logs afterward. [Configuration](./configuration.md) lists their location and retention controls. + +## Data and cleanup + +The default persistent root is `~/.kandev` (on Windows, `.kandev` below the user's profile directory). The SQLite database normally resides at `/data/kandev.db`; repositories, task worktrees, logs, and encrypted settings also live below the home root. + +Runtime program files live in the Homebrew Cellar, the global npm dependency tree, or npm's `_npx` cache. They are not application data. `npm config get cache` and `npm root -g` show the latter two roots. + +Uninstalling the package does not remove ``. Before removing that directory, stop Kandev and any service, confirm no task or executor is running, and take a backup. See [Operations](./operations.md) for safe database backup and restore procedures. + +## Update + +The installer owns CLI updates: + +```bash +brew upgrade kandev +npm install -g kandev@latest +npx -y kandev@latest +``` + +Release packages pin the shim and native runtime packages to the same SemVer. Do not copy only one binary from a different release into a bundle. A service unit contains installation-specific executable and bundle paths; after the package upgrade, reinstall it with the same service flags and restart it as described above. ## Troubleshooting -### "No Kandev runtime found for {platform}" +### No runtime package found -The CLI couldn't find an installed runtime. Install one: +Confirm npm 7 or later, an exact supported OS/architecture pair, and optional-dependency installation: ```bash -# via npm (requires npm 7+ for optional dep resolution) -npx kandev@latest -# via Homebrew -brew install kdlbs/kandev/kandev +npm --version +npm install -g kandev@latest +kandev --version ``` -If you're on npm 6 or older, optional dependencies aren't installed by `npx`. Upgrade npm: `npm install -g npm@latest`. +Remove `--omit=optional` from npm configuration for this install. On unsupported targets, use a supported machine or remote environment; do not point `KANDEV_BUNDLE_DIR` at a bundle from another platform. -### Port Already in Use +### A required binary or remote helper is missing -The CLI automatically finds available ports. If you need a specific port: +Reinstall or upgrade the whole package. Runtime validation intentionally fails when `kandev`, `agentctl`, or a required Linux/macOS remote helper is absent. Mixing archives or pruning package files produces this error. + +### The requested port is already in use + +Omit the explicit port to allow fallback, or choose a verified free port: ```bash kandev --port 18080 ``` -### Backend Takes Too Long to Start +If the failure remains, check both the configured `KANDEV_SERVER_HOST` and local listeners. The launcher's loopback preflight cannot guarantee that a later `0.0.0.0` bind will succeed. -Increase the health check timeout: +### Startup health check times out + +First run with visible logs, then increase the timeout only if startup is genuinely slow: ```bash -KANDEV_HEALTH_TIMEOUT_MS=60000 kandev +kandev --verbose +KANDEV_HEALTH_TIMEOUT_MS=90000 kandev --verbose ``` -### Start Mode: "Backend binary not found" +The launcher prints buffered backend output when startup fails. Common causes are an invalid `config.yaml`, a database migration/permission error, an occupied explicit port, or a damaged runtime bundle. -Build the project first: +### Browser does not open -```bash -make build -kandev start -``` +Open the printed URL manually. Linux requires a working `xdg-open`; WSL often needs manual browser launch. `--headless` and `KANDEV_NO_BROWSER=1` intentionally skip the opener. -## Data Storage +### Configuration appears ignored -| Path | Contents | -|------|----------| -| `~/.kandev/data/` | SQLite database and app data | -| Homebrew Cellar | Installed runtime (when installed via brew) | -| Under the global package root printed by `npm root -g` | Installed runtime dependency tree for `npm install -g kandev` | -| `/_npx//node_modules/` | Temporary package tree, including the platform runtime, for `npx kandev` | - -Run `npm config get cache` to print ``. npm chooses the `_npx` -run-directory name and may remove cached runs, so do not treat that path as -persistent application data. +The public CLI has no `--config` option. `config.yaml` must be in the launcher's backend working directory or `/etc/kandev/`, and environment values override it. For predictable installed/service deployments, prefer explicit environment variables and verify the active home and database paths printed at startup. See [Configuration](./configuration.md). diff --git a/docs/public/configuration.md b/docs/public/configuration.md index 2357b03ca8..30a6521978 100644 --- a/docs/public/configuration.md +++ b/docs/public/configuration.md @@ -5,150 +5,363 @@ description: "Configure Kandev workspaces, runtimes, settings, and environment b # Configuration -Kandev's backend reads configuration from three sources, in this order of precedence (later sources override earlier ones): +Kandev has three distinct configuration surfaces: -1. Built-in defaults (`apps/backend/internal/common/config/config.go`). -2. A YAML config file (`config.yaml`). -3. Environment variables (`KANDEV_*`). +- backend startup configuration: defaults, `config.yaml`, then environment variables; +- persistent product settings edited in the web UI and stored in the database; and +- executor, agent, repository, and workflow profiles stored through their own Settings pages. -Both the file and env vars are optional; the backend boots with sensible defaults out of the box. See [`docker.md`](./docker.md) and [`k8s.md`](./k8s.md) for deployment-specific tables; this page is the full reference. +This page is the startup-configuration reference. Executor-specific fields are covered in [Executors](./executors.md), and deployment examples are in [Docker](./docker.md), [Kubernetes](./k8s.md), and [Run as a service](./run-as-a-service.md). -## Config file +## Load order and lifecycle -The backend looks for `config.yaml` in the following order: +At backend startup, later sources override earlier ones: -- An explicit path passed at startup (used in tests). -- The current working directory. -- `/etc/kandev/`. +1. embedded production-profile values and built-in defaults; +2. the first readable `config.yaml`; and +3. environment variables. -A missing file is not an error - defaults plus env vars take over. +The public launcher has no `--config` option. It searches for a file named exactly `config.yaml` in the backend working directory and then `/etc/kandev/`. A missing file is allowed. Malformed YAML, unmarshal errors, and validated invalid values stop startup. -## Environment variables +Configuration is read at process start; there is no file watcher. Restart Kandev after changing YAML or environment variables. The CLI, desktop shell, service manager, Docker, or Kubernetes may set environment variables on the backend, so those values can override a file unexpectedly. Use the process/service/container environment as the final source of truth. -Backend configuration normally uses the `KANDEV_` prefix. Nested keys map by replacing `.` with `_` and uppercasing; **camelCase becomes one uppercase run** (no underscore inserted), because Viper does not synthesize a snake_case form. A small set of explicitly bound compatibility aliases is also supported, including the unprefixed `AGENTCTL_PORT`. +## Environment-variable naming -| YAML key | Env var | -|---|---| -| `server.port` | `KANDEV_SERVER_PORT` | -| `server.webInternalUrl` | `KANDEV_SERVER_WEBINTERNALURL` (or alias `KANDEV_WEB_INTERNAL_URL`) | -| `database.dbName` | `KANDEV_DATABASE_DBNAME` | -| `logging.maxSizeMb` | `KANDEV_LOGGING_MAXSIZEMB` | -| `homeDir` | `KANDEV_HOME_DIR` (alias) | -| `logging.level` | `KANDEV_LOG_LEVEL` (alias) | -| `agent.standalonePort` | `AGENTCTL_PORT` or `KANDEV_AGENT_STANDALONE_PORT` (aliases) | +Viper maps a nested YAML key by replacing `.` with `_`, adding `KANDEV_`, and uppercasing. It does **not** split camelCase words. For example: -The aliases on the right are explicit bindings - see `LoadWithPath` in `config.go` for the full list. New keys should follow the deterministic rule (`KANDEV_
_`) unless compatibility requires an explicit alias. +```text +database.dbName -> KANDEV_DATABASE_DBNAME +logging.maxSizeMb -> KANDEV_LOGGING_MAXSIZEMB +repoClone.basePath -> KANDEV_REPOCLONE_BASEPATH +``` -## Full `config.yaml` example +Some common camelCase keys have explicit compatibility aliases. Use the documented exact names below; snake_case spellings not listed here are not equivalent. -Every key shown here has a default - copying the whole file changes nothing. Use it as a starting point and delete what you don't need to override. +## Complete backend reference + +### Root and server + +| YAML key | Environment variable | Default | Current behavior | +|---|---|---|---| +| `homeDir` | `KANDEV_HOME_DIR` | `~/.kandev` | Root for data, tasks, worktrees, cloned repositories, sessions, and logs. A leading `~/` expands. | +| `server.host` | `KANDEV_SERVER_HOST` | `0.0.0.0` | HTTP listen address. Use `127.0.0.1` for local-only access. | +| `server.port` | `KANDEV_SERVER_PORT` | `38429` | UI, HTTP API, WebSocket, and MCP port; must be `1`-`65535`. The launcher normally supplies its selected port. | +| `server.readTimeout` | `KANDEV_SERVER_READTIMEOUT` | `30` | HTTP read timeout in seconds. | +| `server.writeTimeout` | `KANDEV_SERVER_WRITETIMEOUT` | `30` | HTTP write timeout in seconds. | +| `server.webInternalUrl` | `KANDEV_WEB_INTERNAL_URL` | empty | Development reverse-proxy target for a separately running web app. Installed releases normally serve embedded assets. | + +The default host exposes the server on every interface even though the CLI prints a `localhost` URL. The current local product path must not be treated as an authenticated multi-user perimeter. For remote access, bind to loopback and use a trusted authenticated tunnel/proxy, or isolate the network at the deployment layer. + +### Database + +| YAML key | Environment variable | Default | Current behavior | +|---|---|---|---| +| `database.driver` | `KANDEV_DATABASE_DRIVER` | `sqlite` | `sqlite` or `postgres` (case-normalized). | +| `database.path` | `KANDEV_DATABASE_PATH` | `/data/kandev.db` | SQLite database path. Empty resolves to the default. | +| `database.host` | `KANDEV_DATABASE_HOST` | `localhost` | PostgreSQL only. | +| `database.port` | `KANDEV_DATABASE_PORT` | `5432` | PostgreSQL only; must be `1`-`65535`. | +| `database.user` | `KANDEV_DATABASE_USER` | `kandev` | Required and non-empty for PostgreSQL. | +| `database.password` | `KANDEV_DATABASE_PASSWORD` | empty | PostgreSQL password; requirement depends on server authentication policy. | +| `database.dbName` | `KANDEV_DATABASE_DBNAME` | `kandev` | Required and non-empty for PostgreSQL. | +| `database.sslMode` | `KANDEV_DATABASE_SSLMODE` | `disable` | `disable`, `require`, `verify-ca`, or `verify-full`. | +| `database.maxConns` | `KANDEV_DATABASE_MAXCONNS` | `25` | PostgreSQL maximum pool size. | +| `database.minConns` | `KANDEV_DATABASE_MINCONNS` | `5` | PostgreSQL minimum pool size. | + +SQLite is the supported default and enables WAL mode. PostgreSQL deployments must provision the database, network policy, TLS trust, backups, and credentials before starting Kandev. Passing the password in an environment variable avoids putting it in YAML but still exposes it to processes/administrators allowed to inspect the environment; use your platform's secret injection controls. + +`database.path` is an advanced override. Persistence honors it, but the current **Settings → System → Database** and **Backups** services derive their displayed path, WAL files, backup source, and restore destination from `/data/kandev.db`. Those UI operations are therefore not reliable for a custom SQLite location. Use operator-managed backup/restore for a custom path and verify the actual database path printed at startup. + +Database-only snapshots also omit `/data/master.key`, the AES-256 key used to decrypt stored secrets. Preserve that owner-only key with an independently secured home/data backup; restoring the database without its matching key leaves encrypted credentials unreadable. See [Operations](./operations.md). + +### Event bus and NATS + +| YAML key | Environment variable | Default | Current behavior | +|---|---|---|---| +| `nats.url` | `KANDEV_NATS_URL` | empty | Empty uses the in-process event bus; otherwise connect to NATS. | +| `nats.clusterId` | `KANDEV_NATS_CLUSTERID` | `kandev-cluster` | Accepted compatibility field; the current NATS client does not consume it. | +| `nats.clientId` | `KANDEV_NATS_CLIENTID` | `kandev-client` | NATS connection name. | +| `nats.maxReconnects` | `KANDEV_NATS_MAXRECONNECTS` | `10` | Reconnect limit; the client uses a two-second reconnect wait and a 5 MiB reconnect buffer. | +| `events.namespace` | `KANDEV_EVENTS_NAMESPACE` | derived | Queue-group namespace. Empty derives a stable, sanitized hash from database identity. | + +An external NATS URL moves event traffic across the configured network and can embed credentials/TLS parameters. Protect it as a secret where applicable, require TLS for untrusted networks, and keep namespaces distinct when deployments share one NATS server. `clusterId` does not provide isolation in the current implementation. + +### Docker runtime + +| YAML key | Environment variable | Default | Current behavior | +|---|---|---|---| +| `docker.enabled` | `KANDEV_DOCKER_ENABLED` | `true` | Registers the local Docker executor. The client connects lazily, so startup can succeed without a daemon. | +| `docker.host` | `KANDEV_DOCKER_HOST` | `DOCKER_HOST`, otherwise platform socket | Docker endpoint used by the client. Defaults to `unix:///var/run/docker.sock` on Unix and `npipe:////./pipe/docker_engine` on Windows. | +| `docker.apiVersion` | `KANDEV_DOCKER_APIVERSION` | empty | Empty uses Docker API negotiation. | +| `docker.tlsVerify` | `KANDEV_DOCKER_TLSVERIFY` | `false` | Accepted compatibility field; not wired into the current client. | +| `docker.defaultNetwork` | `KANDEV_DOCKER_DEFAULTNETWORK` | `kandev-network` | Accepted compatibility field; not wired into current executor networking. | +| `docker.volumeBasePath` | `KANDEV_DOCKER_VOLUMEBASEPATH` | `/var/lib/kandev/volumes` on Unix; `%LOCALAPPDATA%\kandev\volumes` on Windows | Accepted compatibility field; not wired into current executor volume placement. | + +The Docker socket is effectively root-equivalent on many hosts. Do not publish it or assume `docker.tlsVerify` secures a TCP daemon—it currently does not. Configure TLS through a supported Docker endpoint/environment and validate it independently, or keep the daemon local. See [Docker](./docker.md) and [Executors](./executors.md). + +### Core agent service + +| YAML key | Environment variable | Default | Current behavior | +|---|---|---|---| +| `agent.standaloneHost` | `KANDEV_AGENT_STANDALONE_HOST` | `localhost` | Host of the core `agentctl` control server. | +| `agent.standalonePort` | `AGENTCTL_PORT` or `KANDEV_AGENT_STANDALONE_PORT` | `39429` | Preferred control port. The launcher may supply a free fallback. | + +The launcher starts `agentctl`, performs a one-time nonce handshake, and supplies the resulting per-launch token internally. Do not persist or proxy its bootstrap/auth state. Agent command, model, environment, permission, and MCP configuration belongs in agent profiles rather than this section. + +### Authentication, Office, voice, and feature flags + +| YAML key | Environment variable | Default | Current behavior | +|---|---|---|---| +| `auth.jwtSecret` | `KANDEV_AUTH_JWTSECRET` | generated value | Accepted and validated compatibility configuration; the current main HTTP product path does not use it as an authentication boundary. | +| `auth.tokenDuration` | `KANDEV_AUTH_TOKENDURATION` | `3600` | Must be positive, but is not consumed by the current main HTTP product path. | +| `office.jwtSigningKey` | `KANDEV_OFFICE_JWTSIGNINGKEY` | random per start | HMAC key for Office agent-runtime JWTs. Set a stable secret when Office tasks must survive restarts. | +| `voice.openAIApiKey` | `KANDEV_VOICE_OPENAI_API_KEY` | empty | Server-side transcription fallback when browser speech recognition is unavailable. Empty disables the fallback and its endpoint returns unavailable. | +| `features.office` | `KANDEV_FEATURES_OFFICE` | `false` in production | Experimental Office UI, routes, services, and automation. | + +Do not infer security from `auth.jwtSecret`: setting it currently does not turn the local server into an authenticated public service. Office's JWT key has a narrower, active purpose. Store both active secrets and third-party API keys in your deployment secret manager; never commit them in `config.yaml`. + +The voice fallback sends audio to the configured OpenAI transcription service and incurs that provider's network, data-handling, and billing behavior. Browser-native speech recognition has its own browser/vendor behavior and does not use this server key. + +### Logging + +| YAML key | Environment variable | Default | Current behavior | +|---|---|---|---| +| `logging.level` | `KANDEV_LOG_LEVEL` | `info` | `debug`, `info`, `warn`, or `error`. The CLI normally supplies `warn`, `info`, or `debug`. | +| `logging.format` | `KANDEV_LOGGING_FORMAT` | `text`, or `json` in production/Kubernetes | `text` or `json`; `auto` is not accepted. | +| `logging.outputPath` | `KANDEV_LOGGING_OUTPUTPATH` | `stdout` | `stdout`, `stderr`, or a file path. | +| `logging.maxSizeMb` | `KANDEV_LOGGING_MAXSIZEMB` | `100` | File-output rotation threshold. Zero uses lumberjack's 100 MiB default. | +| `logging.maxBackups` | `KANDEV_LOGGING_MAXBACKUPS` | `5` | Rotated-file count; zero means unlimited. | +| `logging.maxAgeDays` | `KANDEV_LOGGING_MAXAGEDAYS` | `30` | Rotated-file age; zero means unlimited. | +| `logging.compress` | `KANDEV_LOGGING_COMPRESS` | `true` | Gzip rotated files. | + +The format default becomes JSON when `KUBERNETES_SERVICE_HOST` is non-empty or `KANDEV_ENV` is exactly `production`/`prod`; otherwise it is text. Rotation settings apply only to file output. Active log files are created owner-only (`0600`) on Unix, so a log shipper running as another user cannot read them without an explicit permission design. + +Debug output may contain repository paths, subprocess output, prompts, file content, and tool-call data. Treat it as sensitive. + +### Repository, worktree, and clone paths + +| YAML key | Environment variable | Default | Current behavior | +|---|---|---|---| +| `repositoryDiscovery.roots` | `KANDEV_REPOSITORYDISCOVERY_ROOTS` | `[]` | Local roots exposed to repository discovery. Prefer absolute paths. Array encoding through environment variables is Viper-dependent; YAML is clearer. | +| `repositoryDiscovery.maxDepth` | `KANDEV_REPOSITORYDISCOVERY_MAXDEPTH` | `5` | Positive directory traversal depth. | +| `worktree.enabled` | `KANDEV_WORKTREE_ENABLED` | `true` | Enables the worktree provider. | +| `worktree.defaultBranch` | `KANDEV_WORKTREE_DEFAULTBRANCH` | `main` | Accepted compatibility field; current task behavior uses each repository's stored/detected default branch instead. | +| `worktree.cleanupOnRemove` | `KANDEV_WORKTREE_CLEANUPONREMOVE` | `true` | Accepted compatibility field; current lifecycle cleanup is controlled by repository/task operations, not this value. | +| `worktree.fetchTimeoutSeconds` | `KANDEV_WORKTREE_FETCHTIMEOUTSECONDS` | `60` | Git fetch timeout during worktree preparation. | +| `worktree.pullTimeoutSeconds` | `KANDEV_WORKTREE_PULLTIMEOUTSECONDS` | `60` | Git pull timeout during worktree preparation. | +| `repoClone.basePath` | `KANDEV_REPOCLONE_BASEPATH` | `/repos` | Base directory for provider-backed clones. A leading `~/` expands. | + +Discovery roots grant Kandev visibility into local filesystem trees and repositories. Scope them narrowly. Worktrees and clones can contain credentials or generated files ignored by Git; review repository copy-file and setup/cleanup settings before remote execution. See [Git operations](./git-operations.md). + +### Debug configuration + +| YAML key | Environment variable | Default | Current behavior | +|---|---|---|---| +| `debug.devMode` | `KANDEV_DEBUG_DEV_MODE` | `false` | Enables diagnostic endpoints and agent-message debug logging. | +| `debug.pprofEnabled` | `KANDEV_DEBUG_PPROF_ENABLED` | `false` | Legacy alias; also enables debug mode. | + +Debug mode is high risk. It enables local diagnostic surfaces and implies `KANDEV_DEBUG_AGENT_MESSAGES=true` and `KANDEV_DEBUG_PPROF_ENABLED=true` when not explicitly locked by the environment. ACP JSONL frames include complete prompts, file content, and tool calls. Do not enable it on a shared or network-exposed backend. + +## Minimal examples + +For a local-only CLI server with an isolated home: + +```bash +KANDEV_SERVER_HOST=127.0.0.1 \ +KANDEV_HOME_DIR="$PWD/.kandev-local" \ +kandev --headless +``` + +For a file-based deployment, override only what is needed: + +```yaml +homeDir: "/srv/kandev" + +server: + host: "127.0.0.1" + port: 38429 + +logging: + format: "json" + outputPath: "/var/log/kandev/backend.log" + +repositoryDiscovery: + roots: + - "/srv/repositories" +``` + +A complete shape, including compatibility fields, is: ```yaml -# Kandev root directory. Empty = ~/.kandev (or KANDEV_HOME_DIR if set). -# All workspace artifacts (data, tasks, worktrees, repos, sessions) live here. homeDir: "" server: host: "0.0.0.0" - port: 38429 # API + WebSocket + Web UI - readTimeout: 30 # seconds - writeTimeout: 30 # seconds - webInternalUrl: "" # internal URL the backend uses to call the web app + port: 38429 + readTimeout: 30 + writeTimeout: 30 + webInternalUrl: "" database: - driver: "sqlite" # "sqlite" or "postgres" - path: "" # sqlite: empty = $homeDir/data/kandev.db - - # postgres-only fields below (ignored when driver=sqlite) + driver: "sqlite" + path: "" host: "localhost" port: 5432 - user: "kandev" # required when driver=postgres - password: "" # required when driver=postgres in most setups - dbName: "kandev" # required when driver=postgres - sslMode: "disable" # disable | require | verify-ca | verify-full + user: "kandev" + password: "" + dbName: "kandev" + sslMode: "disable" maxConns: 25 minConns: 5 nats: - url: "" # empty = use in-memory event bus - clusterId: "kandev-cluster" + url: "" + clusterId: "kandev-cluster" # compatibility-only today clientId: "kandev-client" maxReconnects: 10 events: - namespace: "" # empty = derive from runtime data identity + namespace: "" docker: - enabled: true # disables Docker-based executors when false - host: "" # empty = platform default (unix:///var/run/docker.sock, etc.) - apiVersion: "" # empty = auto-negotiate - tlsVerify: false - defaultNetwork: "kandev-network" - volumeBasePath: "" # empty = /var/lib/kandev/volumes (Linux/macOS) + enabled: true + host: "unix:///var/run/docker.sock" # use the Windows named pipe on Windows + apiVersion: "" + tlsVerify: false # compatibility-only today + defaultNetwork: "kandev-network" # compatibility-only today + volumeBasePath: "/var/lib/kandev/volumes" # compatibility-only today agent: standaloneHost: "localhost" - standalonePort: 39429 # agentctl control port + standalonePort: 39429 auth: - jwtSecret: "" # empty = auto-generate a random dev secret on boot - tokenDuration: 3600 # seconds + jwtSecret: "" # compatibility-only for the main HTTP product path + tokenDuration: 3600 # compatibility-only for the main HTTP product path logging: - level: "info" # debug | info | warn | error - format: "text" # accepted values: text | json; omit for environment-based default - outputPath: "stdout" # stdout | stderr | /path/to/file.log - - # Rotation - only applied when outputPath is a file path. - # Active log files are created with mode 0600 (owner-only). - maxSizeMb: 100 # rotate at this size; 0 = lumberjack default (100MB) - maxBackups: 5 # 0 = unlimited - maxAgeDays: 30 # 0 = unlimited - compress: true # gzip rotated files + level: "info" + format: "text" + outputPath: "stdout" + maxSizeMb: 100 + maxBackups: 5 + maxAgeDays: 30 + compress: true repositoryDiscovery: - roots: [] # absolute paths to scan for local git repos + roots: [] maxDepth: 5 worktree: enabled: true - defaultBranch: "main" - cleanupOnRemove: true + defaultBranch: "main" # compatibility-only today + cleanupOnRemove: true # compatibility-only today fetchTimeoutSeconds: 60 pullTimeoutSeconds: 60 repoClone: - basePath: "" # empty = $homeDir/repos + basePath: "" debug: - pprofEnabled: false # enables /debug/pprof and /api/v1/debug/memory + devMode: false + pprofEnabled: false + +office: + jwtSigningKey: "" + +voice: + openAIApiKey: "" + +features: + office: false ``` -## Required vs optional +Copying this entire file is unnecessary and can freeze old defaults in a deployment. Keep only deliberate overrides. On Windows, do not copy the Unix Docker host/path literals from this example. -Almost every field has a default and is optional. The exceptions: +## Runtime feature toggles -| Field | When required | What happens otherwise | -|---|---|---| -| `database.user` | `database.driver=postgres` | Startup fails with `database.user is required for postgres driver` | -| `database.dbName` | `database.driver=postgres` | Startup fails with `database.dbName is required for postgres driver` | -| `database.password` | `database.driver=postgres` in most setups (some Postgres configs allow passwordless local auth) | Connection fails at runtime | -| `auth.jwtSecret` | Never strictly required, but in production set an explicit value | A random secret is generated on boot - tokens become invalid on restart | +**Settings → System → Feature Toggles** manages two startup-time flags: -Validated value sets (any other value is a startup error): +| Key | Environment lock | Production default | Effect | +|---|---|---|---| +| `features.office` | `KANDEV_FEATURES_OFFICE` | off | Experimental autonomous-agent Office surfaces and automation. | +| `debug.devMode` | `KANDEV_DEBUG_DEV_MODE` (also locked by explicit legacy/debug-message vars) | off | High-risk diagnostic endpoints and ACP frame logging. | -| Field | Allowed values | +UI changes are persisted in the database and require a restart. An explicitly set environment value wins and locks the UI control. Otherwise a database override wins over the embedded profile/default. Resetting a toggle removes its database override. + +The source checkout's `make dev` activates the embedded development profile, which enables Office, debug surfaces, ACP logging, and a mock agent. Installed `run`/desktop builds select the safe production profile unless the environment explicitly opts in. E2E mock variables and routes are test-only and must never be enabled on a public deployment. + +## Credentials and product settings + +Most integrations, executor profiles, agent profiles, MCP servers, repository settings, and UI preferences are persistent database records edited under **Settings**. They are not fields in `config.yaml`. Secret values use an encrypted secret store backed by `/data/master.key`; filesystem permissions, database backups, and key backup are part of the security boundary. + +For headless injection, Kandev can also read agent credentials from the process environment by their required name (for example `ANTHROPIC_API_KEY`) or the `KANDEV_`-prefixed form. `KANDEV_CREDENTIALS_FILE` adds a fallback JSON provider: + +```json +{ + "ANTHROPIC_API_KEY": "replace-at-deployment-time", + "OPENAI_API_KEY": "replace-at-deployment-time" +} +``` + +The file is loaded lazily, expects a flat string-to-string object, and is cached; restart after changing it. A missing file behaves as no file credentials, while unreadable or invalid JSON produces credential-resolution errors. The database secret store and environment providers are consulted before this file. Restrict file permissions to the Kandev service account and never commit it. + +Profile environment variables are eventually injected into agent subprocesses or remote executor environments. Anyone who can edit a profile, inspect a remote host/container, enable debug frame logs, or run commands as the Kandev account may be able to access them. Use least-privilege, task-scoped credentials and rotate them after exposure. + +## Advanced operator tuning + +These startup-only variables are supported by specific runtime components but are not YAML fields. Leave them at defaults unless diagnosing a measured problem. + +| Variable | Default | Parsing and effect | +|---|---:|---| +| `KANDEV_GH_MAX_CONCURRENT` | `8` | Positive integer process-wide cap for `gh` subprocesses; invalid/non-positive uses default. | +| `KANDEV_GIT_MAX_CONCURRENT` | `12` | Positive integer process-wide cap for `git` subprocesses; invalid/non-positive uses default. | +| `KANDEV_QUEUE_MAX_PER_SESSION` | `10` | Queued user messages per session. Invalid uses default; zero/negative disables the cap. | +| `KANDEV_ACP_IDLE_TIMEOUT` | `1h` | Go duration after which idle agentctl instances are reaped; `0` disables. Invalid uses default. | +| `KANDEV_ACP_IDLE_REAPER_INTERVAL` | `1m` | Go duration between idle scans. Intended mainly for testing; use the default in production. | +| `KANDEV_ACP_NOTIF_QUEUE` | `131072` | Per-connection ACP inbound notification capacity; positive values clamp to `1024`-`131072`, invalid uses default. | +| `KANDEV_PLAN_COALESCE_WINDOW_MS` | `300000` | Non-negative milliseconds for same-author plan revision coalescing; invalid/negative uses five minutes. | +| `KANDEV_OFFICE_SCHEDULER_TICK_MS` | `5000` | Positive integer safety-net interval for queued/retry run claiming. New-run signals are event-driven; invalid/non-positive uses five seconds. | +| `KANDEV_MCP_LOG_FILE` | unset | File path for per-agentctl MCP debug logs. Logs tool names, arguments, session IDs, results for tool errors, and timings; invalid paths warn and disable this sink. | +| `OTEL_EXPORTER_OTLP_ENDPOINT` | unset | Enables OTLP/HTTP tracing for backend and agentctl spans; unset uses a no-op tracer. See the transport warning below. | + +Changing concurrency/queue values trades memory and process pressure against throughput. Values are captured during process/component initialization and need a restart. + +The current OTLP exporter strips an `http://` or `https://` prefix from the configured endpoint and always uses `WithInsecure()`. Treat this as implementation-bound cleartext transport: send it only to a trusted private collector over a protected network, not directly across an untrusted network. The service name is `kandev-agentctl`, and spans can include task/session/execution IDs plus raw agent-event JSON truncated to 8192 characters. That payload can contain prompts, files, and tool data. Use collector-side access controls and retention accordingly. + +### ACP debug-log controls + +These apply only when `KANDEV_DEBUG_AGENT_MESSAGES=true`: + +| Variable | Default | |---|---| -| `server.port` | `1`-`65535` | -| `database.driver` | `sqlite`, `postgres` | -| `database.port` | `1`-`65535` (only validated when `driver=postgres`) | -| `database.sslMode` | `disable`, `require`, `verify-ca`, `verify-full` (only validated when `driver=postgres`) | -| `logging.level` | `debug`, `info`, `warn`, `error` | -| `logging.format` | `json`, `text` | +| `KANDEV_DEBUG_LOG_DIR` | `/logs/acp` | +| `KANDEV_DEBUG_ACP_MAX_FILES` | `200` | +| `KANDEV_DEBUG_ACP_RETENTION_HOURS` | `48` | +| `KANDEV_DEBUG_ACP_MAX_FILE_BYTES` | `8388608` (8 MiB) | -## Tips +Retention values must be positive integers; invalid/non-positive values use defaults. Directories and files use owner-only `0700`/`0600` modes on Unix. Rotation, age pruning, and file-count pruning bound normal growth, but these files remain highly sensitive and can exist inside a Docker executor rather than on the host. -- **Env vars override the file.** Useful for secrets (`KANDEV_DATABASE_PASSWORD`) and per-environment knobs (`KANDEV_LOG_LEVEL`). -- **K8s / Docker:** prefer env vars for everything; skip the YAML file entirely. -- **Local dev:** drop a `config.yaml` next to where you run the backend; viper picks it up from the current working directory. -- **Format auto-detection:** when `logging.format` is omitted, Kandev defaults to `json` if `KANDEV_ENV` is `production`/`prod` or `KUBERNETES_SERVICE_HOST` is set; otherwise it defaults to `text`. The literal value `auto` is not accepted. +## Validation and troubleshooting + +Startup validation currently enforces: + +- `server.port`: `1`-`65535`; +- `database.driver`: `sqlite` or `postgres`; +- PostgreSQL port, non-empty user/database name, and supported SSL mode; +- positive `auth.tokenDuration`; +- logging level/format; and +- positive `repositoryDiscovery.maxDepth`. + +Other fields can pass configuration validation and still fail later—for example an unreachable NATS/PostgreSQL/Docker endpoint, unwritable log path, nonsensical timeout, or incompatible pool sizes. A field appearing in the schema does not prove its subsystem is available. + +If a value appears ignored: + +1. confirm the exact environment spelling, especially camelCase keys; +2. inspect the launcher/service/container environment for an overriding value; +3. confirm `config.yaml` is in the backend working directory or `/etc/kandev/`; +4. restart the backend; and +5. check whether the field is marked compatibility-only above. + +Use `kandev --verbose` to surface startup errors. Do not use `--debug` merely to diagnose a YAML typo on an exposed machine; verbose logs are usually sufficient. + +Variables used only to assemble/test the runtime—such as `KANDEV_WEB_DIST_DIR`, `KANDEV_DESKTOP_RUNTIME_DIR`, mock/E2E switches, supervisor socket/manifest values, and bootstrap nonces—are internal implementation contracts, not supported deployment configuration. `KANDEV_BUNDLE_DIR` is the narrow exception documented for installer/package integration in [CLI](./cli.md); end users should still let the installer set it. diff --git a/docs/public/contributing.md b/docs/public/contributing.md index 661342698f..4f02afeeeb 100644 --- a/docs/public/contributing.md +++ b/docs/public/contributing.md @@ -1,15 +1,15 @@ --- title: "Contributing to Kandev" -description: "Set up the repository, choose the right change boundary, run checks, update public docs, and prepare a reviewable pull request." +description: "Set up Kandev, find the owning subsystem, run focused checks, update public behavior, and prepare a reviewable change." --- # Contributing to Kandev -Kandev is a Go backend, Vite/React web application, Node launcher, Tauri desktop shell, and a collection of runtime helpers. Start by understanding the user-visible behavior and the ownership boundary of the subsystem you intend to change. +Kandev combines a Go server and native launcher, a Vite/React web client, a TypeScript development supervisor and npm shim, a Tauri desktop shell, and task-environment helpers. Begin at the subsystem that owns the behavior; do not recreate its rules in a neighboring layer. ## Set up the repository -The pinned toolchain is defined in `mise.toml`: Node 24, pnpm 9.15.9, Go 1.26.0, and supporting tools. +The pinned toolchain in `mise.toml` currently includes Node 24, pnpm 9.15.9, Go 1.26.0, and supporting tools. ```bash git clone https://github.com/kdlbs/kandev.git @@ -17,46 +17,45 @@ cd kandev make bootstrap ``` -For browser end-to-end work, also run: +`make bootstrap` best-effort installs host prerequisites, installs the pinned tools, runs `pnpm install --frozen-lockfile` in `apps/`, and configures hooks. For browser E2E dependencies, use `make bootstrap-e2e`. Run `make doctor` to re-install the optional pre-commit and commit-message hooks. -```bash -make bootstrap-e2e -``` - -`make bootstrap` installs the pinned tools when needed, installs workspace dependencies, and configures repository hooks. Run `make doctor` when local and CI behavior disagree. - -## Start the development application +## Run locally ```bash make dev ``` -This starts the Go backend and Vite web development server with automatic port selection. The terminal output shows the actual URLs. Use `make dev-backend` or `make dev-web` when isolating one side. +This is the normal development path. The TypeScript supervisor starts the Go backend and Vite, selects available ports, points Go at Vite, and isolates application state under the checkout's `.kandev-dev/`. Use the printed URLs. -The production-shaped local path is: +`make dev-web` starts only Vite on its fixed development port; it has no live API by itself. `make dev-backend` starts only the backend with the normal production-profile home unless you override it. For an intentionally isolated backend-only run: ```bash -make build -make start +KANDEV_HOME_DIR="$PWD/.kandev-dev" KANDEV_DEBUG_DEV_MODE=true make dev-backend ``` -## Find the right layer +Use `make build` for a production build. Use `make start` for a production-shaped local start; it installs dependencies, builds and synchronizes the embedded web application, then launches Kandev. -| Change | Start in | +## Find the owner + +| Change | Start here | |---|---| -| Task, workflow, repository, integration, persistence, or API behavior | `apps/backend/internal//` | -| Agent process, ACP, MCP, terminal, Git, or remote runtime behavior | `apps/backend/internal/agent/`, `agentctl/`, or the executor package | -| User interface, state, routing, or browser API calls | `apps/web/` | -| CLI launch/install behavior | `apps/cli/` and `apps/backend/internal/launcher/` | -| Desktop shell behavior | `apps/desktop/` | -| Public user documentation | `docs/public/` | -| CI/release behavior | `.github/workflows/` and `scripts/` | +| Domain behavior, API, integration, or persistence | `apps/backend/internal//` | +| Server construction and startup wiring | `apps/backend/internal/backendapp/` | +| Agent definitions, discovery, runtime, or executors | `apps/backend/internal/agent/` | +| agentctl binary and task-environment sidecar | `apps/backend/cmd/agentctl/`, `apps/backend/internal/agentctl/` | +| Browser routes, state, API calls, or UI | `apps/web/` | +| Shared web UI, types, or themes | `apps/packages/` | +| Development supervisor or npm runtime shim | `apps/cli/` | +| Installed native launcher behavior | `apps/backend/internal/launcher/` | +| Tauri process, updater, or native desktop boundary | `apps/desktop/` | +| Public documentation | `docs/public/` | +| CI, packaging, or release automation | `.github/workflows/`, `scripts/` | -Read the nearest tests and existing domain types before adding a new abstraction. Prefer an established handler/service/repository or API/state/component path over a parallel framework. +Read the nearest types, tests, and startup registration before adding an abstraction. Backend domains do not all use one layout: follow the local handler/controller, service, repository/store, and provider pattern. -## Develop with focused checks +## Test while developing -During iteration, run the narrowest relevant command. Before opening a PR, run: +Run the narrowest relevant package or workspace test during iteration. Before review, format first, then run: ```bash make fmt @@ -65,39 +64,34 @@ make test make lint ``` -Any change under `apps/web/` must add or update Playwright coverage in `apps/web/e2e/` and run `make test-e2e`. See [Testing](testing.md) for targeted commands and fixture rules. +`make test` covers Go, web, CLI, and repository-script tests. Browser Playwright, PostgreSQL, real-agent, container, and desktop-launch suites have separate prerequisites and commands; see [Testing](testing.md). -## Update public behavior and docs together +Repository policy requires every change under `apps/web/` to add or update a Playwright scenario in `apps/web/e2e/` and run `make test-e2e`. -If a change affects commands, configuration, settings, workflows, executors, integrations, APIs, screenshots, status language, or user-facing terminology, update `docs/public/**` in the same PR. +## Keep public behavior current -When adding a page: +Update `docs/public/**` in the same change when commands, configuration, settings, workflows, executors, integrations, APIs, screenshots, support status, or user terminology changes. -1. create a flat, stable Markdown slug in `docs/public/`; -2. add title and description frontmatter; -3. add the slug once to `docs/public/meta.json`; -4. link it from the relevant overview and related pages; -5. run `node scripts/validate-public-docs.mjs`. +For a new page, create a stable Markdown slug with title and description frontmatter, add it once to `docs/public/meta.json`, link it from related pages, and run: -The full publication contract is in the [docs contribution guide](README.md). +```bash +node --test scripts/validate-public-docs.test.mjs +node scripts/validate-public-docs.mjs +``` -## Prepare the pull request +The complete source and Landing build contract is in the [public docs contribution guide](README.md). -- Keep one logical change per PR. -- Explain user impact and the technical boundary. -- List exact automated and manual verification. -- Include screenshots or a short recording for UI behavior. -- Call out migrations, new credentials, security changes, or compatibility risks. -- Do not mix generated artifacts, local databases, recordings, or unrelated formatting into the diff. -- Understand and be able to explain any agent-generated code you submit. +## Review checklist -The root `CONTRIBUTING.md` contains the community and licensing policy. By contributing, you agree to the repository's AGPL-3.0 license. +- The change has one clear user impact and owning subsystem. +- Wire changes update Go DTOs, TypeScript types/clients, compatibility behavior, and protocol docs together. +- Persistence changes include fresh-schema and upgrade-path tests. +- Credentials, external text, shell arguments, URLs, paths, and logs respect their trust boundary. +- Exact automated and manual checks are recorded; skipped suites have a reason. +- UI changes include Playwright coverage plus screenshots or a short recording. +- Generated artifacts, local databases, recordings, and unrelated formatting are absent. +- You understand and can explain all submitted code, including agent-generated code. -## Next guides +The root `CONTRIBUTING.md` defines community and licensing policy. Contributions are under the repository's AGPL-3.0 license. -- [Architecture](architecture.md) -- [Backend development](backend-development.md) -- [Web development](web-development.md) -- [Testing](testing.md) -- [Extending Kandev](extending-kandev.md) -- [Release process](release-process.md) +Continue with [Architecture](architecture.md), [Backend development](backend-development.md), [Web development](web-development.md), [Testing](testing.md), [Extending Kandev](extending-kandev.md), or [Release process](release-process.md). diff --git a/docs/public/coordination.md b/docs/public/coordination.md index 3ca3c7f0d6..ecc52f2d48 100644 --- a/docs/public/coordination.md +++ b/docs/public/coordination.md @@ -1,94 +1,220 @@ --- title: "Coordinate Work" -description: "Split work across subtasks, dependencies, repositories, branches, plans, messages, and pull requests." +description: "Coordinate parallel sessions, subtasks, repositories, branches, messages, and human gates." --- # Coordinate Work -Kandev supports both human-guided workflows and coordinator-led agent patterns. Both use the same task model: explicit repository scope, workflow state, profiles, dependencies, messages, and review surfaces. +Kandev can coordinate several agents without turning every unit of work into a separate task. Choose the smallest boundary that gives the work the isolation, workflow state, and review surface it needs. -## Subtasks +## Choose a coordination boundary -A child task is appropriate when a parent goal contains work that should have its own conversation, status, branch, or pull request. +| Need | Use | Filesystem relationship | Independent workflow state | +|---|---|---|---| +| Another agent on the same files and goal | Additional named session | Shares the task environment | No | +| A child deliverable that must see current uncommitted files | Subtask with **Inherit parent workspace** | Shares the parent's materialized workspace | Yes | +| A child deliverable needing isolated files or a different repository | Subtask with **Create new workspace** | Gets its own materialized environment | Yes | +| One deliverable spanning repositories | Multi-repository task | Separate worktree per attachment | No; one task workflow | -By default, a subtask inherits the parent task's: +Shared files make handoff cheap but allow concurrent edits to collide. Separate environments isolate file state, but only the executor determines whether host processes and credentials are also isolated. -- workspace and workflow; -- agent and executor profiles; -- repository attachments and base branches; -- materialized task workspace when `workspace_mode` is `inherit_parent`. +> **Office status:** Office is in progress and feature-flagged. Persistent coordinator teams, arbitrary-depth task trees, dependency properties, labels, named documents, reviewer/approver quorum, routines, and budgets are not stable regular-Kanban capabilities. -Use inherited workspace mode when the child is a focused phase or collaborator that must see the same uncommitted files. Concurrent writers can conflict because they are editing the same worktrees. +## Run parallel sessions in one task -Use a new materialized workspace when the child needs independent branches and filesystem state. A coordinator can also target a sibling repository explicitly so a frontend or infrastructure subtask stays under one parent while working in another repo. +Use an additional session when agents need the same task, repository attachments, and materialized environment. -## Dependencies + -Blocker relationships express order independently of the board column. Use them when a task cannot produce a valid result until another task finishes. Related-task views and task MCP expose parent, child, sibling, blocker, and blocked-by context. +1. Open the task workbench. +2. Choose **Add panel (+) → New Agent**, or run **New Agent** from the command panel. +3. Select a compatible agent profile. Missing credentials link to the relevant **Settings → Executors** or **Settings → Agents** configuration. +4. Choose context: -A dependency is coordination metadata, not a merge queue. The receiving task still needs to fetch, rebase, cherry-pick, or otherwise consume the prerequisite code according to the repository workflow. + - **Blank** starts without copied conversation context. + - **Copy initial prompt** copies the task's initial prompt. + - **Summarize session** asks the configured utility agent to summarize the current session. It is unavailable without a utility agent. -## Multiple repositories and branches +5. Enter the required prompt and select **Start Agent**. -One task can attach several repositories. Kandev materializes one workspace per attachment and groups files, diffs, review, and pull requests by repository. +The dialog shows the environment, branch, and executor the session will share. Two sessions can edit the same files concurrently; assign files or phases explicitly. -One task can also attach another branch from the same repository. Use that when the task intentionally produces separate PRs or needs to compare/work across branch lines. Each attachment records its own base branch; changing a diff base changes review context and ahead/behind calculations, not the actual commits. +Right-click a session tab to **Rename…**, **Set as Primary**, stop, resume, delete, share, use **Handoff** to another profile, or **Close Others**, when that action is available for the session state. Names are trimmed and limited to 120 characters. **Handoff** creates another session; it does not move the task or transfer its workflow state. -Before an agent works across attachments, state: +### Spawn a session from an agent + +Task agents can call `spawn_session_kandev` with a prompt and optional `name`, `agent_profile_id`, and `task_id`. + +- With no task ID, it adds a session to the current task. +- A target task must be in the same workspace as the spawning task. +- Profile selection prefers an explicit profile, then the spawner's profile for the same task, then the target task's primary-session profile. +- The spawned session receives hidden attribution context identifying the sender and reply route. +- A failure to apply the optional name does not fail an otherwise successful launch. + +Use the returned task ID, session ID, and state for subsequent targeted messages. + +## Create subtasks + +A subtask is appropriate when the child needs its own title, sessions, workflow position, history, or pull request. + +Open it from either: + +- the current task action beside **New Task** in the sidebar (**New subtask of current task**); or +- the task workbench **Task** split menu → **Subtask**. + +The dialog proposes `_Parent title_ / Subtask _N_`. Title and prompt are required, and **Create Subtask** starts the agent immediately. The subtask inherits the parent's workflow. Its profile starts from the active parent session's profile and can be changed, but the dialog does not filter profiles for executor compatibility. + +Choose a workspace mode: + +| Mode | Default and inheritance | Use it when | +|---|---|---| +| **Inherit parent workspace** | Default when the parent has an active worktree. Reuses the parent's executor, repositories, worktrees, branch, and current uncommitted files. Repository and executor controls are hidden. | The child is a focused collaborator on the same file state. | +| **Create new workspace** | Default when the parent has no active worktree branch. Lets you choose another configured/discovered repository, folder, remote URL, branch, and executor. | The child needs isolation, a different branch, or a different repository. | + +The context choices are **Blank**, **Copy initial prompt**, and, when a utility agent is configured, **Summarize session**. Context supplies background; it does not create a shared conversation. Attachments and prompt enhancement are also available. + +The subtask dialog currently does not enforce agent/executor credential compatibility or disable non-worktree executors after two or more repository rows are selected. For an isolated multi-repository subtask, choose **git-worktree**. For every subtask, choose an agent profile configured on the inherited or selected executor; otherwise creation can succeed while agent launch or repository materialization fails. + +Regular Kanban allows one subtask level: a root task can have children, but a child cannot have another child. Split further work into sibling subtasks, additional sessions, or a separate top-level task. Arbitrary-depth trees belong to the in-progress Office surface. + +### Create a subtask from an agent + +Call `create_task_kandev` with `parent_id: "self"`. `workspace_mode` defaults to `inherit_parent`; set it to `new_workspace` for isolated materialization. + +- `start_agent` defaults to `true`, so a description is required unless it is set to `false`. +- The tool inherits the parent workspace, workflow, profile, executor, repositories, and base branches unless overridden. +- Inherited repository attachments deliberately do not copy an explicit checkout branch. +- An explicit same-repository child uses the inherited base branch. An explicit cross-repository child defaults to that repository's default branch unless `base_branch` is supplied. +- Every created task must resolve an agent profile, even with `start_agent: false`. +- Profile precedence is explicit profile, parent/current/source task metadata or primary-session profile, destination-step profile override, workflow default, then workspace default. With no explicit workflow step, the destination is the workflow's start step. +- If no executor or executor profile is explicit or inherited, task MCP uses the built-in **git-worktree** executor. It does not consult the workspace's **Default Executor** for this fallback. +- The one-level Kanban depth rule still applies. +- An ephemeral Quick Chat task cannot be a parent; omit `parent_id` and create a top-level task instead. + +For predictable top-level creation, pass `repository_url`, `repository_id`, or `local_path`, as the tool contract requests. In task mode, the backend currently inherits repositories from the calling source task when no locator is supplied; if that source is repository-free, it can create a repository-free task despite the stricter tool description. The regular UI's **None** source remains the explicit route for intentional repository-free work. + +## Coordinate with targeted messages + +`message_task_kandev` sends a message to another task's primary session by default. Supply a session ID to target a particular sibling session on the current task. + +| Target state | Result | +|---|---| +| Running or starting | Queues the message FIFO for a later turn. | +| Waiting, idle, or completed | Starts a new turn immediately. | +| Created but not started | Starts the session with the message. | +| Failed or cancelled | Returns an error. | + +The default delivery mode is queued. Each session accepts 10 queued messages by default; operators can change it with `KANDEV_QUEUE_MAX_PER_SESSION`, and a value of `0` or less disables the limit. Only one queued message drains per agent turn. When the cap is reached, the sender receives a structured `queue_full` error and should retry after a target turn completes. + +`delivery_mode: "interrupt"` cancels current work and prioritizes the message, but only a direct parent task may interrupt its child. Other senders receive an error. If immediate dispatch cannot proceed safely, the response can still report the message as queued. + +Additional boundaries: + +- A task cannot message its own primary session through the default route, and a session cannot message itself. +- Normal targeted messages can cross workspaces when the sender has the exact task ID. Session spawning cannot. +- Sender metadata and content become part of the target conversation. Do not send secrets. +- Use bounded requests with the repository, branch, expected result, and reply target instead of treating messages as shared memory. + +Use `get_task_conversation_kandev` to read a primary or explicit session conversation. It supports limits, before/after cursors, ascending or descending order, and message-type filters. Use `list_related_tasks_kandev` for the current or another same-workspace task to list its parent, direct children, siblings, stored blocker relationships, and associated GitHub pull requests. + +## Wait for child tasks + +On the parent's current workflow step, configure **When Child Tasks Complete** to move next, previous, or to a selected step. It runs once when every active direct child is `COMPLETED`, `FAILED`, or `CANCELLED` and the parent has an active session. + +It ignores archived and ephemeral children, ignores grandchildren, and does not run if the parent has no children. The qualifying parent-session states are `CREATED`, `STARTING`, `RUNNING`, and `WAITING_FOR_INPUT`; no session, `IDLE`, or a terminal session prevents the transition. Terminal child state means work stopped; it does not mean every child succeeded. Put a human Review step after the transition when failures, diffs, or pull requests must be inspected. + +For a human gate, use **On Turn Complete → Do nothing (wait for user)** and require a person to review before moving the task or sending the next instruction. `step_complete_kandev` proves only that the agent emitted the configured completion signal. + +## Work across repositories and branches + +### Create a multi-repository task + +In **New Task**, add more **Repo** or **Remote** rows and select a base branch for each. Multi-repository creation currently supports only the **git-worktree** executor. Kandev materializes a worktree per repository and scopes Changes, review, and pull-request surfaces by repository. + +Before starting, document: - which repository owns each deliverable; -- whether worktrees are shared or isolated; -- the expected base and PR target for every branch; -- the order in which dependent PRs should merge; -- which tests must run in each repository. +- the base and pull-request target for each attachment; +- which remote credential can fetch and push each repository; +- the merge order for dependent changes; and +- the test command required in each repository. -## Shared plans and handoffs +### Add another branch after creation -Task plans are versioned documents that humans and agents can edit. A coordinator can create or update the parent plan, then give each child a scoped description and file-ownership boundary. +The regular task UI does not currently expose an after-creation add-branch form. A task agent using the **git-worktree** executor can call `add_branch_to_task_kandev`. -Handoffs can move a task to another workflow step with a prompt for the next agent. Keep the handoff factual: completed work, current branch/commit, tests run, known failures, required inputs, and files that must not be overwritten. +- Identify exactly one repository by task repository ID, GitHub repository URL, or local path. Omit the locator only when the task has one unique repository. +- For a multi-repository task, the repository locator is required. +- An empty checkout branch creates a fresh feature branch from the selected base. +- The base defaults to the repository's default branch when omitted. +- A repository/base/checkout tuple must be unique on the task. +- The tool can add a second branch from an attached repository or attach a branch from another repository. +- A launched task must use the **git-worktree** executor. A task with no fixed environment yet is allowed through the prelaunch path, but it must ultimately use git-worktree for the added branch to be materialized as a sibling worktree. -Do not rely on conversation summaries as the only durable state. Put decisions in the plan or repository and make commits before switching isolated branches. +On a live task, branch materialization is synchronous; if it fails, Kandev removes the new task-repository row and returns an error. On a prelaunch task, a materialization failure leaves the row in place and defers worktree creation to the next session launch. Branch names that differ but sanitize to the same on-disk slug are rejected because they would collide at the worktree path. -## Cross-task messages and conversations +Use `update_repository_base_branch_kandev` with a task-repository ID to change the comparison base. The database update is authoritative. Resetting cached session bases, refreshing Changes, base commit, ahead/behind counts, and cumulative diff in a live tracker are best-effort side effects; a failure is logged without rolling back the new base, and the persisted value is rebuilt on the next session launch. The tool does not rewrite commits, switch the checkout, or change an existing pull request's target branch. -Task MCP can send a message to another task's primary session and read task conversations with pagination and message-type filters. A running task queues the message for a subsequent turn; an idle task can receive it immediately. +Separate worktrees isolate file state. They do not by themselves isolate host credentials, ports, background processes, or other machine resources. -Use messages for bounded requests or status, not as an implicit shared-memory bus. Include the task/repository/branch identifiers and the expected response. Avoid sending secrets or large source dumps through cross-task messages. +## Keep durable coordination state -## Coordinator-led pattern +Regular tasks have one versioned plan. Use the parent plan for the overall breakdown, then put each child's precise scope and file ownership in that child's description. A subtask does not share a second live copy of the parent plan. -A typical coordinator flow is: +Before handing work to another session or task, record: -1. Inspect workspace, workflow, repositories, available profiles, and related tasks. -2. Record a plan with clear deliverables and ownership. -3. Create child tasks with the correct repository and workspace mode. -4. Add dependencies where order matters. -5. Monitor conversations and associated PR state. -6. Ask blocked children for concrete status or provide missing context. -7. Integrate and verify each branch in dependency order. -8. Signal workflow completion with a summary, handoff, and blockers. +- completed work and remaining scope; +- repository, checkout branch, and relevant commit; +- tests run and exact failures; +- pull-request state and required reviewers; +- blockers and expected input; and +- files another writer must not overwrite. -The coordinator has only the tools and permissions exposed to its selected profile and Kandev task MCP. It does not bypass branch protection, GitHub approval, executor credentials, or human workflow gates. +Commit before switching isolated branches when the repository process requires it. Conversation summaries are useful context, but plans, repository files, commits, and pull requests are the durable sources of truth. -## Human-guided pattern +## Coordinator-led and human-led patterns -The same primitives support a more controlled process: +A supported coordinator-led Kanban flow is: -- a human creates and scopes each task; -- Plan or Review steps pause for explicit approval; -- agents can propose subtasks but a human reviews their scope and profile; -- protected credentials stay outside broad agent profiles; -- a human decides what merges and what ships. +1. Inspect the workspace, workflow, repository attachments, available profiles, and related tasks. +2. Record one parent plan with deliverables and ownership. +3. Create parallel sessions for shared-file work and subtasks for independent workflow state. +4. Use isolated subtask workspaces for concurrent writers or separate branches. +5. Send bounded targeted messages and monitor conversations and pull requests. +6. Let the direct-child completion event move the parent to Review. +7. Have a person inspect changes and merge results in the documented order. -Choose the level of autonomy per workflow and repository risk. The task model does not force one operating style. +The coordinator has only the tools, filesystem access, credentials, and permissions exposed by its agent and executor profiles. It cannot bypass branch protection, provider approval rules, or human workflow gates. -## Limits and safety +A human-led flow uses the same primitives but keeps **Do nothing** transitions at planning, review, or release steps. Humans can create each task, approve agent-proposed subtasks, restrict credentials to narrow profiles, and decide what merges or ships. -- Shared workspaces do not prevent file conflicts between agents. -- Separate worktrees do not isolate host credentials or processes unless the executor does. -- Cross-repository work still needs compatible credentials for every remote. -- Associated PR data depends on configured GitHub/GitLab access and branch association. -- Office mode is a separate feature-flagged autonomy workspace; do not assume its persistent teams, budgets, or routines are part of regular task MCP coordination. +### Dependencies and Office -Related: [Automation and MCP](automation-and-mcp.md), [Tasks and workflows](tasks-and-workflows.md), and [Sessions and review](sessions-and-review.md). +Regular Kanban does not currently expose a dependency editor or blocker filter. Stored blocker data can appear through related-task MCP, but it is not a regular-board planning surface. For supported ordering, use parent/child structure, **When Child Tasks Complete**, explicit messages, workflow gates, and pull-request review. + +Office prototypes **Blocked by** and **Blocking** properties with same-workspace, self-reference, and cycle validation, plus persistent coordinators and deeper trees. Office remains in progress; do not rely on these properties or its advanced quorum/routine behavior for a stable Kanban workflow. + +## Troubleshooting + +- **New Agent has no usable profile:** configure credentials under **Settings → Agents** and a compatible executor under **Settings → Executors**. +- **Summarize session is missing:** configure a utility agent; otherwise use **Blank** or **Copy initial prompt**. +- **Parallel agents overwrite work:** stop one writer, assign disjoint files, or create an isolated subtask workspace. +- **Subtask cannot be created:** confirm title, prompt, compatible profile, workflow, and the one-level Kanban depth limit. +- **Subtask creates but its agent or repositories fail to start:** the dialog does not enforce agent/executor compatibility. Confirm the agent is configured on that executor, and use **git-worktree** for two or more repository rows. +- **Inherited subtask sees unexpected changes:** it intentionally shares the parent's materialized files and branch. +- **Message remains queued:** the target is busy and only one queued message drains per turn. Check for `queue_full` before retrying. +- **Interrupt is rejected:** only the target's direct parent may use interrupt delivery. +- **Agent cannot spawn on another task:** `spawn_session_kandev` is same-workspace only; create a task or use a normal targeted message instead. +- **Parent does not advance after children finish:** the parent needs an active session in `CREATED`, `STARTING`, `RUNNING`, or `WAITING_FOR_INPUT` in addition to terminal direct children. +- **A multi-repository executor is disabled:** multi-repository tasks require **git-worktree**. +- **Add branch is rejected:** launched tasks require a worktree executor; multi-repository tasks require an unambiguous locator, repository URLs must be GitHub URLs, and sanitized branch slugs must be unique. +- **Changed base did not refresh immediately:** the database value is saved even when live tracker refresh fails; the next session launch rebuilds the comparison state. +- **Changed base did not retarget the PR:** the base-update tool changes Kandev's comparison context, not Git history or provider PR metadata. + +Related: [Tasks and workflows](tasks-and-workflows.md), [Sessions and review](sessions-and-review.md), [Automation and MCP](automation-and-mcp.md), [Agents and profiles](agents-and-profiles.md), and [Executors](executors.md). diff --git a/docs/public/coverage.json b/docs/public/coverage.json new file mode 100644 index 0000000000..3fc6173f13 --- /dev/null +++ b/docs/public/coverage.json @@ -0,0 +1,286 @@ +{ + "version": 1, + "areas": [ + { + "id": "install-and-launch", + "audiences": ["user", "operator"], + "stability": "stable", + "docs": ["index", "use-kandev", "cli", "desktop-app", "run-as-a-service"], + "sources": ["apps/cli/bin/native-shim.js", "apps/cli/src/cli.ts", "apps/desktop/src-tauri/src/lib.rs"], + "tests": ["apps/cli/bin/native-shim.test.mjs", "apps/desktop/e2e/desktop-launch-smoke.test.mjs"] + }, + { + "id": "backend-configuration", + "audiences": ["operator", "contributor"], + "stability": "stable", + "docs": ["configuration", "operations", "cli"], + "sources": ["apps/backend/internal/common/config/config.go", "apps/backend/internal/agentctl/server/config/config.go", "apps/backend/internal/profiles/profiles.go"], + "tests": ["apps/backend/internal/common/config/config_test.go", "apps/backend/internal/agentctl/server/config/config_test.go", "apps/backend/internal/profiles/profiles_test.go"] + }, + { + "id": "workspaces-and-repositories", + "audiences": ["user"], + "stability": "stable", + "docs": ["use-kandev", "tasks-and-workflows", "configuration"], + "sources": ["apps/backend/internal/task/handlers/workspace_handlers.go", "apps/web/components/app-sidebar/app-sidebar-workspace-picker.tsx"], + "tests": ["apps/backend/internal/task/handlers/workspace_handlers_test.go", "apps/web/components/app-sidebar/app-sidebar-workspace-picker.test.tsx"], + "settingsRoutes": ["/settings/workspace"], + "mcpTools": ["list_repositories_kandev", "list_workspaces_kandev"] + }, + { + "id": "tasks-and-workflows", + "audiences": ["user"], + "stability": "stable", + "docs": ["tasks-and-workflows", "workflow-tips", "workflow-import-export", "workflow-sync"], + "sources": ["apps/backend/internal/orchestrator/workflow_store.go", "apps/web/components/kanban-with-preview.tsx"], + "tests": ["apps/backend/internal/orchestrator/workflow_store_test.go", "apps/web/components/kanban-with-preview.test.ts"], + "mcpTools": [ + "create_workflow_kandev", + "create_workflow_step_kandev", + "delete_workflow_kandev", + "delete_workflow_step_kandev", + "import_workflow_kandev", + "list_workflow_steps_kandev", + "list_workflows_kandev", + "reorder_workflow_steps_kandev", + "update_workflow_kandev", + "update_workflow_step_kandev" + ] + }, + { + "id": "task-lifecycle", + "audiences": ["user"], + "stability": "stable", + "docs": ["tasks-and-workflows", "coordination", "git-operations"], + "sources": ["apps/backend/internal/orchestrator/task_operations.go", "apps/backend/internal/mcp/handlers/config_task_handlers.go"], + "tests": ["apps/backend/internal/orchestrator/task_operations_test.go", "apps/backend/internal/mcp/handlers/handlers_test.go"], + "mcpTools": [ + "add_branch_to_task_kandev", + "archive_task_kandev", + "create_task_kandev", + "delete_task_kandev", + "list_tasks_kandev", + "move_task_kandev", + "step_complete_kandev", + "update_repository_base_branch_kandev", + "update_task_kandev", + "update_task_state_kandev" + ] + }, + { + "id": "sessions-and-coordination", + "audiences": ["user"], + "stability": "stable", + "docs": ["sessions-and-review", "coordination", "automation-and-mcp"], + "sources": ["apps/backend/internal/mcp/handlers/spawn_session.go", "apps/web/components/task/dockview-session-tabs.ts"], + "tests": ["apps/backend/internal/mcp/handlers/spawn_session_test.go", "apps/web/components/task/dockview-session-tabs.test.ts"], + "mcpTools": [ + "ask_user_question_kandev", + "get_task_conversation_kandev", + "get_task_document_kandev", + "list_related_tasks_kandev", + "list_task_documents_kandev", + "message_task_kandev", + "spawn_session_kandev", + "write_task_document_kandev" + ] + }, + { + "id": "plans-walkthroughs-and-review", + "audiences": ["user"], + "stability": "stable", + "docs": ["sessions-and-review", "developer-tools", "automation-and-mcp"], + "sources": ["apps/web/components/review/review-dialog.tsx", "apps/web/components/review/walkthrough-overlay.tsx"], + "tests": ["apps/web/components/review/review-dialog.build-files.test.ts", "apps/web/components/review/walkthrough-overlay.test.tsx"], + "mcpTools": [ + "create_task_plan_kandev", + "delete_task_plan_kandev", + "delete_walkthrough_kandev", + "get_task_plan_kandev", + "get_walkthrough_kandev", + "show_walkthrough_kandev", + "update_task_plan_kandev" + ] + }, + { + "id": "developer-workbench-and-git", + "audiences": ["user"], + "stability": "stable", + "docs": ["developer-tools", "git-operations", "sessions-and-review"], + "sources": [ + "apps/web/components/session-commands.tsx", + "apps/web/components/task/chat/messages/shell-output-disclosure.tsx", + "apps/web/components/task/chat/token-usage-display.tsx", + "apps/web/components/task/editors-menu.tsx", + "apps/backend/internal/agentctl/server/process/workspace_git_status.go" + ], + "tests": [ + "apps/web/e2e/tests/command-panel.spec.ts", + "apps/web/e2e/tests/task/editors-menu-worktree-picker.spec.ts", + "apps/web/hooks/domains/session/use-shell-command-output.test.ts", + "apps/web/components/task/chat/token-usage-display.test.tsx", + "apps/backend/internal/agentctl/server/process/workspace_git_status_test.go" + ] + }, + { + "id": "agents-profiles-and-prompts", + "audiences": ["user"], + "stability": "stable", + "docs": ["agents-and-profiles", "configuration", "add-agent-cli"], + "sources": [ + "apps/backend/internal/profiles/profiles.go", + "apps/web/app/settings/agents/page.tsx", + "apps/web/components/settings/agent-usage-section.tsx", + "apps/web/hooks/domains/settings/use-dynamic-models.ts" + ], + "tests": [ + "apps/backend/internal/profiles/profiles_test.go", + "apps/web/components/settings/agent-profile-page.test.tsx", + "apps/web/components/settings/agent-usage-section.test.tsx", + "apps/web/hooks/domains/settings/use-dynamic-models.test.ts" + ], + "settingsRoutes": ["/settings/agents", "/settings/prompts", "/settings/utility-agents"], + "mcpTools": [ + "create_agent_profile_kandev", + "delete_agent_profile_kandev", + "list_agent_profiles_kandev", + "list_agents_kandev", + "update_agent_kandev", + "update_agent_profile_kandev" + ] + }, + { + "id": "executors-and-environments", + "audiences": ["user", "operator"], + "stability": "stable", + "docs": ["executors", "remote-cloud-environment", "docker", "k8s"], + "sources": ["apps/backend/internal/agent/executor/executor.go", "apps/web/app/settings/executors/page.tsx"], + "tests": ["apps/web/e2e/tests/docker/docker-launch.spec.ts", "apps/web/e2e/tests/ssh/executor-crud.spec.ts"], + "settingsRoutes": ["/settings/executor/new", "/settings/executors"], + "mcpTools": [ + "create_executor_profile_kandev", + "delete_executor_profile_kandev", + "list_executor_profiles_kandev", + "list_executors_kandev", + "update_executor_profile_kandev" + ] + }, + { + "id": "provider-integrations", + "audiences": ["user", "operator"], + "stability": "stable", + "docs": ["integrations", "configuration"], + "sources": ["apps/web/components/app-sidebar/sections/integrations-section.tsx", "apps/backend/internal/integrations/workspacescope/workspacescope.go"], + "tests": ["apps/web/components/app-sidebar/sections/integrations-section.test.tsx", "apps/backend/internal/integrations/workspacescope/workspacescope_test.go"], + "settingsRoutes": [ + "/settings/integrations", + "/settings/integrations/github", + "/settings/integrations/gitlab", + "/settings/integrations/jira", + "/settings/integrations/linear", + "/settings/integrations/sentry", + "/settings/integrations/slack" + ] + }, + { + "id": "automations-and-external-mcp", + "audiences": ["user", "operator"], + "stability": "stable", + "docs": ["automation-and-mcp", "integrations", "workflow-tips"], + "sources": ["apps/backend/internal/automation/service.go", "apps/backend/internal/mcp/server/server.go"], + "tests": ["apps/backend/internal/automation/service_test.go", "apps/backend/internal/mcp/server/server_test.go"], + "settingsRoutes": ["/settings/automations", "/settings/external-mcp"], + "mcpTools": ["get_mcp_config_kandev", "update_mcp_config_kandev"] + }, + { + "id": "personal-settings-and-secrets", + "audiences": ["user"], + "stability": "stable", + "docs": ["security", "configuration", "developer-tools", "operations"], + "sources": ["apps/web/src/settings-routes.tsx", "apps/web/components/settings/general-settings.tsx"], + "tests": ["apps/web/src/settings-routes.test.ts", "apps/web/components/settings/terminal-settings.test.ts"], + "settingsRoutes": [ + "/settings/general", + "/settings/general/appearance", + "/settings/general/editors", + "/settings/general/keyboard-shortcuts", + "/settings/general/notifications", + "/settings/general/secrets", + "/settings/general/sprites", + "/settings/general/task-actions", + "/settings/general/terminal", + "/settings/voice-mode" + ] + }, + { + "id": "security-and-trust", + "audiences": ["user", "operator"], + "stability": "stable", + "docs": ["security", "configuration", "executors", "automation-and-mcp", "operations"], + "sources": ["apps/backend/internal/backendapp/middleware.go", "apps/backend/internal/gateway/websocket/origin.go", "apps/backend/internal/secrets/crypto.go"], + "tests": ["apps/backend/internal/backendapp/middleware_test.go", "apps/backend/internal/gateway/websocket/origin_test.go"] + }, + { + "id": "system-health-and-maintenance", + "audiences": ["operator"], + "stability": "stable", + "docs": ["operations", "configuration", "feature-status"], + "sources": ["apps/backend/internal/system/settings/store.go", "apps/web/components/settings/system/feature-toggles-settings.tsx"], + "tests": ["apps/backend/internal/system/settings/store_test.go", "apps/web/components/settings/system/feature-toggles-settings.test.tsx"], + "settingsRoutes": [ + "/settings/system/about", + "/settings/system/backups", + "/settings/system/database", + "/settings/system/feature-toggles", + "/settings/system/licenses", + "/settings/system/logs", + "/settings/system/status", + "/settings/system/updates" + ] + }, + { + "id": "deployment-and-platforms", + "audiences": ["operator"], + "stability": "stable", + "docs": ["docker", "k8s", "run-as-a-service", "windows-support", "operations"], + "sources": ["Dockerfile", "apps/cli/src/service/index.ts"], + "tests": ["apps/web/e2e/tests/docker/docker-launch.spec.ts", "apps/cli/src/service/install_helpers.test.ts"] + }, + { + "id": "office-mode", + "audiences": ["user", "operator"], + "stability": "experimental", + "docs": ["coordination", "feature-status"], + "sources": ["apps/backend/internal/office/routes.go", "apps/web/src/office-routes.tsx"], + "tests": ["apps/backend/internal/office/onboarding/service_test.go", "apps/web/src/office-routes.test.ts"] + }, + { + "id": "websocket-protocol", + "audiences": ["contributor", "integrator"], + "stability": "stable", + "docs": ["websocket-api", "architecture", "extending-kandev"], + "sources": ["apps/backend/pkg/websocket/actions.go", "apps/backend/pkg/websocket/handler.go"], + "tests": ["apps/backend/pkg/websocket/handler_test.go", "apps/backend/pkg/websocket/handler_race_test.go"] + }, + { + "id": "contributor-workflow", + "audiences": ["contributor"], + "stability": "stable", + "docs": ["contributing", "architecture", "backend-development", "web-development", "testing", "remote-cloud-environment", "extending-kandev", "release-process"], + "sources": ["CONTRIBUTING.md", "Makefile"], + "tests": ["scripts/lint-harness-files.test.py", ".github/scripts/lint-action-pinning_test.py"] + } + ], + "exclusions": { + "settingsRoutes": [ + { "route": "/settings", "reason": "Alias for General settings." }, + { "route": "/settings/changelog", "reason": "Redirect to System updates." }, + { "route": "/settings/general/changes-panel", "reason": "Legacy redirect to Appearance." }, + { "route": "/settings/general/chat-input", "reason": "Legacy redirect to Keyboard Shortcuts." }, + { "route": "/settings/general/resource-metrics", "reason": "Legacy redirect to Appearance." }, + { "route": "/settings/general/shell", "reason": "Legacy redirect to Terminal." }, + { "route": "/settings/system", "reason": "Redirect to System status." } + ], + "mcpTools": [] + } +} diff --git a/docs/public/desktop-app.md b/docs/public/desktop-app.md index 418efd84fa..376ffcd5b7 100644 --- a/docs/public/desktop-app.md +++ b/docs/public/desktop-app.md @@ -5,110 +5,174 @@ description: "Install and run the Tauri desktop app with the native Kandev runti # Kandev Desktop App -Kandev desktop is a Tauri app that starts the native Kandev runtime locally and shows the existing Kandev UI in a system WebView. It does not require Node.js at runtime. Node.js, pnpm, Rust, and the Tauri CLI are build-time tools only. +Kandev Desktop packages the native Kandev runtime inside a Tauri application and displays the normal web UI in the operating system WebView. Choose it for a native launcher, application menus, window-state persistence, desktop notifications, and signed in-app updates. It does not require Node.js, pnpm, Go, or Rust at runtime. -## Install Artifacts +The [CLI](./cli.md) is a better fit for headless machines, browser-only access, or an OS service. Desktop and CLI releases use the same SemVer and the same default Kandev data layout, but they are separate installation and update channels. -GitHub releases publish desktop artifacts alongside the existing runtime tarballs. Desktop artifact names start with `kandev-desktop--` and each artifact has a `.sha256` checksum. +## Supported artifacts -Supported desktop platforms: +Download artifacts from [GitHub Releases](https://github.com/kdlbs/kandev/releases). Each desktop filename begins with `kandev-desktop--`; use the installer format for your platform, not an updater archive. -- `macos-arm64` -- `macos-x64` -- `linux-x64` -- `linux-arm64` -- `windows-x64` +| Platform label | Installer formats | In-app update support | +|---|---|---| +| `macos-arm64` | `.dmg` | Signed `.app.tar.gz` updater bundle | +| `macos-x64` | `.dmg` | Signed `.app.tar.gz` updater bundle | +| `linux-arm64` | AppImage, `.deb`, `.rpm` | AppImage installations only | +| `linux-x64` | AppImage, `.deb`, `.rpm` | AppImage installations only | +| `windows-x64` | NSIS `.exe` | Signed `.nsis.zip` updater bundle | -macOS releases provide a `.dmg`, Windows releases provide an NSIS installer, and Linux releases -provide AppImage, `.deb`, and `.rpm` packages. AppImage is the portable Linux format and the only -Linux format supported by Kandev's in-app updater. Install `.deb` or `.rpm` when you want your -system package manager to own installation and removal. +Windows ARM64 is not a native release target. x64 emulation may run the installer on some ARM Windows systems, but that is dependency-bound and not covered by the release matrix. -When signing inputs are configured, macOS artifacts are Developer ID signed and notarized and Windows artifacts are code signed. When those inputs are missing, the release still publishes unsigned desktop development builds with a release-notes warning. Linux artifacts are checksum-gated; package-manager signatures can be added later. +The `.app.tar.gz`, `.AppImage.tar.gz`, and `.nsis.zip` files are updater payloads; do not install them manually. Every published desktop artifact also has a sibling `.sha256` file. -Unsigned macOS or Windows desktop artifacts may require manual OS security bypasses and should not be presented as trusted downloads. +## Verify a download -## Runtime Requirements - -The desktop app packages the native Kandev runtime binaries and starts: - -```text -kandev --headless --port -``` - -The backend binds to `127.0.0.1` and serves the same embedded Vite UI used by the CLI/Homebrew/npm runtime. - -Platform WebView requirements: - -- macOS: system WebKit. -- Windows: Microsoft WebView2 runtime. -- Linux: WebKitGTK and related desktop libraries. The `.deb`/`.rpm` packages install declared - dependencies; AppImage users must provide the host WebKitGTK/runtime libraries required by - their distribution. - -Kandev data still lives in the existing Kandev home directory, `~/.kandev` by default, unless overridden by existing environment/config settings. - -## Desktop Updates - -The desktop app checks the signed GitHub Releases update feed after startup and no more than once -every 24 hours while it remains open. It never downloads, installs, or restarts silently. Open -**Settings -> System -> Updates** or choose **Check for Updates** from the application menu to run -a fresh check, review the available version and release notes, and confirm installation. - -Kandev verifies the Tauri updater signature before installing on every platform. Apple -notarization and Windows code signing are separate publisher-identity layers: when those -credentials are unavailable, Tauri-signed updates still work but the application remains an -unsigned development build and can trigger OS trust warnings. Linux in-app updates install the -signed AppImage update bundle. Before the updater restarts Kandev, the desktop shell stops the -backend process it owns. Worktrees, settings, tasks, and the SQLite database remain in the Kandev -data directory. - -Linux `.deb` and `.rpm` installs do not self-update. Update those through your package manager or -by installing a newer package manually. When an automatic check is offline or the release feed is -unavailable, Kandev keeps running and tries again later; a manual check shows the error on the -Updates page. If installation fails, reopen that page and retry or install the matching release -artifact manually. Kandev rejects payloads with missing or invalid Tauri updater signatures and -rejects downgrades. - -The npm and Homebrew channels continue to update through: +Run checksum verification from the directory containing both files. On macOS or Linux: ```bash -brew upgrade kandev -npm install -g kandev@latest -npx kandev@latest +shasum -a 256 -c '.sha256' ``` -Those channels are separate from desktop installers, even though all release artifacts share the same SemVer. +On Linux, `sha256sum -c '.sha256'` is equivalent. On Windows PowerShell: -## Native Desktop Behavior +```powershell +$artifact = '.\.exe' +$expected = (Get-Content "$artifact.sha256").Split()[0].ToLowerInvariant() +$actual = (Get-FileHash $artifact -Algorithm SHA256).Hash.ToLowerInvariant() +if ($actual -ne $expected) { throw 'SHA-256 checksum mismatch' } +``` -The desktop application menu provides New Task, contextual Close, Settings, zoom, full screen, -Help, and update commands. On macOS, `Cmd+,` opens the existing General settings page. `Cmd/Ctrl+W` -closes the top dialog or an eligible file/diff/commit/preview tab; when there is no closeable -context it does nothing and never shuts down the window or backend. Closing the main window with -the OS close control or choosing Quit exits Kandev and stops the owned backend. +A checksum detects a damaged or substituted file only when you trust the release page from which the checksum was obtained. macOS Developer ID signing/notarization and Windows Authenticode signing are publisher-identity layers separate from the Tauri updater signature. Release automation can publish unsigned development installers when platform signing credentials are unavailable; the release notes identify that condition. Do not bypass an OS trust warning unless you have independently verified and accepted that unsigned build. -Kandev can send native notifications when a task is waiting for input or a session fails. These -respect the existing notification preference. Desktop notification clicks may focus Kandev, but -Kandev does not infer task navigation from an ordinary focus or app activation event. +## Install -External web and email links open in the system browser or mail client. Kandev keeps its own -loopback routes, local previews, downloads, and blob URLs in the WebView. +### macOS -## Agent CLI Discovery +1. Select the `macos-arm64` DMG for Apple silicon or `macos-x64` for an Intel Mac. +2. Verify the checksum. +3. Open the DMG and drag Kandev to **Applications**. +4. Start Kandev from Applications. -Desktop apps launched from an OS app launcher may not inherit the same shell initialization as a terminal. Kandev preserves available environment variables and adds common user binary directories such as `/usr/local/bin`, `/opt/homebrew/bin`, `/usr/bin`, `/bin`, `~/.local/bin`, and common agent install directories. +The app uses the system WebKit runtime. A properly signed release should pass Gatekeeper and notarization checks. To uninstall, quit Kandev and move **Kandev.app** from Applications to the Trash. -If an agent CLI is not discoverable from the desktop app: +### Windows -- Set the agent profile command to the full executable path. -- Confirm the executable works from a normal terminal. -- Put custom install directories in a location visible to GUI apps, or configure them through OS-level environment settings rather than only shell startup files. +1. Download the `windows-x64` NSIS `.exe` and its checksum. +2. Verify the checksum in PowerShell. +3. Run the installer and start Kandev from the Start menu. -Existing explicit command paths in Kandev settings remain authoritative over `PATH` lookup. +Kandev uses Microsoft WebView2. Current Windows installations normally include it; if the window cannot create a WebView, install or repair the Microsoft WebView2 Runtime and retry. Uninstall through **Settings → Apps → Installed apps → Kandev → Uninstall**. -## Release Trust +### Linux AppImage -See [desktop-tauri-signing.md](../desktop-tauri-signing.md) for the CI secrets and automatic signing behavior used for desktop releases. -The release feed identifies signed updater artifacts; before installation, Tauri verifies the selected -artifact against the updater public key embedded in the app. +AppImage is portable and is the only Linux installation type the in-app updater handles: + +```bash +chmod +x './' +'./' +``` + +The host must provide the WebKitGTK and desktop libraries required by Tauri for that distribution. Desktop integration of a raw AppImage is distribution/tooling dependent. + +### Linux package manager + +Use `.deb` on Debian/Ubuntu-family systems or `.rpm` on RPM-family systems. Installing from a local file lets the package manager resolve declared dependencies: + +```bash +sudo apt install './' +``` + +```bash +sudo dnf install './' +``` + +Install a newer package the same way to update. Package-manager installs do not use Kandev's in-app updater; removal and package ownership remain with `apt`/`dnf`. + +## Startup and local security + +At launch, the desktop shell validates all packaged runtime binaries, selects a port, and starts: + +```text +kandev --headless --port +``` + +Desktop forces the backend to `127.0.0.1`, supplies a random 256-bit health token, and accepts readiness only when the loopback health response returns that token. The WebView's privileged desktop commands are also limited to the exact owned backend origin. This isolates the desktop control bridge from unrelated local pages; it is not a reason to expose another Kandev process to the network. + +The preferred desktop port is `38430`. If only that port is unavailable, Kandev asks the OS for a free loopback port. `KANDEV_DESKTOP_PORT` can request a different port, but a non-UTF-8 value, a non-integer, `0`, or a value above `65535` is a fatal startup configuration error. If the requested port is occupied, the desktop still falls back to an OS-assigned loopback port. + +Startup waits up to 60 seconds for the owned backend. A missing packaged binary, invalid port, configuration/database error, or early backend exit appears on the startup screen with the most recent captured backend output. Reinstall the complete artifact if runtime validation reports a missing `kandev`, `agentctl`, or remote helper. + +## Data, processes, and cleanup + +Desktop inherits normal backend configuration. By default, persistent data lives in `~/.kandev` (below the user profile directory on Windows). Set `KANDEV_HOME_DIR` in the desktop process environment before launch to isolate or relocate it; see [Configuration](./configuration.md). The desktop app's own platform app-data directory separately stores window geometry. + +Only one desktop instance runs per OS user/application scope. Launching the app a second time shows, unminimizes, and focuses the existing main window; it does not start a second backend. + +Closing the main OS window or choosing **Quit Kandev** quits the application and stops the backend it owns. There is no tray/background mode or desktop autostart service. On Unix, shutdown sends a graceful termination and force-kills after five seconds if needed; on Windows it terminates the owned process tree. Active external executor resources may have their own lifecycle—check [Executors](./executors.md) before manual cleanup. + +The application menu exposes New Task, Settings (`Cmd/Ctrl+,`), contextual Close (`Cmd/Ctrl+W`), zoom, full-screen, Help, update, and Quit actions. Contextual Close asks the web UI to close its top dialog or eligible file/diff/commit/preview tab; if nothing is closeable it does not shut down the window or backend. The desktop shell saves window geometry in its platform app-data directory and clamps restored geometry to an available display. + +Uninstalling the desktop application does not delete the Kandev home. Keep it to preserve workspaces and settings, or back it up and remove it separately only after all Kandev processes and executors are stopped. See [Operations](./operations.md). + +## Agent CLI discovery + +Applications started from Finder, Launchpad, desktop menus, or the Windows Start menu often receive less `PATH` configuration than terminal shells. Kandev preserves the available path and appends common locations: + +- macOS/Linux: `/usr/local/bin`, `/usr/bin`, `/bin`, `/opt/homebrew/bin`, `~/.local/bin`, `~/.bun/bin`, and `~/.opencode/bin` (plus the Linuxbrew prefix on Linux); +- Windows: the user's roaming npm directory and Scoop shims. + +If an agent command is still not found: + +1. run the executable successfully in a normal terminal; +2. set the agent profile command to its full executable path; and +3. keep credentials and custom environment variables in the profile or an OS-level environment source visible to GUI applications, not only an interactive shell startup file. + +Explicit profile command paths remain authoritative over `PATH` lookup. + +## Updates + +After the backend is ready, Kandev performs one update check immediately and then no more than once every 24 hours while that same process remains open. Use **Settings → System → Updates** or **Check for Updates…** in the application menu for a fresh check. + +Kandev never downloads, installs, or restarts for an update without confirmation. It shows the target version and release notes, downloads the platform-specific payload after approval, verifies the Tauri signature against the public key embedded in the app, stops its backend, installs, and restarts. Only one check/install operation can run at a time, and downgrade payloads are rejected. + +In-app updates are available only when release automation published a complete signed updater set and `latest.json`. If updater signing credentials were absent, normal installers can still exist while the update feed is intentionally omitted. Offline/feed failures do not stop Kandev; a manual check displays the error. + +Linux `.deb` and `.rpm` installations must be updated through their package manager or by installing a newer package. The updater validates only an AppImage execution environment on Linux. If an update fails, keep the existing app closed only as long as the OS installer requires, then install the matching release artifact manually; the Kandev home is not part of the installer payload. + +## Links, activation, and notifications + +Kandev has no registered public URL scheme, file association, or command-line deep-link protocol. A second application launch only activates the existing window. Links and routes inside the Kandev UI still work normally, but an external `kandev://…` link is not a supported product path. + +External `http`, `https`, and `mailto` destinations open through the system browser or mail client. The desktop bridge rejects URLs with embedded credentials, unsupported schemes, `localhost`/subdomains of `localhost`, loopback IPs, and unspecified IPs; Kandev routes, previews, downloads, and blob URLs remain in the WebView. RFC 1918/private-LAN hosts are not categorically blocked by this validator. + +Native notifications are limited to a session waiting for input or failing, and respect the application's notification preference and OS permission. Requests carry task/session identifiers for validation and event de-duplication, but the desktop registers no notification activation/deep-link handler. Clicking a notification is therefore not a supported navigation path. + +## Troubleshooting + +### Desktop startup failed + +Read the captured backend detail on the startup screen. Then check, in order: + +- the artifact matches the current OS and architecture; +- the full application bundle was installed rather than copying a binary out of it; +- `KANDEV_DESKTOP_PORT` is valid or unset; +- `config.yaml` and `KANDEV_*` values are valid; and +- the configured Kandev home/database is writable and not already locked by an incompatible process. + +Reinstalling repairs packaged program files but intentionally leaves user data untouched. + +### Agent works in a terminal but not Desktop + +Use the full command path in the agent profile and verify GUI-visible credentials. Shell aliases and functions are not executables and cannot be discovered by `PATH`. + +### Update check reports no usable feed + +Confirm network access to GitHub Releases and read that release's notes. An unsigned installer-only release deliberately has no `latest.json`; install a newer verified installer manually. Proxy/TLS interception can also prevent signature-feed access even when the application itself works offline. + +### Linux AppImage does not open + +Confirm its executable bit and launch it from a terminal to see missing-library output. Install the WebKitGTK/Tauri dependencies for the distribution, or use the provided `.deb`/`.rpm` so the package manager resolves dependencies. + +## Release trust details + +See [desktop-tauri-signing.md](../desktop-tauri-signing.md) for the release workflow's platform-signing inputs and fallback behavior. Tauri updater signatures, macOS notarization, Windows Authenticode, and published SHA-256 files serve different trust checks; one does not substitute for all the others. diff --git a/docs/public/developer-tools.md b/docs/public/developer-tools.md index 32e062184a..f7ad9cc4ad 100644 --- a/docs/public/developer-tools.md +++ b/docs/public/developer-tools.md @@ -1,95 +1,189 @@ --- title: "Developer Tools" -description: "Use quick chat, utility agents, voice input, editors, terminals, prompts, notifications, and personal settings." +description: "Use quick chat, utility agents, saved prompts, voice input, editors, language servers, and task terminals." --- # Developer Tools -Kandev combines task coordination with the developer surfaces needed to inspect and finish work. Most tools operate in the current task environment, while quick chat and utility agents handle shorter work that does not need a full workflow. +Kandev includes short-lived chat, reusable AI helpers, dictation, file and editor integration, language servers, and terminals. Some tools run in a task environment; others run on the Kandev backend host or in the browser. That boundary determines which files, executables, credentials, and network services they can reach. -## Quick chat +## Quick Chat -Quick chat opens a focused agent conversation without first creating a kanban task. Use it for short questions, repository orientation, command help, or other disposable interactions. +Quick Chat is an agent conversation outside the board. Use it for repository orientation, experiments, and disposable questions that do not need workflow state, review gates, dependencies, or a delivery record. -Use a normal task when the work needs durable workflow state, a dedicated branch or worktree, structured documents, review gates, dependencies, or a pull request. Quick chat is not a substitute for that delivery record. +Select **Quick Chat** beside **New Task** in the expanded sidebar, or select its standalone row in the collapsed sidebar. + +### Start a chat + +1. Choose an agent profile. Quick Chat requires one and defaults to the workspace's default agent profile when configured. +2. Optionally add one or more workspace repositories. +3. For each repository, choose a branch. The same repository cannot be added twice. +4. Select **Start chat**. + +Each selected repository gets an isolated worktree from the chosen branch. Uncommitted changes in your original checkout are not copied. Without a repository, Kandev creates an ephemeral working directory under `/quick-chat/` (by default `~/.kandev/quick-chat/`). + +Quick Chat supports multiple tabs, tab renaming, and **+** to create another chat. Structured profiles show ACP chat; CLI-passthrough profiles show their PTY interface. The desktop window is resizable, while mobile uses a full-screen view. + +Closing a real chat tab permanently deletes its conversation, hidden backing task data, and associated worktree. There is no undo. Kandev also deletes abandoned chats after seven days; cleanup runs when the backend starts and then once per day. Only chats whose session is `RUNNING` or `IDLE` are protected from age-based cleanup. Old `CREATED`, `STARTING`, or `WAITING_FOR_INPUT` chats can expire, so do not use Quick Chat for durable work. + +If **Start chat** is disabled, select a profile and finish every repository/branch row. If a repository is missing, confirm that it belongs to the current workspace and refresh the repository configuration. Use a normal task when the result must remain visible on a board or become a reviewed PR. ## Utility agents -**Settings > Utility Agents** configures small agent-backed jobs used by Kandev itself. Built-in uses include generating or refining: +Open **Settings > Utility Agents** (`/settings/utility-agents`). Utility agents are one-shot ACP calls used to generate small pieces of text; they do not create a durable task conversation. -- task prompts; -- branch names; -- commit messages; -- pull-request descriptions; -- session summaries. +The built-in actions are: -Each utility can select an agent/profile and prompt template. These jobs can send repository or task context to the configured model provider, so choose credentials and profiles with the same care as a normal session. Keep the fallback behavior usable when a utility agent or provider is unavailable. +- `commit-message` +- `commit-description` +- `branch-name` +- `pr-title` +- `pr-description` +- `enhance-prompt` +- `summarize-session` -## Prompts +Set a global **Default utility agent model** by choosing an inference-capable agent and one of its models. Each built-in action can inherit that pair or override it, and its prompt template is editable. Kandev probes the agent for its live model list; refresh or retry when model discovery fails. -**Settings > Prompts** manages reusable prompt templates. Workflows, utility agents, and task actions can reference them so a team can standardize planning, implementation, review, and handoff instructions. +You can also create a custom utility. Name, prompt, agent, and model are required; description is optional. Prompt fields offer autocomplete for supported `{{...}}` template variables. A per-action override must specify a usable agent/model pair; otherwise resolution falls back to the global pair. Buttons that depend on a utility are disabled or return an error when no valid model is available. -Prompts are process policy, not enforcement. Pair them with workflow gates, scoped executor permissions, tests, and branch protection when those controls matter. +Utility calls run as ephemeral processes on the Kandev backend host. Kandev records the resolved prompt, response, selected model, token counts when provided, duration, status, and error. The prompt can include repository, diff, task, or conversation context, so its content goes to the selected model provider. Apply the same credential, retention, and data-classification rules as a normal agent session. -## Voice mode +### Configuration Chat -**Settings > Voice Mode** configures dictation in the chat composer. Available engines depend on browser and deployment support: +The same settings page configures the **Configuration Chat Agent** for each workspace. Choose a profile, choose **No default**, or rely on the workspace default profile. Kandev remembers the first explicit selection as that workspace's configuration-chat default. -- browser Web Speech; -- local in-browser Whisper Web; -- server-side Whisper transcription. +Open Configuration Chat from the floating chat button on Settings pages or run **Configuration Chat** from the `Cmd/Ctrl+K` command menu. It supports multiple tabs and uses a repository-less ephemeral task. Its configuration-mode MCP can inspect and change workflows, agent profiles, and MCP configuration. The selected profile's model, credentials, permissions, and external MCP settings apply. Review requested configuration mutations before approving them. -Settings include language, click-to-toggle or hold-to-talk activation, and optional auto-send. Server-side transcription requires its configured provider credential and sends audio outside the browser. Review the privacy and retention policy of the selected engine before using customer or proprietary content. +Closing a Configuration Chat tab removes only the browser tab state; it does not delete the backing `config_mode` task. Those tasks are excluded from the seven-day Quick Chat sweeper and can accumulate. Profile deletion attempts to clean matching ephemeral tasks, but otherwise there is no equivalent close-to-delete lifecycle in this UI. -## Files, editor, and language servers +## Saved prompts -The task workbench can browse and edit files, search the workspace, preview images, and show diagnostics or language features when a matching language server is installed. **Settings > General > Editors** controls editor integrations and discovery. +Open **Settings > Prompts** (`/settings/prompts`) to add, edit, or delete reusable prompts. A saved prompt needs a unique name and non-empty content. -The file tree reflects the selected task repository or attachment. In a multi-repository task, check the repository label before editing or reviewing. Agent changes can arrive while a file is open, so refresh the diff before commenting on line numbers. +Type `@` in the task chat composer and select a prompt. The visible message keeps the `@name`; Kandev expands the prompt content into hidden system context for the agent. References are recognized only at the start of the text or after whitespace and must match the stored name. Prompt content can reference other saved prompts. Expansion stops at a depth of eight, skips cycles, and includes each prompt only once. -## Terminal and shell +Kandev seeds these built-ins: -The integrated terminal runs in the task environment. For local/worktree executors that is the host; for Docker, SSH, or Sprites it is the selected runtime through agentctl. +- `code-review` +- `open-pr` +- `merge-base` +- `ci-auto-fix` +- `changes-walkthrough` -**Settings > General > Terminal** controls shell behavior. A command that works in your login shell may fail under a service account, container, or remote environment because PATH, credentials, working directory, or startup files differ. +Built-ins are marked in the UI but remain editable. Editing `ci-auto-fix` or `changes-walkthrough` changes the corresponding PR repair or walkthrough action. Seed insertion does not overwrite edits. If you delete a built-in, it stays absent for the current backend run and is seeded again on the next service start. There is no reset-to-default button. -## Changes, review, and walkthroughs +A saved prompt is an instruction, not an authorization or policy boundary. Executor permissions, human gates, tests, and provider protections still control what can happen. -The Changes panel groups repository state, staged/unstaged files, commits, branches, and pull requests. Review comments can be attached to exact lines and sent back to the agent. A generated code walkthrough presents an ordered tour of relevant file ranges. +## Voice Mode -See [Sessions and review](sessions-and-review.md) for the full review loop and [Git operations](git-operations.md) before rebasing or resolving conflicts. +Open **Settings > Voice Mode** (`/settings/voice-mode`). Voice Mode inserts a transcript at the cursor in the active chat composer. -## Personal settings +Defaults are: -General settings include: +| Setting | Default | +|---|---| +| Enabled | On | +| Engine | Automatic | +| Language | Auto-detect | +| Activation | Click to toggle | +| Auto-send | Off | +| Whisper Web model | Base, approximately 75 MB | +| Shortcut | `Cmd/Ctrl+Shift+M` | -- appearance, panel layout, and resource-metric visibility; -- keyboard shortcuts; -- desktop/browser notifications; -- task-action confirmations; -- terminal and editor preferences; -- named secrets used by profiles. +The shortcut is also configurable under **Settings > General > Keyboard Shortcuts**. Hold-to-talk applies on a fine-pointer device. On touch/coarse-pointer devices, Kandev uses toggle behavior while preserving the stored preference. With auto-send enabled, a successful transcript is sent as soon as it is inserted. -Keyboard shortcuts are editable in **Settings > General > Keyboard Shortcuts**. Avoid assigning the same chord to global browser, terminal, editor, and Kandev actions. +### Choose an engine -Notifications depend on browser/desktop permission and operating-system policy. Treat them as convenience signals rather than the only alert path for an unattended workflow. +| Engine | Where recognition happens | Requirements and data flow | +|---|---|---| +| **Automatic** | First available engine | Selects the first currently available capability in this order: Web Speech, Whisper Web, then Whisper Server. A pinned engine that is unavailable is resolved through the same capability order. | +| **Web Speech** | Browser-provided implementation | No audio is sent to the Kandev backend. Browser/vendor behavior and privacy policy still apply, and some implementations require a network service. | +| **Whisper Web** | In the browser | Downloads and caches an ONNX model from Hugging Face, then runs local inference in a worker. Tiny is about 40 MB, Base about 75 MB, and Small about 240 MB. | +| **Whisper Server** | Kandev backend and OpenAI | The browser uploads audio to Kandev; Kandev sends it to OpenAI's `whisper-1` transcription API. Configure `KANDEV_VOICE_OPENAI_API_KEY` on the backend. | -## Resource and usage visibility +Whisper Server accepts at most 10 MiB per request and has a 60-second backend timeout. It returns an unavailable error when the key is not configured, a payload-too-large error above the limit, and an upstream error when transcription fails. Automatic selection can choose this unconfigured server when both browser capabilities are unavailable. -Kandev can show host or environment CPU, memory, disk, load, and other supported metrics. It can also surface task/session statistics and provider usage hints where an integration exposes them. +Engine choice is capability selection, not runtime failover. Once recognition or transcription starts, an error does not retry the request through the next engine. The backend `/api/v1/transcribe` route has no Kandev authentication and spends the configured OpenAI key; protect the whole backend origin and do not publish that endpoint directly. -These values help diagnose capacity and activity; they are not quotas, billing guarantees, or security controls. Configure hard limits in the executor, provider, or host platform. +Microphone capture requires browser permission and normally HTTPS or localhost. Whisper Web also needs `getUserMedia`, `MediaRecorder`, workers, enough browser storage, and a first-use network download. The UI mentions common Chrome, Edge, and Safari versions, but Kandev does not enforce a browser/version allow-list; runtime availability is determined from the required APIs. If recording fails, check site permission, input device, secure context, model download/cache, browser support, and network access. The composer must remain enabled; switching tasks or disabling the input cancels recording. + +## Files and editor integrations + +The task **Files** panel browses, searches, opens, and edits task-worktree files. Kandev rejects file paths that escape the resolved worktree. A session with one worktree opens that worktree directly in a host editor. When a session has several worktrees, the editor button asks which repository or worktree to open, and each configured editor in the adjacent menu expands to the same repository-and-branch picker. Check that selection before launching an editor from a multi-repository task. Older API clients that omit `worktree_id` retain the first-worktree fallback. + +**VS Code (Embedded)** runs code-server inside the task environment and displays it in a workbench panel. Opening it starts code-server independently of the agent process. It launches with `--auth none` and binds `0.0.0.0` on a random port inside that runtime; network/firewall isolation is therefore important, especially for Local and SSH environments. If the binary is absent, agentctl attempts to download it into `~/.kandev/tools/code-server`; first use needs a supported host platform and access to the GitHub release. Use the panel error and task-environment logs when installation, startup, or proxying fails. + +Use the workbench top bar's split-editor action to open the selected session worktree in the default editor. Its menu lets you choose another configured editor. A file's **Open with** menu can also open a specific editor, copy the path, or ask the operating system to show the folder. + +Open **Settings > General > Editors** (`/settings/general/editors`) to set a default and configure integrations. Kandev discovers these built-in desktop editors when installed: + +- Visual Studio Code +- Zed +- Cursor +- Windsurf +- IntelliJ IDEA +- GoLand +- PhpStorm + +You can add: + +- a custom command with `{cwd}`, `{file}`, `{rel}`, `{line}`, and `{column}` placeholders; +- VS Code Remote SSH with a required host and optional user and URI scheme; +- a hosted editor base URL, to which Kandev adds file or folder query parameters. + +Desktop editor discovery and custom commands run on the Kandev backend host. The executable must be installed there and visible in its `PATH`; in a remote browser deployment, a local-editor command may therefore launch on the server rather than your laptop. Remote SSH needs a reachable SSH host and a registered VS Code URI handler. Hosted URLs need an accessible service and receive the absolute backend-host path in their `file` or folder query parameter. Configure only trusted custom commands because invoking one executes it on the host. + +## Language servers + +Language-server settings are part of **Settings > General > Editors**. Kandev currently registers servers for: + +- TypeScript and JavaScript; +- Go; +- Rust; +- Python. + +Auto-start and auto-install are off for every language by default. Enable only the languages used by the workspace, then save settings. A file toolbar can start or stop a server manually; browser-local storage remembers manual enablement for that session and language. Kandev disconnects an unused server connection after two minutes. + +Server lookup checks installed executables and `/lsp-servers` (by default `~/.kandev/lsp-servers`). Auto-install uses different toolchains: + +- TypeScript/JavaScript and Python install npm packages into Kandev's language-server storage; +- Go runs `go install ...@latest` and therefore needs a working Go toolchain; +- Rust downloads a release for supported macOS or Linux, x86-64 or ARM64 hosts. Windows is not registered for automatic Rust installation. + +The default Go server configuration enables semantic tokens. A custom language-server configuration must be a JSON object; Kandev sends it through the language-server workspace configuration request. Installing a language server does not install project dependencies or make an unsupported language available. If startup fails, check the backend log/status, executable `PATH`, supported host platform, toolchain/network access, project dependencies, and that the file belongs to the active task worktree. + +Language-server subprocesses run on the Kandev backend host with the task workspace path as their working directory; they are not launched inside a Docker container, SSH host, or Sprites sandbox. Remote-only paths, binaries, and project dependencies are therefore normally unavailable to this LSP path even when the agent itself can use them. + +## Integrated terminal + +The task terminal is a PTY in the task environment. Local tasks run through the local environment; Docker, SSH, and other remote executors route the terminal to their configured runtime. + +On desktop, select **+ > Terminals > New Terminal**. Parked terminal sessions can appear in the same menu for reopening. The tab context menu offers **Rename** and **Terminate**, not a separate Close action. Selecting the tab's **X** deletes the terminal and asks for confirmation when it is busy. Removing the panel through layout management can instead park it and keep its PTY alive; terminating a live terminal or deleting a parked entry destroys it. On mobile, select **Terminal** in task navigation and use the terminal picker to create another. + +Do not confuse a user terminal with a CLI-passthrough agent tab: both use PTYs, but only the agent tab is the agent's native interface. `Cmd/Ctrl+J` toggles the bottom terminal area. + +Open **Settings > General > Terminal** (`/settings/general/terminal`) to configure: + +- preferred shell, which defaults to the system shell; the built-in choices are zsh, bash, and sh on macOS/Linux, and PowerShell (`pwsh`), Windows PowerShell, and cmd on Windows, plus a custom executable; +- terminal font, with a default Menlo/Monaco-style stack; +- font size, default 13 px and allowed range 8–24 px; +- URL handling, which defaults to a new browser tab and can instead use Kandev's built-in browser panel. + +Shell changes apply whenever a new or restarted terminal is created, including inside an existing task. Only an already-live PTY keeps its current shell. A custom shell must exist in the task environment. Fonts render only when available to the browser. Commands can behave differently from your login shell because the executor may use another user, `PATH`, credentials, working directory, or startup files. + +If no terminal can be created, wait for the task environment to become ready and confirm its executor is reachable. If a reopened terminal is dead, create a new one; the original PTY or remote connection may have exited. ## Choose the right surface | Need | Use | |---|---| -| Short disposable question | Quick chat | -| Branch, review, or delivery state | Normal task and workflow | -| Repeatable model-assisted metadata | Utility agent | -| Shared process instructions | Prompt template and workflow | -| Inspect or edit task files | Workbench editor | -| Run project commands | Task terminal | -| Explain a change to a reviewer | Changes panel and code walkthrough | +| Disposable question or experiment | Quick Chat | +| Board history, branch, review, dependency, or PR | A normal task and workflow | +| Small generated title, summary, or description | Utility agent | +| Reusable task-chat instructions | Saved prompt | +| Dictate into chat | Voice Mode | +| Browse or edit task files | Files and editor integration | +| Diagnostics for supported languages | Language server | +| Run a command in the task runtime | Integrated terminal | -Related: [Get started](use-kandev.md), [Tasks and workflows](tasks-and-workflows.md), and [Agents and profiles](agents-and-profiles.md). +Related: [Use Kandev](use-kandev.md), [Sessions and review](sessions-and-review.md), [Agents and profiles](agents-and-profiles.md), and [Integrations](integrations.md). diff --git a/docs/public/docker.md b/docs/public/docker.md index 59b924ed36..0e7d92c7dc 100644 --- a/docs/public/docker.md +++ b/docs/public/docker.md @@ -1,170 +1,165 @@ --- title: "Docker" -description: "Run Kandev and agent execution environments with Docker." +description: "Run the Kandev control plane in Docker and understand Docker-based agent execution." --- -# Docker Guide +# Docker -Run Kandev in a Docker container. For Kubernetes deployment, see [k8s.md](k8s.md). +The published image runs the Kandev control plane: native backend, web UI, API, WebSocket endpoint, external MCP endpoint, and the host-side `agentctl`. This is different from the **Local Docker executor**, which creates a separate container for an agent. -## Quick Start +For Kubernetes, see [Kubernetes](k8s.md). For executor profiles, see [Executors](executors.md#local-docker). + +## Quick start + +Bind to host loopback unless an authenticated reverse proxy protects the service: ```bash -docker run -p 38429:38429 -v kandev-data:/data ghcr.io/kdlbs/kandev:latest +docker volume create kandev-data +docker run -d \ + --name kandev \ + --restart unless-stopped \ + -p 127.0.0.1:38429:38429 \ + -v kandev-data:/data \ + ghcr.io/kdlbs/kandev:latest ``` -Open `http://localhost:38429` in your browser. - -## Using the Pre-built Image - -Kandev publishes images to GitHub Container Registry for `linux/amd64` and `linux/arm64`: +Open `http://localhost:38429` and follow logs with: ```bash -# Latest release -docker pull ghcr.io/kdlbs/kandev:latest - -# Specific version -docker pull ghcr.io/kdlbs/kandev:0.9.0 +docker logs -f kandev ``` -### Choosing your image: vanilla vs. universal +Kandev currently has no built-in multi-user web login or API authorization boundary. `auth.jwtSecret` does not add one. Docker's unqualified `-p 38429:38429` publishes on every host interface, so do not use that form on an untrusted network. Use loopback, a private network/VPN, or an authenticated reverse proxy with TLS. -Two flavors are published. The default vanilla image is smallest and bundles npm-installable agent CLIs only. The `:universal` image (~1.4 GB) adds language toolchains (Go, Rust, build-essential), linters, and Playwright Chromium system libs - pick this if your agents work on Go/Rust/Python projects or drive headless browsers. +## Published images + +The release workflow publishes multi-architecture `linux/amd64` and `linux/arm64` images to `ghcr.io/kdlbs/kandev`. + +| Flavor | Moving tag | Version tags | Contents | +|---|---|---|---| +| Base | `latest` | `X.Y.Z`, `vX.Y.Z` | Kandev, Node 24/npm, Git, `gh`, Python/pipx, Apprise, Azure CLI, and the Azure DevOps extension | +| Universal | `universal` | `X.Y.Z-universal`, `vX.Y.Z-universal` | Base plus Go, Rust, pnpm, build tools, common developer CLIs, and Playwright Chromium system libraries | + +The universal image does not include Playwright browser downloads, JDKs, .NET, or database servers. Its tool versions are pinned in `Dockerfile.universal` for each release. See the [image guide](https://github.com/kdlbs/kandev/blob/main/docs/images.md) for the inclusion policy and derived-image examples. + +Use a version tag or digest in a persistent deployment: ```bash -docker pull ghcr.io/kdlbs/kandev:universal +docker pull ghcr.io/kdlbs/kandev:X.Y.Z +docker pull ghcr.io/kdlbs/kandev:X.Y.Z-universal ``` -See the [repository image guide](https://github.com/kdlbs/kandev/blob/main/docs/images.md) for the full comparison, inclusion policy, and recipes for deriving your own image. +Replace `X.Y.Z` with a real release. `latest` moves on release. `universal` moves on release and is also rebuilt from the current `latest` base each Monday by the repository's scheduled workflow; dated `universal-weekly-YYYYMMDD` tags identify those rebuilds. Release-versioned universal tags remain unchanged. -## Building from Source +The repository root `Dockerfile` is release packaging, not a source-build Dockerfile. The release workflow first builds a native bundle for each architecture, prepares a build context containing `bundle/` plus `docker-entrypoint.sh`, builds the amd64 and arm64 images separately, and joins their digests into the published manifest. A plain `docker build .` from a checkout fails because `bundle/` is intentionally absent. -The root `Dockerfile` is a release-image Dockerfile: it copies prebuilt binaries from a `bundle/` directory in the Docker build context. It does not compile the repository inside Docker. On a Linux host matching the target architecture, build the service bundle and prepare the same context layout used by the release workflow: +For extra OS tools, derive from a pinned published image so the release bundle and entrypoint stay intact: -```bash -make service-bundle -rm -rf ctx -mkdir -p ctx/bundle -cp -R dist/kandev/. ctx/bundle/ -cp docker-entrypoint.sh ctx/ -docker build -f Dockerfile -t kandev:latest ctx +```dockerfile +FROM ghcr.io/kdlbs/kandev:X.Y.Z +USER root +RUN apt-get update \ + && apt-get install -y --no-install-recommends postgresql-client \ + && rm -rf /var/lib/apt/lists/* ``` -For a cross-architecture image, first produce or download the matching `kandev-linux-x64.tar.gz` or `kandev-linux-arm64.tar.gz` release bundle, extract its top-level `kandev/` directory into `ctx/bundle/`, copy `docker-entrypoint.sh` into `ctx/`, and build that context with the matching `--platform`. Setting `--platform` without a matching bundle only labels an image containing the wrong native binaries. +Leaving the final configured user as root is intentional for this base flavor: the inherited entrypoint repairs `/data` and drops to `kandev` before starting the command. If a derivative finishes with `USER kandev`, provision writable volume ownership in advance, as the universal flavor does. -## Data Persistence +## Image runtime behavior -Kandev stores its SQLite database and git worktrees in `/data`. Mount a volume to persist data across container restarts: +The base image: -```bash -# Named volume (recommended) -docker run -v kandev-data:/data ghcr.io/kdlbs/kandev:latest +- uses Debian Bookworm and exposes only TCP 38429; +- sets `KANDEV_HOME_DIR=/data`, `HOME=/data/home`, and `NPM_CONFIG_PREFIX=/data/.npm-global`; +- disables the Docker executor with `KANDEV_DOCKER_ENABLED=false`; +- starts `kandev start --backend-port 38429 --verbose` under `tini`; +- starts as root only long enough to create `/data/home` and recursively `chown /data`, then runs Kandev as user `kandev` (UID 1000) through `gosu`. -# Bind mount to a host directory -docker run -v /path/on/host:/data ghcr.io/kdlbs/kandev:latest -``` +The universal derivative sets `USER kandev`, so it skips the base entrypoint's root ownership repair. Pre-create a writable bind mount before using that flavor. Recursive ownership repair in the base image makes named volumes convenient, but can be slow on a large bind mount and can fail on root-squashed network storage. To run either image directly as UID 1000, pre-create every required path with suitable ownership and test the storage driver. -Without a volume, data is lost when the container is removed. +## Persistence -### What lives on the volume +Always mount `/data`. Removing a container without a volume removes its database, workspaces, installed agent CLIs, and authentication state. -| Path | Contents | +| Volume path | Data | |---|---| -| `/data/data/` | SQLite database (`kandev.db`, `-wal`, `-shm`) | -| `/data/worktrees/`, `/data/tasks/`, `/data/repos/`, `/data/sessions/`, `/data/lsp-servers/` | Per-session state | -| `/data/.npm-global/` | Agent CLIs installed via `npm install -g` (`NPM_CONFIG_PREFIX`) | -| `/data/home/` | `$HOME` for the in-container `kandev` user — `gh` CLI and agent CLI auth state | +| `/data/data/` | SQLite `kandev.db`, WAL/SHM files, and SQLite backup snapshots | +| `/data/tasks/`, `/data/worktrees/`, `/data/repos/`, `/data/sessions/`, `/data/lsp-servers/` | Task and repository runtime state | +| `/data/agent-sessions/` | Selectively seeded per-execution agent credential/session directories for Docker executors | +| `/data/.npm-global/` | Agent CLIs installed at runtime | +| `/data/home/` | Persistent home for `gh`, agent CLI auth, Azure config, caches, and user configuration | -### Persistent agent and `gh` CLI auth +For a bind mount: -The image sets `HOME=/data/home`, so every CLI that writes its auth under `$HOME` lands on the volume and survives container restarts and image upgrades: +```bash +sudo install -d -o 1000 -g 1000 /srv/kandev +docker run -d \ + --name kandev \ + -p 127.0.0.1:38429:38429 \ + -v /srv/kandev:/data \ + ghcr.io/kdlbs/kandev:X.Y.Z +``` -- `gh` CLI — `~/.config/gh/hosts.yml` -- Claude Code — `~/.claude/.credentials.json`, `~/.claude.json` -- Codex — `~/.codex/auth.json`, `~/.codex/config.toml` -- Auggie — `~/.augment/session.json` -- GitHub Copilot — `~/.copilot/...` -- OpenCode, Amp — `~/.config//...` +Confirm UID/GID policy on your host before copying this example. The image guarantees UID 1000 for the user but does not promise GID 1000 for its primary group; its root entrypoint normally corrects ownership using the image's actual group. -A one-time `docker exec -it kandev gh auth login` (or `claude login`, `codex login`, etc.) is enough; you do not need to redo it after `docker pull` and recreating the container. +### CLI authentication inside the container -> The GitHub PAT configured in **Settings → Integrations → GitHub** is stored as a secret in the database and has always persisted. The `HOME=/data/home` setup covers the separate `gh auth login` flow that the backend falls back to when no `GITHUB_TOKEN` secret is set. +The base and universal flavors have different configured users. Always select `kandev` explicitly for interactive login and installs so files under `/data/home` remain usable by the service: + +```bash +docker exec --user kandev -it kandev gh auth login +docker exec --user kandev -it kandev sh +``` + +The same rule applies to `claude login`, `codex login`, and manual `npm install -g` commands. Treat `/data/home` and the database as secret material when backing them up. ## Configuration -Configuration is done via `KANDEV_`-prefixed environment variables: +Pass backend settings as environment variables or mount a read-only `/etc/kandev/config.yaml`. See [Configuration](configuration.md) for the canonical field names, environment mapping, validation, and precedence. + +Container-specific defaults are: + +| Setting | Image value | Notes | +|---|---|---| +| `KANDEV_HOME_DIR` | `/data` | Root for database and runtime state | +| `HOME` | `/data/home` | Persistent CLI credentials and caches | +| `NPM_CONFIG_PREFIX` | `/data/.npm-global` | Runtime-installed npm CLIs | +| `KANDEV_DOCKER_ENABLED` | `false` | Overrides the backend's ordinary host default | +| `KANDEV_NO_BROWSER` | `1` | Prevents browser launch | +| Internal listener | `38429` | Fixed by the image command unless that command is replaced | +| Default log level | `info` | The image command passes `--verbose`; an explicit `KANDEV_LOG_LEVEL` wins | + +Example: ```bash -docker run -p 38429:38429 \ +docker run -d \ + --name kandev \ + -p 127.0.0.1:38429:38429 \ -v kandev-data:/data \ - -e KANDEV_LOG_LEVEL=debug \ - ghcr.io/kdlbs/kandev:latest + -e KANDEV_LOG_LEVEL=warn \ + ghcr.io/kdlbs/kandev:X.Y.Z ``` -### Environment Variables - -See [`configuration.md`](./configuration.md) for the full reference (including the YAML form and every knob the backend reads). The table below covers the env vars most often set in a Docker deployment. - -| Variable | Required | Default | Description | -|----------|----------|---------|-------------| -| `KANDEV_HOME_DIR` | No | `/data` | Kandev home directory - contains `data/` (DB), `tasks/`, `worktrees/`, `repos/`, `sessions/`, and `lsp-servers/` | -| `KANDEV_DATABASE_DRIVER` | No | `sqlite` | Database driver (`sqlite` or `postgres`) | -| `KANDEV_DATABASE_PATH` | No | `$KANDEV_HOME_DIR/data/kandev.db` | SQLite database file path (override) | -| `KANDEV_LOG_LEVEL` | No | `info` | Log level: `debug`, `info`, `warn`, `error` | -| `KANDEV_LOGGING_FORMAT` | No | environment-selected (`text` in ordinary Docker runs) | Explicit format: `text` or `json`. The default becomes `json` when `KANDEV_ENV` is `production` or `prod`. | -| `KANDEV_LOGGING_OUTPUTPATH` | No | `stdout` | Log destination: `stdout`, `stderr`, or a file path (rotated when a file) | -| `KANDEV_LOGGING_MAXSIZEMB` | No | `100` | Rotate the log file when it exceeds this size (MB). File output only. | -| `KANDEV_LOGGING_MAXBACKUPS` | No | `5` | Max rotated files to retain (`0` = unlimited). File output only. | -| `KANDEV_LOGGING_MAXAGEDAYS` | No | `30` | Max age of rotated files in days (`0` = unlimited). File output only. | -| `KANDEV_LOGGING_COMPRESS` | No | `true` | Gzip rotated files. File output only. | -| `KANDEV_DOCKER_ENABLED` | No | `false` | Enable Docker runtime for agents (see below) | - -> **File-mode note:** when `KANDEV_LOGGING_OUTPUTPATH` is a file path, the active log file is created with mode `0600` (owner read/write only). Run any log shipper or sidecar as the same user, or use `stdout`/`stderr` and let the container runtime collect logs. -> -> **Upgrading from a pre-`KANDEV_HOME_DIR` image?** The SQLite DB path moved from `/data/kandev.db` to `/data/data/kandev.db`. The backend auto-migrates the legacy `kandev.db` (plus any `-wal`/`-shm` files) on first boot — look for `Migrated SQLite database from pre-KANDEV_HOME_DIR location` in the logs. If you prefer to pin the old location instead, set `-e KANDEV_DATABASE_PATH=/data/kandev.db`. If you previously set `KANDEV_DATA_DIR`, replace it with `KANDEV_HOME_DIR`. - -### PostgreSQL - -To use PostgreSQL instead of SQLite: +To expose a different host port, leave the internal command alone: ```bash -docker run -p 38429:38429 \ - -e KANDEV_DATABASE_DRIVER=postgres \ - -e KANDEV_DATABASE_HOST=host.docker.internal \ - -e KANDEV_DATABASE_PORT=5432 \ - -e KANDEV_DATABASE_USER=kandev \ - -e KANDEV_DATABASE_PASSWORD=secret \ - -e KANDEV_DATABASE_DBNAME=kandev \ - ghcr.io/kdlbs/kandev:latest -``` - -## Port - -Kandev exposes a single port. The Go backend serves the API, WebSocket, static SPA assets, and page boot data — all on one port: - -| Port | Service | -|------|---------| -| `38429` | API + WebSocket + Web UI | - -Override the port: - -```bash -docker run -p 9080:9080 \ +docker run -d \ + --name kandev \ + -p 127.0.0.1:9080:38429 \ -v kandev-data:/data \ - ghcr.io/kdlbs/kandev:latest \ - kandev start --backend-port 9080 + ghcr.io/kdlbs/kandev:X.Y.Z ``` +Open `http://localhost:9080`. If you replace the container command to change its internal port, publish that same port and retain `--verbose` if info logs are desired. + ## Docker Compose -Create a `docker-compose.yml`: - ```yaml services: kandev: - image: ghcr.io/kdlbs/kandev:latest + image: ghcr.io/kdlbs/kandev:X.Y.Z ports: - - "38429:38429" + - "127.0.0.1:38429:38429" volumes: - kandev-data:/data restart: unless-stopped @@ -175,16 +170,19 @@ volumes: ```bash docker compose up -d +docker compose logs -f kandev ``` -### With PostgreSQL +### PostgreSQL example + +PostgreSQL moves database rows out of the Kandev volume, but `/data` is still required for workspaces, CLI installs, auth files, and other runtime state. ```yaml services: kandev: - image: ghcr.io/kdlbs/kandev:latest + image: ghcr.io/kdlbs/kandev:X.Y.Z ports: - - "38429:38429" + - "127.0.0.1:38429:38429" volumes: - kandev-data:/data environment: @@ -192,7 +190,7 @@ services: KANDEV_DATABASE_HOST: postgres KANDEV_DATABASE_PORT: "5432" KANDEV_DATABASE_USER: kandev - KANDEV_DATABASE_PASSWORD: secret + KANDEV_DATABASE_PASSWORD: "${KANDEV_DB_PASSWORD:?set KANDEV_DB_PASSWORD}" KANDEV_DATABASE_DBNAME: kandev depends_on: postgres: @@ -200,18 +198,18 @@ services: restart: unless-stopped postgres: - image: postgres:17 + image: postgres:16 environment: POSTGRES_USER: kandev - POSTGRES_PASSWORD: secret + POSTGRES_PASSWORD: "${KANDEV_DB_PASSWORD:?set KANDEV_DB_PASSWORD}" POSTGRES_DB: kandev volumes: - postgres-data:/var/lib/postgresql/data healthcheck: - test: ["CMD-SHELL", "pg_isready -U kandev"] + test: ["CMD-SHELL", "pg_isready -U kandev -d kandev"] interval: 5s timeout: 3s - retries: 5 + retries: 10 restart: unless-stopped volumes: @@ -219,36 +217,11 @@ volumes: postgres-data: ``` -## Reverse Proxy +Put `KANDEV_DB_PASSWORD` in a permission-restricted deployment secret, not shell history or committed YAML. Kandev's built-in System backup/restore covers SQLite only; use `pg_dump` and a tested PostgreSQL restore procedure here. -Since Kandev serves everything on a single port, a reverse proxy only needs to forward all traffic to port 38429. No extra environment variables are needed — the frontend automatically uses `window.location.origin` to reach the API. +## Reverse proxy and network policy -### Docker Compose with Caddy - -```yaml -services: - kandev: - image: ghcr.io/kdlbs/kandev:latest - volumes: - - kandev-data:/data - restart: unless-stopped - - caddy: - image: caddy:2 - ports: - - "80:80" - - "443:443" - volumes: - - caddy-data:/data - - ./Caddyfile:/etc/caddy/Caddyfile - restart: unless-stopped - -volumes: - kandev-data: - caddy-data: -``` - -Example `Caddyfile`: +The backend serves SPA, API, `/ws`, `/mcp`, and `/health` on one origin. Proxy the entire root path and preserve WebSocket upgrades. Caddy does this automatically: ```text kandev.example.com { @@ -256,44 +229,37 @@ kandev.example.com { } ``` -## Docker-in-Docker (Agent Containers) +TLS alone is not authentication. Put an identity-aware/authenticated gateway in front, restrict direct access to the Kandev container, and apply CSRF/origin policy appropriate to that gateway. A subpath such as `/kandev/` is not a documented deployment base; prefer a dedicated host at `/`. -By default, `KANDEV_DOCKER_ENABLED=false` inside the container. To enable Docker-based agent execution, mount the Docker socket: +## Using Docker for agent environments + +Host-installed Kandev can use **Settings > Executors > Docker** against a same-host daemon. The daemon must be reachable through global `docker.host`; the current runtime assumes bind-mount source paths exist on that daemon host. See [Local Docker executor](executors.md#local-docker) for image, credential, port, resume, and cleanup behavior. + +### Containerized control plane limitation + +Mounting `/var/run/docker.sock` into the Kandev service container is **not by itself a complete configuration**. Agent creation asks the daemon to bind-mount: + +- the Linux `agentctl` helper path resolved inside the control-plane container; +- per-execution directories under the control plane's Kandev home; +- a local clone source in the filesystem-URL case. + +The Docker daemon resolves those paths on the daemon host, not inside the Kandev container. A named `/data` volume and the image's `/app/...` helper therefore do not automatically exist at matching host paths. The current Docker executor also selects a Linux/amd64 helper unconditionally, so the agent container must be Linux/amd64-compatible (native or correctly emulated). + +For reliable Local Docker execution, run the Kandev control plane on the Docker host. Alternatively, build a custom deployment that mirrors every required source at identical absolute host/container paths and test cleanup, architecture, permissions, and upgrades. This advanced layout has no supplied Compose or Kubernetes manifest. Use SSH or Sprites when the control plane itself must remain containerized. + +Giving Kandev a Docker socket or daemon API grants near-root control of that host. Protect the endpoint, never expose an unauthenticated TCP daemon, and do not give untrusted Kandev users profile-build access. + +Remote Docker profiles are not a workaround: that executor runtime is currently unimplemented. + +## Health and observability + +`GET /health` returns 503 during startup and 200 after routes are registered and the listener is accepting connections: ```bash -docker run -p 38429:38429 \ - -v kandev-data:/data \ - -v /var/run/docker.sock:/var/run/docker.sock \ - -e KANDEV_DOCKER_ENABLED=true \ - ghcr.io/kdlbs/kandev:latest +curl --fail http://localhost:38429/health ``` -> **Note:** Mounting the Docker socket gives the container full access to the host's Docker daemon. Only do this in trusted environments. - -## Upgrading - -```bash -docker pull ghcr.io/kdlbs/kandev:latest -docker compose up -d # or: docker stop kandev && docker rm kandev && docker run ... -``` - -The volume at `/data` carries over the database, worktrees, npm globals, and `$HOME` for agent CLIs, so there is no manual migration step. - -> **Upgrading across the `HOME=/data/home` change:** if you previously ran `docker exec` to `gh auth login` or log in to agent CLIs on a pre-`HOME=/data/home` image, that state lived in the ephemeral `/home/kandev` inside the container and is not carried over. Log in once on the new container and it will persist for all subsequent upgrades. If you want to preserve the old state, copy it onto the volume before recreating the container: -> -> ```bash -> docker exec kandev sh -c 'cp -a /home/kandev/. /data/home/ 2>/dev/null || true' -> ``` - -## Health Check - -The backend exposes a `/health` endpoint: - -```bash -curl http://localhost:38429/health -``` - -For Docker health checks in compose: +It is a startup/readiness signal, not a deep database, Git, Docker, provider, or agent check. A Compose health check can still use it: ```yaml healthcheck: @@ -304,18 +270,39 @@ healthcheck: start_period: 15s ``` -## Troubleshooting +Container logs go to stdout/stderr by default. If configuration sends logs to a file, place that path on persistent storage and account for Kandev's file rotation settings. + +## Upgrade and remove + +Back up first; then pull and recreate with the same `/data` volume and configuration: ```bash -# View logs -docker logs kandev +docker compose pull kandev +docker compose up -d kandev +docker compose logs -f kandev +``` -# Follow logs -docker logs -f kandev +SQLite schema migrations run at startup and create a pre-migration SQLite snapshot when needed. PostgreSQL does not receive that built-in snapshot. A container/image rollback does not roll back the database schema, so keep a tested backup from before the upgrade. See [Operations](operations.md) for backup and restore details. -# Shell into the container -docker exec -it kandev /bin/bash +Stopping or removing the service container leaves the named volume: -# Check data volume +```bash +docker stop kandev +docker rm kandev docker volume inspect kandev-data ``` + +Delete `kandev-data` only after verifying that its database, workspaces, and credentials are no longer needed. + +## Troubleshooting + +- **UI unreachable:** check `docker ps`, published address/port, host firewall, and `docker logs kandev`; then call `/health`. +- **Permission denied under `/data`:** inspect mount ownership and root-squash behavior. The runtime user is UID 1000 after entrypoint setup. +- **CLI login works only as root:** repeat it with `docker exec --user kandev`; repair ownership of the affected files before restarting. +- **Image pull fails:** authenticate to GHCR if your network policy requires it and verify the tag/platform with `docker buildx imagetools inspect`. +- **Database connection fails:** test DNS/TCP from the Kandev container, credentials, database name, and `sslMode`. +- **WebSocket disconnects behind proxy:** forward the whole origin, enable upgrade support, and increase proxy idle timeouts. +- **Docker agent container fails to mount helper/session paths:** the control plane is probably containerized or using a remote daemon whose filesystem paths do not match; use a same-host control plane or a fully mirrored custom layout. +- **Disk growth:** inspect `/data`, retained Docker agent containers, image layers/build cache, and Docker volumes before deleting anything. + +Related pages: [Configuration](configuration.md), [Executors](executors.md), [Operations](operations.md), and [Run as a Service](run-as-a-service.md). diff --git a/docs/public/executors.md b/docs/public/executors.md index 06fcd25d34..d5180090ba 100644 --- a/docs/public/executors.md +++ b/docs/public/executors.md @@ -1,75 +1,213 @@ --- title: "Executors" -description: "Choose and configure local, worktree, Docker, SSH, remote Docker, or Sprites task environments." +description: "Choose and configure local, worktree, Docker, SSH, or Sprites task environments." --- # Executors -An executor decides where Kandev materializes a task workspace and runs agentctl, terminals, Git operations, and the agent subprocess. An executor profile stores reusable environment policy for that runtime. +An executor determines where Kandev creates a task environment and runs `agentctl`, the selected agent, terminals, and Git commands. An executor profile supplies reusable settings for that executor. A task environment is the concrete workspace created for one task; several sessions may reuse it. -## Runtime choices +## Current support -| Executor | Workspace and process location | Isolation and prerequisites | +| Executor | Current status | Workspace | Use it when | +|---|---|---|---| +| Worktree | Supported; normal default | Dedicated Git worktree on the Kandev host | Parallel coding on a trusted machine | +| Local | Supported | The selected checkout, or an explicit folder for a repository-free task | One controlled task must work in that exact folder | +| Local Docker | Supported when the global Docker runtime is enabled and its daemon is reachable | `/workspace` in a new Docker container | You need a repeatable container boundary | +| Sprites.dev | Supported, provider-dependent | `/workspace` in a provider sandbox | You need remote compute and accept provider lifecycle/billing | +| SSH | Available with important repository-setup limitations | A task folder on a trusted SSH host | The task can start in an empty remote folder, or you manage repository materialization separately | +| Remote Docker | **Not implemented** | None | Do not select or create this type | + +`mock_remote` also exists in backend models for tests. It is not a product executor. + +Remote Docker deserves explicit treatment: the backend registers the runtime type, but its create and stop methods return `remote_docker runtime is not yet implemented`. The current **Settings > Executors** hub does not offer it. Older routes and stored fields such as `docker_host`, `docker_tls_verify`, and `docker_cert_path` do not make it operational. + +## Create and select a profile + +Open **Settings > Executors**, then choose **Local**, **Worktree**, **Docker**, **Sprites.dev**, or **SSH** under **Create New Profile**. Local and Worktree profiles already exist in a new database. + + + +A profile stores: + +- its name; +- environment variables, either as a literal value or a Kandev secret reference; +- a prepare script and cleanup script; +- an MCP policy JSON object; +- runtime-specific configuration. + +Literal environment values are stored with the profile. Use secret references for credentials. Resolved values and copied credential files normally become accessible to the agent and commands in that environment. SSH is narrower: its remote agent process receives only the credential allowlist documented below, not arbitrary profile variables. + +The MCP editor checks only that the value is a JSON object. Its presets cover stdio, HTTP, and SSE transport allowances, server allowlists, and URL rewrites. Test restrictive policies with the actual MCP servers the agent needs; see [Automation and MCP](automation-and-mcp.md). + +Profile edits apply when Kandev provisions a launch, but a Docker container or Sprite resume can reconnect to the already provisioned process, image, environment, credentials, and files. Use **Reset Environment** or explicitly destroy the resource when a change must take effect on a fresh environment. Deleting or editing a profile does not tear down an already-running resource. + +### Script behavior is runtime-specific + +Do not treat the two script fields as universal hooks: + +| Runtime | Prepare script | Profile cleanup script | |---|---|---| -| Local | Selected local checkout; process runs on the Kandev host. | Lowest setup cost and lowest isolation. Avoid concurrent writers. | -| Worktree | Dedicated Git worktree on the Kandev host. | Isolates branches/files between tasks, but shares the host OS, user, and accessible credentials. | -| Local Docker | Container managed by the host Docker engine. | Requires Docker and an image/profile. Container is a stronger process/filesystem boundary than a worktree. | -| Remote Docker | Container managed through a configured remote Docker host. | Requires remote daemon connectivity and credential/network planning. | -| SSH | Workspace and agentctl on a reachable Linux host. | Requires SSH authentication, remote dependencies, and enough disk/process capacity. | -| Sprites | Remote sandbox provided through Sprites. | Requires provider credentials and network access; environment lifetime and billing follow the provider. | +| Local / Worktree | Runs on the host during preparation. A failure is shown but is non-fatal, so the agent can still start for diagnosis. | Not executed by the executor runtime. Repository-level worktree cleanup is a separate repository setting. | +| Local Docker | Runs inside the container before `agentctl`. Failure is logged but `agentctl` still starts. | Not executed. | +| Sprites | Runs inside a newly created sandbox. Failure aborts the launch and destroys that new sandbox. | Runs, with a 60-second limit, only when a live execution is stopped with a task/session archived or deleted reason; failure does not prevent the subsequent destroy attempt. Plain Stop, **Reset Environment**, and profile-page direct destroy do not run this script. | +| SSH | **Currently not executed.** | **Currently not executed.** | -Kandev also has mock executor types for tests. They are not production runtime choices. +Keep working prepare scripts noninteractive and idempotent. Kandev resolves supported placeholders and appends its managed branch checkout for Docker and Sprites after the user script. A profile cleanup script must never remove paths outside the environment it owns. -## Executor profiles +Two current preparation exceptions are easy to miss: -A profile can contain: +- A repository-free Local task bypasses the environment-preparer stage, even when it uses an explicit workspace folder. Its profile prepare script therefore does not run. +- A Worktree task with two or more attached repositories runs each repository's setup script while creating that repository's worktree, but the current multi-repository preparer does not run the executor profile's task-level prepare script. -- a name and runtime-specific configuration; -- prepare script run while creating/preparing the environment; -- cleanup script run during teardown where supported; -- environment variables as plaintext or Kandev secret references; -- MCP policy; -- image, host, credential, or provider settings specific to the executor. +## Worktree -Scripts run with the executor's permissions and can modify the task environment. Keep them idempotent, fail loudly, avoid interactive input, and pin important tooling. A cleanup script should not delete paths outside the environment it owns. +Worktree creates a dedicated host Git worktree and runs the standalone `agentctl` service against it. It separates branches and files between tasks, but the process still has the Kandev user's host permissions, network access, and readable credentials. -## Pick an executor by risk and workload +Repository settings control base branch, branch naming, pull-before-create, repository setup/cleanup scripts, and optional copies of ignored files. Copy ignored files narrowly: `.env` and similar files often contain production secrets. Multi-repository tasks receive one materialized worktree per attachment; use the per-repository setup scripts because the profile-level prepare script is currently skipped for that path. -Use **Worktree** for normal parallel coding on one trusted machine. It prevents two task branches from writing the same checkout but does not protect the host from agent commands. +Normal stop keeps the task environment available. Task deletion or **Reset Environment** removes the tracked worktree when configured to clean worktrees. Preserve or push valuable changes first; see [Git Operations](git-operations.md). -Use **Local Docker** when repository setup is reproducible in an image and you want a container boundary. Plan how source, caches, credentials, ports, and Docker socket access are mounted; mounting the host Docker socket grants powerful host control. +Typical failures: -Use **SSH**, **Remote Docker**, or **Sprites** when local CPU, memory, architecture, network placement, or long-running availability is unsuitable. The remote host must be able to fetch every attached repository and reach required package registries/integrations. +- dirty or conflicting source repository state; +- base branch missing locally or remotely; +- worktree path already registered in Git metadata; +- setup dependencies absent on the host; +- repository cleanup failure leaving a stale worktree. -Use **Local** only when intentionally operating the selected checkout. It can be correct for no-repository tasks or a controlled single task, but it is easy for concurrent work to collide. +Use `git worktree list --porcelain` in the source repository when diagnosing stale registrations. Do not delete a worktree directory by hand before checking whether Git still tracks it. -## Task environments and sessions +## Local -Kandev tracks task environment state as creating, ready, stopped, or failed. The environment owns the workspace and agentctl control server. Multiple sessions can share it, and each session has an executor-running record with runtime, process/container identifiers, worktree path/branch, health timestamps, and errors. +Local runs directly in the selected checkout. It provides no file isolation: concurrent tasks, the user, and other tools can edit the same files and switch the same branch. A requested branch checkout can fail when the checkout is dirty or has an unfinished merge. -Stopping a session does not always delete the task environment. Resumable runtimes can retain it for a later turn. Archive/delete/cleanup behavior depends on task action, profile, and runtime. +Use Local for an intentionally shared checkout, a controlled single task, or a repository-free task with an explicit workspace folder. Prefer Worktree for parallel coding. Stop ends the agent process but does not clean the checkout or undo its changes. -## Repository and credential behavior +## Local Docker -- Git operations execute inside the task environment through agentctl. -- Multi-repository tasks materialize every attachment and address repositories by subpath. -- Worktree tasks can copy selected ignored files, such as `.env` files, from the source repository. Configure this narrowly; ignored files often contain secrets. -- Local host credentials are not automatically available in a container or remote machine. Use scoped secret/profile configuration or the runtime's supported credential copy flow. -- SSH credential-copy options can expose local auth files to the remote environment. Review the selected files and remote host trust first. +### Prerequisites and profile creation -## Resource metrics and capacity +Install a reachable Docker Engine and leave `docker.enabled: true` (the non-containerized backend default). The published Kandev service image overrides this to `false`; see [Docker](docker.md#using-docker-for-agent-environments). The runtime health method is currently a no-op and the client is initialized lazily, so a green control-plane startup does not prove daemon access; image build or first task launch is the effective check. -Kandev can show host CPU, memory, disk, temperature, and load plus execution-environment metrics for supported remote/container runtimes. Enable the desired metrics in General settings. Metrics are observability, not hard resource limits; set limits in Docker/provider/host policy where needed. +Choose **Settings > Executors > Docker**. The current UI requires an image tag, Dockerfile content, and a successful **Build Image** operation before it creates the profile. **Use defaults** supplies: -Parallel agents can saturate CPU, memory, disk I/O, process limits, network, or provider quotas. Prefer several bounded remote environments over one overloaded host when tasks are large. +- image tag `kandev/multi-agent:latest`; +- `node:22-slim`; +- `git`, CA certificates, and `curl`; +- `/workspace` as the working directory. + +The build request sends a single Dockerfile-only context to the configured daemon. `COPY` cannot see repository files. Every Dockerfile instruction runs with the daemon's authority, so profile creation is an administrative operation on that daemon. + +At launch Kandev: + +1. uses the profile's `image_tag`; +2. creates `kandev-agent-` with Kandev task/session labels; +3. bind-mounts a released Linux `agentctl` helper read-only at `/usr/local/bin/agentctl`; +4. publishes control and agent ports to random ports on Docker-host loopback; +5. runs the resolved prepare script, which normally clones attached repositories into `/workspace` and checks out the Kandev branch; +6. starts `agentctl` even if prepare failed, then creates the agent instance. + +The repository workspace itself is not a normal host bind mount. For a local filesystem clone URL, Kandev temporarily mounts that local clone source read-only so the in-container `git clone` can read it. Images need the selected agent's dependencies; they do not need to contain `agentctl`. + +The daemon connection comes from global Kandev configuration. At present, the client uses `docker.host` and optional `docker.apiVersion`. The accepted `docker.tlsVerify`, `docker.defaultNetwork`, and `docker.volumeBasePath` settings are not applied by the current Docker client/container manager. Per-executor `docker_host` values are also not used by this runtime. + +The current container manager always selects the Linux/amd64 `agentctl` helper. Use a Linux/amd64-compatible agent image and daemon (native or correctly emulated); native ARM64 agent containers are not yet wired to the released ARM64 helper. + +Kandev passes each agent definition's CPU and memory limits to Docker. These are agent implementation defaults, not executor-profile controls. Apply additional daemon, cgroup, storage, and network policy outside Kandev when required. + +### Credentials and security + +Docker profiles can inject resolved environment secrets. For agent file-based authentication, Kandev selectively seeds a per-execution directory under `/agent-sessions/` and mounts that directory at the agent's expected config path. It does not intentionally mount the entire host home. + +A container is a useful boundary, not a hostile-code security sandbox. The Docker daemon has host-level power, bind mounts expose their sources, the agent can use every injected secret, and the default image has outbound network access. Kandev does **not** mount the Docker socket into agent containers automatically. + +Plain Stop preserves a healthy container for resume. A later launch reconnects to an existing running container, or starts one in a stopped/exited state; if reconnect fails, it creates a fresh container. Archive, delete, stale cleanup, explicit removal in the profile page, and **Reset Environment** can stop or force-remove it. Inspect matching containers before manual cleanup: + +```bash +docker ps -a --filter label=kandev.managed=true +``` + +## Sprites.dev + +### Configure + +1. Save the provider token as a Kandev secret. +2. Choose **Settings > Executors > Sprites.dev**. +3. Select that secret for the required `SPRITES_API_TOKEN` profile environment variable. +4. Review remote credential methods, Git identity, prepare/cleanup scripts, and network policy. + +New Sprites profiles initially select the local `gh` CLI token method. Kandev may also copy explicitly selected agent credential files, resolve selected Kandev secrets into agent environment variables, or run an agent auth setup script. Credential upload is best-effort: provisioning can continue while later agent authentication fails. The remote sandbox receives highly sensitive data; use a scoped provider token and least-privilege repository credentials. + +Network rules are stored in `sprites_network_policy_rules` as JSON entries with `domain`, `action` (`allow` or `deny`), and optional `include`. Kandev applies them only on fresh sandbox creation, and currently does so after credential upload, prepare, controller startup, and agent-instance creation. Bootstrap traffic can therefore occur before the profile policy is installed. A parse/provider failure is reported as skipped and does not abort launch. Provider semantics remain authoritative; do not treat this late, best-effort step as a security boundary, and test the resulting policy. + +Fresh launch creates a sandbox named `kandev-`, uploads the Linux/amd64 `agentctl`, uploads credentials, runs prepare, starts the controller, and opens a local proxy to its control port. The current Sprites path does not probe sandbox architecture; it assumes x86-64. A failed fresh launch destroys the new sandbox. Resume reconnects to the recorded sandbox; if it no longer exists or has expired, Kandev warns and provisions a fresh one on the recorded branch. + +Plain Stop preserves the sandbox and workspace for resume. Archive/delete terminal stops attempt to destroy it, and the profile page can list and explicitly destroy Kandev-named sandboxes with the selected provider token. **Reset Environment** also requests sandbox destruction, but the current direct-reset path does not carry the profile's Sprites secret into that destroy request; after a reset or backend restart, verify the old sandbox in the profile page and destroy it there if it remains. Provider retention, quotas, network behavior, and billing remain provider-dependent. Destroying a sandbox out of band breaks any session that still references it. + +## SSH + +SSH is implemented as a separate remote connection per session. Kandev uploads a platform-matched `agentctl` helper over SFTP, starts it in the remote task directory, and forwards its port to local loopback. + +### Host requirements + +- Linux `amd64`/`arm64` or macOS `amd64`/`arm64`; +- SSH public-key authentication and SFTP; +- `bash` on Linux or `zsh` on macOS by default, or a compatible configured login shell; +- TCP forwarding enabled by `sshd` and enough `MaxSessions` capacity; +- the selected agent command already installed and visible to a login shell; +- writable remote home and adequate disk/process capacity. + +Released Kandev bundles include helpers for all four platform combinations. The full automated SSH task E2E target currently exercises a Linux/amd64 container; other platform gates and helper selection are unit-tested. + +### Create the connection + +Choose **Settings > Executors > SSH**. Enter a name plus either a Host or a host alias from `~/.ssh/config`. The backend resolver can inherit `HostName`, `Port`, `User`, `IdentityFile`, and one `ProxyJump`; explicit form values win. The current create form defaults and persists Port `22` and identity source `ssh-agent`, so enter a non-22 alias port and desired identity source explicitly instead of assuming those two values inherit. `IdentitiesOnly` and arbitrary OpenSSH directives are not consumed by Kandev. + +Authentication choices are: + +- `ssh-agent (SSH_AUTH_SOCK)`; or +- an unencrypted private-key file. + +Password and keyboard-interactive authentication are not supported. A passphrase-protected key file must first be loaded with `ssh-add`, then used through ssh-agent. + +Run **Test Connection**, independently verify the observed SHA256 host fingerprint, select **Trust this host**, then save. Kandev pins the final target fingerprint and refuses a changed key. With ProxyJump, the target remains pinned, but bastion handling is weaker: Kandev checks `~/.ssh/known_hosts` when available and rejects a changed known key, while an unknown bastion key is accepted on first use. Verify and pre-populate the bastion key yourself. + +The profile editor exposes remote shell and agent-readiness checks. Backend/API configuration also recognizes `ssh_workdir_root` (default `~/.kandev`) and `ssh_shell`; the current profile UI exposes `ssh_shell` but not a workdir-root field. + +The remote-auth card is built from the currently enabled agents. Depending on an agent's declared methods, it can copy selected local credential files, resolve a stored secret into that agent's authentication environment variable, or run an agent-specific setup script on the remote host. GitHub can instead use a stored `GITHUB_TOKEN` or the local `gh auth token`. These transfers write sensitive material under the remote user's home and are best-effort—verify authentication on the remote after saving. Although the profile editor also stores Git name/email controls for SSH, the current SSH runtime does not apply them; configure Git identity on the remote host yourself. + +### Current repository limitation + +The SSH runtime currently creates `/tasks/` and a per-session `.kandev/sessions/` directory, but it does **not** clone or otherwise materialize attached repositories. It also ignores the profile prepare and cleanup scripts. Therefore repository-backed coding through SSH is not yet a complete supported path. Use it only when an empty remote task directory is sufficient or when another trusted mechanism places the required content there and you have verified the exact path. + +The runtime preflights the selected agent command and reports an installation hint when missing; it does not install the agent or its toolchain. Only these resolved credential environment names are forwarded to the remote agent: `CLAUDE_CODE_OAUTH_TOKEN`, `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, `GOOGLE_API_KEY`, `GITHUB_TOKEN`, and `GH_TOKEN`. Arbitrary profile variables and control-plane process variables are not forwarded to that agent process. Agent-specific auth setup scripts can still consume a selected stored secret and materialize their own remote login state. + +Stop attempts to kill the session's remote `agentctl` and remove only the remote session-runtime directory, then closes forwarding and SSH. Remote cleanup is best-effort: when the connection has already failed, the process or session directory can remain. The task directory always remains and no background sweeper currently removes it. The cached helper and checksum at `~/.kandev/bin/agentctl` and `agentctl.sha256` also remain for later sessions. Periodically audit the remote process list, session directories, and `/tasks/` after confirming no session needs the data. Resume re-dials SSH and reuses a live recorded PID when possible; otherwise Kandev starts a fresh remote controller. + +## Lifecycle and cleanup + +The task environment reports `creating`, `ready`, `stopped`, or `failed`; individual execution records have finer states. Stop is deliberately not synonymous with destroy for resumable Docker and Sprites environments. + +Use a task's **Reset Environment** action when you need a clean materialization. Kandev blocks reset while a task session is starting or running, can optionally push the current branch, and requests teardown of the recorded worktree/container/sandbox. It normally keeps the environment record when teardown returns an error. The current Sprites credential-context limitation described above can instead report success while leaving the provider sandbox, so verify it separately. SSH task-directory cleanup remains manual as described above. + +Before deleting any environment, push or otherwise preserve uncommitted work. Profile deletion and provider/daemon-side deletion can bypass normal lifecycle safeguards. ## Troubleshooting -- **Prepare failed:** inspect the environment error and logs, then run the script in the same shell/runtime with noninteractive settings. -- **Agentctl unavailable:** confirm the helper was built/copied for the remote architecture and that its control port is reachable inside the environment. -- **Repository clone fails:** test remote URL, branch, DNS, SSH known hosts, and token scope from the execution host. -- **Container starts but tools are missing:** rebuild/pin the image or install tools in the prepare script. -- **SSH task dies after disconnect:** verify remote process supervision and inspect the stored remote PID/agentctl logs. -- **No disk space:** remove stale task environments/worktrees after confirming branch state, and inspect **Settings > System > Disk usage**. +- **Profile missing at launch:** ensure the executor is active, the profile still exists, and the task/workflow references the correct IDs. +- **Prepare reports failure but agent starts:** expected for Local, Worktree, and Docker; inspect the failed step output and retry commands inside the same environment. +- **Docker unavailable:** verify global `docker.enabled`, effective `docker.host`, daemon permission, image existence, and the released Linux helper path. +- **Docker clone fails:** test the clone URL, base branch, DNS, CA trust, and token scope from inside the selected image. +- **Sprite cannot resume:** check provider token, quota, sandbox existence, expiration, and network policy; a missing sandbox triggers fresh provisioning. +- **SSH handshake fails:** test ssh-agent/key access, host fingerprint, bastion trust, SFTP, TCP forwarding, and remote OS/architecture. +- **SSH agent is missing:** run the reported `command -v` check through the configured login shell and install the agent on that host. +- **Disk usage grows:** inspect **Settings > System > Disk usage**, Docker containers, provider sandboxes, host worktrees, and retained SSH task directories before removal. -Related guides: [Docker](docker.md), [Windows](windows-support.md), and [Operations](operations.md). +Related guides: [Docker](docker.md), [Git Operations](git-operations.md), [Operations](operations.md), and [Windows Support](windows-support.md). diff --git a/docs/public/extending-kandev.md b/docs/public/extending-kandev.md index 21c54b61b4..477148e27f 100644 --- a/docs/public/extending-kandev.md +++ b/docs/public/extending-kandev.md @@ -1,68 +1,88 @@ --- title: "Extending Kandev" -description: "Add an agent, executor, integration, workflow capability, MCP tool, or settings surface using established extension points." +description: "Add agents, executors, integrations, workflow behavior, MCP tools, settings, or workbench surfaces at their real ownership boundaries." --- # Extending Kandev -Kandev has extension seams for agents, executors, integrations, workflow behavior, MCP tools, and UI settings. An extension is complete only when discovery/configuration, runtime behavior, failure handling, tests, and public documentation agree. +An extension is complete when discovery/configuration, durable state, runtime behavior, recovery, security, tests, packaging, and public documentation agree. Registration and UI presence alone are not completion. -## Add an agent CLI +## Add an agent -Agent definitions and protocol behavior live under `apps/backend/internal/agent/agents/` and register through `internal/agent/registry`. An integration describes install/discovery, ACP launch, optional passthrough, login, models/modes/capabilities, permission behavior, and MCP materialization. +For a local passthrough-only CLI, users can choose **Settings → Agents → Add TUI Agent**. That path persists a custom definition and default profile; no source patch is required. -Follow [Adding a new agent CLI](add-agent-cli.md). Test command construction, missing installation, login requirements, model/mode updates, permissions, resume, and the selected passthrough MCP strategy. +To ship a built-in integration, add an `agents.Agent` implementation under `apps/backend/internal/agent/agents/` and register it in `internal/agent/registry/registry.go`. Structured agents currently need an ACP-speaking process. Optional interfaces add inference, CLI passthrough, native-binary preference, or interactive login. + +Follow [Add an Agent CLI](add-agent-cli.md). Cover stable identity, installation detection, command construction, permissions, authentication, model/mode probing, resume, MCP delivery, remote runtime behavior, assets, registry tests, and packaging when a binary is bundled. ## Add an executor -Executor types are shared in `internal/agent/executor`; lifecycle implementations and registration live around `internal/agent/runtime/lifecycle`. Runtime-specific packages handle Docker, SSH, Sprites, worktrees, or host execution. +Executors span product configuration and runtime implementation: -A production executor needs: +- `internal/task/models/models.go` defines persisted `ExecutorType`, `Executor`, and `ExecutorProfile`; +- `internal/agent/executor/` keeps product-to-runtime mappings typed; +- `internal/agent/runtime/lifecycle/executor_backend.go` defines the runtime backend; +- `internal/agent/runtime/lifecycle/env_preparer.go` separates environment preparation; +- `internal/backendapp/agents.go` registers backends and preparers; +- task repository defaults/handlers and web settings expose built-in profiles. -- profile models and secure credential fields; -- environment create/prepare/stop/cleanup behavior; -- agentctl delivery and control-channel connectivity; -- repository and multi-repository materialization; -- terminal, Git, files, ports, and metrics as supported; -- durable runtime IDs/status/errors for recovery; -- settings UI and validation; -- lifecycle, failure, and cleanup tests. +Local and worktree use the same standalone process backend but different materialization. Docker, remote Docker, SSH, and Sprites own distinct create/connect/cleanup behavior. -Do not advertise an executor before remote architecture, restart, cancellation, partial-create cleanup, and credential boundaries are documented. +A production executor needs profile persistence and validation, scoped credentials, create/prepare/start/observe/stop/cleanup, agentctl delivery and resolution, multi-repository materialization, durable runtime IDs/status/errors, startup recovery, settings UI, and lifecycle/failure tests. Cleanup must be idempotent and must not delete another task's host, container, directory, or credential. -## Add an integration +Do not advertise an executor until restart, cancellation, partial-create cleanup, connectivity, supported capabilities, cost, and credential boundaries are documented. -Provider packages such as `internal/github`, `gitlab`, `jira`, `linear`, `sentry`, and `slack` demonstrate the pattern: workspace-scoped config, secret storage, connection testing, provider client, service, handlers, optional watches/events, and settings UI. +## Add a provider integration -Keep provider payloads behind a domain adapter. Validate server URLs and token types, redact errors, handle pagination/rate limits, and treat external text as untrusted agent input. A polling or webhook integration also needs deduplication and a visible health/run state. +Choose the closest existing provider domain under `apps/backend/internal/`. There is no universal configuration shape: + +- Jira and Linear use workspace-scoped connection state; +- Sentry supports multiple named workspace instances; +- GitHub and GitLab split some global authentication/host state from workspace watches or presets; +- Slack follows a workspace-oriented pattern. + +A domain may contain a client, store/repository, service, provider, handlers, and poller. Shared helpers under `internal/integrations/` cover secret adapters, health polling, and workspace scope. Construction and non-fatal provider startup live in `internal/backendapp/`. Web clients/hooks live in `lib/api/domains/` and `hooks/domains/`; settings and shared provider UI live under `app/settings/` and `components/integrations/`. + +Preserve the chosen global/workspace/instance scope through storage, secrets, routes, and UI. Validate custom hosts against SSRF, redact tokens and provider errors, handle pagination/rate limits, deduplicate polls/webhooks, expose health, and treat external titles/descriptions/comments as untrusted content. Add provider fakes and user-journey coverage. ## Add workflow or automation behavior -Workflow models, service, engine, adapters, and events live under `internal/workflow/`. Workspace automations live under `internal/automation/`. Preserve the distinction between task-local transitions and rules that create new work. +`internal/workflow/` owns workflow models, repository, service, engine, adapters, and transitions. `internal/automation/` owns workspace rules that can create or react to work. Preserve the distinction between a task transition and an automation trigger. -New events/actions need durable semantics, import/export representation when applicable, cycle/duplicate protection, UI editing, and tests for old workflow definitions. +New actions or events need durable semantics, import/export compatibility when applicable, retry and duplicate handling, cycle prevention, UI editing, and tests using old saved workflow definitions. ## Add an MCP tool -Task/config MCP handlers and server registration live under `internal/mcp/handlers` and `internal/mcp/server`. Define a strict schema, enforce caller/task/workspace policy, return actionable structured errors, and test over the actual MCP transport. +`internal/mcp/server/server.go` owns tool schemas and availability by task, config, external, or office mode. Backend behavior lives in `internal/mcp/handlers/`; registration and dependencies are wired from `internal/backendapp/`. Agentctl hosts MCP transports and relays calls over the agent stream. -Do not trust caller-supplied task identity when the server can inject it. Consider destructive-action confirmation, relationship reachability, pagination, and concurrent task state. Update [Automation and MCP](automation-and-mcp.md) when user capability changes. +A new relayed tool normally requires: -## Add a settings or workbench surface +1. a schema, tool name, and mode assignment in the server; +2. a WebSocket action in `apps/backend/pkg/websocket/actions.go`; +3. a handler and registration in `internal/mcp/handlers/`; +4. server mode/count, handler, transport, and integration tests; +5. an update to [Automation and MCP](automation-and-mcp.md) when capability changes. -Backend configuration should have a typed API and durable ownership before a web form is added. Web settings route through `apps/web/src/settings-routes.tsx`; task surfaces live in the relevant workbench components and state slices. +Inject task/session identity from server context instead of trusting arguments. Enforce task/workspace reachability, confirmation for destructive actions, pagination, concurrency behavior, and least-privilege credentials. The backend's external MCP routes currently have no Kandev user-auth middleware; deployment network controls are part of the security boundary. -Cover initial load, no dependency, invalid credential, save, test, error, reconnect, and mobile behavior. Use status text in addition to color and make every control keyboard-accessible. +## Add settings, flags, or workbench UI -## Extension completion checklist +Create backend ownership and validation before a web form. Web settings require the appropriate domain client/hook, `src/settings-routes.tsx`, page/components, and sidebar or general navigation. Add a global state slice only when multiple surfaces or WebSocket hydration require it. -- Registry/startup wiring cannot silently omit the extension. -- Config and secrets have clear scope and redaction. -- Capability detection is truthful when optional dependencies are absent. -- Failures persist enough state for the user to recover. -- Cleanup is idempotent and does not delete unrelated state. -- Unit, integration, and user-visible E2E coverage exists. -- Public docs include setup, trust boundary, troubleshooting, and status. -- Release packaging contains every required binary, asset, or platform variant. +Wire runtime feature flags through every enforcement layer in one change. Add the default to `profiles.yaml` and `FeaturesConfig`, register its environment variable and runtime behavior in `internal/runtimeflags/registry.go`, and gate backend construction, handlers, or other call sites. Mirror the field in the frontend feature types, use `useFeature` for client surfaces, and call `notFound()` from the server layout or page when a guessed URL must remain unavailable. Add backend and frontend tests for both states. A flag is incomplete if any layer can expose or execute the feature while it is disabled. Do not turn an internal package or environment variable into a public setting without first defining its compatibility contract and support status. + +Workbench changes must preserve task/session/repository selection, dock-layout restoration, reconnect behavior, and the separate mobile layout. Test unavailable dependencies, invalid credentials, save/test failures, keyboard access, and narrow viewports. + +## Completion checklist + +- Startup/registry wiring cannot silently omit the extension. +- Persisted types, defaults, migrations, APIs, and UI use one stable identity. +- Config and secrets have explicit global/workspace/task/instance scope and redaction. +- Optional dependencies produce truthful capability and health state. +- Retry, recovery, cancellation, and idempotent cleanup are defined. +- Cross-process and wire compatibility is tested. +- Unit, integration, and required Playwright coverage pass. +- Release bundles contain every required binary, asset, image, and platform mapping. +- Public docs cover setup, status, trust boundary, limits, and troubleshooting. Related: [Architecture](architecture.md), [Backend development](backend-development.md), [Web development](web-development.md), and [Testing](testing.md). diff --git a/docs/public/feature-status.md b/docs/public/feature-status.md index 7ed961af6f..d1544450f5 100644 --- a/docs/public/feature-status.md +++ b/docs/public/feature-status.md @@ -1,59 +1,119 @@ --- title: "Feature Status" -description: "Understand which Kandev capabilities are supported, dependency-bound, experimental, or still in progress." +description: "Check which Kandev capabilities are supported, dependency-bound, limited, experimental, or still in progress." --- # Feature Status -These docs describe the current `main` branch and are not versioned yet. A released binary can be older than this page. Check **Settings > System > About** or `kandev --version`, then compare the release notes when a screen or option differs. +This inventory describes the current `main` branch. A capability counts as shipped only when current backend behavior, a user-facing path, and tests agree. A database field, hidden route, ADR, specification, or mock-only E2E path is not enough. + +Released builds can lag this page. Check **Settings > System > About** or `kandev --version` and compare the matching GitHub tag when behavior differs. ## Status meanings | Status | Meaning | |---|---| -| Supported | Shipped in the normal product path and covered by maintained source/tests. | -| Dependency-bound | Shipped, but availability depends on an agent, provider, platform, credential, or executor. | -| Experimental | Available behind a toggle or with a deliberately limited operating contract. | -| In progress | Present in source or planning, but not documented as a supported production workflow. | -| Internal | Engineering material, test support, or implementation detail rather than a user feature. | +| Supported | Available through the normal product path and backed by maintained implementation and tests. | +| Dependency-bound | Shipped, but useful behavior depends on a provider, agent CLI, executor, platform, credential, or install channel. | +| Limited | Implemented and reachable, but the current UI or operating contract has a material restriction called out here. | +| Experimental | Intentionally unstable or based on a fragile/nonstandard integration contract. | +| In progress | Source, schema, tests, a flag, or a stub exists, but there is no supported production workflow yet. | +| Internal | Development, test, diagnostic, or implementation support rather than an end-user feature. | -## User-facing capability matrix +## Tasks, workflows, and coordination -| Area | Status | Notes | +| Capability | Status | Current boundary | |---|---|---| -| Workspaces, repositories, kanban, workflows, tasks, labels, and documents | Supported | Core coordination model. See [Tasks and workflows](tasks-and-workflows.md). | -| Parallel sessions and integrated files, terminal, changes, preview, plans, and PR review | Supported | Exact panels vary by viewport and task context. See [Sessions and review](sessions-and-review.md). | -| Code walkthroughs with anchored review feedback | Supported | Requires a compatible agent session with task MCP and matching files. | -| Subtasks, dependencies, multi-repository tasks, and additional task branches | Supported | Shared workspaces can still conflict; see [Coordination](coordination.md). | -| ACP agents and bring-your-own TUI agents | Dependency-bound | Installation, login, models, modes, resume, usage, and MCP capabilities vary by CLI. | -| Local, worktree, Docker, remote Docker, SSH, and Sprites executors | Dependency-bound | Each runtime has platform, host, image, credential, and lifecycle prerequisites. | -| GitHub, GitLab, Jira, Linear, Sentry, and Slack integrations | Dependency-bound | Provider API, account scope, and workspace configuration determine behavior. | -| Workflow import/export and GitHub-backed workflow sync | Supported | Imported profiles may require local mapping; sync applies guarded updates. | -| Task MCP and external backend MCP | Supported | External MCP has no Kandev user-auth boundary and must be network-protected. | -| Scheduled, webhook, GitHub PR/push/CI automations | Supported | Begin with narrow filters, credentials, and concurrency. | -| Desktop app, CLI, service, Docker, and remote-host deployment | Supported | Platform-specific installation and signing details vary by release channel. | -| Kubernetes deployment guide | Experimental | The guide describes deploying Kandev; a first-class Kandev Kubernetes executor/operator is not shipped. | -| Resource metrics, statistics, backups, logs, updates, and task Gist shares | Supported | Some provider metrics and update actions depend on install channel. | -| Voice engines and language-server/editor integration | Dependency-bound | Browser APIs, models, binaries, credentials, and platform support vary. | -| Feature toggles | Experimental | Intended for unfinished or diagnostic behavior and may require restart. | -| Office mode | In progress | Feature-flagged autonomy work; persistent agent teams, routines, budgets, and related UX are not yet a supported public contract. | -| Kubernetes executor/operator | In progress | Listed as a future remote runtime direction, not a current executor choice. | +| Workspaces, local repositories, Kanban board, task list, workflow steps, and task archive | Supported | A fresh database contains Default Workspace and a Development workflow. Repository settings add existing local Git paths; remote GitHub sources enter through New Task. Archive state is immediate, while runtime/resource cleanup is asynchronous and best-effort. | +| Workflow templates, custom steps, prompts, per-step agent profiles, WIP/pull controls, and auto-archive | Supported | Configure under **Settings > Workspaces > _workspace_ > Workflows**. Imported/synced configuration can still require local profile mapping. | +| Regular workflow events and actions | Supported | Current UI covers step entry, turn start/completion, step exit, and direct-child completion, including auto-start, plan mode, reset context, moves, explicit completion gating, and human wait steps. The generic Office triggers and quorum/participant actions are not part of this contract. | +| Human-gated operation | Supported | A step can do nothing on turn completion and wait for a user; an optional `step_complete_kandev` signal can gate auto-advance. Kandev does not bypass repository checks, provider permissions, or branch protection. | +| One structured plan per regular task | Dependency-bound | Plan mode, the Plan panel, revisions, compare/revert, and Implement are shipped. Creating/updating the plan from an agent requires task MCP support; passthrough-only sessions do not have the same contract. | +| Multiple named task documents | In progress | Storage, revisions, Office UI, and Office MCP tools exist. Regular Kanban has no document panel and its task MCP does not register the document tools. | +| Task labels and label filters | In progress | The shared task row stores a JSON label field, but label catalogs, pickers, filters, and maintained E2E coverage are on the feature-flagged Office surface, not regular Kanban. | +| Named parallel sessions | Supported | Users can add, name, make primary, stop, resume, delete, and hand off sessions. Blank, copied, or summarized context is available when starting another agent; resume still depends on retained agent/executor state. | +| Agent-spawned sessions and targeted task messages | Dependency-bound | `spawn_session_kandev` and `message_task_kandev` are task-MCP tools. Queue limits, session IDs, parent/child interrupt rules, and agent MCP capability apply. | +| One-level subtasks | Supported | Regular Kanban supports a parent plus direct children through UI and MCP. A child may inherit the parent's materialized workspace or request a new one; inherited/shared files can conflict. Arbitrary-depth Office trees remain in progress. | +| Blocker/dependency editing | In progress | Same-workspace storage and cycle checks exist, and related-task MCP can report stored relations. The picker and blocker properties are currently Office UI; regular Kanban has no complete create/edit/filter path. | +| Multi-repository tasks | Limited | New Task supports multiple local or remote GitHub rows, and Changes/Review/PR surfaces group by repository. Two or more repositories currently require the Worktree executor. | +| Additional task branches | Limited | `add_branch_to_task_kandev` can attach another branch from the same or another repository only with a Worktree task. There is no equivalent regular after-start UI. Changing Kandev's comparison base does not retarget commits or an existing pull request. | +| Workflow YAML import/export | Supported | Available for regular Kanban workflows. Office workflows are deliberately excluded. | +| GitHub-backed workflow sync | Dependency-bound | Requires GitHub access. Synced workflows are read-only in Kandev and guarded updates can need local agent/executor mapping. | +| Coordinator-led teams, participants/quorum, arbitrary task trees, routines, budgets, and heartbeats | In progress | These belong to Office. Office is disabled in the production profile and remains an evolving, feature-flagged autonomy layer. | -## Source and publication boundary +## Sessions, review, and developer tools -User-ready pages live in `docs/public/**` and are published at `kandev.ai/docs`. Other files under `docs/**` can be architecture notes, ADRs, plans, specifications, test procedures, release internals, or historical context. Their presence does not change a capability's public status. +| Capability | Status | Current boundary | +|---|---|---| +| Structured chat, tool activity, files, integrated PTYs, and session lifecycle | Dependency-bound | Panel availability follows the task environment, agent protocol, executor, and viewport. A terminal needs a materialized task environment. | +| Changes and cumulative Review | Supported | Supports staged, unstaged, commit, and multi-repository views; persisted reviewed hashes, stale detection, and anchored comments are implemented. Discard is destructive and permanent. | +| Agent-authored code walkthrough | Dependency-bound | One saved walkthrough per task is supported through task MCP. A new walkthrough replaces the old one; anchors can drift, and the backend does not prove that referenced file content is still current. | +| GitHub pull-request review, checks, merge, and CI-fix automation | Dependency-bound | Requires GitHub credentials and repository permissions. CI automation is opt-in. Azure has a create-PR path only; other Git hosts do not get the full task PR panel. | +| Share a session snapshot as a secret GitHub Gist | Dependency-bound | Requires GitHub auth and a structured session outside `CREATED`/`STARTING`. The viewing URL is rendered through `gist.githack.com`; unlisted URLs and heuristic redaction are not access control or exhaustive secret detection. Payloads are capped and shares can be revoked. | +| Quick Chat | Dependency-bound | Requires an agent profile. Repository-backed chats use isolated Kandev worktrees; closing deletes the ephemeral task permanently. Chats older than seven days are swept at startup and every 24 hours unless their session is `RUNNING` or `IDLE`. | +| Utility agents and custom one-shot helpers | Dependency-bound | Require an ACP inference-capable agent and usable model. Passthrough-only agents do not satisfy this path. | +| Configuration Chat | Limited | Uses an ephemeral repository-less task plus configuration MCP. Closing its browser tab does not delete the backing task, and config-mode tasks are excluded from Quick Chat expiry. | +| Voice input | Dependency-bound | Browser Web Speech, a browser-downloaded Whisper model, and server Whisper paths exist. Selection does not retry another engine after a recognition error. Server Whisper requires `KANDEV_VOICE_OPENAI_API_KEY` (empty by default); without it the transcription route returns 503. | +| Embedded VS Code | Limited | code-server runs inside the task runtime with `--auth none` on `0.0.0.0:`. It relies on task-runtime and network isolation rather than its own login. | +| Built-in editors and host editor launch | Dependency-bound | Built-in editing is shipped. External editors depend on host discovery/custom commands. A multi-worktree session presents a repository/worktree picker before host launch; clients that omit the worktree ID retain a first-worktree fallback. A remote executor path may not work without remote-editor configuration. | +| Language servers | Limited | Current built-in mapping covers TypeScript/JavaScript, Go, Rust, and Python. Auto-start/installation default off, and subprocesses run on the backend host rather than inside Docker, SSH, or Sprites runtimes. | +| Integrated shell terminals | Dependency-bound | Create, reopen, rename, and terminate are shipped. Shell settings affect new terminals, and executor/task-environment availability controls where the PTY runs. | +| Prompts, keyboard shortcuts, notifications, appearance, and task actions | Supported | These are normal settings surfaces. Browser notification permission, custom command validity, and local shell/editor availability still apply to their effects. | -When behavior changes, update the implementation, tests, relevant public page, and this matrix in the same pull request. See [Contributing to the docs](README.md) and [Contributing to Kandev](contributing.md). +## Agents, executors, integrations, and automation -## Verify behavior in your installation +| Capability | Status | Current boundary | +|---|---|---| +| Built-in ACP agent registry and profiles | Dependency-bound | The registry supports many CLIs, but installation, authentication, models, modes, configuration options, resume, and usage reporting come from the installed CLI and account. | +| Bring-your-own TUI/passthrough agent | Limited | Native PTY interaction is supported. Passthrough does not provide full structured chat, task MCP, resumable ACP state, usage, plans, utility-agent execution, or workspace automation compatibility. | +| Profile permissions, CLI flags, literal environment values, and encrypted secret references | Supported | Secret references resolve at launch; a missing/deleted reference omits that variable. Auto-approve and permission-skipping settings enlarge the process trust boundary. | +| Local and Worktree executors | Supported | Both are seeded. They execute on the Kandev host; a worktree isolates Git checkouts, not processes, network, credentials, or the rest of the filesystem. | +| Local Docker and Sprites executors | Dependency-bound | Require the relevant daemon or provider, compatible host/image, token, platform, and credential-copy configuration. | +| SSH executor | Limited | Remote sessions support trusted Linux or macOS hosts on amd64 or arm64, with public-key authentication, SFTP, and TCP forwarding. Kandev does not currently clone/materialize attached repositories there, ignores profile prepare/cleanup scripts, and retains the remote task directory for manual cleanup. | +| Remote Docker executor | In progress | The type and legacy/direct settings fields are registered, but runtime create/stop return `not yet implemented`, and the current **Settings > Executors** hub does not offer it. | +| GitHub integration | Dependency-bound | Workspace repository scope, issue/PR browsing, actions, watches, presets, reviews, checks, and Gists depend on GitHub auth, scopes, rate limits, and repository permissions. Supported Local Docker, Sprites, and SSH launch paths may receive the globally stored token as `GITHUB_TOKEN`/`GH_TOKEN`; workspace repository scope does not constrain its privileges. | +| GitLab integration | Limited | Global connection plus issue/MR search, browse, and external links are shipped. The current `/gitlab` page cannot launch tasks, and task surfaces do not expose discussions, reply/resolve, full review feedback, or pipeline actions; watches/presets also lack an end-user settings path. | +| Jira integration | Limited | Search/browse, supported transitions, launch presets, task launch, and watches require provider credentials. Launch copies data rather than creating a durable association; each watch poll reads only the first 50 JQL matches. | +| Linear integration | Limited | Search/browse, state changes, task launch, and watches require provider credentials. Launch has no preset/durable association; a watch reads 50 matches in provider order or at most 250 with explicit local sorting. | +| Sentry integration | Limited | Multi-instance configuration, issue watches, and current-task issue actions are shipped; there is no top-level browser. Watches read only the newest first page, and the status picker allows multiple selections while the backend accepts at most one. | +| Slack integration | Experimental | Uses a nonstandard browser-session token/cookie and polling rather than OAuth or a bot installation. A first scan can process up to 30 existing matches, and reactions/replies are best-effort. | +| Integration **Enabled** switches | Limited | Jira/Linear/Sentry/Slack switches are browser-local presentation state; they do not stop backend pollers. Pause/delete watches or remove the Slack/provider configuration when polling must stop. | +| Manual, preset/`@every` schedule, authenticated webhook, and GitHub PR-open automations | Limited | Task and Run modes are shipped, but Run cannot answer permission prompts. PR triggers need explicit repositories and ignore the saved Opened checkbox. Webhooks silently retain only the first 1 MiB. **Play** remains clickable while disabled but produces no history. | +| Fixed cron timezone behavior | Limited | The UI accepts fixed cron and timezone values, but the current interval scheduler does not honor the configured timezone and some fixed cron expressions may never fire. Prefer tested presets or `@every` intervals. | +| GitHub push and CI automation triggers | In progress | Trigger types and UI labels exist as coming-soon/stub paths; they are not working production triggers. GitHub's separate opt-in PR CI-fix automation is shipped. | +| Automation Run mode | Limited | Creates a hidden ephemeral task and starts immediately. It cannot require user input. Automatic worktree/branch reaping applies only when a Worktree runtime ID is present; other executors follow their own lifecycle. | -For a feature you intend to rely on: +## MCP, clients, and operation -1. confirm the running Kandev version; -2. inspect the relevant settings screen and health status; -3. verify the selected agent/executor/provider dependency; -4. test the smallest representative task without production credentials; -5. confirm failure, retry, review, and cleanup behavior; -6. keep a human gate until the workflow has proven safe for its repository. +| Capability | Status | Current boundary | +|---|---|---| +| Task-scoped Kandev MCP | Dependency-bound | Injected into compatible task sessions with scoped task/session, plan, walkthrough, branch, workflow-step, and coordination tools. Available tools vary by MCP mode and task/session context. | +| Profile-defined external MCP servers | Limited | Stdio, HTTP, SSE, and streamable HTTP are implemented, but current launch resolution starts from an allow-all baseline and overlays only explicit executor policy. Blank remote policies therefore allow transports instead of applying the separate deny-all remote default. A stdio process is per-session and cannot be shared like a network server. | +| External Kandev MCP endpoint | Supported | **Settings > External MCP** exposes streamable HTTP at `/mcp` and SSE at `/mcp/sse` for configuration and task management. It has no Kandev user authentication; keep it on loopback/VPN or place it behind an authenticated proxy. It intentionally lacks live-session question, plan, walkthrough, spawn, and message tools. | +| External MCP tool preview | Limited | The endpoint registration is authoritative. The current settings preview omits several registered tools and should not be used as a complete allow-list. | +| Office MCP and autonomous coordination | In progress | Office-only document, team, routine, and coordinator behavior is feature-gated and is not part of the supported external or regular task MCP contract. | +| CLI/browser runtime, Linux/macOS service, Docker deployment, and remote hosting | Dependency-bound | Platform, filesystem, browser, systemd/launchd, container, reverse-proxy, and network configuration determine availability. The web app, API, WebSocket, and external MCP add no Kandev user-login boundary; protect the whole remotely reachable origin. | +| Native desktop app | Dependency-bound | macOS, Windows, and Linux artifacts and a Tauri shell are implemented. Signing trust and update behavior differ by platform and release artifact. | +| Kubernetes deployment guide | Experimental | The guide describes one deployment approach; Kandev does not ship a Kubernetes executor or operator. | +| Statistics and host resource metrics | Supported | Workspace stats and on-demand CPU/memory/disk display are shipped. Execution-environment metrics depend on executor/runtime support. | +| SQLite backups, database maintenance, status, and logs | Supported | Backups use SQLite `VACUUM INTO` snapshots under the data root. Retention, free space, file permissions, and off-host copies remain operator responsibilities. | +| Update checking and applying updates | Dependency-bound | Checking is shipped. Package-manager installs update through their package manager; backend self-update is service-install-specific; desktop updater support varies by artifact/platform. | +| Feature Toggle settings | Supported | The page exposes Office and Debug overrides, environment locks, and restart-required state. Debug is high risk because logs/endpoints can expose prompt, file, and tool data. A toggle being present does not promote its target feature to supported. | +| Office mode | In progress | Production defaults `KANDEV_FEATURES_OFFICE=false`; development and E2E enable it for implementation/testing. Its routes, agents, labels, documents, dependencies, routines, skills, routing, costs, and approvals may change between releases. | +| Mock providers and E2E-only routes | Internal | They are selected only by development/test runtime profiles and must not be exposed as product integrations. | -Open a bug with the version, platform, executor, agent profile, reproduction steps, expected behavior, logs, and screenshots when the current release disagrees with these docs. +## Publication boundary + +Published user documentation lives in `docs/public/**`. Files elsewhere under `docs/**` can be proposed ADRs, draft specifications, implementation plans, test notes, or historical decisions. Their presence does not change a row above. + +Before relying on a dependency-bound, limited, or experimental capability: + +1. record the running Kandev version and platform; +2. inspect its current settings screen and health state; +3. test the smallest representative workflow with non-production credentials; +4. verify failure, retry, permission, review, and cleanup behavior; +5. keep a human gate until the workflow has proved safe for that repository and executor. + +When reporting disagreement, include the Kandev version, platform, executor and profile, provider, reproduction steps, expected behavior, logs, and screenshots without secrets. + +Related guides: [Tasks and workflows](tasks-and-workflows.md), [Coordination](coordination.md), [Sessions and review](sessions-and-review.md), [Developer tools](developer-tools.md), [Agents and profiles](agents-and-profiles.md), [Executors](executors.md), [Integrations](integrations.md), [Automation and MCP](automation-and-mcp.md), and [Operations](operations.md). diff --git a/docs/public/git-operations.md b/docs/public/git-operations.md index 08f29ddeba..8dfc02c270 100644 --- a/docs/public/git-operations.md +++ b/docs/public/git-operations.md @@ -1,159 +1,186 @@ --- title: "Git Operations" -description: "Understand how Kandev manages repositories, worktrees, branches, and review surfaces." +description: "Use Kandev worktrees, commits, remote operations, pull requests, and cleanup safely." --- -# Git Operations for Worktrees +# Git Operations -This document describes the architecture for Git operations (Pull, Push, Rebase, Merge, and Abort) on worktrees in Kandev. +Kandev runs Git commands in the selected repository workspace for a task session. The Worktree executor gives a session a dedicated host worktree; Local uses the configured shared checkout, while container and remote executors use runtime-specific workspace behavior described in [Executors](executors.md). The browser sends a WebSocket request to the backend, which forwards it to `agentctl` in that executor. This keeps the command's filesystem, Git configuration, network access, SSH agent, and provider CLI in the same environment as the agent. -## Overview +Use the task's **Changes** panel to inspect, stage, discard, commit, push, reset, or rename a branch. The task toolbar and command panel also expose **Commit Changes**, **Push**, **Pull**, **Create PR**, **Rebase**, and **Merge** when a Git-capable session is selected. -Git operations are executed **in agentctl**, not in the backend. This ensures consistent behavior whether running locally or in Docker/remote environments. The backend acts as a proxy, forwarding requests from the frontend WebSocket to agentctl's HTTP API. +## Prerequisites and trust boundary -## Supported Operations +The repository must be a valid Git checkout in the executor workspace and the session's `agentctl` must be reachable. Remote commands use the remote named `origin`; configure its URL and credentials before relying on Pull, Push, Rebase, Merge, or Create PR. -| Operation | Direction | Description | -|-----------|-----------|-------------| -| **Pull** | Remote → Worktree | Fetch and merge changes from remote for the worktree's branch | -| **Push** | Worktree → Remote | Push local commits to remote | -| **Rebase** | Base → Worktree | Rebase worktree branch onto base branch (rewrites history) | -| **Merge** | Base → Worktree | Merge base branch into worktree branch (creates merge commit) | -| **Abort** | - | Abort an in-progress merge or rebase | +These UI operations enter through Kandev's `/ws` endpoint, which currently has no backend authentication. Anyone who can reach an unprotected backend can invoke destructive Git actions with the executor's permissions. Keep Kandev on loopback or behind an authenticated, origin-protected reverse proxy; see [WebSocket API](websocket-api.md). -## Architecture +Credentials are resolved where `agentctl` runs. A host SSH agent, credential helper, `gh` login, or `az` login is not automatically available inside every Docker, SSH, or remote executor. Give the executor only the repository access it needs and test with a disposable branch. See [Executors](executors.md) for executor-specific credential handling. -### Request Flow +When the Worktree executor is selected, its filesystem separation is not a security sandbox. Host worktrees share the repository's Git object database and local refs, and agents can run arbitrary repository commands allowed by their executor. -```mermaid -flowchart TD - A["Frontend UI (Git Actions Dropdown)"] --> B["WebSocket Client"] - B -->|"worktree.pull/push/rebase/merge/abort"| C["Backend WebSocket Gateway"] - C --> D["Git Handlers"] - D -->|"GetExecutionBySessionID"| E["Lifecycle Manager"] - E --> F["AgentExecution"] - F --> G["agentctl.Client"] - G -->|"HTTP POST /api/v1/git/{operation}"| H["agentctl HTTP API"] - H --> I["Git Command Execution"] - I -->|"exec.Command git"| J["Worktree Directory"] - J --> K["HTTP Response"] - K --> L["Backend Handler"] - L --> M["WebSocket Response"] - M --> N["Frontend"] +## Managed worktree and branch lifecycle + +This section describes Kandev's Worktree executor and other executor paths that create managed Git workspaces. Local, Docker, remote Docker, SSH, and Sprites have executor-specific checkout, mount, and cleanup behavior; see [Executors](executors.md) before assuming these host paths or isolation properties apply. + +By default, managed worktrees live under the configured task-data directory, commonly: + +```text +~/.kandev/tasks/{task-directory}/{repository-name} +~/.kandev/tasks/{task-directory}/{repository-name}-{branch-slug} ``` -### Notification Flow (Git Status Updates) +Additional branches are siblings of the primary repository worktree, not directories nested inside it. Multi-repository tasks have one worktree per repository. Kandev reuses a valid session/repository worktree; if its directory is missing, it attempts to recreate it from the recorded local or remote branch. -After any git operation, the workspace state changes. Updates flow through the event bus: +For a new task branch, the repository default template is: -```mermaid -flowchart TD - A["agentctl WorkspaceTracker"] -->|"git_status message"| B["Backend StreamManager"] - B -->|"OnGitStatus callback"| C["EventPublisher.PublishGitStatus"] - C --> D["Event Bus"] - D --> E["TaskNotificationBridge"] - E --> F["Hub.BroadcastToSession"] - F --> G["Frontend WebSocket"] - G --> H["Zustand Store update"] +```text +feature/{title}-{suffix} ``` -## agentctl API +`{title}` is an ASCII-safe, lower-case task-title slug and `{suffix}` is a short collision-avoidance value. Repository settings can change the template. When `pull_before_worktree` is omitted it defaults to `true`: Kandev best-effort fetches the base branch before creating the worktree. The public configuration defaults both fetch and fast-forward pull timeouts to 60 seconds. An authentication, network, or timeout failure can fall back to an available local or remote-tracking ref with a visible warning; a base branch that cannot be resolved, including its configured fallback, stops creation. -### Endpoints +When a task opens an existing branch or GitHub PR, Kandev fetches that branch; for a numbered GitHub PR it can fetch `refs/pull/NUMBER/head`, including fork PRs. If the intended branch is already checked out in another worktree, the new worktree uses a suffixed local branch and tracks the original `origin` branch when available. If remote fetch fails but the local branch exists, Kandev can continue with that possibly stale branch and reports the fallback. -| Method | Path | Description | -|--------|------|-------------| -| POST | `/api/v1/git/pull` | Pull from remote | -| POST | `/api/v1/git/push` | Push to remote | -| POST | `/api/v1/git/rebase` | Rebase onto base branch | -| POST | `/api/v1/git/merge` | Merge base branch in | -| POST | `/api/v1/git/abort` | Abort merge/rebase | +After creation, Kandev copies any repository-configured files and runs its setup script. Setup-script failure is non-fatal: the worktree remains and the session surfaces a warning. Cleanup scripts run before worktree removal, but their failure also does not prevent removal. -### Request/Response Format +## Everyday operations -**Pull Request:** -```json -{ "rebase": false } +All operations below run in the selected repository workspace. + +| UI operation | Effective Git behavior | Important consequence | +|--------------|------------------------|-----------------------| +| Pull | `git pull origin BRANCH`, optionally with `--rebase`. | Uses the current branch when any upstream exists. With no upstream it falls back to `origin/main`, then `origin/master`, then the current branch. It does not parse an upstream that points to a differently named remote branch. | +| Push | `git push origin CURRENT_BRANCH`; adds `--set-upstream` when requested or no upstream exists. | Force Push uses `--force-with-lease`, not unconditional `--force`. It still rewrites remote history when the lease is valid. | +| Rebase | Fetches `origin BASE`, then rebases onto `origin/BASE`. | Rewrites local commits. If conflict files are detected from Git output, Kandev attempts `git rebase --abort` automatically and returns the file list. | +| Merge | Fetches `origin BASE`, then merges `origin/BASE`. | Conflicts are deliberately left in the worktree. Resolve and commit them, or use Abort Merge. | +| Abort | Runs `git merge --abort` or `git rebase --abort`. | Fails when that operation is not in progress or the repository cannot be restored. | +| Stage | With paths, `git add -- PATHS`; with an empty path list, `git add -A`. | Empty means all changes, including deletions. | +| Unstage | With paths, `git reset HEAD -- PATHS`; with an empty path list, `git reset HEAD`. | Keeps working-tree content. | +| Commit | Optionally runs `git add -A`, then `git commit -m MESSAGE`; Amend adds `--amend`. | The normal UI defaults to staging all when it invokes this helper. Amend rewrites `HEAD`. | +| Discard | Restores tracked paths from `HEAD`; added and untracked files are unstaged and deleted. | Removes both staged and unstaged work. Explicit paths are required, but deletion is not recoverable through Kandev. | +| Edit branch | `git branch -m NEW_NAME` for the current local branch. | Does not rename/delete the old remote branch or automatically repair every external reference. Push the new branch explicitly. | + +Only one Git operation can run at a time for a given repository operator. A second concurrent request is rejected as “another git operation is already in progress.” Different repositories in a multi-repository workspace have separate operators. + +Most Git command failures are normal responses with `success:false`, `error`, and sometimes `conflict_files`; they are not WebSocket transport errors. Read the result body even when the request itself completed. The web client waits 60 seconds for an ordinary Git operation. + +### Multi-repository tasks + +In a multi-repository task, every wire request must identify one repository with its `repo` subpath. The workspace root is not itself a Git repository, so omitting `repo` normally fails. The UI handles this for you: + +- Per-file Stage, Unstage, and Discard are routed to the file's repository. +- Stage All and Unstage All fan out to repositories that have files. +- Commit fans out only to repositories with staged changes. +- Push fans out only to repositories that are ahead. +- Pull, Rebase, Merge, and Abort fan out to all listed repositories. +- The multi-repository toolbar lets you select an individual repository for Commit, Push, Create PR, Pull, Rebase, Merge, or Force Push. + +A fan-out can partially succeed. The UI continues after a failure and reports per-repository outcomes; inspect each repository before retrying a history-changing action. + +## Commit history and reset behavior + +The Changes panel's session history is calculated relative to the session's recorded base commit or current merge base, so it focuses on commits created on the task branch. Kandev refreshes status and emits session Git updates after mutations, but the underlying Git repository remains authoritative. + +Two similarly named actions have very different semantics: + +- **Revert latest commit** (`worktree.revert_commit`) is not `git revert`. It accepts only the exact current `HEAD` SHA and runs `git reset --soft HEAD~1`, moving the branch back one commit while leaving its changes staged. It creates no inverse commit. +- **Reset to commit** moves `HEAD` to an existing 4–40 character hexadecimal commit SHA. `soft` leaves changes staged, `mixed` leaves them unstaged, and `hard` discards tracked working-tree and index changes. The current UI offers Soft and Hard and requires the short SHA before Hard; the wire handler also accepts Mixed and defaults a missing mode to `mixed`. + +Do not reset or amend commits already consumed by other users unless you intend to rewrite and force-push the branch. Kandev hides some revert/reset actions for commits it knows are pushed, but that UI guard is not a repository policy or API authorization boundary. + +## Create a pull request + +**Create PR** first runs: + +```bash +git push --set-upstream origin HEAD ``` -**Push Request:** -```json -{ "force": false, "set_upstream": true } -``` +It then selects a provider from the `origin` hostname: -**Rebase/Merge Request:** -```json -{ "base_branch": "main" } -``` +| Provider | Required runtime tools | Creation behavior | +|----------|------------------------|-------------------| +| GitHub | Authenticated `gh` CLI | `gh pr create` with title, body, current head, optional base, and optional `--draft`. | +| Azure Repos | Authenticated `az` CLI plus the `azure-devops` extension | `az repos pr create` with parsed organization, project, repository, source, optional target, and optional draft. | -**Abort Request:** -```json -{ "operation": "merge" } -``` +Other remote providers are rejected by this action. A normal Git push can still work with another provider. Kandev's GitHub-specific task association is performed only for a returned GitHub `/pull/` URL; an Azure PR can be created without becoming a GitHub watch. -Every request also accepts an optional `repo` subpath for multi-repository task workspaces. Omit it for a single-repository workspace. +Title is required. Body and base branch may be empty; if base is empty, the provider chooses its default. The web UI defaults new PRs to draft and waits up to 120 seconds. Provider credentials and remote push permission must exist in the executor, and Git hooks or branch policies can still reject the push or PR. + +## WebSocket operation reference + +These are the registered Kandev WebSocket actions. Every payload requires `session_id`; `repo` is optional only for a single-repository workspace. + +| Action | Additional payload | +|--------|--------------------| +| `worktree.pull` | `rebase` boolean | +| `worktree.push` | `force` and `set_upstream` booleans | +| `worktree.rebase` | required `base_branch` | +| `worktree.merge` | required `base_branch` | +| `worktree.abort` | `operation`: exactly `merge` or `rebase` | +| `worktree.commit` | required non-empty `message`; `stage_all`; `amend` | +| `worktree.stage` | `paths` list; empty means all | +| `worktree.unstage` | `paths` list; empty means all | +| `worktree.discard` | required non-empty `paths` list | +| `worktree.create_pr` | required `title`; `body`; `base_branch`; `draft` | +| `worktree.revert_commit` | required `commit_sha`, which must be exact `HEAD` | +| `worktree.rename_branch` | required `new_name` | +| `worktree.reset` | required `commit_sha`; `mode` is `soft`, `mixed`, or `hard` | + +Example request and normal operation result: -**Response (all operations):** ```json { - "success": true, - "operation": "pull", - "output": "Already up to date.", - "error": null, - "conflict_files": [] + "id": "pull-1", + "type": "request", + "action": "worktree.pull", + "payload": { + "session_id": "SESSION_ID", + "rebase": false, + "repo": "kandev" + } } ``` -## Locking - -A per-instance mutex in agentctl prevents concurrent git operations on the same worktree. If an operation is already running, new requests return HTTP 409 Conflict. - -## Conflict Handling - -- **Rebase conflicts**: Automatically aborted (`git rebase --abort`), conflict files returned -- **Merge conflicts**: Left in place for user/agent resolution, conflict files returned -- **Abort endpoint**: Allows manual abort of conflicted merge/rebase - -## Frontend Integration - -### WebSocket Actions - -- `worktree.pull` - Pull from remote -- `worktree.push` - Push to remote -- `worktree.rebase` - Rebase onto base -- `worktree.merge` - Merge base branch -- `worktree.abort` - Abort operation - -### Hook Usage - -```typescript -const { pull, push, rebase, merge, abort, isLoading, error } = useGitOperations(sessionId); - -// In component - - - - +```json +{ + "id": "pull-1", + "type": "response", + "action": "worktree.pull", + "payload": { + "success": true, + "operation": "pull", + "output": "Already up to date." + }, + "timestamp": "2026-07-16T10:00:00Z" +} ``` -`rebase` and `merge` require the base branch as their first argument. In a -multi-repository workspace, pass the repository subpath as the optional second -argument, for example `rebase("main", "kandev")`. +Read-only Git actions used by the Changes panel include `session.commit_diff`, `session.git.commits`, `session.cumulative_diff`, and `session.git.snapshots`. See [WebSocket API](websocket-api.md) for transport and subscription behavior. -## Files +`agentctl` also implements `/api/v1/git/*` HTTP routes inside the execution runtime. Those routes are an internal backend-to-runtime control surface, not the public Kandev backend API. External clients should not discover or expose executor-local agentctl ports; use the registered Kandev WebSocket actions. -### agentctl +## Cleanup and data loss -- `server/api/git.go` - HTTP handlers -- `server/process/git.go` - Git command execution -- `client/git.go` - Client methods +Worktree cleanup runs the repository cleanup script, forcibly removes the Git worktree directory, and may remove the local branch: -### Backend +- Normal task deletion cleans all owned task worktrees and runs `git branch -D` for their local branches. Remote branches are not deleted, but uncommitted and unpushed-only work can be lost. +- **Reset Environment** is allowed only when no task session is `STARTING` or `RUNNING`. It can optionally push first; a failed requested push aborts the reset. Teardown removes the worktree but deliberately preserves the local branch, then the next launch materializes a fresh environment. +- Office handoff cleanup also preserves the branch when it releases a worktree. -- `internal/agent/handlers/git_handlers.go` - WebSocket handlers -- `pkg/websocket/actions.go` - Action constants +Before deleting a task or performing a hard reset, commit and push anything you need. A cleanup-script failure does not save the directory: Kandev logs the failure and proceeds. If `git worktree remove --force` fails, managed cleanup can fall back to deleting the directory and pruning Git's stale worktree record. -### Frontend +## Troubleshooting -- `lib/types/backend.ts` - TypeScript types -- `hooks/use-git-operations.ts` - WebSocket action functions and React hook +- **No agent/client available:** launch or prepare the session and confirm its executor is healthy. Workspace Git actions can reconstruct runtime control after a backend restart, but still need a valid task environment. +- **Remote/authentication error:** test `git fetch origin` inside the same executor workspace. Verify SSH agent forwarding, token/credential helper availability, remote URL, DNS, and firewall access there. +- **Pull fetched the wrong branch:** Kandev always uses `origin` and, once any upstream exists, the current local branch name. Align local and remote branch names or use an explicit terminal command. +- **Rebase failed but no rebase remains:** detected rebase conflicts are auto-aborted. Use the returned `conflict_files`, resolve with a manual workflow, or merge instead. +- **Merge remains conflicted:** this is expected. Resolve and commit, or choose Abort Merge. Do not start another Git operation until the repository is consistent. +- **Create PR failed after push:** the branch may already be remote. Fix `gh`/`az` authentication, install the Azure extension if applicable, then retry PR creation without assuming the push was rolled back. +- **Operation timed out:** inspect status before retrying. A client timeout or lost WebSocket response does not prove the underlying command did nothing. +- **Multi-repository operation failed at workspace root:** choose the repository in the toolbar or include its exact `repo` subpath in the request. +- **Missing work after cleanup:** inspect the preserved local branch for Reset Environment/handoff, or the remote branch if it was pushed. Task deletion may already have force-deleted the local branch. + +Related guides: [Configuration](configuration.md), [Executors](executors.md), [Operations](operations.md), and [WebSocket API](websocket-api.md). diff --git a/docs/public/index.md b/docs/public/index.md index 091a9dfef4..447658dbcd 100644 --- a/docs/public/index.md +++ b/docs/public/index.md @@ -1,49 +1,51 @@ --- title: "Kandev Documentation" -description: "Choose a guide for running agentic development work or contributing to Kandev itself." +description: "Choose a guide for installing Kandev, running agent work, reviewing changes, or contributing to Kandev." --- # Kandev Documentation -Kandev is an open-source workbench for planning, running, coordinating, and reviewing software work with coding agents. It runs on infrastructure you control, works with multiple agent providers, and keeps the human review surface close to the repository state. +Kandev is an open-source workbench for assigning repository work to coding agents, coordinating parallel sessions, and reviewing the resulting changes. It runs on infrastructure you control. Agent CLIs, models, executors, and external services remain separate dependencies with their own credentials and limits. -Choose the path that matches what you are doing now. +## Run work in Kandev -## Use Kandev +Start with the page that matches the job in front of you: -Start here when you want to install Kandev, connect repositories and tools, run agents, or design a team workflow. +- [Get started](use-kandev.md): install Kandev, add a local repository, configure an agent, and run a first task. +- [Tasks and workflows](tasks-and-workflows.md): create tasks, use plans, configure workflow steps, and understand the Office-only document and label boundary. +- [Sessions and review](sessions-and-review.md): work with named sessions, chat, files, terminal, changes, preview, pull requests, and walkthroughs. +- [Coordinate work](coordination.md): use parallel sessions, targeted messages, subtasks, dependencies, multiple repositories, and additional branches. +- [Agents and profiles](agents-and-profiles.md): configure agent CLIs, models, modes, permissions, environment, passthrough, and credentials. +- [Executors](executors.md): choose local, worktree, Docker, SSH, or Sprites execution and understand the isolation boundary. +- [Integrations](integrations.md): configure GitHub, GitLab, Jira, Linear, Sentry, and Slack for a workspace. +- [Developer tools](developer-tools.md): use quick chat, prompts, utility agents, voice input, editors, terminal, shortcuts, and notifications. +- [Automation and MCP](automation-and-mcp.md): create scheduled or event-driven work, use task MCP, and connect an external MCP client. +- [Feature status](feature-status.md): check support boundaries, dependencies, experimental features, and unfinished work. +- [Security and trust](security.md): choose a safe deployment boundary, constrain agent access, protect credentials, and preserve human review. -- [Get started](use-kandev.md): install Kandev and complete a first task. -- [Tasks and workflows](tasks-and-workflows.md): choose a workflow, create tasks, and move work through gates. -- [Sessions and review](sessions-and-review.md): use chat, files, terminal, changes, plans, pull requests, and code walkthroughs. -- [Coordinate work](coordination.md): use subtasks, dependencies, multiple repositories, multiple branches, plans, and handoffs. -- [Agents and profiles](agents-and-profiles.md): select models and modes, configure CLI flags and environment, and understand permission controls. -- [Executors](executors.md): choose local, worktree, Docker, SSH, or Sprites execution. -- [Integrations](integrations.md): connect GitHub, GitLab, Jira, Linear, Sentry, and Slack. -- [Developer tools](developer-tools.md): quick chat, prompts, utility agents, voice, editors, terminal, shortcuts, and notifications. -- [Automation and MCP](automation-and-mcp.md): automate workflow transitions and let agents coordinate Kandev tasks. -- [Feature status](feature-status.md): see what is supported, optional, experimental, or still in progress. - -For installation and deployment details, use the [CLI reference](cli.md), [desktop guide](desktop-app.md), [configuration reference](configuration.md), or one of the runtime guides under **Run and Operate**. +Installation and operation have dedicated references: [CLI](cli.md), [desktop app](desktop-app.md), [configuration](configuration.md), [service](run-as-a-service.md), [Docker](docker.md), [Kubernetes](k8s.md), and [operations](operations.md). ## Contribute to Kandev -Start here when you are changing Kandev, extending an integration, or maintaining its public documentation. +These pages describe the current source tree and its development conventions: -- [Contribution workflow](contributing.md): prepare a development environment and open a focused pull request. -- [Architecture](architecture.md): understand the unified Go backend, embedded web app, persistence, events, agent runtime, and external boundaries. +- [Contribution workflow](contributing.md): prepare a development environment and submit a focused change. +- [Architecture](architecture.md): understand the Go backend, embedded web app, persistence, events, agent runtime, and external boundaries. - [Backend development](backend-development.md): follow handler, service, repository, event, migration, and WebSocket conventions. -- [Web development](web-development.md): work with the Vite/React app, state slices, API clients, WebSocket handlers, responsive task UI, and design system. -- [Testing](testing.md): choose targeted Go, Vitest, CLI, desktop, or Playwright coverage and use isolated fixtures. +- [Web development](web-development.md): work with the React app, state, API clients, WebSocket handlers, responsive task UI, and design system. +- [Testing](testing.md): choose targeted Go, Vitest, CLI, desktop, or Playwright coverage. +- [Remote cloud development](remote-cloud-environment.md): bootstrap and test the Kandev source tree in an ephemeral VM or cloud workspace. - [Extension guides](extending-kandev.md): add agents, executors, integrations, settings, MCP tools, prompts, and workflow behavior. -- [Adding an agent CLI](add-agent-cli.md): implement a new ACP or TUI integration end to end. -- [Release process](release-process.md): understand versioning, release automation, runtime bundles, desktop artifacts, npm, Homebrew, and containers. -- [Public docs guide](README.md): update a page or add a new entry to this navigation. +- [Adding an agent CLI](add-agent-cli.md): implement an ACP or TUI integration end to end. +- [Release process](release-process.md): understand versioning and the CLI, runtime, desktop, npm, Homebrew, and container release paths. +- [Public docs guide](README.md): update or add a published page. -## Product Boundary +## Supported product boundary -The regular task workbench and task-scoped Kandev MCP are shipped features. **Office mode is separate and remains feature-flagged and in progress.** Internal plans, ADRs, and experiments can explain direction, but they are not promises that a feature is generally available. The [feature status page](feature-status.md) records that boundary explicitly. +The regular Kanban workbench, task sessions, review surfaces, and task-scoped Kandev MCP are the supported product path. Some capabilities still depend on a particular agent, executor, provider, platform, credential, or install channel; those differences are called out on their topic pages. -## Current Version +**Office is separate, disabled in the production runtime profile, feature-flagged, and still in progress.** Its source, tests, specifications, and internal plans do not make persistent teams, routines, budgets, or coordinator-led autonomy a supported production contract. See [Feature status](feature-status.md). -These docs describe the current `main` branch and are not versioned yet. Commands and configuration are verified against source, but a released build can lag `main`. Check the version in **Settings > System > About** or run `kandev --version`, then consult the corresponding GitHub tag when exact historical behavior matters. +## Match docs to the running version + +These docs describe the current `main` branch and are not versioned. A released build can lag behind them. Check **Settings > System > About** or run `kandev --version`, then use the matching GitHub tag when exact historical behavior matters. diff --git a/docs/public/integrations.md b/docs/public/integrations.md index e7a508a264..9c1cf3b17f 100644 --- a/docs/public/integrations.md +++ b/docs/public/integrations.md @@ -1,64 +1,195 @@ --- title: "Integrations" -description: "Connect workspace-scoped GitHub, GitLab, Jira, Linear, Sentry, and Slack accounts to Kandev." +description: "Connect GitHub, GitLab, Jira, Linear, Sentry, and Slack, then browse external work or create watched tasks." --- # Integrations -Integrations bring repository, ticket, incident, and notification context into a Kandev workspace. They do not replace the credentials used by an executor or agent process. A GitHub integration can let Kandev inspect and create pull requests, for example, while the task environment still needs its own Git credentials to fetch or push. +Integrations let Kandev's backend read and update provider data. They power repository and issue browsers, task associations, watches, pull-request review, and provider-specific task launchers. -## Configure an integration +They do **not** provide every credential a task needs. Keep these paths distinct: -Open **Settings > Workspaces**, select the workspace, and open **Integrations**. The same providers also appear under the top-level **Settings > Integrations** route for the active workspace. +- an integration credential lets the Kandev backend call a provider API; +- Git or SSH credentials in an executor let the task fetch and push a repository; +- an agent login or API key lets the coding CLI call its model provider. -1. Choose a provider. -2. Enter its server or organization details and a scoped credential. -3. Save and run the available connection test. -4. Confirm the integration reports healthy before relying on it in a workflow. -5. Create a small test task or notification before enabling an unattended automation. +GitHub is the important exception. For Local Docker, Sprites, and SSH launches (and the registered Remote Docker path, although that runtime is not implemented), Kandev resolves profile remote-auth secrets and selected remote credentials first. A resulting `GITHUB_TOKEN` or `GH_TOKEN` wins. Otherwise Kandev injects the globally stored `GITHUB_TOKEN`/`github_token` secret as both variables, with automatic extraction from the local `gh` login as the final fallback. The workspace repository list does **not** restrict what that injected token can access. Local and Worktree sessions continue to use credentials available on the host. -Integration configuration and secrets are workspace-scoped. Configure each workspace independently when teams or repositories use different accounts. +A task can therefore display a pull request while a host worktree cannot push, or edit a repository while Kandev cannot read checks. Diagnose the failing credential path separately and treat a GitHub integration token as a credential that remote task agents may receive. -## Supported providers +## Open integration settings -| Provider | Kandev uses it for | Configuration notes | +Select **Settings > Workspaces > _Workspace_ > Integrations**, then choose a provider. The direct routes are: + +- `/settings/workspace/{workspaceId}/integrations/github` +- `/settings/workspace/{workspaceId}/integrations/gitlab` +- `/settings/workspace/{workspaceId}/integrations/jira` +- `/settings/workspace/{workspaceId}/integrations/linear` +- `/settings/workspace/{workspaceId}/integrations/sentry` +- `/settings/workspace/{workspaceId}/integrations/slack` + +Compatibility routes under **Settings > Integrations** use the active workspace where the provider has workspace settings. + +GitHub authentication is installation-wide and the current integration targets `github.com`. GitLab authentication and its selected host are also installation-wide. Jira, Linear, and Slack configuration is workspace-specific. Sentry supports multiple named instances per workspace. Do not assume that configuring one workspace gives another the same provider scope. + +Provider secrets saved by these forms use Kandev's encrypted secret store. The backend must still decrypt them to make API requests. Limit access to settings and the Kandev data directory, and use the narrowest provider scope that works. + +### The Enabled switch + +Jira, Linear, Sentry, and Slack pages show an **Enabled** switch. It is a browser-local preference, saved per installation in that browser and on by default. It controls some client-side entry points, availability checks, and configuration fetches; settings pages can still poll provider health. It does not delete backend configuration and does not stop a server-side watch or Slack poller. Pause/delete watches or remove the provider configuration when processing must stop. + +Health results are cached and periodically refreshed (normally about every 90 seconds in the settings UI). Use **Test connection** after changing a URL or credential rather than waiting for the next probe. + +## GitHub + +Use GitHub for pull requests, issues, reviews, checks, repository discovery, task associations, and provider-triggered work. Browse it at `/github` after connecting an account. + +### Authenticate + +Open the workspace GitHub settings. Authentication is resolved installation-wide in this order: + +1. an authenticated `gh` CLI; +2. `GITHUB_TOKEN` in the backend environment; +3. `GH_TOKEN` in the backend environment; +4. a stored secret named `GITHUB_TOKEN` or `github_token`; +5. no authenticated client. + +If `gh` is installed, use the host terminal action and run `gh auth login` as Kandev's service user. The token form validates the GitHub API before saving an encrypted token; the UI calls out `repo` and `read:org` scopes for a classic personal access token. Scope any token to only the organizations, repositories, and write operations that Kandev needs. + +The status panel reports the authenticated user, selected method, rate-limit information, and diagnostics. Clearing Kandev's stored token does not remove a valid CLI or environment credential, and a valid `gh` login continues to take precedence over a stored token. + +### Configure and use the workspace + +Workspace GitHub settings control repository scope, default/saved searches, quick-action prompts, pull-request analytics, review watches, and issue watches. At `/github`, search or browse pull requests and issues, save queries, apply prompt presets, and launch a Kandev task. An associated pull request also appears in task review surfaces for feedback, checks, reviews, and merge actions. + +A **Review Watch** polls a GitHub search and creates review work. It requires a workflow, starting step, prompt, and workspace. The default query is `type:pr state:open review-requested:@me -is:draft`; add repository filters or replace the query as needed. An optional agent or executor profile overrides the selected step's defaults. The poll interval defaults to 300 seconds and accepts 60–3,600 seconds. + +An **Issue Watch** behaves similarly for issues. Its default search is `type:issue state:open`. Choose labels or provide a custom GitHub query; the custom query takes precedence over label selection. + +Both watch types default to the **Auto** cleanup policy: delete merged/closed tasks only when the user has not typed a message. **Always** deletes even after user engagement; **Never** retains every task. You can pause a watch, poll immediately, or clean completed work. Deleting a GitHub review or issue watch best-effort cascade-deletes the tasks it owns. **Reset** is also destructive: after its preview, it permanently cascade-deletes every watch-created task, including archived tasks, and clears cursor/deduplication state so current matches become eligible again. Review-watch reset schedules a re-import; issue-watch reset re-imports on its next poll. Reset is not a way to keep old tasks and rerun a query. + +Repository scope and watch filters are workspace-specific even though the GitHub login is global. They constrain backend browsing and watch queries, not the permissions of a token injected into a remote executor. GitHub workspace configuration can be copied, but credentials and watches are deliberately not copied. + +## GitLab + +Use `/gitlab` to browse/search merge requests and issues and follow links to GitLab. The current public page does **not** launch Kandev tasks. For an already associated merge request, the task top bar can show an external link and aggregate state, but Kandev does not currently expose GitLab discussions, reply/resolve controls, full review feedback, or pipeline review actions. + +The selected GitLab host and authentication are installation-wide, even though the page is reachable from workspace settings. `https://gitlab.com` is the default. For a self-managed instance, enter an HTTP or HTTPS base URL that the Kandev backend can reach. One Kandev installation can select only one GitLab host at a time; it cannot simultaneously browse `gitlab.com` and a self-managed host. + +Authentication is resolved in this order: + +1. an authenticated `glab` CLI for the selected host; +2. `GITLAB_TOKEN` in the backend environment; +3. a stored secret named `GITLAB_TOKEN` or `gitlab_token`; +4. no authenticated client. + +The token form validates the `/user` API before saving. The UI requires a token with `api` and `read_user`; Kandev does not perform an authorization grant or request those scopes for you. GitLab's `api` scope is broad and write-capable, so use a dedicated, minimally privileged account where possible. Diagnostics distinguish a host that cannot be reached from a missing or rejected credential. Clearing the stored token does not sign out `glab` or remove an environment token. + +At `/gitlab`, use built-in searches or per-user saved searches. The project picker only filters the current client-side result set (up to 25 items); it is not a provider permission boundary or a project-scoped server query. Open an item in GitLab for provider-side actions. + +Current public GitLab settings expose the connection only. Although internal services contain watcher and preset data types, there is no current end-user settings workflow for GitLab watches or prompt presets. Do not rely on those features until they appear in the UI. + +Unlike GitHub, Kandev does not automatically inject the stored GitLab integration token into task executors. Configure the executor's Git credentials separately when a task must fetch from or push to GitLab. + +## Jira + +Jira configuration is workspace-specific. Use `/jira` to search with JQL, save views, open issue details, run supported transitions, and launch tasks with Jira prompt presets. Launch copies Jira URL/content into the task title and description; it does not store a durable Jira issue association on the task. + +Enter the site URL (a missing scheme is normalized to HTTPS), choose **Cloud** or **Server/Data Center**, and optionally set a default project key. Authentication options are: + +| Deployment | Method | Required values | |---|---|---| -| GitHub | Repository discovery, issues, pull requests, reviews, checks, branches, Gist task shares, and GitHub-triggered automations. | Prefer a fine-grained token or app access limited to the required repositories and actions. | -| GitLab | Repository and merge-request context for GitLab-hosted work. | Set the correct GitLab base URL for self-managed instances and scope the token to the projects Kandev needs. | -| Jira | Ticket lookup, project/status discovery, transitions, and issue-watch triggers. | Jira Cloud uses API-token authentication. Personal access tokens are for Jira Server or Data Center. | -| Linear | Issue/team context and watches that can create Kandev work from matching issues. | Verify the selected workspace/team and token permissions before enabling a watch. | -| Sentry | Issue and project context for debugging and task creation flows. | Limit the token to the organizations and projects used by the Kandev workspace. | -| Slack | Workspace communication and Slack-triggered agent runs where configured. | Treat channel access, bot permissions, and any exposed MCP endpoint as part of the deployment trust boundary. | +| Jira Cloud | API token (recommended) | Atlassian account email and API token. | +| Jira Cloud | Browser session | Only the value of the `cloud.session.token` or `tenant.session.token` cookie. Do not include the cookie name or `=`. | +| Server/Data Center | Personal access token | Bearer personal access token with the required read/write access. | -The settings screen in the running release is authoritative for fields and supported operations. Provider APIs and token formats can change independently of Kandev. +Cloud API tokens are not accepted for Server/Data Center, and Server/Data Center PATs are not the Cloud token flow. Browser-session JWTs expire and are less reliable than an API token; Kandev surfaces the decoded expiry and warns as it approaches. -## Repository access versus provider access +When editing, a blank secret preserves the saved credential only if the URL, account identity, and authentication method still match. Supply a new secret when changing those identity fields. Save, select **Test connection**, and check the background health result. -There are three distinct credential paths: +### Jira issue watches -- **Kandev integration credentials** let the backend call a provider API. -- **Git credentials in the task environment** let Git fetch and push repository data. -- **Agent credentials** let the selected coding agent call its model provider and any tools it owns. +Create a watch with JQL, test the query, then choose a workflow and starting step. A new watch starts with `project = PROJ AND status = "Open" ORDER BY created DESC`; replace `PROJ` before testing. Repository selection is optional: leaving it blank creates repo-less tasks. When a repository is selected, a blank branch resolves to that repository's default branch. Blank agent and executor profile fields inherit the starting step's defaults. Customize the task prompt and set a poll interval, which defaults to 300 seconds and accepts 60–3,600 seconds. -One does not automatically grant the others. A task can display a pull request while its executor cannot push, or an agent can edit a worktree while Kandev cannot read CI checks. Diagnose the failing path separately. +The maximum in-flight value defaults to 5. Leave it blank for no cap. A cap defers remaining matches rather than importing them all at once. Each poll fetches only the first 50 JQL matches and does not paginate. Already-seen issues still occupy that provider result window, so a stable broad query can leave later matches unseen indefinitely; narrow the JQL enough that every important issue can enter the first page. Pause the watch before changing a broad query. Jira task-preset prompts can use ticket key, URL, title, and description placeholders from the preset editor. -## Use external context safely +Deleting a Jira watch leaves its previously created tasks in place. **Reset** is destructive: after the preview, it permanently deletes every watch-created task, including archived tasks, clears cursor/deduplication state, and makes current matches eligible for the next poll. -Issue bodies, pull-request comments, commit messages, chat messages, and incident details are untrusted input. If a workflow forwards that content to an agent, it can contain instructions intended to influence the agent. +## Linear -- Keep write credentials out of read-only triage workflows. -- Review generated branches, comments, transitions, and notifications before broad automation. -- Restrict repository and channel scope instead of using account-wide tokens. -- Do not include secrets in task descriptions or provider comments. -- Use explicit human gates for production changes and sensitive issue queues. +Linear configuration is workspace-specific. Enter a personal API key and optionally a default team. Kandev calls the fixed Linear GraphQL endpoint at `https://api.linear.app/graphql` and sends the key as its authorization value. Leaving the credential blank during an edit keeps the stored key. -## Troubleshooting +After saving and testing the connection, use `/linear` to search by text, team, or assignee, view issue details, change supported states, and launch tasks. Linear launch uses fixed title/description construction, has no prompt-preset editor, and does not store a durable Linear issue association. -- **Connection test fails:** check the provider base URL, token type, expiration, scopes, and network access from the Kandev backend host. -- **Repository or project is missing:** confirm the authenticated account can see it and that workspace filters do not exclude it. -- **Kandev can read but not write:** add only the specific write scope needed, then repeat the test. -- **Pull request state is stale:** refresh the task and inspect provider health; webhook and polling behavior differs by integration. -- **Task environment cannot push:** fix its Git/SSH credential path. Changing the backend integration token may not affect Git running inside an executor. -- **Automation creates unexpected work:** disable the automation, inspect its provider filters and run history, then narrow repositories, branches, authors, labels, or issue query. +Linear watches can filter by team, states, labels, priorities, assignee, creator, estimate range, and free-text query. At least one of those filters is required. They also define dispatch order, workflow and starting step, optional repository/base branch/profile overrides, prompt, poll interval, and a maximum in-flight count. New watches default to five in-flight tasks and **Priority (high → low)** dispatch. The poll interval defaults to 300 seconds and accepts 60–3,600 seconds; clear the in-flight field for no cap. -Related: [Tasks and workflows](tasks-and-workflows.md), [Automation and MCP](automation-and-mcp.md), and [Executors](executors.md). +Leaving the repository blank creates repo-less tasks. When a repository is selected, a blank branch resolves to its default branch. Test narrow filters before enabling the watch. Deleting a Linear watch retains existing tasks; **Reset** permanently deletes every watch-created task, including archived tasks, clears cursor/deduplication state, and makes current matches eligible for the next poll. + +Linear polling is also bounded. **Default (Linear order)** reads one page of 50; an explicit dispatch sort reads at most five pages of 50 before sorting locally. Matches outside that window can remain unseen, and reset does not bypass the bound. + +## Sentry + +Sentry configuration is workspace-specific and supports multiple named instances. This is useful when one Kandev workspace spans different Sentry organizations or self-hosted installations. + +Create an instance with a unique name, base URL, and bearer authentication token. The default URL is `https://sentry.io`; replace it for self-hosted Sentry. A URL with no scheme becomes HTTPS. It must be a bare HTTP(S) host root—paths, queries, and fragments are rejected. The UI lists `org:read`, `project:read`, and `event:read` as the required read scopes. + +On any saved edit, a blank token preserves the existing token, including when the URL changes. The pre-save **Test connection** candidate cannot reuse that stored token after a URL change, so paste the token to test the new URL before saving. + +A Sentry watch binds to one instance, organization, and project; the selected instance is immutable after creation. It can filter environment, level, one status, and a free-text Sentry query, then select a workflow/step, optional repository/base/profile overrides, prompt, poll interval, and maximum in-flight count. New watches default to `fatal` and `error` levels, `unresolved` status, a 24-hour stats period, five in-flight tasks, and a 300-second poll interval. The interval accepts 60–3,600 seconds; clear the in-flight field for no cap. Although the UI currently permits selecting several statuses, the backend rejects save with more than one because Sentry has no OR form for `is:`. Passthrough agent profiles are not offered to watches. + +Leaving the repository blank creates repo-less tasks. With a selected repository, a blank branch resolves to its default branch. Deleting a Sentry watch retains its existing tasks; **Reset** permanently deletes every watch-created task, including archived tasks, clears cursor/deduplication state, and makes current matches eligible for the next poll. + +Each Sentry poll reads only the newest first page (up to 100 issues) and does not paginate. Older matches can remain unseen while newer/seen issues occupy that page; reset does not force a complete backlog import. + +An instance cannot be deleted while a watch references it. Because the instance binding is immutable, delete those watches first and recreate them against another instance if needed. Sentry issues appear in task issue-selection/current-task surfaces; there is no top-level `/sentry` browser comparable to GitHub, GitLab, Jira, or Linear. + +## Slack + +Slack support currently uses a browser-session polling connection. It is intended for a controlled personal workspace and is more fragile than OAuth or a bot installation. Kandev does not currently offer a Slack OAuth/bot install flow. + +Configure, per workspace: + +- an `xoxc-...` browser session token; +- only the value of the Slack `d` cookie; +- a **Utility Agent** from **Settings > Utility agents**; +- a command prefix, default `!kandev`; +- a polling interval, default 30 seconds and allowed range 5–600 seconds. + +The workspace owns this configuration record, but it does not hard-pin the destination of a created task. The built-in triage prompt deliberately lists every Kandev workspace and asks the agent to choose one. Separate workspace configurations keep separate polling cursors; reusing the same Slack account and prefix in several configurations can therefore process the same authored message more than once. + +With a saved configuration whose latest authentication health check succeeded, Kandev polls messages visible to the connected Slack user. The browser-local **Enabled** preference is not part of this backend gate. A message authored by that same Slack user and beginning with the prefix can trigger in a channel or direct message. + +On the first successful scan the watermark is empty. Slack search returns the newest 30 matching messages, and Kandev processes those matches oldest-first; enabling the integration can therefore act on up to 30 messages that already existed. Use a unique prefix, remove or edit old matching messages, or be ready to remove unintended tasks before first configuration. + +For each match, Kandev best-effort adds an eyes reaction, fetches the surrounding thread, and gives the request, thread, and external Kandev MCP endpoint to the selected utility agent. It then best-effort posts the agent's final response in-thread. Reaction failure does not stop task creation. Reply failure still advances the watermark, so a task can exist without a Slack reply. A thread-fetch or agent-run failure stops that scan's batch and retries the failed and later matches on a future scan. + +This is external/configuration MCP, not a task-scoped MCP session: the endpoint also exposes destructive task and configuration tools. Use a constrained utility agent and model, treat matching Slack text as untrusted input, and review the [external MCP security boundary](automation-and-mcp.md#external-mcp-security-boundary). + +Slack has no separate prompt editor. It uses the chosen Utility Agent's prompt from **Settings > Utility agents**, which can reference `{{SlackInstruction}}`, `{{SlackThread}}`, `{{SlackPermalink}}`, `{{SlackUser}}`, `{{SlackChannelID}}`, and `{{SlackTS}}`. If the raw utility prompt contains any Slack-specific placeholder, its resolved value is the complete prompt. Otherwise Kandev uses the resolved utility prompt as the system text and appends Slack context; the built-in triage instructions are used only when that resolved prompt is blank. + +Slack does not trigger on reactions, expose a slash command/shortcut, mirror task status, or provide a live chat bridge to a running coding agent. It searches matching messages rather than performing a one-time history import, which is why the first scan can process existing matches. Browser session credentials can expire without notice; reconnect when polling starts returning authentication failures. Turning off the browser-local **Enabled** switch does not stop the backend poller—remove the saved Slack configuration to stop it. + +## Copy configuration between workspaces + +Supported integration pages offer **Copy configuration** with provider-specific behavior: + +- GitHub copies repository scope, saved/default searches, and quick-action presets. It does not copy authentication or watches. +- Jira, Linear, and Slack copy the workspace configuration and encrypted credential, replacing the target's provider configuration and re-running health checks. They do not copy watches. +- Sentry adds copies of the source instances with new IDs and copied secrets, preserves target instances, and deduplicates conflicting names. It does not copy watches. +- GitLab host and authentication are already global, so there is no workspace copy action. + +Workspace automations are never copied by this action. Review the target workspace's repository and workflow scope before enabling any copied connection. + +## Security and troubleshooting + +Issue bodies, pull-request comments, commit messages, Slack threads, and incident details are untrusted prompt input. Use read-only credentials for triage, restrict repositories/projects/channels, and keep a human workflow gate before merge, release, deployment, or sensitive transitions. + +- **Connection test fails:** verify the base URL, deployment type, token format, expiration, scopes, and network/DNS access from the backend host. +- **Cleared token but connection remains:** a higher-priority CLI or environment credential is still active for GitHub or GitLab. +- **Repository, project, or team is missing:** confirm the connected identity can see it and check workspace filters/defaults. +- **Kandev can read but cannot write:** add only the specific provider write scope needed, then repeat the test. +- **Task cannot fetch or push:** fix Git/SSH credentials in the executor. GitLab, Jira, Linear, Sentry, and Slack integration credentials are not task Git credentials. GitHub remote launches are the exception: inspect the profile's `GITHUB_TOKEN`/`GH_TOKEN`, then the globally stored GitHub token or local `gh` fallback that Kandev may have injected. +- **A watch still runs after disabling the provider:** the Enabled switch is browser-local. Pause/delete the watch, or remove the backend configuration. +- **Unexpected work is created:** pause the watch or automation, inspect its query, last-polled/status fields, and created-task list, then narrow provider filters before resetting or polling again. Watch tables do not provide a separate run/import history. + +Related: [Tasks and workflows](tasks-and-workflows.md), [Sessions and review](sessions-and-review.md), and [Automation and MCP](automation-and-mcp.md). diff --git a/docs/public/k8s.md b/docs/public/k8s.md index 087246fefe..7a644b10a3 100644 --- a/docs/public/k8s.md +++ b/docs/public/k8s.md @@ -1,295 +1,269 @@ --- title: "Kubernetes" -description: "Deploy Kandev in Kubernetes environments." +description: "Deploy a single Kandev control plane to Kubernetes with explicit persistence, security, and lifecycle constraints." --- -# Kubernetes Deployment Guide +# Kubernetes -This guide covers building the Kandev Docker image and deploying it to a Kubernetes cluster. +Kandev ships example Kubernetes YAML in `k8s/`. It is a single-replica, persistent deployment example—not a Helm chart, operator, or supported high-availability topology. The release workflow publishes container images but does not apply these manifests to a cluster. Review and adapt every manifest before production use. + +## Architecture and limitations + +The example creates: + +| File | Resource | Current value | +|---|---|---| +| `k8s/configmap.yaml` | `ConfigMap/kandev-config` | `/data` home, info logs, Docker executor disabled | +| `k8s/pvc.yaml` | `PersistentVolumeClaim/kandev-data` | 10 GiB, `ReadWriteOnce`, default StorageClass | +| `k8s/deployment.yaml` | `Deployment/kandev` | one replica, `Recreate`, example resources and probes | +| `k8s/service.yaml` | `Service/kandev` | `ClusterIP`, TCP 38429 | +| `k8s/ingress.yaml` | `Ingress/kandev` | ingress-nginx-oriented example for `kandev.example.com` | + +One pod serves the SPA, API, WebSocket, external MCP endpoint, and `/health` on port 38429. With SQLite, the same PVC holds the database, workspaces, CLI installs, and authentication files. + +Keep `replicas: 1`. PostgreSQL and NATS are useful external dependencies, but they do not by themselves make Kandev horizontally scalable: task workspaces, local agent processes, control connections, and other runtime state remain pod/filesystem-local. A tested shared-filesystem and runtime-ownership design would also be required. No multi-replica product deployment is currently documented or validated. + +The supplied `Recreate` strategy intentionally stops the old pod before starting the new one. Upgrades therefore have downtime. ## Prerequisites -- Docker (for building the image) -- A container registry (Docker Hub, GHCR, ECR, etc.) -- A Kubernetes cluster with `kubectl` configured -- A StorageClass that supports `ReadWriteOnce` PVCs (for SQLite persistence) +- a Linux `amd64` or `arm64` cluster with `kubectl` configured; +- registry egress to `ghcr.io`, or a mirrored image; +- a default StorageClass that can provision `ReadWriteOnce`, or an explicit `storageClassName`; +- enough PVC capacity for database, repositories, worktrees, caches, and agent CLIs; +- optional ingress controller, DNS, TLS, and an external authentication gateway; +- outbound access required by selected repositories, package registries, agents, integrations, SSH hosts, or Sprites. -## Building the Image +The published image is documented in [Docker](docker.md#published-images). Pin a version or digest; do not use a moving tag for a controlled rollout. -The root `Dockerfile` consumes a prebuilt Linux release bundle; it does not compile the repository inside Docker. On a Linux host matching the cluster architecture, prepare the same build context used by the release workflow: +## Deploy the example safely + +The checked-in Deployment says `image: kandev:latest`. That is a placeholder, not the published GHCR reference; because the tag is `latest`, Kubernetes also defaults its pull policy to `Always`. Replace it. If you deliberately test a node-preloaded local image, set an appropriate `IfNotPresent` or `Never` pull policy in your own manifest. Apply a pinned published image without editing the source file: ```bash -make service-bundle -rm -rf ctx -mkdir -p ctx/bundle -cp -R dist/kandev/. ctx/bundle/ -cp docker-entrypoint.sh ctx/ -docker build -f Dockerfile -t kandev:latest ctx +export KANDEV_IMAGE='ghcr.io/kdlbs/kandev:X.Y.Z' + +kubectl apply \ + -f k8s/configmap.yaml \ + -f k8s/pvc.yaml \ + -f k8s/service.yaml + +kubectl set image \ + -f k8s/deployment.yaml \ + kandev="$KANDEV_IMAGE" \ + --local -o yaml | kubectl apply -f - + +kubectl rollout status deployment/kandev +kubectl get pod -l app=kandev ``` -For cross-architecture or multi-architecture images, produce the matching `kandev-linux-x64.tar.gz` and/or `kandev-linux-arm64.tar.gz` bundles first. For each platform, extract the bundle's top-level `kandev/` directory into `ctx/bundle/`, copy `docker-entrypoint.sh` into `ctx/`, and build that context for the matching platform. A `--platform` flag alone does not cross-compile the native binaries. See the [Docker guide](./docker.md#building-from-source) for the context layout. +Replace `X.Y.Z` with a real release. Add `-n ` consistently if deploying outside `default`; the supplied resources do not declare a namespace. -### Using the Pre-built Image +Do not apply `k8s/ingress.yaml` yet. It contains a placeholder host, no TLS section, no Kandev authentication, and controller-specific annotations. -Kandev publishes images to GitHub Container Registry. Pull directly: +For private initial access: ```bash -docker pull ghcr.io/kdlbs/kandev:latest +kubectl port-forward service/kandev 38429:38429 ``` -Or reference it in your K8s deployment: +Open `http://localhost:38429`. + +## Persistence and filesystem permissions + +With `KANDEV_HOME_DIR=/data`, the PVC includes: + +- `/data/data/kandev.db`, WAL/SHM files, and SQLite snapshots; +- `/data/tasks`, `/data/worktrees`, `/data/repos`, `/data/sessions`, and `/data/lsp-servers`; +- `/data/agent-sessions` for selectively seeded Docker-agent state; +- `/data/.npm-global` for runtime-installed npm agent CLIs; +- `/data/home` for CLI auth, Azure config, caches, and user configuration. + +The base image starts as root, recursively fixes `/data` ownership, then drops to the `kandev` user at UID 1000. This may violate a restricted Pod Security policy, fail on root-squashed storage, or make a large-volume restart slow. The universal image is configured to run directly as `kandev` and therefore does not perform that ownership repair. + +For a non-root pod, provision the volume for UID 1000 and test your CSI driver's `fsGroup` behavior. A common starting point is: ```yaml -image: ghcr.io/kdlbs/kandev:latest +spec: + template: + spec: + securityContext: + fsGroup: 1000 + fsGroupChangePolicy: OnRootMismatch + containers: + - name: kandev + securityContext: + runAsNonRoot: true + runAsUser: 1000 ``` -### Choosing your image: vanilla vs. universal +This is storage-policy guidance, not a universally portable manifest. Some CSI drivers ignore or implement `fsGroup` differently. Verify a write to `/data/data` and `/data/home` before relying on it. -Kandev publishes two flavors: the default vanilla image (smallest, npm-installable agent CLIs only) and a `:universal` image (~1.4 GB) that adds language toolchains (Go, Rust, build-essential), linters, and Playwright Chromium system libs - useful when your agents work on Go/Rust/Python projects or drive headless browsers. +PVC retention depends on the StorageClass reclaim policy. Deleting `PersistentVolumeClaim/kandev-data` can permanently remove database, repositories, and credentials; back up and verify the target before doing so. -```yaml -image: ghcr.io/kdlbs/kandev:universal -``` +## Configuration and secrets -See the [repository image guide](https://github.com/kdlbs/kandev/blob/main/docs/images.md) for the full comparison, inclusion policy, and recipes for deriving your own image when you need something neither flavor includes. - -## Deploying to Kubernetes - -### Quick Start - -```bash -# Apply all manifests -kubectl apply -f k8s/ - -# Check status -kubectl get pods -l app=kandev -kubectl logs -l app=kandev -f -``` - -### What Gets Created - -| Resource | File | Purpose | -|----------|------|---------| -| Deployment | `deployment.yaml` | Single-replica pod running backend + web | -| Service | `service.yaml` | ClusterIP exposing port 38429 | -| ConfigMap | `configmap.yaml` | Non-sensitive environment configuration | -| PVC | `pvc.yaml` | 10Gi persistent volume for SQLite + worktrees | -| Ingress | `ingress.yaml` | Example ingress with WebSocket support | - -### Accessing the UI - -**Port-forward** (quickest for testing): - -```bash -kubectl port-forward svc/kandev 38429:38429 -# Open http://localhost:38429 -``` - -**Ingress**: Edit `k8s/ingress.yaml` to set your domain, then apply. The ingress routes all traffic to the backend on port 38429; the Go backend serves API, WebSocket, and SPA traffic on that port. - -### Custom Domain / Reverse Proxy - -No extra configuration is needed. The frontend automatically uses `window.location.origin` to reach the API, which works with any domain, reverse proxy, or ingress setup. - -## Installing Agent CLIs - -The kandev image ships with `git`, `gh` (GitHub CLI), `node`, and `npm`, but **does not bundle the coding-agent CLIs** (`claude-code`, `codex`, `auggie`, etc.) — agent choice is per-user, and bundling all of them would bloat the image significantly. - -> Looking to add tools *beyond* agent CLIs - language toolchains, build tools, internal CLIs? See the [repository image guide](https://github.com/kdlbs/kandev/blob/main/docs/images.md) for the universal-image option and recipes for deriving your own image. - -To install an agent inside the running pod, open **Settings → Agents** in the UI and click **Install** on the agent card under "Available to Install". The backend runs the agent's hard-coded install script (`npm install -g `) and rescans on success. - -The image sets `NPM_CONFIG_PREFIX=/data/.npm-global` so user-installed npm globals land on the PV and **survive pod restarts and image upgrades**. The same persistence applies if you `kubectl exec` and install manually: - -```bash -kubectl exec -it deployment/kandev -- npm install -g @anthropic-ai/claude-code -``` - -After installing, log in with the agent's own auth (e.g. `claude login`), then click **Rescan** on the agents page. - -### Persistent agent and `gh` CLI auth - -The image sets `HOME=/data/home` for the `kandev` user, so every CLI that writes its auth state under `$HOME` lands on the PV and survives pod restarts and image upgrades. This includes: - -- `gh` CLI — `~/.config/gh/hosts.yml` -- Claude Code — `~/.claude/.credentials.json`, `~/.claude.json` -- Codex — `~/.codex/auth.json`, `~/.codex/config.toml` -- Auggie — `~/.augment/session.json` -- GitHub Copilot — `~/.copilot/...` -- OpenCode, Amp — `~/.config//...` - -So a one-time `kubectl exec -it deployment/kandev -- gh auth login` (or `claude login`, `codex login`, etc.) is enough; you do not need to redo it after `kubectl set image` or a `helm upgrade`. - -> The GitHub PAT configured in **Settings → Integrations → GitHub** is stored as a secret in the SQLite DB (or your external Postgres) and has always persisted. The `HOME=/data/home` setup covers the separate `gh auth login` flow that the backend falls back to when no `GITHUB_TOKEN` secret is set. - -## Configuration - -Kandev reads configuration via `KANDEV_`-prefixed environment variables (Viper). Put only non-sensitive values in `k8s/configmap.yaml`; inject passwords and signing keys into the Deployment from a Kubernetes Secret. - -### Core Settings - -See [`configuration.md`](./configuration.md) for the full reference (every backend knob and its YAML form). The tables below cover what's most commonly set in K8s manifests. - -| Env Var | Required | Default | Description | -|---------|----------|---------|-------------| -| `KANDEV_SERVER_PORT` | No | `38429` | Server port (API + WebSocket + Web UI) | -| `KANDEV_HOME_DIR` | No | `/data` | Kandev home directory - contains `data/` (DB), `tasks/`, `worktrees/`, `repos/`, `sessions/`, and `lsp-servers/` | -| `KANDEV_DATABASE_DRIVER` | No | `sqlite` | Database driver (`sqlite` or `postgres`) | -| `KANDEV_DATABASE_PATH` | No | `$KANDEV_HOME_DIR/data/kandev.db` | SQLite database file path (override) | -| `KANDEV_NATS_URL` | No | empty | Shared NATS event bus URL. Required when multiple backend replicas must share events; empty selects a process-local in-memory bus. | -| `KANDEV_DOCKER_ENABLED` | No | `false` | Enable Docker runtime for agents (requires DinD) | -| `KANDEV_LOG_LEVEL` | No | `info` | Log level: `debug`, `info`, `warn`, `error` | -| `KANDEV_LOGGING_FORMAT` | No | environment-selected (`json` in K8s) | Explicit format: `json` or `text`. The literal value `auto` is not accepted. | -| `KANDEV_LOGGING_OUTPUTPATH` | No | `stdout` | Log destination: `stdout`, `stderr`, or a file path (rotated when a file) | -| `KANDEV_LOGGING_MAXSIZEMB` | No | `100` | Rotate the log file when it exceeds this size (MB). File output only. | -| `KANDEV_LOGGING_MAXBACKUPS` | No | `5` | Max rotated files to retain (`0` = unlimited). File output only. | -| `KANDEV_LOGGING_MAXAGEDAYS` | No | `30` | Max age of rotated files in days (`0` = unlimited). File output only. | -| `KANDEV_LOGGING_COMPRESS` | No | `true` | Gzip rotated files. File output only. | - -> **Logging in K8s:** prefer the default `stdout` so kubelet collects logs. If you set `KANDEV_LOGGING_OUTPUTPATH` to a file, the active log is created with mode `0600` (owner read/write only); any sidecar reading it must run as the same user. -> -> **Upgrading from a pre-`KANDEV_HOME_DIR` deployment?** The SQLite DB path moved from `/data/kandev.db` to `/data/data/kandev.db`, and `KANDEV_DATA_DIR` is gone — point `KANDEV_HOME_DIR` at the same volume mount (`/data`) instead. (`KANDEV_WORKTREE_BASEPATH` still works as an explicit override if you want to keep worktrees outside the home dir.) The backend auto-migrates the legacy `kandev.db` (plus any `-wal`/`-shm` files) on first boot — look for `Migrated SQLite database from pre-KANDEV_HOME_DIR location` in the pod logs. If you'd rather pin the old path, set `KANDEV_DATABASE_PATH=/data/kandev.db` in the ConfigMap. - -Keep `KANDEV_DATABASE_PASSWORD`, `KANDEV_AUTH_JWTSECRET`, and `KANDEV_OFFICE_JWTSIGNINGKEY` out of ConfigMaps. Reference Secret keys from the Deployment instead: +Non-sensitive values may stay in `kandev-config`. Put database passwords and deployment credentials in a Kubernetes `Secret`, then reference keys from the container: ```yaml env: - name: KANDEV_DATABASE_PASSWORD valueFrom: secretKeyRef: - name: kandev-secrets - key: database-password - - name: KANDEV_AUTH_JWTSECRET - valueFrom: - secretKeyRef: - name: kandev-secrets - key: auth-jwt-secret - - name: KANDEV_OFFICE_JWTSIGNINGKEY - valueFrom: - secretKeyRef: - name: kandev-secrets - key: office-jwt-signing-key + name: kandev-database + key: password ``` -### PostgreSQL Settings (when `KANDEV_DATABASE_DRIVER=postgres`) - -| Env Var | Required | Default | Description | -|---------|----------|---------|-------------| -| `KANDEV_DATABASE_HOST` | No | `localhost` | PostgreSQL host | -| `KANDEV_DATABASE_PORT` | No | `5432` | PostgreSQL port | -| `KANDEV_DATABASE_USER` | Yes | `kandev` | Database user | -| `KANDEV_DATABASE_PASSWORD` | Usually | (empty) | Database password - required unless your Postgres allows passwordless auth | -| `KANDEV_DATABASE_DBNAME` | Yes | `kandev` | Database name | -| `KANDEV_DATABASE_SSLMODE` | No | `disable` | SSL mode (`disable`, `require`, `verify-ca`, `verify-full`) | - -## Database: SQLite vs PostgreSQL - -### SQLite (default) - -- Zero-config, works out of the box -- Database stored at `/data/data/kandev.db` on the PV (derived from `KANDEV_HOME_DIR=/data`) -- **Single replica only** (SQLite is single-writer) -- Deployment strategy is `Recreate` to prevent concurrent writes -- Good for small teams / personal use - -### PostgreSQL (recommended for production) - -- Supports multiple replicas for horizontal scaling when paired with shared NATS -- Change deployment strategy to `RollingUpdate` -- Set via environment variables: - -```yaml -# Non-sensitive values in configmap.yaml -KANDEV_DATABASE_DRIVER: postgres -KANDEV_DATABASE_HOST: postgres.default.svc.cluster.local -KANDEV_DATABASE_PORT: "5432" -KANDEV_DATABASE_USER: kandev -KANDEV_DATABASE_DBNAME: kandev -KANDEV_NATS_URL: nats://nats.default.svc.cluster.local:4222 -``` - -Supply `KANDEV_DATABASE_PASSWORD` from a Secret as shown above. When using Postgres, the PVC is still needed for worktree storage but the database itself is external. An empty `KANDEV_NATS_URL` selects an isolated in-memory event bus in each process, so notifications and orchestration events do not coordinate across replicas; do not scale beyond one backend replica without shared NATS. - -## Persistent Storage - -The PVC at `/data` stores: - -- **SQLite database** (`/data/data/kandev.db`, `/data/data/kandev.db-wal`, `/data/data/kandev.db-shm`) -- **Git worktrees** (`/data/worktrees/`), **tasks** (`/data/tasks/`), **repos** (`/data/repos/`), **sessions** (`/data/sessions/`), and **LSP servers** (`/data/lsp-servers/`) -- **User home** (`/data/home/`) — `$HOME` for the in-pod `kandev` user; holds `gh` CLI auth and agent CLI auth state (see [Persistent agent and `gh` CLI auth](#persistent-agent-and-gh-cli-auth) above) -- **npm globals** (`/data/.npm-global/`) — agent CLIs installed via `npm install -g` - -The PVC uses `ReadWriteOnce` access mode. If your cluster requires a specific StorageClass, add it to `k8s/pvc.yaml`: - -```yaml -spec: - storageClassName: your-storage-class -``` - -## Health Checks - -The deployment includes both probes on the `/health` endpoint: - -- **Liveness probe**: Restarts the pod if the backend becomes unresponsive (30s interval, 3 failures) -- **Readiness probe**: Removes the pod from service during startup or issues (10s interval, 3 failures) - -The CLI launcher also performs an internal health check — it waits for the backend to be healthy before starting the web server. - -## Scaling - -**Single replica (SQLite)**: The default configuration uses `replicas: 1` with `Recreate` strategy. This ensures only one instance writes to SQLite at a time. - -**Multiple replicas (PostgreSQL + NATS)**: Switch to Postgres, configure the same `KANDEV_NATS_URL` on every replica, change the deployment strategy to `RollingUpdate`, and increase replicas: - -```yaml -spec: - replicas: 3 - strategy: - type: RollingUpdate - rollingUpdate: - maxSurge: 1 - maxUnavailable: 0 -``` - -## Upgrading - -Rebuild the release bundle and refresh `ctx/` using [Building the Image](#building-the-image) before running the image build below. +Create the example secret without committing its value: ```bash -# Build and push new image -docker build -f Dockerfile -t your-registry.com/kandev:v1.1.0 ctx -docker push your-registry.com/kandev:v1.1.0 - -# Update deployment -kubectl set image deployment/kandev kandev=your-registry.com/kandev:v1.1.0 - -# Or edit the deployment directly -kubectl edit deployment kandev +kubectl create secret generic kandev-database \ + --from-literal=password='' ``` -SQLite migrations run automatically on startup — no manual migration step needed. +Shell history and the Kubernetes API still see this literal. Prefer your cluster's normal encrypted secret-delivery workflow. Kandev secrets created in the UI live in its database, so database backups are sensitive too. -> **Upgrading across the `HOME=/data/home` change:** if you used `kubectl exec` to `gh auth login` or log in to agent CLIs on a pre-`HOME=/data/home` image, that state lived in the ephemeral `/home/kandev` and is not carried over. Log in once on the new pod and it will persist for all subsequent upgrades. If you want to keep the old state, copy it onto the PV before upgrading: -> -> ```bash -> kubectl exec deployment/kandev -- sh -c 'cp -a /home/kandev/. /data/home/ 2>/dev/null || true' -> ``` +See [Configuration](configuration.md) for exact YAML and `KANDEV_` names. Important image/example values: + +| Setting | Example value | Meaning | +|---|---|---| +| `KANDEV_HOME_DIR` | `/data` | Persistent Kandev root | +| `KANDEV_DOCKER_ENABLED` | `false` | No Docker daemon in the supplied pod | +| `KANDEV_LOG_LEVEL` | `info` | Backend log threshold | +| `KANDEV_DATABASE_DRIVER` | `sqlite` by default | Set `postgres` for an external database | + +Kubernetes detection makes the default log format JSON. Logs remain on stdout unless `logging.outputPath` is changed. + +### PostgreSQL + +For PostgreSQL, configure at least: + +```yaml +env: + - name: KANDEV_DATABASE_DRIVER + value: postgres + - name: KANDEV_DATABASE_HOST + value: postgres.example.internal + - name: KANDEV_DATABASE_PORT + value: "5432" + - name: KANDEV_DATABASE_USER + value: kandev + - name: KANDEV_DATABASE_DBNAME + value: kandev + - name: KANDEV_DATABASE_SSLMODE + value: verify-full + - name: KANDEV_DATABASE_PASSWORD + valueFrom: + secretKeyRef: + name: kandev-database + key: password +``` + +Use the SSL mode and trust material required by your database. PostgreSQL moves only database data; keep the `/data` PVC. Kandev's built-in backup/restore is SQLite-only, so schedule `pg_dump` and test restoration independently. + +## Agent execution in a pod + +Local and Worktree profiles run agents inside the Kandev pod. Install agent CLIs from **Settings > Agents**, or derive an image that contains them. Runtime npm installs persist under `/data/.npm-global`. Choose the universal image when tasks need its additional build toolchains, but account for the larger image and non-root volume requirement. + +The checked-in ConfigMap disables Local Docker. Do not add only a Docker socket mount: the current runtime also needs helper, credential-session, and local-clone bind sources to exist at identical paths on the Docker daemon host, and it currently selects a Linux/amd64 helper. See [containerized control plane limitation](docker.md#containerized-control-plane-limitation). A privileged Docker-in-Docker sidecar has a separate security and persistence model and no supplied Kandev manifest. + +SSH and Sprites profiles can run from Kubernetes if the pod can reach their endpoints and has the required secrets/helper bundle. SSH currently does not materialize attached repositories; review [SSH limitations](executors.md#current-repository-limitation). Remote Docker is unimplemented. + +Interactive commands should run as the service user. With the base image, a Kubernetes exec starts as root, so use: + +```bash +kubectl exec -it deployment/kandev -- gosu kandev gh auth login +``` + +The universal image already runs as `kandev`; use `kubectl exec -it deployment/kandev -- gh auth login`. Prefer Kandev secret/profile flows over ad hoc pod login where possible. + +## Resources and probes + +The example requests 250 millicores and 512 MiB, with limits of 2 CPU and 2 GiB. Those are placeholders, not capacity recommendations. Local/Worktree agents share the pod limit with the control plane and can exceed it during builds. Measure workload memory, CPU, ephemeral storage, PVC growth, and process counts; then set requests/limits accordingly. + +Both example probes call `/health`. That endpoint returns 503 during startup and 200 once routes are wired and the TCP listener accepts connections. It is a readiness signal, not a deep check of database, repository, Docker, provider, or agent health. The supplied liveness probe therefore tests the same shallow condition. + +Long migrations or slow storage may need a startup probe to prevent premature liveness restarts: + +```yaml +startupProbe: + httpGet: + path: /health + port: backend + periodSeconds: 5 + failureThreshold: 60 +``` + +Tune from observed startup time. Keep readiness on `/health`; use separate external monitoring for dependencies and real workflows. + +## Ingress and exposure + +Kandev has no built-in user-auth boundary. Do not expose the example Ingress publicly until an authenticated gateway and TLS are in place. + +Before applying `k8s/ingress.yaml`: + +1. replace `kandev.example.com`; +2. configure the real `ingressClassName` or class annotation; +3. add TLS/certificate configuration; +4. add an identity-aware authentication layer; +5. preserve WebSocket upgrades and long idle timeouts; +6. ensure clients cannot bypass the gateway through the Service or node network. + +The example's `nginx.ingress.kubernetes.io/configuration-snippet` is ingress-nginx-specific and is disabled by policy in many clusters. Adapt it to your controller; modern controllers may handle WebSocket upgrades without a custom snippet. Proxy the application at `/` on a dedicated host. A subpath deployment is not a documented base-path configuration. + +Apply only after review: + +```bash +kubectl apply -f k8s/ingress.yaml +kubectl describe ingress kandev +``` + +## Backup, upgrade, and rollback + +Before an upgrade, create and verify a database backup and preserve any irreplaceable task branches. See [Operations](operations.md). + +```bash +kubectl set image deployment/kandev \ + kandev=ghcr.io/kdlbs/kandev:X.Y.Z +kubectl rollout status deployment/kandev +kubectl logs deployment/kandev --tail=200 +``` + +The `Recreate` strategy stops active local agents. SQLite migrations run on startup and create a pre-migration snapshot when required. PostgreSQL migrations do not invoke `pg_dump`. + +`kubectl rollout undo` changes the image, not the database schema. A binary downgrade may not understand a newer schema; restore the matching pre-upgrade database backup when required. Reapplying the checked-in `k8s/deployment.yaml` without the image transformation resets the image to `kandev:latest`, so keep your production customization in your own overlay or deployment repository. + +## Remove while retaining data + +Delete compute and routing resources separately from the PVC: + +```bash +kubectl delete ingress kandev --ignore-not-found +kubectl delete deployment kandev +kubectl delete service kandev +kubectl delete configmap kandev-config +kubectl get pvc kandev-data +``` + +Do not delete the PVC until its database, workspaces, auth state, and backups have been exported or intentionally discarded. ## Troubleshooting ```bash -# Check pod status -kubectl get pods -l app=kandev - -# View logs -kubectl logs -l app=kandev -f - -# Shell into the pod (if needed) -kubectl exec -it deployment/kandev -- /bin/bash - -# Check PVC status -kubectl get pvc kandev-data - -# Describe pod for events +kubectl get pod -l app=kandev -o wide kubectl describe pod -l app=kandev +kubectl logs deployment/kandev --tail=200 +kubectl get pvc kandev-data +kubectl describe pvc kandev-data +kubectl get events --sort-by=.lastTimestamp ``` + +- **`ImagePullBackOff`:** the example's placeholder `kandev:latest` was not replaced, the tag is wrong, registry egress is blocked, or image-pull credentials are missing. +- **`CrashLoopBackOff` with permission errors:** check PVC ownership, Pod Security admission, root-squash, UID 1000, and universal/base image behavior. +- **Liveness kills startup:** inspect migration/storage timing and add/tune a startup probe. +- **UI works through port-forward but not ingress:** check host/DNS, TLS, auth-gateway route, WebSocket support, and controller-rejected annotations. +- **Agent CLI missing:** install it through Settings or bake it into a derived image; confirm `/data/.npm-global/bin` is on `PATH`. +- **SQLite locked or pod pending after scaling:** return to one replica and `Recreate`; do not share one SQLite database between pods. +- **PVC full:** inspect worktrees, repositories, caches, CLI installs, logs, and retained task state before expanding or deleting anything. + +Related pages: [Docker](docker.md), [Configuration](configuration.md), [Executors](executors.md), and [Operations](operations.md). diff --git a/docs/public/media/feature-guides/NOTES.txt b/docs/public/media/feature-guides/NOTES.txt new file mode 100644 index 0000000000..ddc168f63c --- /dev/null +++ b/docs/public/media/feature-guides/NOTES.txt @@ -0,0 +1,63 @@ +# Kandev feature-guide media capture notes + +Candidate generated in disposable capture root. Promote only after final parent review. + +## Provenance and isolation + +- Capture root: `$CAPTURE_ROOT` (disposable; absolute host path omitted). +- Source: current shared Kandev checkout, exercised through a worker-scoped E2E backend and real browser controls. +- Data: fresh temporary HOME, KANDEV_HOME_DIR, SQLite database, repository, worktree roots, browser profile, and non-production ports. +- Providers/agent: mock-only. No real provider call, production credential, shared user database, or existing task workspace. +- Encoding metadata: `metadata/capture.json` +- Source bundle: `source/capture.spec.ts`, `source/encode.mjs`, `source/product-loop-camera-compact.mjs`, `source/qa.mjs`, `source/smoke.spec.ts` + +## Commands + +- Capture/encoding source and per-clip camera values are retained in the disposable source and metadata bundle listed above. +- QA: `node "$CAPTURE_ROOT/source/qa.mjs" --visual-pass --teardown-confirmed` +- Probe template: `ffprobe -v error -show_streams -show_format -of json ` +- Cadence template: `ffprobe -v error -select_streams v:0 -show_entries frame=best_effort_timestamp_time,pkt_duration_time -of json