{"_id":"@agentaily/agent-loop","name":"@agentaily/agent-loop","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@agentaily/agent-loop","version":"0.1.0","description":"A minimal, runtime-agnostic agent loop framework with first-class skills, memory, and sessions. Edge-ready (Cloudflare Workers), zero runtime dependencies.","type":"module","license":"MIT","author":{"name":"agentaily"},"homepage":"https://github.com/agentaily/agent-loop#readme","repository":{"type":"git","url":"git+https://github.com/agentaily/agent-loop.git"},"bugs":{"url":"https://github.com/agentaily/agent-loop/issues"},"keywords":["agent","agent-loop","llm","tool-calling","skills","memory","sessions","cloudflare-workers","deepseek","openai"],"sideEffects":false,"main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./providers":{"types":"./dist/providers/index.d.ts","import":"./dist/providers/index.js"},"./adapters/cf-kv":{"types":"./dist/adapters/cf-kv.d.ts","import":"./dist/adapters/cf-kv.js"}},"scripts":{"build":"tsup","dev":"tsup --watch","typecheck":"tsc --noEmit","test":"vitest run","test:watch":"vitest","changeset":"changeset","release":"changeset publish","prepublishOnly":"npm run build"},"devDependencies":{"@changesets/changelog-github":"^0.7.0","@changesets/cli":"^2.31.0","tsup":"^8.3.5","typescript":"^5.6.3","vitest":"^2.1.8"},"engines":{"node":">=18"},"publishConfig":{"access":"public"},"gitHead":"488552c0374c572f1cb974f39139a88f51927bfd","_id":"@agentaily/agent-loop@0.1.0","_nodeVersion":"22.23.0","_npmVersion":"11.18.0","dist":{"integrity":"sha512-RaFTmCHmOmoLfnleCalcmn8wOdm2coUpMb48HCiPDXwg11cxPrPFiWesnopBWIgKrhUWK6V1lp4u11bYd9oYrA==","shasum":"c86b89612bf58efa732efa9f9f3faea4a8121c7b","tarball":"https://registry.npmjs.org/@agentaily/agent-loop/-/agent-loop-0.1.0.tgz","fileCount":17,"unpackedSize":91772,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@agentaily%2fagent-loop@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQD2zDh72EDUR9JKDk09MSQq6lccS568v3oAdI9CKryeJwIgC4W+C+ycwrq7HZey1b9/wVGP0Po8Jnfvv5wEV/7nKZc="}]},"_npmUser":{"name":"yarnovo","email":"yarnb@qq.com"},"directories":{},"maintainers":[{"name":"yarnovo","email":"yarnb@qq.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/agent-loop_0.1.0_1782959961041_0.5598951442159634"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-02T02:39:20.889Z","0.1.0":"2026-07-02T02:39:21.173Z","modified":"2026-07-02T02:39:21.531Z"},"maintainers":[{"name":"yarnovo","email":"yarnb@qq.com"}],"description":"A minimal, runtime-agnostic agent loop framework with first-class skills, memory, and sessions. Edge-ready (Cloudflare Workers), zero runtime dependencies.","homepage":"https://github.com/agentaily/agent-loop#readme","keywords":["agent","agent-loop","llm","tool-calling","skills","memory","sessions","cloudflare-workers","deepseek","openai"],"repository":{"type":"git","url":"git+https://github.com/agentaily/agent-loop.git"},"author":{"name":"agentaily"},"bugs":{"url":"https://github.com/agentaily/agent-loop/issues"},"license":"MIT","readme":"# @agentaily/agent-loop\n\nA minimal, runtime-agnostic **agent loop** with first-class **skills**, **memory**, and **sessions**.\n\n- **Tiny & zero runtime deps** — core is a few hundred lines; only uses `fetch`.\n- **Edge-ready** — runs on Cloudflare Workers, Node 18+, Deno, Bun, and browsers.\n- **Provider-agnostic** — ships an OpenAI-compatible adapter (works with DeepSeek); bring your own.\n- **Pluggable storage** — in-memory by default; a Cloudflare KV adapter included.\n\nBuilt to power the client/edge agent loops behind agentaily's \"chat × everything\" products (first consumer: the `2bti` worker).\n\n## Install\n\n```bash\nnpm i @agentaily/agent-loop\n```\n\n## Quick start\n\n```ts\nimport { Agent, defineTool } from '@agentaily/agent-loop'\nimport { deepseek } from '@agentaily/agent-loop/providers'\n\nconst getWeather = defineTool({\n  name: 'get_weather',\n  description: 'Get the current weather for a city',\n  parameters: {\n    type: 'object',\n    properties: { city: { type: 'string' } },\n    required: ['city'],\n  },\n  handler: (args) => ({ city: args.city, tempC: 21, sky: 'clear' }),\n})\n\nconst agent = new Agent({\n  provider: deepseek({ apiKey: process.env.DEEPSEEK_KEY! }),\n  instructions: 'You are a concise, friendly assistant.',\n  tools: [getWeather],\n})\n\nconst res = await agent.run('what is the weather in Tokyo?')\nconsole.log(res.text)\n```\n\n## The loop\n\n`agent.run(message)` does exactly what you'd hand-write:\n\n```\nuser message ─▶ call LLM (with tools + skill index + memory index)\n                   │\n          tool calls? ──no──▶ final answer ✔  (session saved)\n                   │yes\n          run each tool ─▶ append results ─▶ loop  (up to maxSteps)\n```\n\n`run()` returns `{ text, session, steps, stoppedOnMaxSteps }`. Every step is\nobservable via the `onStep` callback.\n\n## Skills — progressive disclosure\n\nA **skill** is a named, markdown-described capability. The model only sees each\nskill's `name: description` in the system prompt; it pulls the full instructions\nin on demand via the built-in `load_skill` tool. Skills may carry their own\ntools, which become available **only after** the skill is loaded.\n\n```ts\nimport { parseSkill } from '@agentaily/agent-loop'\n\nconst refunds = parseSkill(`---\nname: refunds\ndescription: process customer refunds\n---\nTo refund an order, call issue_refund with the order id, then confirm to the user.`)\n\nconst agent = new Agent({ provider, skills: [refunds], tools: [/* ... */] })\n```\n\nYou can also pass plain `Skill` objects (`{ name, description, instructions, tools? }`)\nor a `SkillRegistry`.\n\n## Memory — durable facts across sessions\n\nA `MemoryStore` holds facts that outlive a single conversation. The built-in\n`remember` / `recall` tools let the agent write and search it, and a compact\nindex of what's remembered is injected into every system prompt.\n\n```ts\nimport { InMemoryMemoryStore } from '@agentaily/agent-loop'\nconst memory = new InMemoryMemoryStore()\nconst agent = new Agent({ provider, memory })\n// the model can now call remember({key, value}) and recall({query})\n```\n\n## Sessions — multi-turn conversations\n\nA `SessionStore` persists conversation history. Resume by passing `sessionId`:\n\n```ts\nconst first = await agent.run('my name is Sam')\nawait agent.run({ message: 'what is my name?', sessionId: first.session.id })\n```\n\nDefault is in-memory. On Cloudflare Workers, persist to KV:\n\n```ts\nimport { KVSessionStore, KVMemoryStore } from '@agentaily/agent-loop/adapters/cf-kv'\nconst agent = new Agent({\n  provider,\n  sessions: new KVSessionStore(env.AGENT_KV),\n  memory: new KVMemoryStore(env.AGENT_KV),\n})\n```\n\nSee [`examples/cf-worker`](./examples/cf-worker) for a complete Worker endpoint.\n\n## API surface\n\n| Export | What |\n| --- | --- |\n| `Agent` | the loop; `new Agent(opts).run(input)` |\n| `defineTool` | build a `{ name, description, parameters, handler }` tool |\n| `SkillRegistry`, `parseSkill` | manage / parse markdown skills |\n| `InMemorySessionStore`, `InMemoryMemoryStore` | default stores |\n| `buildSystemPrompt`, `renderMemoryIndex` | prompt assembly helpers |\n| `@agentaily/agent-loop/providers` → `openaiCompatible`, `deepseek` | LLM adapters |\n| `@agentaily/agent-loop/adapters/cf-kv` → `KVSessionStore`, `KVMemoryStore` | Cloudflare KV storage |\n\n### `AgentOptions`\n\n| option | default | notes |\n| --- | --- | --- |\n| `provider` | — | required; an `LLMProvider` |\n| `instructions` | — | base system prompt (persona / rules) |\n| `tools` | `[]` | always-available app tools |\n| `skills` | `[]` | `Skill[]` or a `SkillRegistry` |\n| `memory` | new `InMemoryMemoryStore` | long-term facts |\n| `sessions` | new `InMemorySessionStore` | conversation history |\n| `builtins` | `true` | inject `load_skill` / `remember` / `recall` |\n| `maxSteps` | `8` | provider round-trips before bailing |\n| `temperature`, `maxTokens` | — | forwarded to the provider |\n| `onStep` | — | `(event) => void` per loop step |\n\n## Bring your own provider\n\nImplement one method:\n\n```ts\nimport type { LLMProvider } from '@agentaily/agent-loop'\n\nconst myProvider: LLMProvider = {\n  async chat({ system, messages, tools, temperature, maxTokens, signal }) {\n    // call your model, return { content, toolCalls? }\n    return { content: '...', toolCalls: [] }\n  },\n}\n```\n\n## Develop\n\n```bash\nnpm install\nnpm test          # vitest (mocked provider — no network)\nnpm run typecheck\nnpm run build     # tsup -> dist (ESM + d.ts)\n```\n\n## License\n\nMIT © agentaily\n","readmeFilename":"README.md","_rev":"1-3136d3554160bd49a0617d91e1ff8943"}