Native Coding Profiles

Authoring, testing, and running Kaya-native agent loops with cryptographically pinned capabilities and deterministic evaluation

Kaya native coding profiles package complete, deterministic agent loops directly in Kaya code. Rather than relying on external, opaque coding agent runtimes, a Kaya coding profile is a versioned, hermetic artifact executed by the Kaya engine inside the host daemon (kayad).

This guide explains how coding profiles work, how to author and customize them, how the least-privilege capability model protects your workspace, and how to test profiles deterministically without paid network credentials.


Architecture Overview

A coding session runs across five tightly coupled, contract-governed layers:

+------------------------------------------------------------------------+
| 1. Frontend Client Layer                                               |
|    - CLI: `kaya code <path> [--event|--value]`                         |
|    - Embedded Rust: `KayaClient` + `KayaProjectedSession`              |
+-----------------------------------+------------------------------------+
                                    | REST + Server-Sent Events (SSE)
                                    v
+------------------------------------------------------------------------+
| 2. Session Authority (`kayad`)                                         |
|    - Verifies content digest SHA-256 over profile package              |
|    - Synthesizes effective ToolMap and enforces approval policies      |
|    - Maintains append-only durable session log                         |
+-----------------------------------+------------------------------------+
                                    | In-memory Engine
                                    v
+------------------------------------------------------------------------+
| 3. Kaya Profile Loop                                                   |
|    - `profile.json`: Contract manifest, inputs, capability constraints |
|    - `main.kaya`: Script entrypoint defining agent behavior            |
|    - `lib://kaya/agent-loop-v1`: Standard turn lifecycle               |
+-----------------------------------+------------------------------------+
                                    | Tool calls
                                    v
+------------------------------------------------------------------------+
| 4. Workspace Tools & Providers                                         |
|    - First-party providers: `read_range`, `glob`, `grep`, `git_diff`    |
|    - Mutation providers: `apply_patch`, `run_command` (if granted)     |
+-----------------------------------+------------------------------------+
                                    | Log entries
                                    v
+------------------------------------------------------------------------+
| 5. Projections & Presentation                                          |
|    - Pure Rust reducers: `SessionView`, `TerminalRenderer`             |
|    - Exactly-once streaming, reconnect replay, and NDJSON emission     |
+------------------------------------------------------------------------+

Profile Anatomy

A profile is a directory or packaged bundle containing:

  1. profile.json: The declarative contract manifest.
  2. main.kaya: The script entrypoint.
  3. Optional local modules: Helper .kaya files imported by main.kaya.

1. The Manifest (profile.json)

The manifest declares the profile identity, input schema, requested capabilities, and execution budget.

Here is the manifest for kaya.code.default:

{
  "contract_version": 1,
  "profile_id": "kaya.code.default",
  "profile_revision": "2026.09",
  "display_name": "Kaya Code",
  "description": "Default Kaya-native coding profile with inspect, search, patch, diff, and check loop.",
  "entry_program": "main.kaya",
  "inputs": [
    {
      "name": "codingModel",
      "description": "Admitted model alias used for coding turns.",
      "schema": {
        "zodTypeName": "Default",
        "innerType": { "zodTypeName": "String", "checks": [{ "kind": "min", "value": 1 }] },
        "defaultValue": "default"
      }
    },
    {
      "name": "maxUserTurns",
      "description": "Maximum interactive user turns before completing the session.",
      "schema": {
        "zodTypeName": "Default",
        "innerType": { "zodTypeName": "Number", "checks": [{ "kind": "min", "value": 1 }, { "kind": "finite" }] },
        "defaultValue": 20
      }
    }
  ],
  "capabilities": [
    { "capability": "kaya.model.default", "interface": "kaya:ai/generate@1", "operation": "generate", "requirement": "optional" },
    { "capability": "kaya.tool.read_range", "interface": "kaya:workspace-inspection@1", "operation": "read_range", "requirement": "required" },
    { "capability": "kaya.tool.glob", "interface": "kaya:workspace-inspection@1", "operation": "glob", "requirement": "required" },
    { "capability": "kaya.tool.grep", "interface": "kaya:workspace-inspection@1", "operation": "grep", "requirement": "required" },
    { "capability": "kaya.tool.git_status", "interface": "kaya:git-inspection@1", "operation": "git_status", "requirement": "required" },
    { "capability": "kaya.tool.git_diff", "interface": "kaya:git-inspection@1", "operation": "git_diff", "requirement": "required" },
    { "capability": "kaya.tool.apply_patch", "interface": "kaya:workspace-edit@1", "operation": "apply_patch", "requirement": "required" },
    { "capability": "kaya.tool.run_command", "interface": "kaya:workspace-command@1", "operation": "run", "requirement": "required" }
  ],
  "budget_request": {
    "max_agent_turns": 20,
    "max_tool_steps_per_turn": 12,
    "max_total_tool_calls": 100
  }
}

2. The Entrypoint (main.kaya)

The entrypoint imports the agent loop library and configures the policy:

import type AgentLoopPolicy, type AgentLoopOutcome, runAgentLoop from "lib://kaya/agent-loop-v1"

const codingTools = [
  { name: "read_range" },
  { name: "glob" },
  { name: "grep" },
  { name: "apply_patch" },
  { name: "git_status" },
  { name: "git_diff" },
  { name: "run_command" }
]

const systemPrompt = "You are a careful coding agent working inside one host-confined workspace. Inspect relevant files before editing. Search narrowly, apply contextual patches, review the resulting diff, and run the smallest useful check. Never claim a check passed unless its tool result says so. If a mutation has an indeterminate outcome, stop: do not retry it automatically, and explain the uncertainty to the user."

let outcome = runAgentLoop(policy: {
  conversation: "default",
  input_prompt: "Enter instruction: ",
  system_prompt: systemPrompt,
  model: codingModel,
  tools: codingTools,
  model_search: false,
  max_user_turns: maxUserTurns
})

Requirement vs. Grant Semantics

Kaya enforces least-privilege capability boundaries at the daemon host authority level. A profile declares what tools it requires, but the host determines what tools are actually admitted to the session's ToolMap.

Read-Only Profiles Fail Closed

Profiles designed for read-only operations—such as kaya.code.plan and kaya.code.review—strictly omit apply_patch and run_command from their manifests:

ProfilePurposeMutation Capabilities (apply_patch, run_command)Inspection Capabilities (read_range, glob, grep, git_*)
kaya.code.defaultFull development lifecycleGrantedGranted
kaya.code.tddTest-driven developmentGrantedGranted
kaya.code.planArchitectural investigation & planningDenied / Filtered OutGranted
kaya.code.reviewPR & git diff reviewDenied / Filtered OutGranted

Even if an LLM generates a tool call for apply_patch while executing kaya.code.plan, the tool invocation is immediately rejected by the daemon because the tool does not exist in the session's admitted ToolMap.

Content Digest Verification

Every profile package is pinned by a SHA-256 digest calculated across its manifest, entrypoint, and imported assets. When a session starts, kayad verifies that the digest matches the authorized profile. If local files are altered or corrupted, launch is aborted with an authority rejection.


Built-in Profiles

Kaya ships with four built-in profiles tailored for distinct engineering phases:

  1. kaya.code.default
    • Use case: General-purpose code edits, refactoring, and feature implementation.
    • Workflow: Inspect -> Search -> Patch -> Diff -> Check.
  2. kaya.code.plan
    • Use case: Architecture planning and code exploration.
    • Properties: Cryptographically read-only; outputs structured plans without modifying workspace state.
  3. kaya.code.review
    • Use case: Code review of changes against a branch or commit.
    • Properties: Cryptographically read-only; focuses on diff inspection, safety, and code standards.
  4. kaya.code.tdd
    • Use case: Test-driven development.
    • Workflow: Locates existing tests, establishes failing red tests, applies minimal implementation patches, and verifies green checks.

Authoring a Custom Profile

To create a specialized profile (e.g., an audit profile that checks security patterns):

Step 1: Create the Directory and Manifest

Create profiles/custom.code.audit/profile.json:

{
  "contract_version": 1,
  "profile_id": "custom.code.audit",
  "profile_revision": "2026.09",
  "display_name": "Security Audit",
  "description": "Read-only security audit profile.",
  "entry_program": "main.kaya",
  "inputs": [
    {
      "name": "codingModel",
      "description": "Model alias for auditing.",
      "schema": {
        "zodTypeName": "Default",
        "innerType": { "zodTypeName": "String", "checks": [{ "kind": "min", "value": 1 }] },
        "defaultValue": "default"
      }
    }
  ],
  "capabilities": [
    { "capability": "kaya.model.default", "interface": "kaya:ai/generate@1", "operation": "generate", "requirement": "optional" },
    { "capability": "kaya.tool.read_range", "interface": "kaya:workspace-inspection@1", "operation": "read_range", "requirement": "required" },
    { "capability": "kaya.tool.glob", "interface": "kaya:workspace-inspection@1", "operation": "glob", "requirement": "required" },
    { "capability": "kaya.tool.grep", "interface": "kaya:workspace-inspection@1", "operation": "grep", "requirement": "required" }
  ],
  "budget_request": {
    "max_agent_turns": 10,
    "max_tool_steps_per_turn": 8,
    "max_total_tool_calls": 50
  }
}

Step 2: Implement the Entry Script

Create profiles/custom.code.audit/main.kaya:

import type AgentLoopPolicy, runAgentLoop from "lib://kaya/agent-loop-v1"

const auditTools = [
  { name: "read_range" },
  { name: "glob" },
  { name: "grep" }
]

const auditPrompt = "You are a read-only security auditor. Scan the codebase for unvalidated inputs, credential leaks, and unsafe deserialization. Produce an audit report with citations to exact file paths and line ranges. You cannot edit files or run shell commands."

let outcome = runAgentLoop(policy: {
  conversation: "audit",
  input_prompt: "Enter audit target or scope: ",
  system_prompt: auditPrompt,
  model: codingModel,
  tools: auditTools,
  model_search: false,
  max_user_turns: 5
})

Running Profiles via the CLI

The kaya code command launches a session with a designated profile:

# Launch with the default coding profile
kaya code /path/to/project

# Launch with the read-only planning profile
kaya code /path/to/project --profile kaya.code.plan

# Launch with the TDD profile in streaming event mode (NDJSON)
kaya code /path/to/project --profile kaya.code.tdd --event

# Launch in value mode for shell scripting (outputs only the final assistant text)
SUMMARY=$(kaya code /path/to/project --profile kaya.code.review --value)

Output Modes

  • Interactive (--interactive or default TTY): Formats reasoning, tool progress, and diffs to stderr, printing the final clean response to stdout.
  • Event (--event): Emits line-delimited JSON (NDJSON) events without ANSI colors, suitable for IDE extensions, CI workflows, and background daemon consumers.
  • Value (--value): Suppresses intermediate progress and prints only the final text output of the session to stdout, ideal for shell composition.

Embedded Rust Integration

You do not need to spawn the kaya CLI subprocess to run profiles in a Rust application. Use KayaClient and KayaProjectedSession directly:

use kaya_client::{KayaClient, KayaProjectedSession, KayaPresentationLiveSessionTarget};
use kaya_presentation_contract as contract;
use kaya_daemon_wire::SessionEvent;

struct MyUiTarget;

#[async_trait::async_trait]
impl KayaPresentationLiveSessionTarget for MyUiTarget {
    async fn apply_presentation_command(
        &mut self,
        command: &contract::PresentationCommand,
    ) -> Result<(), String> {
        match command {
            contract::PresentationCommand::AppendPage(page) => {
                for entry in &page.entries {
                    println!("[{}] {:?}", entry.role, entry.content);
                }
            }
            _ => {}
        }
        Ok(())
    }

    async fn apply_session_event(&mut self, event: &SessionEvent) -> Result<(), String> {
        // Handle low-level telemetry, pings, or tool activity
        Ok(())
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = KayaClient::new("http://127.0.0.1:8765")?;
    let mut target = MyUiTarget;

    let init = contract::PresentationLiveSessionInitialization {
        version: contract::PresentationLiveSessionWireVersion::V1,
        session_id: "my-session-id".to_string(),
        frontier: None,
        page_size: 50,
    };

    let mut session = KayaProjectedSession::open(client, init, &mut target).await?;

    while !session.is_closed() {
        match session.next_wake(&mut target).await {
            Ok(snapshot) => {
                // Projection updated
            }
            Err(kaya_client::KayaClientError::Closed) => break,
            Err(e) => return Err(e.into()),
        }
    }

    Ok(())
}

Deterministic Testing (No Paid API Keys)

Kaya profile conformance is proven deterministically without network credentials or paid model accounts.

Running the Conformance Suite

Run the full black-box conformance suite in packages/kaya-core-rs:

cargo test -p kaya-cli --test code_conformance

This hermetic test suite verifies:

  1. Profile Authority & ToolMap Filtering: Verifies that read-only profiles (plan, review) strictly exclude mutation tools (apply_patch, run_command), while default and tdd grant them.
  2. Terminal Streaming Modes: Tests interactive, event, and value modes against mock session replays, proving stdout/stderr stream separation.
  3. Non-CLI Rust Consumer: Tests direct integration via KayaProjectedSession without touching the CLI binary.
  4. Reconnect & Deduplication: Confirms that streaming reconnects deduplicate already-rendered entries using durable entry IDs.
  5. Binary Isolation: Proves that the native engine operates without spawning third-party agent CLI binaries.

Optional Live Smoke Tests

When developing new capabilities against live foundation models, set the appropriate provider environment variables:

# Optional live testing with OpenAI
export OPENAI_API_KEY="sk-..."
kaya code /path/to/project --profile kaya.code.default

# Optional live testing with Anthropic
export ANTHROPIC_API_KEY="sk-ant-..."
kaya code /path/to/project --profile kaya.code.default

Live tests are never required for repository CI gates or local preflight (bun run ci:preflight).