agent

Composable Agent primitives for Kit applications

Files

FileDescription
.editorconfigEditor formatting configuration
.gitignoreGit ignore rules for local package artifacts
.tool-versionsasdf tool versions
LICENSEMIT license file
README.mdThis file
examples/basic.kitBasic single-agent example with memory
examples/workflow.kitMulti-agent workflow routing example
kit.tomlPackage manifest with metadata and tasks
src/main.kitPublic Kit.Agent facade and re-exports
src/app-runner.kitApp and runner facade with immutable stores
src/artifact-store.kitPure immutable artifact store helpers
src/memory-store.kitMemory store and memory service adapter boundary
src/runner.kitModel adapters, actions, hooks, plugins, and turn runner
src/session-store.kitPure immutable session store helpers
src/session.kitSession construction, event appending, and state patching
src/spec.kitAgent spec builders and memory helpers
src/tool.kitTool declarations, schema metadata, validation, and dispatch
src/types.kitShared Agent types, events, actions, and result records
src/workflow.kitExplicit multi-agent workflow graph runner
tests/core.test.kitCore agent, session, tool, runner, and workflow tests
tests/hooks.test.kitRunner hook and plugin stack tests
tests/memory.test.kitMemory store and service boundary tests
tests/stores.test.kitSession and artifact store tests
tests/tools.test.kitTool declaration and argument validation tests
tests/workflow-invalid-edge.test.kitWorkflow invalid-transfer guard test
tests/workflow-max-steps.test.kitWorkflow max-step guard test
tests/workflow-missing-start.test.kitWorkflow missing-start guard test
tests/workflow-routing.test.kitWorkflow transfer routing test

Dependencies

No Kit package dependencies.

kit-agent is intentionally provider-neutral and store-neutral. Model

providers, memory backends, vector stores, MCP adapters, telemetry packages, and

database packages should integrate through the package records instead of

becoming dependencies of this core package.

Installation

kit add gitlab.com/kit-lang/packages/kit-agent.git

Usage

import Kit.Agent as Agent

main = fn =>
  read-memory = fn(key: String) => if key == "profile:name" then Some "Kit" else None
  write-memory = fn(-key: String, -value: String) => Ok no-op
  delete-memory = fn(-key: String) => Ok no-op
  memory = Agent.memory "profile-memory" read-memory write-memory delete-memory

  agent = Agent.define "assistant" "Answer with the user's profile"
    |> Agent.with-description "Small local example agent"
    |> Agent.with-memory memory

  model = Agent.text-model "local-static" (fn(context) =>
    name = Agent.recall context.agent.memory "profile:name" ?? "there"
    Ok "Hello ${name}; I saw '${context.input}'."
  )

  result = Agent.run-turn agent (Agent.default-session "demo" "user-1") "status?" model

  match result.output
    | Some text -> println text
    | None -> println "no output"

  println "events=${Agent.event-count result.session}"

main

Agent Concepts

Specs

An AgentSpec describes the local agent contract: name, instructions,

description, mode, tools, memory, initial state, and limits. Specs are immutable

records built with helpers such as Agent.define, Agent.with-tool, and

Agent.with-memory.

Events and Sessions

Sessions hold the event stream and current state. Events cover user messages,

agent messages, tool calls, tool results, state deltas, transfers, escalation,

input requests, artifacts, errors, and terminal run events.

Use Agent.default-session, Agent.append-event, and Agent.patch-state for

local session handling. Use src/session-store.kit helpers when a runner needs

to persist session snapshots between turns.

Actions

Model adapters return Result Agent.Action String.

Implemented actions:

  • Respond {content}: append an agent message and terminal event
  • CallTool {id, name, args}: append tool call and tool result/error events
  • PatchState {changes}: merge state changes into the session
  • TransferToAgent {agent}: request workflow routing to another agent
  • EscalateRun {reason}: append an escalation event
  • RequestUserInput {prompt}: append an input request event
  • StopAgent: append a terminal event

Use Agent.text-model when a provider only returns text. Use Agent.model

when a provider, planner, or router returns explicit actions.

App Runner

Agent.app and Agent.runner provide an ADK-style facade around the lower-level

runner functions. A turn returns the updated runner so immutable session,

artifact, and memory stores can be threaded into the next turn.

application = Agent.app "support" agent
runner = Agent.runner application

first = Agent.run-app-turn runner "user-1" "hello" model
second = Agent.run-app-turn first.runner "user-1" "again" model

Use Agent.with-app-hooks to attach lifecycle hooks. Use

Agent.with-app-workflow with Agent.run-app-workflow when the app should run

a configured workflow graph.

Runner Hooks and Plugins

Agent.run-turn-with-hooks adds opt-in lifecycle hooks around the local runner:

before = fn(-context) => Ok UseModel
on-event = fn(event) => Ok event
after = fn(-context, result) => Ok result

hooks = Agent.run-hooks
  before
  on-event
  after

result = Agent.run-turn-with-hooks agent session "hello" model hooks
  • before-run can return UseAction action to skip the model call, or

UseModel to call the configured model.

  • on-event can transform or reject events before they are appended.
  • after-run can transform the final RunResult.

Plugins are named hook bundles. A plugin stack composes hooks in order, giving

integration packages a small lifecycle surface without taking over the runner.

hooks = Agent.run-hooks before on-event after
plugins = Agent.plugin-stack
  |> Agent.with-plugin (Agent.plugin "telemetry" hooks)

application = Agent.app "support" agent
  |> Agent.with-app-hooks (Agent.plugin-stack-hooks plugins)

Use hooks and plugins for local policy checks, telemetry/event shaping, test

doubles, cache lookup, tracing, and package adapters such as kit-otel.

Workflows

Workflows bind agent specs to model adapters and allow explicit transfer edges.

import Kit.Agent.{Respond, TransferToAgent}

router = Agent.define "router" "Route requests to a specialist"
billing = Agent.define "billing" "Handle billing requests"

router-model = Agent.model "router-model" (fn(context) =>
  if String.contains? context.input "invoice" then
    Ok (TransferToAgent {agent: "billing"})
  else
    Ok (Respond {content: "router handled ${context.input}"})
)

billing-model = Agent.text-model "billing-model" (fn(context) =>
  Ok "billing handled ${context.input}"
)

flow = Agent.workflow "support" "router" [
  Agent.runtime router router-model,
  Agent.runtime billing billing-model
] |> Agent.with-edge (Agent.edge "router" "billing")

The runner rejects transfers that do not have an explicit edge and stops with an

agent error when max-steps is reached.

Tool Integration

Tools keep runtime execution provider-neutral with `Map String String -> Result

String String`, while declarations carry schema metadata for provider and

protocol adapters:

import Kit.Agent.{ToolString}

base-schema = Agent.tool-schema "search" "Search documents"
query-schema = Agent.with-schema-param base-schema (Agent.required-param "query" "Search query" ToolString)
schema = Agent.with-schema-metadata query-schema "provider" "mcp"

search = Agent.tool-from-declaration schema (fn(args) =>
  Ok (Map.get args "query" ?? "")
)

ToolDeclaration includes parameters, metadata, long-running markers, and

confirmation requirements. Provider packages can translate declarations into

their native tool schemas while runners call Agent.run-tool for validation and

execution.

Useful integration points:

  • kit-mcp can expose MCP tools as Agent.Tool.
  • HTTP/client packages can expose remote tools.
  • Model-provider packages can translate tool declarations to provider schemas.

Memory and Stores

The core package exposes a small string key/value memory boundary:

Agent.memory name read write delete

For richer long-term memory, kit-agent also includes a pure immutable

reference store and a service boundary:

store = Agent.memory-store
write = Agent.put-memory store "app" "user-1" "session-1" "profile:name" "Kit likes agents" "user" None Map.empty
hits = Agent.search-memory write.store "app" "user-1" "agents"
session-write = Agent.add-session-to-memory write.store session Map.empty

MemoryService mirrors the same app/user/session shape for package-backed

adapters: add memory, search scoped memory, and delete memory for a session.

Adapters should live in integration packages or optional modules. Good backend

targets include:

  • kit-ets for local concurrent ephemeral memory
  • kit-memcached for cache-backed memory
  • kit-redis and kit-hiredis for Redis-backed memory
  • kit-embeddings for retrieval-oriented memory search
  • kit-sqlite, kit-postgres, and kit-duckdb for database-backed memory

kit-agent also includes pure immutable session and artifact stores:

sessions = Agent.session-store
created = Agent.create-session sessions "app" "user-1" Map.empty
updated = Agent.append-session-event created.store "app" "user-1" event

artifacts = Agent.artifact-store
write = Agent.put-artifact artifacts "report" "text/plain" "content" Map.empty
latest = Agent.latest-artifact write.store "report"

These stores are useful for tests and local workflows. Production adapters can

map the same operations to existing Kit storage packages.

Development

Running Examples

Run examples with the interpreter:

kit run examples/basic.kit
kit run examples/workflow.kit

Compile examples to native binaries:

kit build examples/basic.kit -o /tmp/kit-agent-basic --no-spinner
kit build examples/workflow.kit -o /tmp/kit-agent-workflow --no-spinner

Running Tests

Run the test suite:

kit test tests/

Run the test suite with coverage:

kit test tests/ --coverage

Running kit dev

Run the standard development workflow:

kit dev --no-spinner

This will:

  1. Check formatting for src/, examples/, and tests/
  2. Type-check package sources
  3. Type-check examples
  4. Run tests

Generating Documentation

Generate API documentation from doc comments:

kit doc

Note: Kit sources with doc comments (##) will generate HTML documents in

docs/*.html.

Cleaning Build Artifacts

Remove generated files, caches, and build artifacts:

kit task clean

Note: Defined in kit.toml.

Local Installation

To install this package locally for development:

kit install /Users/ejstembler/Projects/ejs/kit-lang/packages/kit-agent

This installs the package to ~/.kit/packages/@kit/agent/, making it available

for import as Kit.Agent in other projects.

License

This package is released under the MIT License - see LICENSE for

details.

Exported Functions & Types

artifact-store

Create an empty immutable artifact store.

() -> ArtifactStore

artifact

Create an artifact value.

(NonEmptyString, PositiveInt, String, String, Map String String) -> Artifact

list-artifacts

List artifacts matching a key in insertion order.

(ArtifactStore, NonEmptyString) -> List Artifact

latest-artifact

Return the latest artifact for a key, if present.

(ArtifactStore, NonEmptyString) -> Option Artifact

get-artifact

Return a specific artifact version for a key.

(ArtifactStore, NonEmptyString, PositiveInt) -> Option Artifact

put-artifact

Add a new artifact version for a key.

(ArtifactStore, NonEmptyString, String, String, Map String String) -> ArtifactWrite

delete-artifacts

Delete all artifact versions for a key.

(ArtifactStore, NonEmptyString) -> ArtifactStore

tool

Create a tool from a name, description, and handler.

(NonEmptyString, String, Map String String -> Result String String) -> Tool

tool-schema

Create an empty tool declaration.

(NonEmptyString, String) -> ToolDeclaration

tool-param

Create a tool parameter declaration.

(NonEmptyString, String, ToolValueType, Bool) -> ToolParam

required-param

Create a required tool parameter declaration.

(NonEmptyString, String, ToolValueType) -> ToolParam

optional-param

Create an optional tool parameter declaration.

(NonEmptyString, String, ToolValueType) -> ToolParam

tool-value-type-name

Return the stable provider-facing name for a tool value type.

ToolValueType -> String

with-schema-param

Add a parameter to a tool declaration.

(ToolDeclaration, ToolParam) -> ToolDeclaration

with-schema-metadata

Add metadata to a tool declaration.

(ToolDeclaration, NonEmptyString, String) -> ToolDeclaration

with-schema-long-running

Mark a tool declaration as long-running.

ToolDeclaration -> ToolDeclaration

with-schema-confirmation

Mark a tool declaration as requiring confirmation.

ToolDeclaration -> ToolDeclaration

tool-from-declaration

Build a runnable tool from a declaration and handler.

(ToolDeclaration, Map String String -> Result String String) -> Tool

tool-declaration

Return a provider-facing declaration for a runnable tool.

Tool -> ToolDeclaration

tool-declarations

Return provider-facing declarations for a list of tools.

List Tool -> List ToolDeclaration

with-tool

Add a tool to an agent spec.

(Spec, Tool) -> Spec

find-tool

Find a tool by name.

(List Tool, NonEmptyString) -> Option Tool

has-tool?

Return true when an agent has a tool by name.

(Spec, NonEmptyString) -> Bool

tool-call

Create a tool-call event for an agent.

(Spec, NonEmptyString, NonEmptyString, Map String String) -> Event

validate-tool-args

Validate required tool arguments.

(Tool, Map String String) -> Result Unit String

run-tool

Run a tool and convert the result to an event.

(Tool, NonEmptyString, Map String String) -> Event

memory-service

Create a long-term memory service adapter.

(NonEmptyString, MemoryEntry -> Result Unit String, (NonEmptyString, NonEmptyString, String) -> Result MemorySearch String, (NonEmptyString, NonEmptyString, String) -> Result Unit String) -> MemoryService

memory-store

Create an empty immutable memory store.

() -> MemoryStore

memory-entry

Create a memory entry.

(NonEmptyString, NonEmptyString, String, String, String, String, Option NonNegativeInt, Map String String) -> MemoryEntry

add-memory

Add a memory entry to a store.

(MemoryStore, MemoryEntry) -> MemoryWrite

put-memory

Create and add a memory entry in one step.

(MemoryStore, NonEmptyString, NonEmptyString, String, String, String, String, Option NonNegativeInt, Map String String) -> MemoryWrite

list-memories

List all memory entries for an app/user pair.

(MemoryStore, NonEmptyString, NonEmptyString) -> List MemoryEntry

list-session-memories

List all memory entries for an app/user/session tuple.

(MemoryStore, NonEmptyString, NonEmptyString, String) -> List MemoryEntry

delete-memories

Delete all memory entries for an app/user/session tuple.

(MemoryStore, NonEmptyString, NonEmptyString, String) -> MemoryStore

search-memory

Search memory entries for an app/user pair by key, author, or content.

(MemoryStore, NonEmptyString, NonEmptyString, String) -> MemorySearch

add-events-to-memory

Add session events with textual content to memory.

(MemoryStore, Session, List Event, Map String String) -> MemoryBatchWrite

add-session-to-memory

Add all textual events from a session to memory.

(MemoryStore, Session, Map String String) -> MemoryBatchWrite

session-store

Create an empty immutable session store.

() -> SessionStore

session-key

Build a stable session key from app name and user id.

(NonEmptyString, NonEmptyString) -> String

save-session

Save or replace a session in a store.

(SessionStore, Session) -> SessionStore

create-session

Create and save a new session.

(SessionStore, NonEmptyString, NonEmptyString, Map String String) -> SessionWrite

load-session

Load a session by app name and user id.

(SessionStore, NonEmptyString, NonEmptyString) -> Option Session

delete-session

Delete a session by app name and user id.

(SessionStore, NonEmptyString, NonEmptyString) -> SessionStore

has-session?

Return true when the store has a session for the app/user pair.

(SessionStore, NonEmptyString, NonEmptyString) -> Bool

session-keys

Return all session keys in store order.

SessionStore -> List String

append-session-event

Append an event to an existing session, creating the session if needed.

(SessionStore, NonEmptyString, NonEmptyString, Event) -> SessionWrite

mode-name

Convert an agent execution mode to a stable string.

Mode -> String

no-memory

Memory adapter that ignores all reads and writes.

Memory

memory

Create a memory adapter from plain functions.

(NonEmptyString, String -> Option String, (String, String) -> Result Unit String, String -> Result Unit String) -> Memory

recall

Read a value from a memory adapter.

(Memory, String) -> Option String

remember

Write a value to a memory adapter.

(Memory, String, String) -> Result Unit String

forget

Delete a value from a memory adapter.

(Memory, String) -> Result Unit String

define

Create a default chat agent spec.

(NonEmptyString, String) -> Spec

with-description

Return a copy of an agent with a new description.

(Spec, String) -> Spec

with-mode

Return a copy of an agent with a new execution mode.

(Spec, Mode) -> Spec

with-memory

Return a copy of an agent with a new memory adapter.

(Spec, Memory) -> Spec

with-initial-state

Return a copy of an agent with new initial state.

(Spec, Map String String) -> Spec

with-max-turns

Return a copy of an agent with a new max-turn limit.

(Spec, PositiveInt) -> Spec

with-tool-timeout

Return a copy of an agent with a new tool timeout.

(Spec, NonNegativeInt) -> Spec

event-kind

Return the stable symbol kind for an agent event.

Event -> Symbol

is-terminal-event?

Return true when an event ends the current agent turn.

Event -> Bool

session

Create a session with explicit initial state.

(NonEmptyString, NonEmptyString, Map String String) -> Session

default-session

Create a session with empty initial state.

(NonEmptyString, NonEmptyString) -> Session

apply-event

Append an event and apply any state changes carried by it.

(Session, Event) -> Session

apply-events

Append a list of events in order.

(Session, List Event) -> Session

event-count

Count events in a session.

Session -> NonNegativeInt

app

Create an app with a root agent.

(NonEmptyString, Spec) -> App

with-app-workflow

Attach a workflow to an app.

(App, Workflow) -> App

with-app-hooks

Attach lifecycle hooks to an app.

(App, RunHooks) -> App

runner

Create a runner with empty in-memory stores.

App -> Runner

with-runner-session-store

Replace the runner session store.

(Runner, SessionStore) -> Runner

with-runner-artifact-store

Replace the runner artifact store.

(Runner, ArtifactStore) -> Runner

with-runner-memory-store

Replace the runner memory store.

(Runner, MemoryStore) -> Runner

runner-session

Load or create the runner session for a user.

(Runner, NonEmptyString) -> Session

run-app-turn

Run one app turn and persist the resulting session.

(Runner, NonEmptyString, String, Model) -> RunnerTurn

run-app-workflow

Run an app workflow and persist the resulting session.

(Runner, NonEmptyString, String) -> Result RunnerWorkflowTurn String

Mode

Agent execution mode.

Variants

Chat
Task
SingleTurn

ToolValueType

Tool parameter value type.

Variants

ToolString
ToolInt
ToolFloat
ToolBool
ToolJson
ToolAny

Event

Agent event stream entry.

Variants

UserMessage {content}
AgentMessage {agent, content}
ToolCall {id, agent, name, args}
ToolResult {id, name, output}
StateDelta {changes}
Transfer {to}
Escalate {reason}
ArtifactDelta {key, version}
NeedsInput {prompt}
AgentError {message}
EndOfAgent {agent}

Action

Action requested by a model or planner.

This mirrors ADK-style event actions while staying provider-neutral.

Variants

Respond {content}
CallTool {id, name, args}
PatchState {changes}
TransferToAgent {agent}
EscalateRun {reason}
RequestUserInput {prompt}
StopAgent

Model

Model adapter used by the local runner.

Variants

Model {name, run}

BeforeRun

Decision returned by a before-run hook.

Variants

UseModel
UseAction {Action}

Plugin

Named lifecycle integration.

Variants

Plugin {name, hooks}

Edge

Directed workflow edge between named agents.

Variants

Edge {source, target}

AppWorkflow

Optional workflow configured for an app.

Variants

NoWorkflow
UseWorkflow {Workflow}

model

Create a model adapter.

(NonEmptyString, Context -> Result Action String) -> Model

text-model

Create a text-only model adapter.

(NonEmptyString, Context -> Result String String) -> Model

no-hooks

Hooks that leave runner behavior unchanged.

RunHooks

run-hooks

Create runner lifecycle hooks.

(Context -> Result BeforeRun String, Event -> Result Event String, (Context, RunResult) -> Result RunResult String) -> RunHooks

compose-hooks

Compose two hook sets in order.

(RunHooks, RunHooks) -> RunHooks

plugin

Create a named plugin from hooks.

(NonEmptyString, RunHooks) -> Plugin

plugin-stack

Create an empty plugin stack.

() -> PluginStack

with-plugin

Append a plugin to a stack.

(PluginStack, Plugin) -> PluginStack

plugin-stack-hooks

Compose hooks from all plugins in stack order.

PluginStack -> RunHooks

action-to-event

Convert an action into the event it records.

(Spec, Action) -> Event

run-action

Apply an action to a session.

(Spec, Session, Action) -> RunResult

run-turn

Run one model-backed agent turn.

(Spec, Session, String, Model) -> RunResult

run-action-with-hooks

Run an action while applying hook event transforms.

(Spec, Session, Action, RunHooks) -> RunResult

run-turn-with-hooks

Run one model-backed agent turn with lifecycle hooks.

(Spec, Session, String, Model, RunHooks) -> RunResult

runtime

Create a runtime entry for an agent/model pair.

(Spec, Model) -> Runtime

edge

Create a directed workflow edge.

(NonEmptyString, NonEmptyString) -> Edge

workflow

Create a workflow with a start agent and runtimes.

(NonEmptyString, NonEmptyString, List Runtime) -> Workflow

with-edge

Add an allowed transfer edge to a workflow.

(Workflow, Edge) -> Workflow

with-max-steps

Set a workflow max-step guard.

(Workflow, PositiveInt) -> Workflow

find-runtime

Find a runtime by agent name.

(List Runtime, NonEmptyString) -> Option Runtime

has-edge?

Return true when an edge allows source to transfer to target.

(List Edge, NonEmptyString, NonEmptyString) -> Bool

last-event

Return the last event in a session.

Session -> Option Event

run-workflow

Run a workflow from its start agent.

(Workflow, Session, String) -> WorkflowResult