A reusable agent-loop runtime backbone for projects that need reliable provider integration and session management without building on low-level orchestration frameworks.
Supports GitHub Copilot and OpenAI Codex as provider backends with a shared loop, persistent sessions, and a thin tool/plugin layer. Auth and provider protocol handling draws on implementation lessons from opencode.
- Bun ≥ 1.3.5 — this package is built and tested exclusively with Bun.
bun add @tiny-agent/tiny-agent-runtimeimport { copilot, loop, createSession, sessionMessages } from "@tiny-agent/tiny-agent-runtime"
import { getAuth } from "@tiny-agent/tiny-agent-runtime"
// Load auth (populated via `bunx tart login copilot`)
const auth = await getAuth("copilot")
if (!auth) throw new Error("run: bunx tart login copilot")
const session = createSession()
const result = await loop({
adapter: copilot,
auth,
model: "gpt-4.1",
msg: sessionMessages(session, { prompt: "Hello, world!" }),
})
console.log(result.text)Log in to a provider:
bunx tart login copilot
bunx tart login codexSend a single prompt:
bunx tart prompt copilot "Explain closures in one sentence"
bunx tart prompt codex --model gpt-5.4-mini "Write a haiku"Start a persistent multi-turn chat:
bunx tart chat copilot
bunx tart chat codex --session my-sessionFor GitHub Enterprise Copilot, pass the enterprise host:
bunx tart login copilot github.example.comor set RUNTIME_COPILOT_ENTERPRISE_URL=github.example.com.
Auth tokens are stored in .tmp/auth.json by default.
Set RUNTIME_AUTH_PATH to override the storage path.
import { getAuth, setAuth, authFile } from "@tiny-agent/tiny-agent-runtime"
const auth = await getAuth("copilot") // read
await setAuth("copilot", auth) // write
console.log(authFile()) // default pathSessions provide persistent multi-turn transcripts backed by JSONL files.
import {
createSession,
createSessionStore,
appendUserText,
sessionMessages,
} from "@tiny-agent/tiny-agent-runtime"
const store = createSessionStore()
const stored = await store.create()
const session = appendUserText(createSession({ id: stored.id }), "What is 2+2?")
// pass sessionMessages(session) to loop(...) or copilot.prompt(...)The runtime ships no built-in tools. Wire app-owned tools via ToolPlugin or LoopTool:
import { createToolRegistry, loop, type ToolPlugin } from "@tiny-agent/tiny-agent-runtime"
const weatherPlugin: ToolPlugin = {
name: "weather-example",
tools: [
{
name: "weather",
description: "Look up weather for a city",
schema: { type: "object", properties: { city: { type: "string" } }, required: ["city"] },
async call(input) {
return JSON.stringify({ forecast: "sunny" })
},
},
],
}
const result = await loop({
adapter: copilot,
auth,
model: "gpt-4.1",
msg,
toolPlugins: [weatherPlugin],
})Set toolTimeoutMs on loop(...) to bound per-tool execution time.
loop(...) aggregates token usage across all steps and returns it on LoopResult.usage.
Part streams include { type: "usage", usage } records when a provider emits them.
The CLI emits usage summaries on stderr only, keeping stdout clean for scripting.
A minimal Bun HTTP server with SSE streaming is included in examples/browser-sample/.
bun run login:copilot # or login:codex
bun run sample # starts http://localhost:3000bun run typecheck
bun run test
bun run buildLive provider validation scripts (require real credentials):
RUNTIME_LIVE_VALIDATION=1 bun run validate:live:copilot:multiturn
RUNTIME_LIVE_VALIDATION=1 bun run validate:live:codex:loopMIT