npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@agentaily/agent-loop

v0.1.0

Published

A minimal, runtime-agnostic agent loop framework with first-class skills, memory, and sessions. Edge-ready (Cloudflare Workers), zero runtime dependencies.

Readme

@agentaily/agent-loop

A minimal, runtime-agnostic agent loop with first-class skills, memory, and sessions.

  • Tiny & zero runtime deps — core is a few hundred lines; only uses fetch.
  • Edge-ready — runs on Cloudflare Workers, Node 18+, Deno, Bun, and browsers.
  • Provider-agnostic — ships an OpenAI-compatible adapter (works with DeepSeek); bring your own.
  • Pluggable storage — in-memory by default; a Cloudflare KV adapter included.

Built to power the client/edge agent loops behind agentaily's "chat × everything" products (first consumer: the 2bti worker).

Install

npm i @agentaily/agent-loop

Quick start

import { Agent, defineTool } from '@agentaily/agent-loop'
import { deepseek } from '@agentaily/agent-loop/providers'

const getWeather = defineTool({
  name: 'get_weather',
  description: 'Get the current weather for a city',
  parameters: {
    type: 'object',
    properties: { city: { type: 'string' } },
    required: ['city'],
  },
  handler: (args) => ({ city: args.city, tempC: 21, sky: 'clear' }),
})

const agent = new Agent({
  provider: deepseek({ apiKey: process.env.DEEPSEEK_KEY! }),
  instructions: 'You are a concise, friendly assistant.',
  tools: [getWeather],
})

const res = await agent.run('what is the weather in Tokyo?')
console.log(res.text)

The loop

agent.run(message) does exactly what you'd hand-write:

user message ─▶ call LLM (with tools + skill index + memory index)
                   │
          tool calls? ──no──▶ final answer ✔  (session saved)
                   │yes
          run each tool ─▶ append results ─▶ loop  (up to maxSteps)

run() returns { text, session, steps, stoppedOnMaxSteps }. Every step is observable via the onStep callback.

Skills — progressive disclosure

A skill is a named, markdown-described capability. The model only sees each skill's name: description in the system prompt; it pulls the full instructions in on demand via the built-in load_skill tool. Skills may carry their own tools, which become available only after the skill is loaded.

import { parseSkill } from '@agentaily/agent-loop'

const refunds = parseSkill(`---
name: refunds
description: process customer refunds
---
To refund an order, call issue_refund with the order id, then confirm to the user.`)

const agent = new Agent({ provider, skills: [refunds], tools: [/* ... */] })

You can also pass plain Skill objects ({ name, description, instructions, tools? }) or a SkillRegistry.

Memory — durable facts across sessions

A MemoryStore holds facts that outlive a single conversation. The built-in remember / recall tools let the agent write and search it, and a compact index of what's remembered is injected into every system prompt.

import { InMemoryMemoryStore } from '@agentaily/agent-loop'
const memory = new InMemoryMemoryStore()
const agent = new Agent({ provider, memory })
// the model can now call remember({key, value}) and recall({query})

Sessions — multi-turn conversations

A SessionStore persists conversation history. Resume by passing sessionId:

const first = await agent.run('my name is Sam')
await agent.run({ message: 'what is my name?', sessionId: first.session.id })

Default is in-memory. On Cloudflare Workers, persist to KV:

import { KVSessionStore, KVMemoryStore } from '@agentaily/agent-loop/adapters/cf-kv'
const agent = new Agent({
  provider,
  sessions: new KVSessionStore(env.AGENT_KV),
  memory: new KVMemoryStore(env.AGENT_KV),
})

See examples/cf-worker for a complete Worker endpoint.

API surface

| Export | What | | --- | --- | | Agent | the loop; new Agent(opts).run(input) | | defineTool | build a { name, description, parameters, handler } tool | | SkillRegistry, parseSkill | manage / parse markdown skills | | InMemorySessionStore, InMemoryMemoryStore | default stores | | buildSystemPrompt, renderMemoryIndex | prompt assembly helpers | | @agentaily/agent-loop/providersopenaiCompatible, deepseek | LLM adapters | | @agentaily/agent-loop/adapters/cf-kvKVSessionStore, KVMemoryStore | Cloudflare KV storage |

AgentOptions

| option | default | notes | | --- | --- | --- | | provider | — | required; an LLMProvider | | instructions | — | base system prompt (persona / rules) | | tools | [] | always-available app tools | | skills | [] | Skill[] or a SkillRegistry | | memory | new InMemoryMemoryStore | long-term facts | | sessions | new InMemorySessionStore | conversation history | | builtins | true | inject load_skill / remember / recall | | maxSteps | 8 | provider round-trips before bailing | | temperature, maxTokens | — | forwarded to the provider | | onStep | — | (event) => void per loop step |

Bring your own provider

Implement one method:

import type { LLMProvider } from '@agentaily/agent-loop'

const myProvider: LLMProvider = {
  async chat({ system, messages, tools, temperature, maxTokens, signal }) {
    // call your model, return { content, toolCalls? }
    return { content: '...', toolCalls: [] }
  },
}

Develop

npm install
npm test          # vitest (mocked provider — no network)
npm run typecheck
npm run build     # tsup -> dist (ESM + d.ts)

License

MIT © agentaily