agentkit-core 0.2.2

Core transcript, content, usage, and cancellation primitives for agentkit.
Documentation
# agentkit-core

Shared primitives for agentkit transcripts, content parts, usage accounting, identifiers, and cancellation.

This crate defines the data model used across the rest of the workspace:

- transcript items and roles
- multimodal content parts
- tool call and tool result payloads
- streaming deltas
- token and cost usage
- cancellation checkpoints for turns and tools

Most other crates in the workspace depend on `agentkit-core` as their common language for messages and events.

## Building a transcript for the agent loop

Every agent turn starts with a `Vec<Item>` transcript. System instructions,
user messages, and context documents are all items; the loop appends assistant
and tool items as the turn progresses.

```rust
use agentkit_core::{Item, ItemKind};

let transcript = vec![
    Item::text(ItemKind::System, "You are a careful coding agent."),
    Item::text(ItemKind::Context, "Project uses Rust 1.80, workspace has 12 crates."),
    Item::text(ItemKind::User, "Summarize the release notes."),
];

assert_eq!(transcript.len(), 3);
assert_eq!(transcript[0].kind, ItemKind::System);
```

## Representing tool calls and results

When the model invokes a tool the loop emits a `ToolCallPart`. After execution
the tool executor wraps the output in a `ToolResultPart` and appends it back
to the transcript as a `Tool` item so the model can observe the result.

```rust
use agentkit_core::{
    Item, ItemKind, Part, ToolCallId, ToolCallPart, ToolOutput, ToolResultPart,
};
use serde_json::json;

// The model asks to read a file.
let tool_call = ToolCallPart::new(
    ToolCallId::new("call-1"),
    "fs.read_file",
    json!({ "path": "CHANGELOG.md" }),
);

// After execution, the tool executor produces a result item.
let tool_result_item = Item::new(
    ItemKind::Tool,
    vec![Part::ToolResult(ToolResultPart::success(
        tool_call.id.clone(),
        ToolOutput::text("## v0.3.0\n- Added compaction."),
    ))],
);

assert!(matches!(tool_result_item.parts[0], Part::ToolResult(_)));
```

## Tracking token usage across turns

`Usage` and `TokenUsage` let you accumulate costs and token counts reported by
model providers. Reporters and compaction triggers inspect these values to
decide when to summarize or stop.

```rust
use agentkit_core::{CostUsage, TokenUsage, Usage};

let turn_usage = Usage::new(
    TokenUsage::new(1200, 350)
        .with_reasoning_tokens(50)
        .with_cached_input_tokens(800),
)
.with_cost(CostUsage::new(0.0042, "USD"));

let tokens = turn_usage.tokens.as_ref().unwrap();
assert_eq!(tokens.input_tokens + tokens.output_tokens, 1550);
```

## Cancelling a running turn

Wire a `CancellationController` into the agent and spawn a Ctrl-C listener
that fires `interrupt()`. The loop checks the handle between steps and
returns `FinishReason::Cancelled` when triggered.

```rust,no_run
use agentkit_core::CancellationController;
use agentkit_loop::{Agent, LoopStep, SessionConfig};
use agentkit_core::FinishReason;
use agentkit_provider_openrouter::{OpenRouterAdapter, OpenRouterConfig};

# async fn run() -> Result<(), Box<dyn std::error::Error>> {
let cancellation = CancellationController::new();
let agent = Agent::builder()
    .model(OpenRouterAdapter::new(
        OpenRouterConfig::new("sk-or-v1-...", "openrouter/auto"),
    )?)
    .cancellation(cancellation.handle())
    .build()?;

let mut driver = agent
    .start(SessionConfig::new("demo"))
    .await?;

// Ctrl-C fires the interrupt, cancelling the in-flight turn.
let interrupt = cancellation.clone();
let ctrl_c = tokio::spawn(async move {
    let _ = tokio::signal::ctrl_c().await;
    interrupt.interrupt();
});

// ... submit input and drive the loop ...

let step = driver.next().await;
ctrl_c.abort();

if let Ok(LoopStep::Finished(result)) = step {
    if result.finish_reason == FinishReason::Cancelled {
        eprintln!("turn cancelled");
    }
}
# Ok(())
# }
```