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

strands-dynamodb-storage

v0.1.2

Published

Amazon DynamoDB storage backend for the Strands Agents SDK — one Storage implementation for Session Manager, Memory Manager, and any subsystem, with optional S3 offload for large values and an optional native vector-search hook.

Readme

strands-dynamodb-storage

An Amazon DynamoDB Storage backend for the Strands Agents TypeScript SDK.

It implements the SDK's unified byte Storage interface (write / read / delete / list / namespace), so one DynamoDB-backed instance can be passed to Session Manager, Memory Manager, the context offloader, transcripts, and any other subsystem that persists bytes — no per-subsystem code. On top of the byte contract it adds S3 offload for large values, optional gzip compression, TTL, and optional native vector search.

Install

npm install strands-dynamodb-storage @strands-agents/sdk @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb
# only if you enable S3 offload for values above the 400 KB item limit:
npm install @aws-sdk/client-s3

The AWS SDK packages are peer dependencies and are lazy-loaded — if you never construct a DynamoDBStorage, you don't pay for them. @aws-sdk/client-s3 is optional (needed only when s3Bucket is set).

Table

A table with a string partition key pk and string sort key sk (PAY_PER_REQUEST recommended):

aws dynamodb create-table --table-name agent-data \
  --attribute-definitions AttributeName=pk,AttributeType=S AttributeName=sk,AttributeType=S \
  --key-schema AttributeName=pk,KeyType=HASH AttributeName=sk,KeyType=RANGE \
  --billing-mode PAY_PER_REQUEST --region us-east-1

You own the table: the package never creates infrastructure and holds no CreateTable permission at runtime. TTL enablement and vector index creation are covered in Provisioning and permissions.

Quick start — session persistence, zero custom code

import { Agent, SessionManager } from '@strands-agents/sdk'
import { DynamoDBStorage } from 'strands-dynamodb-storage'

const storage = new DynamoDBStorage('agent-data', { region: 'us-east-1' })
const agent = new Agent({ sessionManager: new SessionManager({ storage }) })
// sessions now persist to DynamoDB — nothing else to wire.

The same instance backs any subsystem that accepts a Storage — for example, offloading oversized tool results with the context offloader:

import { Agent } from '@strands-agents/sdk'
import { ContextOffloader } from '@strands-agents/sdk/vended-plugins/context-offloader'

const agent = new Agent({ plugins: [new ContextOffloader({ storage })] })

Direct byte usage

import { DynamoDBStorage } from 'strands-dynamodb-storage'

const store = new DynamoDBStorage('agent-data', { region: 'us-east-1' })

await store.write('sessions/s1/snapshot.json', new TextEncoder().encode('{"turn":1}'))
const bytes = await store.read('sessions/s1/snapshot.json') // Uint8Array | null

// list by string prefix -> a native partition Query with begins_with
const keys = await store.list('sessions/s1/')

// Note: prefixes must cover at least a full scope and identifier ('scope/id/').
// list('') and single-segment prefixes are rejected as too broad -- they would
// require a cross-partition Scan. This deliberately narrows the SDK Storage
// contract (whose in-memory backends list everything on ''); SDK subsystems
// always pass namespaced prefixes and are unaffected.

// or a structured DynamoDB query (the intended pk/sk extension point) — no GSI
const scoped = await store.list({ pk: 'sessions/s1', skPrefix: 'scopes/agent/' })

await store.delete('sessions/s1/snapshot.json')

// namespaced view (keys transparently prefixed); nesting composes
const s1 = store.namespace('sessions/s1')
await s1.write('scopes/agent/a1/x', bytes ?? new Uint8Array())

Keys are opaque /-separated paths. The leading two segments become the partition key and the remainder the sort key, so point operations are single-item PutItem/GetItem/DeleteItem and listing is a partition-scoped Query.

Large values → S3 offload (optional)

const store = new DynamoDBStorage('agent-data', {
  region: 'us-east-1',
  s3Bucket: 'my-agent-offload-bucket', // values > ~380 KB go to S3; a pointer item stays in DynamoDB
})

Reads and deletes are transparent (the pointer is followed / the S3 object is cleaned up). Without s3Bucket, an oversized write throws rather than silently truncating.

Compression (optional)

new DynamoDBStorage('agent-data', { region: 'us-east-1', compression: 'gzip' })

Transparent gzip applied before the offload size check, so compressible values stay inline in DynamoDB (lower cost, fewer S3 round-trips). Each item records whether it was compressed, so reads are correct regardless of the current setting; values that don't shrink are stored uncompressed.

TTL (optional)

new DynamoDBStorage('agent-data', { region: 'us-east-1', ttlSeconds: 86_400 }) // 1 day
// per-write override:
await store.write('sessions/tmp/x', data, { ttlSeconds: 3_600 })

Stamps a DynamoDB-native epoch-seconds expireAt attribute (enable TTL on that attribute at the table level for physical cleanup). read/list also filter items whose expiry has passed, covering the lag before DynamoDB physically deletes them. With S3 offload, add an S3 lifecycle rule to reclaim offloaded objects (TTL removes only the DynamoDB pointer). Note that this filtering applies to read/list only: because TTL deletion is asynchronous, search() can briefly return items whose expiry has passed but which DynamoDB has not yet physically deleted.

Semantic search — DynamoDB native vector index

search() gives an agent semantic long-term memory over the same table: write each memory with its embedding, then query by meaning. It is an optional, feature-detected part of the Storage contract (if (storage.search) { … }). This store searches pre-computed embedding vectors and does not embed text: pass a SearchQuery with a vector, as every example does. A plain-string query is rejected with a StorageError at runtime, so a text-search consumer that expects the backend to embed for it must wrap this store with an embedding bridge rather than wiring it in directly. On DynamoDB the search runs against the native vector index, so nearest-neighbour scoring happens in the database -- no second vector store, no ETL -- and because the index is partitioned on pk, every search is scoped to the caller's key space. Creating the table with a vector index (and the IAM permissions needed) is covered in the repository README's Provisioning and permissions.

Write an embedding alongside the bytes, then query:

import { DynamoDBStorage } from 'strands-dynamodb-storage'

const store = new DynamoDBStorage('agent-memory', {
  region: 'us-east-1',
  indexName: 'vector_index', // vector index on the table (the default)
  vectorAttribute: 'vector', // item attribute holding the embedding (the default)
})

// store a memory with its embedding (kept inline even when the payload offloads to S3)
await store.write('memory/u1/m1', new TextEncoder().encode('likes window seats'), {
  vector: [/* embedding, e.g. 1024 floats */],
  metadata: { kind: 'preference' },
})

// nearest-neighbour search, scoped to a partition
const results = await store.search({
  vector: queryEmbedding,
  topK: 5,
  pk: 'memory/u1', // required when the index declares a HASH element
  filter: { kind: 'preference' }, // optional metadata equality filter (applied client-side)
  includeValues: true, // hydrate each match's stored bytes
})
// results: Array<{ key: string; score: number; data?: Uint8Array; metadata?: Record<string, unknown> }>
// ordered nearest-first; score direction follows the index's distance function
// (COSINE/EUCLIDEAN: lower = nearer; DOT_PRODUCT: higher = more similar).

The vectorSearch adapter (optional override)

search() issues DynamoDB SearchVectors natively (requires @aws-sdk/client-dynamodb >= 3.1103.0). A vectorSearch adapter, when configured, overrides the native call — useful for testing or custom routing. The adapter has the shape:

type VectorSearchAdapter = (params: {
  tableName: string
  indexName: string
  vectorAttribute: string
  pk?: string
  vector: number[]
  topK: number
  filter?: Record<string, string | number | boolean>
}) => Promise<Array<{ key: string; score: number; metadata?: Record<string, unknown> }>>

The adapter is purely an override: with none configured, search() issues the native SearchVectorsCommand itself.

Configuration reference

| Option | Purpose | | ------------------------------------ | ------------------------------------------------------------------------ | | region / client | AWS region, or a pre-built DynamoDBDocumentClient (mutually exclusive) | | prefix | Key prefix prepended to every key (a namespace within the table) | | s3Bucket / s3Prefix / s3Client | S3 offload target for large values | | compression | 'gzip' | 'none' (default 'none') | | ttlSeconds / ttlAttribute | TTL duration + attribute name (default expireAt) | | indexName / vectorAttribute | Vector index + embedding attribute (default vector_index / vector) | | vectorSearch | Adapter that performs the native vector search |

Minimal IAM

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:DeleteItem", "dynamodb:Query"],
      "Resource": "arn:aws:dynamodb:us-east-1:ACCOUNT:table/agent-data"
    },
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
      "Resource": "arn:aws:s3:::my-agent-offload-bucket/*"
    }
  ]
}

(The S3 statement is only needed when s3Bucket is configured. Semantic search() additionally needs dynamodb:SearchVectors on the table and its indexes:)

{
  "Effect": "Allow",
  "Action": "dynamodb:SearchVectors",
  "Resource": [
    "arn:aws:dynamodb:us-east-1:ACCOUNT:table/agent-data",
    "arn:aws:dynamodb:us-east-1:ACCOUNT:table/agent-data/index/*"
  ]
}

The full provisioning story (TTL enablement, vector index creation, and the complete least-privilege policy) is in the repository README's Provisioning and permissions.

Examples

Runnable, live-verified examples for every capability, from session resume to a customer-support capstone, live in the examples library. The scripts are Python; this package is a feature-parity mirror, so every pattern translates directly.

License

Apache-2.0