From 9558d8b5ee31427f8c32a1081fcdd12c43fd8d9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Flor=C3=AAncio?= Date: Sun, 16 Aug 2026 17:39:51 +0100 Subject: [PATCH] fix: separate Pi ACP and passthrough commands (#2708) --- README.md | 2 +- .../agent/agents/new_acp_agents_test.go | 35 +++++- apps/backend/internal/agent/agents/pi_acp.go | 22 ++-- .../lifecycle/manager_passthrough_test.go | 6 +- .../task-workspace-content-search.spec.ts | 2 +- ...dated-managed-runtime-version-selection.md | 11 +- docs/plans/pi-runtime-surfaces/plan.md | 90 ++++++++++++++ .../task-01-correct-pi-execution-surfaces.md | 112 ++++++++++++++++++ docs/public/agents-and-profiles.md | 13 ++ docs/specs/agents/runtime-updates.md | 12 +- docs/specs/cli-mode-parity/spec.md | 36 ++++++ 11 files changed, 321 insertions(+), 20 deletions(-) create mode 100644 docs/plans/pi-runtime-surfaces/plan.md create mode 100644 docs/plans/pi-runtime-surfaces/task-01-correct-pi-execution-surfaces.md diff --git a/README.md b/README.md index 9cea7bb037..43cf5edabf 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ Connect Kandev to the tools your team already uses — pull issues into the kanb | **Factory Droid** | `droid` | | **iFlow (beta)** | `@iflow-ai/iflow-cli` | | **Kilocode** | `@kilocode/cli` | -| **Pi** | `pi-acp` | +| **Pi** | ACP adapter: `pi-acp`; CLI passthrough: `pi` | | **Kimi** | `kimi` *(install Kimi CLI from Moonshot AI)* | | **Kiro** | `kiro-cli-chat` *(install Kiro CLI from AWS)* | | **Qoder** | `qodercli` *(install Qoder CLI)* | diff --git a/apps/backend/internal/agent/agents/new_acp_agents_test.go b/apps/backend/internal/agent/agents/new_acp_agents_test.go index 15af7f54a4..89bf59817b 100644 --- a/apps/backend/internal/agent/agents/new_acp_agents_test.go +++ b/apps/backend/internal/agent/agents/new_acp_agents_test.go @@ -2,7 +2,10 @@ package agents import ( "context" + "os" "os/exec" + "path/filepath" + "runtime" "slices" "strings" "testing" @@ -21,8 +24,8 @@ type acpAgentSpec struct { // detectBinaries lists every binary name IsInstalled accepts via // WithCommand. Used by TestNewACPAgents_DetectionRequiresGlobalBinary // to skip the test whenever ANY of these is on PATH — otherwise an - // agent with multiple WithCommand fallbacks (e.g. Pi accepts both - // `pi-acp` and `pi`) flakes when the secondary binary is present. + // agent with multiple WithCommand fallbacks flakes when a secondary binary + // is present. detectBinaries []string expectedArgv []string // BuildCommand and Runtime.Cmd inferenceArgv []string // InferenceConfig.Command @@ -76,11 +79,12 @@ var newACPAgentSpecs = []struct { sessionDirTemplate: "{home}/.kilocode", }}, {func() Agent { return NewPiACP() }, acpAgentSpec{ - id: "pi-acp", displayName: "Pi", detectBinaries: []string{"pi-acp", "pi"}, + id: "pi-acp", displayName: "Pi", detectBinaries: []string{"pi"}, expectedArgv: []string{"npx", "-y", "pi-acp"}, inferenceArgv: []string{"npx", "-y", "pi-acp"}, - passthroughArgv: []string{"npx", "-y", "pi-acp"}, + passthroughArgv: []string{"pi"}, installViaNpm: true, + installScript: "npm install -g --ignore-scripts @earendil-works/pi-coding-agent", sessionDirTemplate: "{home}/.pi", }}, {func() Agent { return NewCursorACP() }, acpAgentSpec{ @@ -287,7 +291,7 @@ func TestNewACPAgents_InstallScript(t *testing.T) { // to use. detectBinaries comes from the spec table so npx-launched agents // (whose argv[0] is "npx") are still verified against their real detection // targets (qwen, iflow, droid, …), and agents with multiple WithCommand -// fallbacks (e.g. Pi accepts both `pi-acp` and `pi`) skip the test +// fallbacks skip the test // whenever ANY of those is on PATH so the test doesn't flake on CI hosts // that happen to have a generic `pi` (Raspberry Pi tooling) installed. func TestNewACPAgents_DetectionRequiresGlobalBinary(t *testing.T) { @@ -310,6 +314,27 @@ func TestNewACPAgents_DetectionRequiresGlobalBinary(t *testing.T) { } } +func TestPiACPDetectionRejectsPiBinaryWithoutVersionSupport(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("shell executable fixture is Unix-specific") + } + + binDir := t.TempDir() + piPath := filepath.Join(binDir, "pi") + if err := os.WriteFile(piPath, []byte("#!/bin/sh\nexit 1\n"), 0o755); err != nil { + t.Fatalf("write fake pi executable: %v", err) + } + t.Setenv("PATH", binDir) + + result, err := NewPiACP().IsInstalled(context.Background()) + if err != nil { + t.Fatalf("IsInstalled error: %v", err) + } + if result.Available { + t.Fatalf("Available=true for pi executable that fails --version") + } +} + // TestNewACPAgents_LogosNonEmpty guards against agents shipping with empty // embedded SVGs (which renders as a broken in the UI). func TestNewACPAgents_LogosNonEmpty(t *testing.T) { diff --git a/apps/backend/internal/agent/agents/pi_acp.go b/apps/backend/internal/agent/agents/pi_acp.go index ae3bdf4679..695ab6914e 100644 --- a/apps/backend/internal/agent/agents/pi_acp.go +++ b/apps/backend/internal/agent/agents/pi_acp.go @@ -16,7 +16,11 @@ var piACPLogoLight []byte //go:embed logos/pi_acp_dark.svg var piACPLogoDark []byte -const piACPPkg = "pi-acp" +const ( + piACPPkg = "pi-acp" + piCLIBin = "pi" + piCLIPkg = "@earendil-works/pi-coding-agent" +) var ( _ Agent = (*PiACP)(nil) @@ -37,7 +41,7 @@ func NewPiACP() *PiACP { Supported: true, Label: "CLI Passthrough", Description: "Show terminal directly instead of chat interface", - PassthroughCmd: NewCommand("npx", "-y", piACPPkg), + PassthroughCmd: NewCommand(piCLIBin), ModelFlag: NewParam("--model", "{model}"), IdleTimeout: 3 * time.Second, BufferMaxBytes: DefaultBufferMaxBytes, @@ -64,13 +68,11 @@ func (a *PiACP) Logo(v LogoVariant) []byte { } func (a *PiACP) IsInstalled(ctx context.Context) (*DiscoveryResult, error) { - // "pi" is short and could in theory collide with unrelated tooling, but - // the pi-mono coding agent (https://github.com/badlogic/pi-mono) ships - // its CLI as `pi`, and a kandev user with `pi` on PATH is overwhelmingly - // likely to be the pi coding-agent installation we're targeting. - // Checking `pi-acp` first lets a dedicated wrapper take precedence - // when both are present. - result, err := Detect(ctx, WithCommand("pi-acp"), WithCommand("pi")) + // The Pi package publishes the short `pi` binary used by passthrough. A + // non-interactive version check is a best-effort filter for unrelated tools + // with the same name; it does not prove package identity. The separate ACP + // probe validates the structured `npx -y pi-acp` surface, not this binary. + result, err := Detect(ctx, WithCommandCheck(piCLIBin, "--version")) if err != nil { return result, err } @@ -111,7 +113,7 @@ func (a *PiACP) Runtime() *RuntimeConfig { func (a *PiACP) RemoteAuth() *RemoteAuth { return nil } func (a *PiACP) InstallScript() string { - return "npm install -g " + piACPPkg + return "npm install -g --ignore-scripts " + piCLIPkg } func (a *PiACP) PermissionSettings() map[string]PermissionSetting { diff --git a/apps/backend/internal/agent/runtime/lifecycle/manager_passthrough_test.go b/apps/backend/internal/agent/runtime/lifecycle/manager_passthrough_test.go index ce72d48b25..734a90a9c2 100644 --- a/apps/backend/internal/agent/runtime/lifecycle/manager_passthrough_test.go +++ b/apps/backend/internal/agent/runtime/lifecycle/manager_passthrough_test.go @@ -536,9 +536,13 @@ func TestPassthroughPiWritesProjectFile(t *testing.T) { mgr, execution, profile := newPassthroughMCPTestManager(t, "pi-acp") piPath := filepath.Join(execution.WorkspacePath, ".pi", "mcp.json") - if _, _, _, _, err := mgr.passthroughAgentCommand(context.Background(), execution, profile); err != nil { + _, _, _, cmd, err := mgr.passthroughAgentCommand(context.Background(), execution, profile) + if err != nil { t.Fatalf("passthroughAgentCommand returned error: %v", err) } + if got, want := cmd.Args(), []string{"pi", "--model", "default"}; !slices.Equal(got, want) { + t.Fatalf("passthrough command = %v, want %v", got, want) + } data, err := os.ReadFile(piPath) if err != nil { diff --git a/apps/web/e2e/tests/search/task-workspace-content-search.spec.ts b/apps/web/e2e/tests/search/task-workspace-content-search.spec.ts index 605ffaa535..343e256bf3 100644 --- a/apps/web/e2e/tests/search/task-workspace-content-search.spec.ts +++ b/apps/web/e2e/tests/search/task-workspace-content-search.spec.ts @@ -276,7 +276,7 @@ test("@search reveals and flashes a cached preview content match selected with E await expect(dialog).toBeVisible(); await dialog.getByRole("combobox").fill(CACHED_PREVIEW_MARKER); const targetResult = dialog.getByTestId("content-search-result").filter({ hasText: targetPath }); - await expect(targetResult).toHaveCount(1); + await expect(targetResult).toHaveCount(1, { timeout: 30_000 }); await testPage.keyboard.press("Enter"); await expect(dialog).not.toBeVisible(); diff --git a/docs/decisions/2026-08-12-validated-managed-runtime-version-selection.md b/docs/decisions/2026-08-12-validated-managed-runtime-version-selection.md index f5e2ddbb0e..7d23a003a1 100644 --- a/docs/decisions/2026-08-12-validated-managed-runtime-version-selection.md +++ b/docs/decisions/2026-08-12-validated-managed-runtime-version-selection.md @@ -1,6 +1,6 @@ # ADR-2026-08-12-validated-managed-runtime-version-selection: Validate and Persist Managed Runtime Version Selection -**Status:** accepted +**Status:** accepted (amended 2026-08-16) **Date:** 2026-08-12 **Area:** backend, frontend, protocol **Supersedes:** @@ -45,6 +45,12 @@ continue to resolve their own runtime. When no selection record exists, Kandev retains the legacy unversioned host behavior until the first candidate is successfully activated. +The managed package and exact-version override belong only to an agent's ACP +command surfaces. They do not replace `PassthroughConfig.PassthroughCmd`, an +interactive authentication helper, or either surface's install recipe. Agents +such as Pi can therefore use an ACP adapter package for structured execution +and a separately distributed native CLI for terminal passthrough. + The selection is install-wide and per built-in agent. A saved selection applies only while its stored package identity matches the agent's current trusted package metadata; a package change starts with no selection and requires fresh @@ -85,3 +91,6 @@ capabilities. transactional selection without reimplementing package storage. - Applying the host selection to remote executors was rejected because each remote runtime has separate platform, cache, credentials, and ownership. +- Reusing the managed ACP package for terminal passthrough was rejected because + an ACP JSON-RPC stdio adapter is not an interactive PTY application, and some + integrations distribute those surfaces as different packages and binaries. diff --git a/docs/plans/pi-runtime-surfaces/plan.md b/docs/plans/pi-runtime-surfaces/plan.md new file mode 100644 index 0000000000..93bd026281 --- /dev/null +++ b/docs/plans/pi-runtime-surfaces/plan.md @@ -0,0 +1,90 @@ +--- +spec: docs/specs/cli-mode-parity/spec.md +created: 2026-08-16 +status: complete +--- + +# Implementation Plan: Separate Pi Runtime Surfaces + +## Overview + +Pi's built-in definition assigns `npx -y pi-acp` to both structured ACP +execution and interactive passthrough. Registry metadata confirms that +`pi-acp` publishes the ACP adapter binary, while +`@earendil-works/pi-coding-agent` publishes the interactive `pi` binary. The +repair keeps the ACP adapter on structured and inference paths, launches `pi` +for passthrough, and makes installation and discovery agree on that executable. + +The durable managed-runtime boundary is recorded in +[`docs/decisions/2026-08-12-validated-managed-runtime-version-selection.md`](../../decisions/2026-08-12-validated-managed-runtime-version-selection.md), +and the observable behavior is specified in +[`docs/specs/cli-mode-parity/spec.md`](../../specs/cli-mode-parity/spec.md) and +[`docs/specs/agents/runtime-updates.md`](../../specs/agents/runtime-updates.md). + +--- + +## Backend + +### Pi agent definition + +- Update `apps/backend/internal/agent/agents/pi_acp.go` so + `BuildCommand`, `Runtime().Cmd`, and `InferenceConfig().Command` remain + `npx -y pi-acp`. +- Change `PassthroughConfig.PassthroughCmd` to the globally installed `pi` + executable. Do not route the passthrough path through managed ACP package or + version resolution. +- Make `IsInstalled` probe `pi`, the executable provisioned for users and + required by passthrough, with a non-interactive `--version` check instead of + accepting an adapter-only `pi-acp` installation or an unrelated `pi` + executable that cannot satisfy the full integration. +- Change `InstallScript` to the supplied safe install recipe: + `npm install -g --ignore-scripts @earendil-works/pi-coding-agent`. + +### Public documentation + +- Update `docs/public/agents-and-profiles.md` to identify `pi` as Pi's detected + and passthrough executable, document the exact install command, and explain + that structured sessions continue through `pi-acp`. +- Clarify the Pi entry in `README.md` so the supported-agent table does not + imply that the ACP adapter is also the interactive CLI. + +## Tests + +- **What:** Structured Pi chat and inference remain on `npx -y pi-acp`, while + `PassthroughCmd` is exactly `pi`; the install recipe and detection binary + match the interactive package. + **File:** `apps/backend/internal/agent/agents/new_acp_agents_test.go`. + **How:** Change the Pi row in the shared command-surface contract before the + production edit. Confirm the command, installation, and detection tests fail + for the reported mismatch, then pass after the repair. +- **What:** The lifecycle's real Pi passthrough resolution, including MCP + project-file materialization, returns `pi` rather than the ACP adapter. + **File:** + `apps/backend/internal/agent/runtime/lifecycle/manager_passthrough_test.go`. + **How:** Extend `TestPassthroughPiWritesProjectFile` to assert the resolved + command argv as well as the existing `.pi/mcp.json` contract. This covers the + registry-to-lifecycle passthrough path where a future managed-runtime change + could otherwise reintroduce the conflation. + +No browser E2E is needed because the settings UI and passthrough terminal UI do +not change; the behavior is the backend command selected behind the existing +profile mode. + +## Verification Results + +- Red: the focused Go contract run failed on Pi's old passthrough command and + install script, as expected. +- Green: the focused Go command, including the `pi --version` collision + regression, passed with 43 tests across the agents and lifecycle packages. +- PR fixup: the detection probe now uses `WithCommandCheck("pi", "--version")` + and restores the rationale comment for the remaining binary-name tradeoff. +- Public docs: `node --test scripts/validate-public-docs.test.mjs` passed all + 61 tests, `node scripts/validate-public-docs.mjs` validated 41 pages, and + `git diff --check` passed. + +## Implementation Tasks + +- [x] [task-01-correct-pi-execution-surfaces](task-01-correct-pi-execution-surfaces.md) + +Execution is sequential in the primary conversation. There are no parallel +candidates for this single vertical repair. diff --git a/docs/plans/pi-runtime-surfaces/task-01-correct-pi-execution-surfaces.md b/docs/plans/pi-runtime-surfaces/task-01-correct-pi-execution-surfaces.md new file mode 100644 index 0000000000..bd0aa87816 --- /dev/null +++ b/docs/plans/pi-runtime-surfaces/task-01-correct-pi-execution-surfaces.md @@ -0,0 +1,112 @@ +--- +id: "01-correct-pi-execution-surfaces" +title: "Correct Pi execution surfaces" +status: done +wave: 1 +depends_on: [] +plan: "plan.md" +spec: "../../specs/cli-mode-parity/spec.md" +--- + +# Task 01: Correct Pi Execution Surfaces + +## Intent + +Keep Pi's ACP JSON-RPC adapter on structured execution surfaces while launching +the separately distributed interactive `pi` CLI for passthrough. Align host +installation and discovery with the executable users actually run. + +## Inputs + +- `docs/specs/cli-mode-parity/spec.md`, sections **ACP and passthrough commands + remain distinct** and the three Pi scenarios. +- `docs/specs/agents/runtime-updates.md`, managed-runtime command-routing + boundary. +- `docs/decisions/2026-08-12-validated-managed-runtime-version-selection.md`, + amended ACP-only ownership decision. +- Existing agent pattern: `apps/backend/internal/agent/agents/claude_acp.go` + keeps its managed ACP package separate from its native passthrough CLI. + +## Dependencies + +None. + +## Files Likely Touched + +- `apps/backend/internal/agent/agents/pi_acp.go` +- `apps/backend/internal/agent/agents/new_acp_agents_test.go` +- `apps/backend/internal/agent/runtime/lifecycle/manager_passthrough_test.go` +- `docs/public/agents-and-profiles.md` +- `README.md` +- `docs/plans/pi-runtime-surfaces/plan.md` +- `docs/plans/pi-runtime-surfaces/task-01-correct-pi-execution-surfaces.md` + +## TDD Sequence + +1. Change the Pi expectations in `new_acp_agents_test.go` to require + `PassthroughCmd == ["pi"]`, detection through `pi --version`, and the exact install + script + `npm install -g --ignore-scripts @earendil-works/pi-coding-agent`. +2. Extend `TestPassthroughPiWritesProjectFile` to require the lifecycle-resolved + passthrough argv to be `["pi"]` while preserving the MCP file assertions. +3. Run the targeted Go command and confirm it fails because production still + returns `npx -y pi-acp` and the old install/detection contract. +4. Apply the minimal `pi_acp.go` changes. Do not change structured ACP or + inference argv. +5. Update the public Pi installation and command-surface documentation. +6. Run the exact verification block and record every result below and in + `plan.md`. + +## Acceptance Criteria + +- Structured `BuildCommand`, `Runtime().Cmd`, and one-shot inference remain + `npx -y pi-acp`. +- Passthrough lifecycle command resolution returns `pi`, including the path + that materializes Pi's project MCP configuration. +- Pi discovery requires `pi`, and installation runs exactly + `npm install -g --ignore-scripts @earendil-works/pi-coding-agent`; discovery + rejects a same-named executable that fails the non-interactive version check. +- Public docs explain the separate ACP adapter and interactive CLI without + implying that managed ACP version selection controls passthrough. + +## Verification + +Run from the repository root: + +```bash +cd apps/backend && go test ./internal/agent/agents ./internal/agent/runtime/lifecycle -run 'Test(NewACPAgents_(AllCommandSurfaces|InstallScript|DetectionRequiresGlobalBinary)|PiACPDetectionRejectsPiBinaryWithoutVersionSupport|PassthroughPiWritesProjectFile)$' -count=1 && cd ../../ && node --test scripts/validate-public-docs.test.mjs && node scripts/validate-public-docs.mjs && git diff --check +``` + +## Parallelism + +`sequential`. The agent definition, shared contract fixture, lifecycle +regression, and documentation describe one coupled behavior. + +## Risks + +- Hosts that have only a global `pi-acp` adapter will become correctly + unavailable until the interactive Pi package is installed; this is required + because adapter-only installation cannot run passthrough. +- The package is installed with `--ignore-scripts` exactly as reported. The + implementation must not relax or remove that safety flag. +- No package version-management support is added for `pi-acp` in this repair. + +## Output Contract + +Report the root-cause fix, files changed, red/green command results, public-doc +validation, blockers, remaining risks, and synchronized task/plan status in the +primary conversation. + +## Results + +- Red: the focused Go contract run failed on the old Pi passthrough argv and + install script before the production change. +- Green: the focused Go command passed with 43 tests across the agents and + lifecycle packages, including the collision regression for `pi --version`. +- Public docs validation passed: 61 validation tests, 41 published pages, and + `git diff --check`. +- The lifecycle regression preserves Pi's existing `--model default` profile + flag while proving the executable is `pi` and the `.pi/mcp.json` file is + materialized. +- PR fixup: restored the `pi` collision rationale and validated the package's + non-interactive version flag with the repository's `WithCommandCheck` helper. diff --git a/docs/public/agents-and-profiles.md b/docs/public/agents-and-profiles.md index 834f3500a1..2a622fe67c 100644 --- a/docs/public/agents-and-profiles.md +++ b/docs/public/agents-and-profiles.md @@ -24,6 +24,19 @@ The production registry currently shows Auggie, Claude, Codex, Copilot, Gemini, Hermes launches with `hermes acp`. Install the required `hermes` executable from its **Settings > Agents** card, which runs the official Hermes installer. Hermes currently supports task and workspace sessions. Office-assigned skill injection is not yet supported. +### Pi command surfaces + +Pi uses separate executables for its two Kandev modes: + +- Structured ACP sessions and one-shot inference use `npx -y pi-acp`. +- CLI Passthrough starts the globally installed `pi` executable. +- The Pi install action runs `npm install -g --ignore-scripts @earendil-works/pi-coding-agent`. + +After installation, select **Rescan**. Kandev detects Pi when `pi` is on the +`PATH` of the backend process and responds to the non-interactive `--version` +check. The `pi-acp` adapter is not the interactive terminal CLI, and managed +ACP runtime selection does not replace the passthrough command. + 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. diff --git a/docs/specs/agents/runtime-updates.md b/docs/specs/agents/runtime-updates.md index ae73389ac6..bb757cdeaf 100644 --- a/docs/specs/agents/runtime-updates.md +++ b/docs/specs/agents/runtime-updates.md @@ -1,7 +1,7 @@ --- status: approved created: 2026-07-26 -updated: 2026-08-12 +updated: 2026-08-16 owner: Kandev --- @@ -40,6 +40,10 @@ broken release again. metadata. A caller can submit only a version returned by the trusted package's npm metadata. Tags, prereleases, package specs, registry URLs, and shell text are rejected. +- Managed runtime resolution changes only the structured ACP command surfaces. + When an agent's interactive passthrough CLI is distributed separately from + its ACP adapter, passthrough keeps its own package, executable, and install + recipe. - Jobs for one agent are idempotent while queued or running. Installation and version management for the same agent cannot run concurrently. @@ -197,6 +201,8 @@ version while its current probe is unknown produces a repair job. sessionless utility prompts use the same active-version resolver. - Native-binary preference continues to win when an agent deliberately selects its supported native binary path. +- Passthrough command construction does not receive or apply the active managed + ACP version. It continues to use the agent's declared interactive CLI. - SSH and container command builders receive no host version override. - A saved selection read error fails the new host command with an actionable error. It does not fall back to an unversioned package. @@ -271,6 +277,10 @@ version while its current probe is unknown produces a repair job. and does not run a second candidate concurrently. - **GIVEN** an SSH or container session, **WHEN** the host active version changes, **THEN** that executor's command remains unchanged. +- **GIVEN** an agent whose interactive passthrough CLI is separate from its + managed ACP adapter, **WHEN** the host active ACP version changes, **THEN** + later passthrough sessions still launch the declared interactive CLI and do + not launch the ACP package under a PTY. - **GIVEN** a phone viewport and a long version catalogue or process log, **WHEN** the operator selects and activates a version, **THEN** the drawer remains contained and the primary action remains touch-reachable. diff --git a/docs/specs/cli-mode-parity/spec.md b/docs/specs/cli-mode-parity/spec.md index 0593ade0ba..9e04d0a73b 100644 --- a/docs/specs/cli-mode-parity/spec.md +++ b/docs/specs/cli-mode-parity/spec.md @@ -1,6 +1,7 @@ --- status: draft created: 2026-05-16 +updated: 2026-08-16 owner: cfl issue: https://github.com/kdlbs/kandev/issues/906 needs-upgrade: true @@ -22,6 +23,21 @@ This spec brings CLI-passthrough mode up to feature parity with ACP for the **ka ## What +### ACP and passthrough commands remain distinct + +A built-in agent that uses a separate ACP adapter and interactive CLI keeps +those execution surfaces separate. Structured chat and one-shot inference use +the ACP adapter. CLI passthrough launches the interactive CLI directly under +the PTY; managed ACP runtime resolution does not replace that passthrough +command. + +For Pi, structured chat and inference use `npx -y pi-acp`, while CLI +passthrough launches the globally installed `pi` executable. Kandev's Pi +install action runs +`npm install -g --ignore-scripts @earendil-works/pi-coding-agent`, and agent +discovery treats a `pi` executable on the Kandev process `PATH` that passes its +non-interactive `--version` check as the installation signal. + ### Prompt allowed at task creation in CLI mode The task-create dialog's prompt textarea is **enabled** when a CLI/passthrough-capable agent is selected. Users can write the prompt the same way they would for ACP. The control flow that today gates this off when `cli_passthrough` is true is removed. @@ -58,6 +74,25 @@ Users can still press Ctrl-C directly inside the xterm terminal. A dedicated too ## Scenarios +### Pi uses its interactive CLI in passthrough mode +- GIVEN a Pi profile with `cli_passthrough: true` and a `pi` executable on the + Kandev process `PATH` that passes `pi --version` +- WHEN Kandev starts its passthrough PTY +- THEN the command starts with `pi` +- AND Kandev does not launch `pi-acp` or `npx` as the passthrough process + +### Pi keeps its ACP adapter for structured execution +- GIVEN a Pi profile with `cli_passthrough: false` +- WHEN Kandev starts structured chat or one-shot inference +- THEN the command remains `npx -y pi-acp` + +### Pi installation provisions the passthrough executable +- GIVEN Pi is unavailable on the Kandev host +- WHEN the operator runs the Pi install action and rescans agents +- THEN Kandev runs + `npm install -g --ignore-scripts @earendil-works/pi-coding-agent` +- AND discovery finds the installed `pi` executable after its version check + ### CLI-mode task can have a prompt - GIVEN a Claude profile with `cli_passthrough: true` - WHEN the user opens the task-create dialog and types in the prompt field @@ -161,6 +196,7 @@ Pressing Ctrl-C inside the passthrough terminal writes `\x03` to PTY stdin. A fu - Parsing PTY output into `task_messages` (transcriber). The terminal panel shows live output; the chat transcript only shows user-sent text in CLI mode. - Billing-type / subscription-quota badge or DTO surface. That belongs to `subscription-usage.md`. - Adding passthrough support to agents that don't have it (`Supported: false`). +- Adding `pi-acp` to the managed runtime update catalogue. - Migrating away from the current ACP bridge. - Headless `-p` mode for Claude (drains API credit; we deliberately do not use it).