Skip to content

[R2] Export KVStore from a /kv subpath and add fileStore(dir) - #292

Merged
LinuxDevil merged 14 commits into
mainfrom
lou-r2-kv-file-store
Oct 2, 2026
Merged

LinuxDevil merged 14 commits into
mainfrom
lou-r2-kv-file-store

Conversation

@LinuxDevil

@LinuxDevil LinuxDevil commented Oct 2, 2026 •

Copy link
Copy Markdown
Owner

Closes #189

What

  • @lousho/build-ai-agent/kv (new subpath, additive): KVStore, KVCheckpointStore, CHECKPOINT_KV_BINDING and the types KVStoreOptions, KVBinding, KVPutOptions (src/deploy/kv.ts, tsup entry deploy/kv, one added exports block). The barrel is Node-free: assertSessionId moved to src/session/sessionId.ts (re-exported from sessionStore.ts, so existing imports are unchanged), kvStore.ts imports SessionStore as a type, and checkpoint.ts imports Message / StorageService as types. src/deploy/kv.test.ts bundles the barrel with esbuild (platform: 'browser', metafile) and asserts no input imports a node: path.
  • fileStore(dir, { historyLimit? }) (root export, src/storage/fileStore.ts): sessions/<id>.json (FileSessionStore), checkpoints/<id>.json, checkpoint-history/<id>.json, approvals/<id>.json. Atomic temp-file + rename writes, no lock files, no StorageService; directories are created on first write; a corrupt history file reads as empty; session/checkpoint and approval ids are validated (approval ids against /^[A-Za-z0-9_-]{1,128}$/).
  • Docs: docs/deployment.md (Worker example importing KVStore from /kv, key table kept), docs/sessions.md (store table rows and the fileStore('./.lousho') snippet), docs/durable-execution.md (history table), docs/api-overview.md. llms.txt / llms-full.txt regenerated. CHANGELOG entry under Unreleased. README unchanged.

Deviation from the ticket

FileApprovalStore.resolve claims the record with an exclusive create of <id>.json.claim (flag: 'wx') instead of renaming the record: on Windows two concurrent renames of the same file could both succeed in testing, so the rename was not a safe claim. A resolver that crashes after claiming leaves the claim file and the approval resolves to null until it is saved again (tested). The concurrency test runs 10 rounds of two store instances resolving one id and gets exactly one record each round.

Also included: src/execution/guardrails.test.ts afterEach retries rmSync on EPERM (Windows keeps the killed timeout child's working directory locked briefly); test-only, needed for the suite to pass locally.

Docs site

No page was added and no ##/###/#### heading was added, renamed or removed. Edited pages: deployment.md, sessions.md, durable-execution.md, api-overview.md. The docs site needs only a content sync, no navigation change.

Verification (on the branch, synced with origin/main)

  • npx tsc --noEmit: ok
  • npm run lint: ok (0 warnings)
  • npm run build / npm run build --workspace=packages/create-lousho-agent: ok
  • npm run test:types: no type errors
  • npm run docs:verify-snippets -- --skip-build: all 207 snippets type-check, 8 also run
  • npm run docs:llms:check: ok
  • npm run pack-smoke: all checks passed (esm 16/16 and cjs 16/16 entries loaded, including ./kv); dist/deploy/kv.d.ts exports all six names; dist/deploy/kv.mjs contains no node:
  • npm run test:coverage: final run after merging the latest origin/main ([M10a] Slack and Discord: a pending ask_question survives a restart #285 and [N13a] web_fetch built-in with pinned-DNS SSRF checks; http_request no longer open to DNS rebinding #277; CHANGELOG conflicts kept both sides, llms regenerated), via vitest run --coverage --config vitest.coverage.config.ts --coverage.reportOnFailure: 225 of 226 files passed, 3220 tests passed, 2 skipped; the one failure was NodeWorkspace.test.ts "returns stdout, stderr and the exit code" timing out at 5 s under load from other agents, and that file alone passed (26 passed, 1 skipped). An earlier run failed only in the Docker integration of SubprocessSandbox.test.ts ("no such container"; shared daemon), which passed 13/13 alone. npx vitest run src/session src/deploy src/storage: 23 files, 244 tests passed
  • npx vitest run src/session src/deploy src/storage src/execution/checkpointHistory.contract.test.ts: 24 files, 296 tests passed
  • npm run fallow: exit 0, dead code: no issues, maintainability 89.7
  • Agent Forge: typecheck ok, typecheck:server ok, test 13 files / 112 tests passed, test:server 14 files / 122 tests passed

Live test spend: none (the ticket makes no live calls).

🤖 Generated with Claude Code

LinuxDevil and others added 14 commits October 2, 2026 14:09
- New subpath @lousho/build-ai-agent/kv (src/deploy/kv.ts): KVStore,
  KVCheckpointStore, CHECKPOINT_KV_BINDING and the KVStoreOptions, KVBinding,
  KVPutOptions types. Its import graph is Node-free: assertSessionId moved to
  src/session/sessionId.ts (re-exported from sessionStore.ts), and the
  type-only imports in kvStore.ts and checkpoint.ts are now `import type`.
  kv.test.ts bundles the barrel with esbuild for the browser platform and
  fails on any node: import.
- fileStore(dir, { historyLimit }) (root export): sessions, checkpoints,
  checkpoint history and approvals as JSON files, written atomically, no lock
  files. Approvals are claimed with an exclusive create so two resolvers get
  the record once (a rename claim is not atomic on Windows).
- Docs: sessions, deployment, durable-execution, api-overview; CHANGELOG.

Closes #189

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…aits for its killed child

The guardrails timeout test kills its slow child with a taskkill it does not
wait for; on Windows the scratch directory stays locked (EPERM) until the
child exits, and with the new kv.test.ts in the suite the afterEach rm hit
that window in 5 of 6 local full runs. The cleanup now retries on EPERM.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…g; longer timeout for the file-backed history contract

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…v-file-store

# Conflicts:
#	CHANGELOG.md
#	package.json
#	tsup.config.ts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@LinuxDevil
LinuxDevil merged commit 6ee9b16 into main Oct 2, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[R2] Export KVStore from a /kv subpath and add fileStore(dir)

1 participant