Skill: arosstale/openclaw-memory-2 (version 2.0.1)

--- name: openclaw-memories description: Agent memory with ALMA meta-learning, LLM fact extraction, and full-text search. Observer calls remote LLM APIs (OpenAI/Anthropic/Gemini). ALMA and Indexer work offline. --- # OpenClaw Memory System Three components for agent memory: 1. **ALMA** — Evolves memory designs through mutation + evaluation (offline) 2. **Observer** — Extracts structured facts from conversations via LLM API (requires API key) 3. **Indexer** — Full-text search over workspace Markdown files (offline) ## Environment Variables Observer requires one of: - `OPENAI_API_KEY` - `ANTHROPIC_API_KEY` - Or pass `apiKey` in config ALMA and Indexer require no keys or network access. ## How It Works ### ALMA (Algorithm Learning via Meta-learning Agents) Proposes memory system designs, evaluates them, keeps the best. Uses gaussian mutation and simulated annealing to explore the design space. ``` alma.propose() → design alma.evaluate(design.id, metrics) → score alma.best() → top design alma.top(5) → leaderboard ``` ### Observer Sends conversation history to an LLM, gets back structured facts: - Kind: world fact / biographical / opinion / observation - Priority: high / medium / low - Entities: mentioned people/places - Confidence: 0.0–1.0 for opinions Fails gracefully — returns empty array if LLM is unavailable. ### Indexer Chunks workspace Markdown files and indexes them for search: - `MEMORY.md` — core facts - `memory/YYYY-MM-DD.md` — daily logs - `bank/entities/*.md` — entity summaries - `bank/opinions.md` — beliefs with confidence ``` indexer.index() → count of chunks indexed indexer.search('query') → ranked results indexer.rebuild() → re-index from scratch ``` ## Install ```bash npm install @artale/openclaw-memory ``` ## Limitations - Indexer uses an in-memory mock database, not real SQLite FTS5. Search works but ranking is simplified. - Observer calls remote APIs — not offline. Only ALMA and Indexer work without network. - No dashboard — removed in v2 for simplicity. ## Source 5 files, 578 lines, 0 runtime dependencies. https://github.com/arosstale/openclaw-memory

path: .github/workflows/ci.yml
content: name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build-and-test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest]
        node-version: [20, 22]

    steps:
      - uses: actions/checkout@v4

      - uses: pnpm/action-setup@v4
        with:
          version: 10

      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: pnpm

      - run: pnpm install
      - run: pnpm build
      - run: pnpm test -- --run


path: README.md
content: # openclaw-memory

Memory system for OpenClaw agents. Three components:

- **ALMA** — meta-learning optimizer that evolves memory designs
- **Observer** — extracts structured facts from conversations via LLM (OpenAI/Anthropic/Gemini)
- **Indexer** — full-text search over workspace Markdown files

## Install

```bash
npm install @artale/openclaw-memory
```

## Usage

```typescript
import { ALMAAgent, ObserverAgent, MemoryIndexer } from '@artale/openclaw-memory';

// ALMA: evolve memory designs
const alma = new ALMAAgent({
  constraints: { chunkSize: { min: 100, max: 1000, type: 'number' } }
});
const design = alma.propose();
alma.evaluate(design.id, { accuracy: 0.9, speed: 0.8 });
console.log(alma.best());

// Observer: extract facts (requires LLM API key)
const observer = new ObserverAgent({
  provider: 'anthropic',  // or 'openai' or 'gemini'
  [REDACTED_SECRET],
});
const facts = await observer.extract([
  { role: 'user', content: 'Alice prefers TypeScript over Python' }
]);

// Indexer: search workspace files
const indexer = new MemoryIndexer({ workspace: './my-workspace' });
indexer.index();
const results = indexer.search('TypeScript');
```

## Environment Variables

Observer requires an LLM API key (one of):
- `OPENAI_API_KEY` — for OpenAI provider
- `ANTHROPIC_API_KEY` — for Anthropic provider

ALMA and Indexer work fully offline with zero dependencies.

## OpenClaw Skill

Drop into your workspace to use as a skill:
```bash
cd ~/.openclaw/workspace/skills
git clone https://github.com/arosstale/openclaw-memory
```

## Architecture

- **5 source files, 578 lines, 0 runtime dependencies**
- In-memory database (no native modules, works everywhere)
- Observer calls remote LLM APIs when configured — not offline
- ALMA and Indexer are fully offline

## License

MIT


path: package.json
content: {
  "name": "@artale/openclaw-memory",
  "version": "2.0.1",
  "description": "Memory system for OpenClaw — ALMA meta-learning + observation extraction + FTS search",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "import": "./dist/index.js",
      "require": "./dist/index.js"
    },
    "./alma": {
      "types": "./dist/alma.d.ts",
      "import": "./dist/alma.js"
    },
    "./observer": {
      "types": "./dist/observer.d.ts",
      "import": "./dist/observer.js"
    },
    "./indexer": {
      "types": "./dist/indexer.d.ts",
      "import": "./dist/indexer.js"
    }
  },
  "scripts": {
    "build": "tsc",
    "test": "vitest run"
  },
  "keywords": [
    "openclaw",
    "memory",
    "alma",
    "agent"
  ],
  "author": "Artale <[REDACTED_SECRET]>",
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "https://github.com/arosstale/openclaw-memory.git"
  },
  "publishConfig": {
    "access": "public"
  },
  "files": [
    "dist",
    "README.md"
  ],
  "dependencies": {},
  "devDependencies": {
    "@types/node": "^20.0.0",
    "typescript": "^5.0.0",
    "vitest": "^1.0.0"
  }
}


path: pnpm-lock.yaml
content: lockfileVersion: '9.0'

settings:
  autoInstallPeers: true
  excludeLinksFromLockfile: false

importers:

  .:
    devDependencies:
      '@types/node':
        specifier: ^20.0.0
        version: 20.19.33
      typescript:
        specifier: ^5.0.0
        version: 5.9.3
      vitest:
        specifier: ^1.0.0
        version: 1.6.1(@types/node@20.19.33)

packages:

  '@esbuild/aix-ppc64@0.21.5':
    resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
    engines: {node: '>=12'}
    cpu: [ppc64]
    os: [aix]

  '@esbuild/android-arm64@0.21.5':
    resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==}
    engines: {node: '>=12'}
    cpu: [arm64]
    os: [android]

  '@esbuild/android-arm@0.21.5':
    resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==}
    engines: {node: '>=12'}
    cpu: [arm]
    os: [android]

  '@esbuild/android-x64@0.21.5':
    resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [android]

  '@esbuild/darwin-arm64@0.21.5':
    resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==}
    engines: {node: '>=12'}
    cpu: [arm64]
    os: [darwin]

  '@esbuild/darwin-x64@0.21.5':
    resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [darwin]

  '@esbuild/freebsd-arm64@0.21.5':
    resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==}
    engines: {node: '>=12'}
    cpu: [arm64]
    os: [freebsd]

  '@esbuild/freebsd-x64@0.21.5':
    resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [freebsd]

  '@esbuild/linux-arm64@0.21.5':
    resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==}
    engines: {node: '>=12'}
    cpu: [arm64]
    os: [linux]

  '@esbuild/linux-arm@0.21.5':
    resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==}
    engines: {node: '>=12'}
    cpu: [arm]
    os: [linux]

  '@esbuild/linux-ia32@0.21.5':
    resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==}
    engines: {node: '>=12'}
    cpu: [ia32]
    os: [linux]

  '@esbuild/linux-loong64@0.21.5':
    resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==}
    engines: {node: '>=12'}
    cpu: [loong64]
    os: [linux]

  '@esbuild/linux-mips64el@0.21.5':
    resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==}
    engines: {node: '>=12'}
    cpu: [mips64el]
    os: [linux]

  '@esbuild/linux-ppc64@0.21.5':
    resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==}
    engines: {node: '>=12'}
    cpu: [ppc64]
    os: [linux]

  '@esbuild/linux-riscv64@0.21.5':
    resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==}
    engines: {node: '>=12'}
    cpu: [riscv64]
    os: [linux]

  '@esbuild/linux-s390x@0.21.5':
    resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==}
    engines: {node: '>=12'}
    cpu: [s390x]
    os: [linux]

  '@esbuild/linux-x64@0.21.5':
    resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [linux]

  '@esbuild/netbsd-x64@0.21.5':
    resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [netbsd]

  '@esbuild/openbsd-x64@0.21.5':
    resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [openbsd]

  '@esbuild/sunos-x64@0.21.5':
    resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [sunos]

  '@esbuild/win32-arm64@0.21.5':
    resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==}
    engines: {node: '>=12'}
    cpu: [arm64]
    os: [win32]

  '@esbuild/win32-ia32@0.21.5':
    resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==}
    engines: {node: '>=12'}
    cpu: [ia32]
    os: [win32]

  '@esbuild/win32-x64@0.21.5':
    resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==}
    engines: {node: '>=12'}
    cpu: [x64]
    os: [win32]

  '@jest/schemas@29.6.3':
    resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==}
    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}

  '@jridgewell/sourcemap-codec@1.5.5':
    resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}

  '@rollup/rollup-android-arm-eabi@4.59.0':
    resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==}
    cpu: [arm]
    os: [android]

  '@rollup/rollup-android-arm64@4.59.0':
    resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==}
    cpu: [arm64]
    os: [android]

  '@rollup/rollup-darwin-arm64@4.59.0':
    resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==}
    cpu: [arm64]
    os: [darwin]

  '@rollup/rollup-darwin-x64@4.59.0':
    resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==}
    cpu: [x64]
    os: [darwin]

  '@rollup/rollup-freebsd-arm64@4.59.0':
    resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==}
    cpu: [arm64]
    os: [freebsd]

  '@rollup/rollup-freebsd-x64@4.59.0':
    resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==}
    cpu: [x64]
    os: [freebsd]

  '@rollup/rollup-linux-arm-gnueabihf@4.59.0':
    resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==}
    cpu: [arm]
    os: [linux]
    libc: [glibc]

  '@rollup/rollup-linux-arm-musleabihf@4.59.0':
    resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==}
    cpu: [arm]
    os: [linux]
    libc: [musl]

  '@rollup/rollup-linux-arm64-gnu@4.59.0':
    resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==}
    cpu: [arm64]
    os: [linux]
    libc: [glibc]

  '@rollup/rollup-linux-arm64-musl@4.59.0':
    resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==}
    cpu: [arm64]
    os: [linux]
    libc: [musl]

  '@rollup/rollup-linux-loong64-gnu@4.59.0':
    resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==}
    cpu: [loong64]
    os: [linux]
    libc: [glibc]

  '@rollup/rollup-linux-loong64-musl@4.59.0':
    resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==}
    cpu: [loong64]
    os: [linux]
    libc: [musl]

  '@rollup/rollup-linux-ppc64-gnu@4.59.0':
    resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==}
    cpu: [ppc64]
    os: [linux]
    libc: [glibc]

  '@rollup/rollup-linux-ppc64-musl@4.59.0':
    resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==}
    cpu: [ppc64]
    os: [linux]
    libc: [musl]

  '@rollup/rollup-linux-riscv64-gnu@4.59.0':
    resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==}
    cpu: [riscv64]
    os: [linux]
    libc: [glibc]

  '@rollup/rollup-linux-riscv64-musl@4.59.0':
    resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==}
    cpu: [riscv64]
    os: [linux]
    libc: [musl]

  '@rollup/rollup-linux-s390x-gnu@4.59.0':
    resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==}
    cpu: [s390x]
    os: [linux]
    libc: [glibc]

  '@rollup/rollup-linux-x64-gnu@4.59.0':
    resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==}
    cpu: [x64]
    os: [linux]
    libc: [glibc]

  '@rollup/rollup-linux-x64-musl@4.59.0':
    resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==}
    cpu: [x64]
    os: [linux]
    libc: [musl]

  '@rollup/rollup-openbsd-x64@4.59.0':
    resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==}
    cpu: [x64]
    os: [openbsd]

  '@rollup/rollup-openharmony-arm64@4.59.0':
    resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==}
    cpu: [arm64]
    os: [openharmony]

  '@rollup/rollup-win32-arm64-msvc@4.59.0':
    resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==}
    cpu: [arm64]
    os: [win32]

  '@rollup/rollup-win32-ia32-msvc@4.59.0':
    resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==}
    cpu: [ia32]
    os: [win32]

  '@rollup/rollup-win32-x64-gnu@4.59.0':
    resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==}
    cpu: [x64]
    os: [win32]

  '@rollup/rollup-win32-x64-msvc@4.59.0':
    resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==}
    cpu: [x64]
    os: [win32]

  '@sinclair/typebox@0.27.10':
    resolution: {integrity: sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==}

  '@types/estree@1.0.8':
    resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}

  '@types/node@20.19.33':
    resolution: {integrity: sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw==}

  '@vitest/expect@1.6.1':
    resolution: {integrity: sha512-jXL+9+ZNIJKruofqXuuTClf44eSpcHlgj3CiuNihUF3Ioujtmc0zIa3UJOW5RjDK1YLBJZnWBlPuqhYycLioog==}

  '@vitest/runner@1.6.1':
    resolution: {integrity: sha512-3nSnYXkVkf3mXFfE7vVyPmi3Sazhb/2cfZGGs0JRzFsPFvAMBEcrweV1V1GsrstdXeKCTXlJbvnQwGWgEIHmOA==}

  '@vitest/snapshot@1.6.1':
    resolution: {integrity: sha512-WvidQuWAzU2p95u8GAKlRMqMyN1yOJkGHnx3M1PL9Raf7AQ1kwLKg04ADlCa3+OXUZE7BceOhVZiuWAbzCKcUQ==}

  '@vitest/spy@1.6.1':
    resolution: {integrity: sha512-MGcMmpGkZebsMZhbQKkAf9CX5zGvjkBTqf8Zx3ApYWXr3wG+QvEu2eXWfnIIWYSJExIp4V9FCKDEeygzkYrXMw==}

  '@vitest/utils@1.6.1':
    resolution: {integrity: sha512-jOrrUvXM4Av9ZWiG1EajNto0u96kWAhJ1LmPmJhXXQx/32MecEKd10pOLYgS2BQx1TgkGhloPU1ArDW2vvaY6g==}

  acorn-walk@8.3.5:
    resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==}
    engines: {node: '>=0.4.0'}

  acorn@8.16.0:
    resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==}
    engines: {node: '>=0.4.0'}
    hasBin: true

  ansi-styles@5.2.0:
    resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
    engines: {node: '>=10'}

  assertion-error@1.1.0:
    resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==}

  cac@6.7.14:
    resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
    engines: {node: '>=8'}

  chai@4.5.0:
    resolution: {integrity: sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==}
    engines: {node: '>=4'}

  check-error@1.0.3:
    resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==}

  confbox@0.1.8:
    resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==}

  cross-spawn@7.0.6:
    resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
    engines: {node: '>= 8'}

  debug@4.4.3:
    resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
    engines: {node: '>=6.0'}
    peerDependencies:
      supports-color: '*'
    peerDependenciesMeta:
      supports-color:
        optional: true

  deep-eql@4.1.4:
    resolution: {integrity: sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==}
    engines: {node: '>=6'}

  diff-sequences@29.6.3:
    resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==}
    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}

  esbuild@0.21.5:
    resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
    engines: {node: '>=12'}
    hasBin: true

  estree-walker@3.0.3:
    resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}

  execa@8.0.1:
    resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==}
    engines: {node: '>=16.17'}

  fsevents@2.3.3:
    resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
    engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
    os: [darwin]

  get-func-name@2.0.2:
    resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==}

  get-stream@8.0.1:
    resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==}
    engines: {node: '>=16'}

  human-signals@5.0.0:
    resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==}
    engines: {node: '>=16.17.0'}

  is-stream@3.0.0:
    resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==}
    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}

  isexe@2.0.0:
    resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}

  js-tokens@9.0.1:
    resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}

  local-pkg@0.5.1:
    resolution: {integrity: sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==}
    engines: {node: '>=14'}

  loupe@2.3.7:
    resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==}

  magic-string@0.30.21:
    resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}

  merge-stream@2.0.0:
    resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}

  mimic-fn@4.0.0:
    resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
    engines: {node: '>=12'}

  mlly@1.8.0:
    resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==}

  ms@2.1.3:
    resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}

  nanoid@3.3.11:
    resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
    hasBin: true

  npm-run-path@5.3.0:
    resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==}
    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}

  onetime@6.0.0:
    resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==}
    engines: {node: '>=12'}

  p-limit@5.0.0:
    resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==}
    engines: {node: '>=18'}

  path-key@3.1.1:
    resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
    engines: {node: '>=8'}

  path-key@4.0.0:
    resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==}
    engines: {node: '>=12'}

  pathe@1.1.2:
    resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==}

  pathe@2.0.3:
    resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}

  pathval@1.1.1:
    resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==}

  picocolors@1.1.1:
    resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}

  pkg-types@1.3.1:
    resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==}

  postcss@8.5.6:
    resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
    engines: {node: ^10 || ^12 || >=14}

  pretty-format@29.7.0:
    resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==}
    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}

  react-is@18.3.1:
    resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}

  rollup@4.59.0:
    resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==}
    engines: {node: '>=18.0.0', npm: '>=8.0.0'}
    hasBin: true

  shebang-command@2.0.0:
    resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
    engines: {node: '>=8'}

  shebang-regex@3.0.0:
    resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
    engines: {node: '>=8'}

  siginfo@2.0.0:
    resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}

  signal-exit@4.1.0:
    resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
    engines: {node: '>=14'}

  source-map-js@1.2.1:
    resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
    engines: {node: '>=0.10.0'}

  stackback@0.0.2:
    resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}

  std-env@3.10.0:
    resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==}

  strip-final-newline@3.0.0:
    resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==}
    engines: {node: '>=12'}

  strip-literal@2.1.1:
    resolution: {integrity: sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==}

  tinybench@2.9.0:
    resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}

  tinypool@0.8.4:
    resolution: {integrity: sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==}
    engines: {node: '>=14.0.0'}

  tinyspy@2.2.1:
    resolution: {integrity: sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==}
    engines: {node: '>=14.0.0'}

  type-detect@4.1.0:
    resolution: {integrity: sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==}
    engines: {node: '>=4'}

  typescript@5.9.3:
    resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
    engines: {node: '>=14.17'}
    hasBin: true

  ufo@1.6.3:
    resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==}

  undici-types@6.21.0:
    resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}

  vite-node@1.6.1:
    resolution: {integrity: sha512-YAXkfvGtuTzwWbDSACdJSg4A4DZiAqckWe90Zapc/sEX3XvHcw1NdurM/6od8J207tSDqNbSsgdCacBgvJKFuA==}
    engines: {node: ^18.0.0 || >=20.0.0}
    hasBin: true

  vite@5.4.21:
    resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==}
    engines: {node: ^18.0.0 || >=20.0.0}
    hasBin: true
    peerDependencies:
      '@types/node': ^18.0.0 || >=20.0.0
      less: '*'
      lightningcss: ^1.21.0
      sass: '*'
      sass-embedded: '*'
      stylus: '*'
      sugarss: '*'
      terser: ^5.4.0
    peerDependenciesMeta:
      '@types/node':
        optional: true
      less:
        optional: true
      lightningcss:
        optional: true
      sass:
        optional: true
      sass-embedded:
        optional: true
      stylus:
        optional: true
      sugarss:
        optional: true
      terser:
        optional: true

  vitest@1.6.1:
    resolution: {integrity: sha512-Ljb1cnSJSivGN0LqXd/zmDbWEM0RNNg2t1QW/XUhYl/qPqyu7CsqeWtqQXHVaJsecLPuDoak2oJcZN2QoRIOag==}
    engines: {node: ^18.0.0 || >=20.0.0}
    hasBin: true
    peerDependencies:
      '@edge-runtime/vm': '*'
      '@types/node': ^18.0.0 || >=20.0.0
      '@vitest/browser': 1.6.1
      '@vitest/ui': 1.6.1
      happy-dom: '*'
      jsdom: '*'
    peerDependenciesMeta:
      '@edge-runtime/vm':
        optional: true
      '@types/node':
        optional: true
      '@vitest/browser':
        optional: true
      '@vitest/ui':
        optional: true
      happy-dom:
        optional: true
      jsdom:
        optional: true

  which@2.0.2:
    resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
    engines: {node: '>= 8'}
    hasBin: true

  why-is-node-running@2.3.0:
    resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
    engines: {node: '>=8'}
    hasBin: true

  yocto-queue@1.2.2:
    resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==}
    engines: {node: '>=12.20'}

snapshots:

  '@esbuild/aix-ppc64@0.21.5':
    optional: true

  '@esbuild/android-arm64@0.21.5':
    optional: true

  '@esbuild/android-arm@0.21.5':
    optional: true

  '@esbuild/android-x64@0.21.5':
    optional: true

  '@esbuild/darwin-arm64@0.21.5':
    optional: true

  '@esbuild/darwin-x64@0.21.5':
    optional: true

  '@esbuild/freebsd-arm64@0.21.5':
    optional: true

  '@esbuild/freebsd-x64@0.21.5':
    optional: true

  '@esbuild/linux-arm64@0.21.5':
    optional: true

  '@esbuild/linux-arm@0.21.5':
    optional: true

  '@esbuild/linux-ia32@0.21.5':
    optional: true

  '@esbuild/linux-loong64@0.21.5':
    optional: true

  '@esbuild/linux-mips64el@0.21.5':
    optional: true

  '@esbuild/linux-ppc64@0.21.5':
    optional: true

  '@esbuild/linux-riscv64@0.21.5':
    optional: true

  '@esbuild/linux-s390x@0.21.5':
    optional: true

  '@esbuild/linux-x64@0.21.5':
    optional: true

  '@esbuild/netbsd-x64@0.21.5':
    optional: true

  '@esbuild/openbsd-x64@0.21.5':
    optional: true

  '@esbuild/sunos-x64@0.21.5':
    optional: true

  '@esbuild/win32-arm64@0.21.5':
    optional: true

  '@esbuild/win32-ia32@0.21.5':
    optional: true

  '@esbuild/win32-x64@0.21.5':
    optional: true

  '@jest/schemas@29.6.3':
    dependencies:
      '@sinclair/typebox': 0.27.10

  '@jridgewell/sourcemap-codec@1.5.5': {}

  '@rollup/rollup-android-arm-eabi@4.59.0':
    optional: true

  '@rollup/rollup-android-arm64@4.59.0':
    optional: true

  '@rollup/rollup-darwin-arm64@4.59.0':
    optional: true

  '@rollup/rollup-darwin-x64@4.59.0':
    optional: true

  '@rollup/rollup-freebsd-arm64@4.59.0':
    optional: true

  '@rollup/rollup-freebsd-x64@4.59.0':
    optional: true

  '@rollup/rollup-linux-arm-gnueabihf@4.59.0':
    optional: true

  '@rollup/rollup-linux-arm-musleabihf@4.59.0':
    optional: true

  '@rollup/rollup-linux-arm64-gnu@4.59.0':
    optional: true

  '@rollup/rollup-linux-arm64-musl@4.59.0':
    optional: true

  '@rollup/rollup-linux-loong64-gnu@4.59.0':
    optional: true

  '@rollup/rollup-linux-loong64-musl@4.59.0':
    optional: true

  '@rollup/rollup-linux-ppc64-gnu@4.59.0':
    optional: true

  '@rollup/rollup-linux-ppc64-musl@4.59.0':
    optional: true

  '@rollup/rollup-linux-riscv64-gnu@4.59.0':
    optional: true

  '@rollup/rollup-linux-riscv64-musl@4.59.0':
    optional: true

  '@rollup/rollup-linux-s390x-gnu@4.59.0':
    optional: true

  '@rollup/rollup-linux-x64-gnu@4.59.0':
    optional: true

  '@rollup/rollup-linux-x64-musl@4.59.0':
    optional: true

  '@rollup/rollup-openbsd-x64@4.59.0':
    optional: true

  '@rollup/rollup-openharmony-arm64@4.59.0':
    optional: true

  '@rollup/rollup-win32-arm64-msvc@4.59.0':
    optional: true

  '@rollup/rollup-win32-ia32-msvc@4.59.0':
    optional: true

  '@rollup/rollup-win32-x64-gnu@4.59.0':
    optional: true

  '@rollup/rollup-win32-x64-msvc@4.59.0':
    optional: true

  '@sinclair/typebox@0.27.10': {}

  '@types/estree@1.0.8': {}

  '@types/node@20.19.33':
    dependencies:
      undici-types: 6.21.0

  '@vitest/expect@1.6.1':
    dependencies:
      '@vitest/spy': 1.6.1
      '@vitest/utils': 1.6.1
      chai: 4.5.0

  '@vitest/runner@1.6.1':
    dependencies:
      '@vitest/utils': 1.6.1
      p-limit: 5.0.0
      pathe: 1.1.2

  '@vitest/snapshot@1.6.1':
    dependencies:
      magic-string: 0.30.21
      pathe: 1.1.2
      pretty-format: 29.7.0

  '@vitest/spy@1.6.1':
    dependencies:
      tinyspy: 2.2.1

  '@vitest/utils@1.6.1':
    dependencies:
      diff-sequences: 29.6.3
      estree-walker: 3.0.3
      loupe: 2.3.7
      pretty-format: 29.7.0

  acorn-walk@8.3.5:
    dependencies:
      acorn: 8.16.0

  acorn@8.16.0: {}

  ansi-styles@5.2.0: {}

  assertion-error@1.1.0: {}

  cac@6.7.14: {}

  chai@4.5.0:
    dependencies:
      assertion-error: 1.1.0
      check-error: 1.0.3
      deep-eql: 4.1.4
      get-func-name: 2.0.2
      loupe: 2.3.7
      pathval: 1.1.1
      type-detect: 4.1.0

  check-error@1.0.3:
    dependencies:
      get-func-name: 2.0.2

  confbox@0.1.8: {}

  cross-spawn@7.0.6:
    dependencies:
      path-key: 3.1.1
      shebang-command: 2.0.0
      which: 2.0.2

  debug@4.4.3:
    dependencies:
      ms: 2.1.3

  deep-eql@4.1.4:
    dependencies:
      type-detect: 4.1.0

  diff-sequences@29.6.3: {}

  esbuild@0.21.5:
    optionalDependencies:
      '@esbuild/aix-ppc64': 0.21.5
      '@esbuild/android-arm': 0.21.5
      '@esbuild/android-arm64': 0.21.5
      '@esbuild/android-x64': 0.21.5
      '@esbuild/darwin-arm64': 0.21.5
      '@esbuild/darwin-x64': 0.21.5
      '@esbuild/freebsd-arm64': 0.21.5
      '@esbuild/freebsd-x64': 0.21.5
      '@esbuild/linux-arm': 0.21.5
      '@esbuild/linux-arm64': 0.21.5
      '@esbuild/linux-ia32': 0.21.5
      '@esbuild/linux-loong64': 0.21.5
      '@esbuild/linux-mips64el': 0.21.5
      '@esbuild/linux-ppc64': 0.21.5
      '@esbuild/linux-riscv64': 0.21.5
      '@esbuild/linux-s390x': 0.21.5
      '@esbuild/linux-x64': 0.21.5
      '@esbuild/netbsd-x64': 0.21.5
      '@esbuild/openbsd-x64': 0.21.5
      '@esbuild/sunos-x64': 0.21.5
      '@esbuild/win32-arm64': 0.21.5
      '@esbuild/win32-ia32': 0.21.5
      '@esbuild/win32-x64': 0.21.5

  estree-walker@3.0.3:
    dependencies:
      '@types/estree': 1.0.8

  execa@8.0.1:
    dependencies:
      cross-spawn: 7.0.6
      get-stream: 8.0.1
      human-signals: 5.0.0
      is-stream: 3.0.0
      merge-stream: 2.0.0
      npm-run-path: 5.3.0
      onetime: 6.0.0
      signal-exit: 4.1.0
      strip-final-newline: 3.0.0

  fsevents@2.3.3:
    optional: true

  get-func-name@2.0.2: {}

  get-stream@8.0.1: {}

  human-signals@5.0.0: {}

  is-stream@3.0.0: {}

  isexe@2.0.0: {}

  js-tokens@9.0.1: {}

  local-pkg@0.5.1:
    dependencies:
      mlly: 1.8.0
      pkg-types: 1.3.1

  loupe@2.3.7:
    dependencies:
      get-func-name: 2.0.2

  magic-string@0.30.21:
    dependencies:
      '@jridgewell/sourcemap-codec': 1.5.5

  merge-stream@2.0.0: {}

  mimic-fn@4.0.0: {}

  mlly@1.8.0:
    dependencies:
      acorn: 8.16.0
      pathe: 2.0.3
      pkg-types: 1.3.1
      ufo: 1.6.3

  ms@2.1.3: {}

  nanoid@3.3.11: {}

  npm-run-path@5.3.0:
    dependencies:
      path-key: 4.0.0

  onetime@6.0.0:
    dependencies:
      mimic-fn: 4.0.0

  p-limit@5.0.0:
    dependencies:
      yocto-queue: 1.2.2

  path-key@3.1.1: {}

  path-key@4.0.0: {}

  pathe@1.1.2: {}

  pathe@2.0.3: {}

  pathval@1.1.1: {}

  picocolors@1.1.1: {}

  pkg-types@1.3.1:
    dependencies:
      confbox: 0.1.8
      mlly: 1.8.0
      pathe: 2.0.3

  postcss@8.5.6:
    dependencies:
      nanoid: 3.3.11
      picocolors: 1.1.1
      source-map-js: 1.2.1

  pretty-format@29.7.0:
    dependencies:
      '@jest/schemas': 29.6.3
      ansi-styles: 5.2.0
      react-is: 18.3.1

  react-is@18.3.1: {}

  rollup@4.59.0:
    dependencies:
      '@types/estree': 1.0.8
    optionalDependencies:
      '@rollup/rollup-android-arm-eabi': 4.59.0
      '@rollup/rollup-android-arm64': 4.59.0
      '@rollup/rollup-darwin-arm64': 4.59.0
      '@rollup/rollup-darwin-x64': 4.59.0
      '@rollup/rollup-freebsd-arm64': 4.59.0
      '@rollup/rollup-freebsd-x64': 4.59.0
      '@rollup/rollup-linux-arm-gnueabihf': 4.59.0
      '@rollup/rollup-linux-arm-musleabihf': 4.59.0
      '@rollup/rollup-linux-arm64-gnu': 4.59.0
      '@rollup/rollup-linux-arm64-musl': 4.59.0
      '@rollup/rollup-linux-loong64-gnu': 4.59.0
      '@rollup/rollup-linux-loong64-musl': 4.59.0
      '@rollup/rollup-linux-ppc64-gnu': 4.59.0
      '@rollup/rollup-linux-ppc64-musl': 4.59.0
      '@rollup/rollup-linux-riscv64-gnu': 4.59.0
      '@rollup/rollup-linux-riscv64-musl': 4.59.0
      '@rollup/rollup-linux-s390x-gnu': 4.59.0
      '@rollup/rollup-linux-x64-gnu': 4.59.0
      '@rollup/rollup-linux-x64-musl': 4.59.0
      '@rollup/rollup-openbsd-x64': 4.59.0
      '@rollup/rollup-openharmony-arm64': 4.59.0
      '@rollup/rollup-win32-arm64-msvc': 4.59.0
      '@rollup/rollup-win32-ia32-msvc': 4.59.0
      '@rollup/rollup-win32-x64-gnu': 4.59.0
      '@rollup/rollup-win32-x64-msvc': 4.59.0
      fsevents: 2.3.3

  shebang-command@2.0.0:
    dependencies:
      shebang-regex: 3.0.0

  shebang-regex@3.0.0: {}

  siginfo@2.0.0: {}

  signal-exit@4.1.0: {}

  source-map-js@1.2.1: {}

  stackback@0.0.2: {}

  std-env@3.10.0: {}

  strip-final-newline@3.0.0: {}

  strip-literal@2.1.1:
    dependencies:
      js-tokens: 9.0.1

  tinybench@2.9.0: {}

  tinypool@0.8.4: {}

  tinyspy@2.2.1: {}

  type-detect@4.1.0: {}

  typescript@5.9.3: {}

  ufo@1.6.3: {}

  undici-types@6.21.0: {}

  vite-node@1.6.1(@types/node@20.19.33):
    dependencies:
      cac: 6.7.14
      debug: 4.4.3
      pathe: 1.1.2
      picocolors: 1.1.1
      vite: 5.4.21(@types/node@20.19.33)
    transitivePeerDependencies:
      - '@types/node'
      - less
      - lightningcss
      - sass
      - sass-embedded
      - stylus
      - sugarss
      - supports-color
      - terser

  vite@5.4.21(@types/node@20.19.33):
    dependencies:
      esbuild: 0.21.5
      postcss: 8.5.6
      rollup: 4.59.0
    optionalDependencies:
      '@types/node': 20.19.33
      fsevents: 2.3.3

  vitest@1.6.1(@types/node@20.19.33):
    dependencies:
      '@vitest/expect': 1.6.1
      '@vitest/runner': 1.6.1
      '@vitest/snapshot': 1.6.1
      '@vitest/spy': 1.6.1
      '@vitest/utils': 1.6.1
      acorn-walk: 8.3.5
      chai: 4.5.0
      debug: 4.4.3
      execa: 8.0.1
      local-pkg: 0.5.1
      magic-string: 0.30.21
      pathe: 1.1.2
      picocolors: 1.1.1
      std-env: 3.10.0
      strip-literal: 2.1.1
      tinybench: 2.9.0
      tinypool: 0.8.4
      vite: 5.4.21(@types/node@20.19.33)
      vite-node: 1.6.1(@types/node@20.19.33)
      why-is-node-running: 2.3.0
    optionalDependencies:
      '@types/node': 20.19.33
    transitivePeerDependencies:
      - less
      - lightningcss
      - sass
      - sass-embedded
      - stylus
      - sugarss
      - supports-color
      - terser

  which@2.0.2:
    dependencies:
      isexe: 2.0.0

  why-is-node-running@2.3.0:
    dependencies:
      siginfo: 2.0.0
      stackback: 0.0.2

  yocto-queue@1.2.2: {}


path: src/__tests__/integration.test.ts
content: import { describe, it, expect, afterEach } from 'vitest';
import { ALMAAgent } from '../alma';
import { ObserverAgent } from '../observer';
import { MemoryIndexer } from '../indexer';
import { mkdirSync, writeFileSync, existsSync, rmSync } from 'fs';
import { join } from 'path';
import { tmpdir } from 'os';

const testDir = join(tmpdir(), 'oc-memory-test-' + Date.now());

afterEach(() => {
  if (existsSync(testDir)) rmSync(testDir, { recursive: true, force: true });
});

describe('ALMA', () => {
  it('proposes and evaluates designs', () => {
    const alma = new ALMAAgent({
      constraints: {
        chunkSize: { min: 100, max: 1000, type: 'number' },
        overlap: { min: 0, max: 0.5, type: 'number' },
      },
    });

    const d1 = alma.propose();
    expect(d1.id).toBeDefined();
    expect(d1.score).toBe(0);

    const score = alma.evaluate(d1.id, { accuracy: 0.8, speed: 0.9 });
    expect(score).toBeCloseTo(0.85);

    const best = alma.best();
    expect(best).toBeDefined();
    expect(best!.id).toBe(d1.id);

    alma.close();
  });

  it('mutates from a base design', () => {
    const alma = new ALMAAgent({
      constraints: { x: { min: 0, max: 1, type: 'number' } },
    });

    const d1 = alma.propose();
    alma.evaluate(d1.id, { score: 0.5 });

    const d2 = alma.propose(d1.id);
    expect(d2.id).not.toBe(d1.id);
    expect(d2.params).toBeDefined();

    alma.close();
  });

  it('tracks top designs', () => {
    const alma = new ALMAAgent({
      constraints: { x: { min: 0, max: 1, type: 'number' } },
    });

    const d1 = alma.propose();
    alma.evaluate(d1.id, { score: 0.3 });

    const d2 = alma.propose();
    alma.evaluate(d2.id, { score: 0.9 });

    const top = alma.top(2);
    expect(top.length).toBe(2);
    expect(top[0].score).toBeGreaterThanOrEqual(top[1].score);

    alma.close();
  });
});

describe('Observer', () => {
  it('creates observer without crashing', () => {
    const obs = new ObserverAgent({ provider: 'openai' });
    expect(obs).toBeDefined();
  });

  it('extract returns empty on LLM failure', async () => {
    const obs = new ObserverAgent({ provider: 'openai', [REDACTED_SECRET]' });
    const result = await obs.extract([
      { role: 'user', content: 'Alice likes TypeScript' },
    ]);
    expect(Array.isArray(result)).toBe(true);
  });
});

describe('Indexer', () => {
  it('indexes files and searches', () => {
    mkdirSync(testDir, { recursive: true });
    writeFileSync(join(testDir, 'MEMORY.md'), '# Memory\nAlice prefers TypeScript.\nBob likes Rust.\n');

    const idx = new MemoryIndexer({ workspace: testDir });
    const count = idx.index();
    expect(count).toBeGreaterThan(0);

    idx.close();
  });

  it('rebuilds index', () => {
    mkdirSync(testDir, { recursive: true });
    writeFileSync(join(testDir, 'MEMORY.md'), 'Some facts here.\n');

    const idx = new MemoryIndexer({ workspace: testDir });
    idx.index();
    const count = idx.rebuild();
    expect(count).toBeGreaterThan(0);

    idx.close();
  });
});


path: src/alma.ts
content: /**
 * ALMA — Algorithm Learning via Meta-learning Agents
 * Evolves memory system designs through mutation + evaluation.
 */

import { randomUUID } from 'crypto';
import { createDB, DB } from './db';

// --- Types ---

export interface DesignParams { [key: string]: number | string | boolean }

export interface Design {
  id: string;
  createdAt: Date;
  params: DesignParams;
  score: number;
  evals: number;
  isBest: boolean;
}

export interface Constraints {
  [key: string]: { min?: number; max?: number; type: 'number' | 'string' | 'boolean' };
}

export interface ALMAConfig {
  constraints?: Constraints;
  populationSize?: number;
  mutationRate?: number;
}

// --- Mutations ---

function gaussian(params: DesignParams, constraints: Constraints, std = 0.1): DesignParams {
  const out = { ...params };
  for (const [k, c] of Object.entries(constraints)) {
    if (c.type !== 'number') continue;
    const cur = (params[k] ?? 0) as number;
    const min = c.min ?? 0, max = c.max ?? 1;
    const u1 = Math.random(), u2 = Math.random();
    const z = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
    out[k] = Math.max(min, Math.min(max, cur + z * std * (max - min)));
  }
  return out;
}

function anneal(params: DesignParams, constraints: Constraints, temp = 0.5): DesignParams {
  const out = { ...params };
  for (const [k, c] of Object.entries(constraints)) {
    if (c.type !== 'number') continue;
    const min = c.min ?? 0, max = c.max ?? 1;
    if (Math.random() < temp) {
      out[k] = Math.random() * (max - min) + min;
    } else {
      out[k] = (params[k] as number ?? 0) + (Math.random() - 0.5) * (1 - temp);
    }
  }
  return out;
}

// --- Agent ---

export class ALMAAgent {
  private db: DB;
  private constraints: Constraints;
  readonly populationSize: number;
  readonly mutationRate: number;

  constructor(config: ALMAConfig = {}) {
    this.db = createDB();
    this.constraints = config.constraints || {};
    this.populationSize = config.populationSize || 20;
    this.mutationRate = config.mutationRate || 0.3;
    this.initSchema();
  }

  private initSchema() {
    this.db.exec(`
      CREATE TABLE IF NOT EXISTS designs (
        design_id TEXT PRIMARY KEY,
        created_at TEXT NOT NULL,
        parameters TEXT NOT NULL,
        score REAL DEFAULT 0.0,
        evals INTEGER DEFAULT 0,
        is_best INTEGER DEFAULT 0
      )
    `);
    this.db.exec(`
      CREATE TABLE IF NOT EXISTS evaluations (
        id INTEGER PRIMARY KEY,
        design_id TEXT NOT NULL,
        score REAL NOT NULL,
        metrics TEXT NOT NULL,
        ts TEXT DEFAULT ''
      )
    `);
  }

  propose(baseId?: string): Design {
    const id = randomUUID().slice(0, 8);
    const now = new Date();

    let params: DesignParams;
    if (baseId) {
      const row = this.db.get('SELECT parameters FROM designs WHERE design_id = ?', baseId);
      const base = row ? JSON.parse(row.parameters) : {};
      params = Math.random() < 0.5
        ? gaussian(base, this.constraints)
        : anneal(base, this.constraints, 0.2);
    } else {
      params = this.randomParams();
    }

    this.db.run(
      'INSERT INTO designs (design_id, created_at, parameters, score, evals, is_best) VALUES (?, ?, ?, ?, ?, ?)',
      id, now.toISOString(), JSON.stringify(params), 0, 0, 0
    );

    return { id, createdAt: now, params, score: 0, evals: 0, isBest: false };
  }

  evaluate(designId: string, metrics: Record<string, number>): number {
    const vals = Object.values(metrics);
    const score = vals.length ? vals.reduce((a, b) => a + b) / vals.length : 0;

    this.db.run(
      'INSERT INTO evaluations (design_id, score, metrics, ts) VALUES (?, ?, ?, ?)',
      designId, score, JSON.stringify(metrics), new Date().toISOString()
    );
    this.db.run('UPDATE designs SET score = ?, evals = evals + 1 WHERE design_id = ?', score, designId);

    // Mark best
    const best = this.db.get('SELECT MAX(score) as max_score FROM designs');
    if (best?.max_score === score) {
      this.db.run('UPDATE designs SET is_best = 0 WHERE is_best = ?', 1);
      this.db.run('UPDATE designs SET is_best = 1 WHERE design_id = ?', designId);
    }

    return score;
  }

  best(): Design | null {
    const row = this.db.get('SELECT * FROM designs WHERE is_best = 1');
    return row ? this.toDesign(row) : null;
  }

  top(k = 5): Design[] {
    return this.db.all('SELECT * FROM designs ORDER BY score DESC LIMIT ?', k).map(this.toDesign);
  }

  close() { this.db.close(); }

  private toDesign(row: any): Design {
    return {
      id: row.design_id,
      createdAt: new Date(row.created_at),
      params: JSON.parse(row.parameters),
      score: row.score,
      evals: row.evals,
      isBest: row.is_best === 1,
    };
  }

  private randomParams(): DesignParams {
    const p: DesignParams = {};
    for (const [k, c] of Object.entries(this.constraints)) {
      if (c.type === 'number') p[k] = Math.random() * ((c.max ?? 1) - (c.min ?? 0)) + (c.min ?? 0);
      else if (c.type === 'boolean') p[k] = Math.random() > 0.5;
    }
    return p;
  }
}


path: src/db.ts
content: /**
 * Database — thin wrapper over sql.js (pure JS SQLite)
 * No native modules. No compilation. Works everywhere.
 */

export interface DB {
  run(sql: string, ...params: any[]): void;
  get(sql: string, ...params: any[]): any;
  all(sql: string, ...params: any[]): any[];
  exec(sql: string): void;
  close(): void;
}

/** In-memory SQLite via sql.js, or a simple Map fallback */
export function createDB(): DB {
  const tables = new Map<string, { cols: string[]; rows: any[] }>();

  return {
    run(sql: string, ...params: any[]) {
      execute(tables, sql, params);
    },
    get(sql: string, ...params: any[]) {
      return query(tables, sql, params, true);
    },
    all(sql: string, ...params: any[]) {
      return query(tables, sql, params, false);
    },
    exec(sql: string) {
      for (const stmt of sql.split(';').filter(s => s.trim())) {
        execute(tables, stmt, []);
      }
    },
    close() {
      tables.clear();
    },
  };
}

function execute(tables: Map<string, any>, sql: string, params: any[]): void {
  const up = sql.toUpperCase().trim();

  if (up.startsWith('CREATE')) {
    const m = sql.match(/CREATE\s+(?:VIRTUAL\s+)?TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?(\w+)/i);
    if (m && !tables.has(m[1])) {
      tables.set(m[1], { cols: [], rows: [] });
    }
    return;
  }

  if (up.startsWith('INSERT') || up.startsWith('REPLACE')) {
    const m = sql.match(/(?:INSERT|REPLACE)\s+(?:OR\s+\w+\s+)?INTO\s+(\w+)\s*\(([^)]+)\)/i);
    if (m && params.length) {
      const t = tables.get(m[1]);
      if (!t) return;
      const cols = m[2].split(',').map(c => c.trim());
      const row: any = {};
      cols.forEach((c, i) => row[c] = params[i]);
      if (up.includes('OR REPLACE')) {
        t.rows = t.rows.filter((r: any) => r[cols[0]] !== params[0]);
      }
      t.rows.push(row);
    }
    return;
  }

  if (up.startsWith('UPDATE')) {
    const m = sql.match(/UPDATE\s+(\w+)\s+SET\s+(.+?)\s+WHERE\s+(\w+)\s*=\s*\?/i);
    if (m && params.length) {
      const t = tables.get(m[1]);
      if (!t) return;
      const sets = m[2].split(',').map(s => s.trim().split(/\s*=\s*/)[0]);
      const whereVal = params[params.length - 1];
      for (const row of t.rows) {
        if (row[m[3]] === whereVal) {
          sets.forEach((col, i) => { if (params[i] !== undefined) row[col] = params[i]; });
        }
      }
    }
    return;
  }

  if (up.startsWith('DROP')) {
    const m = sql.match(/DROP\s+TABLE\s+(?:IF\s+EXISTS\s+)?(\w+)/i);
    if (m) tables.delete(m[1]);
    return;
  }
}

function query(tables: Map<string, any>, sql: string, params: any[], single: boolean): any {
  const m = sql.match(/SELECT\s+.+?\s+FROM\s+(\w+)/i);
  if (!m) return single ? null : [];

  const t = tables.get(m[1]);
  if (!t) return single ? null : [];

  let rows = [...t.rows];

  // WHERE col = ?
  const w = sql.match(/WHERE\s+(\w+)\s*=\s*\?/i);
  if (w && params.length) rows = rows.filter((r: any) => r[w[1]] === params[0]);

  // ORDER BY col DESC
  const o = sql.match(/ORDER\s+BY\s+(\w+)\s+(ASC|DESC)?/i);
  if (o) {
    const desc = o[2]?.toUpperCase() === 'DESC';
    rows.sort((a: any, b: any) => desc ? (b[o[1]] - a[o[1]]) : (a[o[1]] - b[o[1]]));
  }

  // LIMIT
  const l = sql.match(/LIMIT\s+(\?|\d+)/i);
  if (l) {
    const lim = l[1] === '?' ? (params[params.length - 1] ?? 10) : parseInt(l[1]);
    rows = rows.slice(0, lim);
  }

  // MAX() aggregate
  if (sql.match(/MAX\(/i) && rows.length) {
    const col = sql.match(/MAX\((\w+)\)/i);
    if (col) {
      const max = Math.max(...t.rows.map((r: any) => r[col[1]] ?? 0));
      return single ? { max_score: max } : [{ max_score: max }];
    }
  }

  return single ? (rows[0] ?? null) : rows;
}


path: src/index.ts
content: export { ALMAAgent, type Design, type ALMAConfig, type Constraints, type DesignParams } from './alma';
export { ObserverAgent, type Observation, type ObserverConfig, type Message } from './observer';
export { MemoryIndexer, type Chunk, type IndexerConfig } from './indexer';
export { createDB, type DB } from './db';


path: src/indexer.ts
content: /**
 * Indexer — FTS5 full-text search over workspace Markdown files.
 * Chunks files, indexes them, returns ranked results.
 */

import { readFileSync, existsSync, readdirSync } from 'fs';
import { join } from 'path';
import { createDB, DB } from './db';

export interface Chunk {
  id: string;
  file: string;
  lineStart: number;
  lineEnd: number;
  text: string;
  score?: number;
}

export interface IndexerConfig {
  workspace: string;
  chunkSize?: number;
}

export class MemoryIndexer {
  private db: DB;
  private workspace: string;
  private chunkSize: number;

  constructor(config: IndexerConfig) {
    this.workspace = config.workspace;
    this.chunkSize = config.chunkSize || 400;
    this.db = createDB();
    this.db.exec(`
      CREATE VIRTUAL TABLE IF NOT EXISTS chunks USING fts5(
        file UNINDEXED, line_start UNINDEXED, line_end UNINDEXED, content,
        tokenize = 'unicode61'
      )
    `);
  }

  /** Index all memory files in workspace */
  index(): number {
    let total = 0;
    const files = this.findMemoryFiles();
    for (const file of files) {
      total += this.indexFile(file);
    }
    return total;
  }

  /** Index a single file */
  indexFile(filePath: string): number {
    if (!existsSync(filePath)) return 0;
    const content = readFileSync(filePath, 'utf-8');
    const chunks = this.chunk(content);
    for (const c of chunks) {
      this.db.run(
        'INSERT INTO chunks (file, line_start, line_end, content) VALUES (?, ?, ?, ?)',
        filePath, c.lineStart, c.lineEnd, c.text
      );
    }
    return chunks.length;
  }

  /** Search indexed memories */
  search(query: string, k = 5): Chunk[] {
    return this.db.all(
      'SELECT rowid, file, line_start, line_end, content, rank FROM chunks WHERE content MATCH ? ORDER BY rank LIMIT ?',
      query, k
    ).map((r: any) => ({
      id: String(r.rowid ?? ''),
      file: r.file,
      lineStart: r.line_start,
      lineEnd: r.line_end,
      text: r.content,
      score: Math.abs(r.rank ?? 0),
    }));
  }

  /** Rebuild index from scratch */
  rebuild(): number {
    this.db.exec('DROP TABLE IF EXISTS chunks');
    this.db.exec(`
      CREATE VIRTUAL TABLE IF NOT EXISTS chunks USING fts5(
        file UNINDEXED, line_start UNINDEXED, line_end UNINDEXED, content,
        tokenize = 'unicode61'
      )
    `);
    return this.index();
  }

  close() { this.db.close(); }

  private findMemoryFiles(): string[] {
    const files: string[] = [];

    const memoryMd = join(this.workspace, 'MEMORY.md');
    if (existsSync(memoryMd)) files.push(memoryMd);

    const memoryDir = join(this.workspace, 'memory');
    if (existsSync(memoryDir)) {
      for (const f of readdirSync(memoryDir)) {
        if (f.match(/^\d{4}-\d{2}-\d{2}\.md$/)) files.push(join(memoryDir, f));
      }
    }

    const entDir = join(this.workspace, 'bank', 'entities');
    if (existsSync(entDir)) {
      for (const f of readdirSync(entDir)) {
        if (f.endsWith('.md')) files.push(join(entDir, f));
      }
    }

    const opinions = join(this.workspace, 'bank', 'opinions.md');
    if (existsSync(opinions)) files.push(opinions);

    return files;
  }

  private chunk(text: string): Array<{ text: string; lineStart: number; lineEnd: number }> {
    const lines = text.split('\n');
    const chunks: Array<{ text: string; lineStart: number; lineEnd: number }> = [];
    const step = Math.max(1, Math.floor(this.chunkSize * 0.8));
    for (let i = 0; i < lines.length; i += step) {
      const end = Math.min(i + this.chunkSize, lines.length);
      chunks.push({ text: lines.slice(i, end).join('\n'), lineStart: i + 1, lineEnd: end });
    }
    return chunks;
  }
}


path: src/observer.ts
content: /**
 * Observer — Extract structured facts from conversations via LLM.
 * Supports OpenAI, Anthropic, Gemini. Fails gracefully.
 */

export type ObservationKind = 'world' | 'biographical' | 'opinion' | 'observation';

export interface Observation {
  kind: ObservationKind;
  timestamp: Date;
  entities: string[];
  content: string;
  source: string;
  priority: 'high' | 'medium' | 'low';
  confidence?: number;
}

export interface ObserverConfig {
  provider: 'openai' | 'anthropic' | 'gemini';
  model?: string;
  apiKey?: string;
}

export interface Message {
  role: string;
  content: string;
  timestamp?: Date;
}

const EXTRACTION_PROMPT = `Extract structured facts from this conversation.
For each fact, output one line:
KIND|PRIORITY|ENTITIES|CONTENT

KIND: W (world fact), B (biographical), O (opinion), S (observation)
PRIORITY: H (high), M (medium), L (low)
ENTITIES: comma-separated @mentions
CONTENT: the fact itself

Example:
W|H|@Alice,@Bob|Alice and Bob are siblings
O|M|@Alice|Alice prefers TypeScript over Python (confidence: 0.8)

Conversation:
`;

export class ObserverAgent {
  private config: ObserverConfig;

  constructor(config: ObserverConfig) {
    this.config = config;
  }

  async extract(messages: Message[], source = 'conversation'): Promise<Observation[]> {
    const prompt = EXTRACTION_PROMPT + messages.map(m => `${m.role}: ${m.content}`).join('\n');

    let response: string;
    try {
      response = await this.callLLM(prompt);
    } catch (err) {
      console.warn('[memory] LLM extraction failed:', (err as Error).message);
      return [];
    }

    return this.parseResponse(response, source);
  }

  private async callLLM(prompt: string): Promise<string> {
    const key = this.config.apiKey || process.env.OPENAI_API_KEY || process.env.ANTHROPIC_API_KEY || '';

    if (this.config.provider === 'openai') {
      const res = await fetch('https://api.openai.com/v1/chat/completions', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${key}` },
        body: JSON.stringify({
          model: this.config.model || 'gpt-4o-mini',
          messages: [{ role: 'user', content: prompt }],
          max_tokens: 2000,
        }),
      });
      const data = await res.json() as any;
      return data.choices?.[0]?.message?.content || '';
    }

    if (this.config.provider === 'anthropic') {
      const res = await fetch('https://api.anthropic.com/v1/messages', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'x-api-key': key,
          'anthropic-version': '2023-06-01',
        },
        body: JSON.stringify({
          model: this.config.model || 'claude-3-5-haiku-20241022',
          max_tokens: 2000,
          messages: [{ role: 'user', content: prompt }],
        }),
      });
      const data = await res.json() as any;
      return data.content?.[0]?.text || '';
    }

    // Gemini
    const res = await fetch(
      `https://generativelanguage.googleapis.com/v1beta/models/${this.config.model || 'gemini-2.0-flash'}:generateContent?key=${key}`,
      {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ contents: [{ parts: [{ text: prompt }] }] }),
      }
    );
    const data = await res.json() as any;
    return data.candidates?.[0]?.content?.parts?.[0]?.text || '';
  }

  private parseResponse(text: string, source: string): Observation[] {
    const kindMap: Record<string, ObservationKind> = { W: 'world', B: 'biographical', O: 'opinion', S: 'observation' };
    const prioMap: Record<string, 'high' | 'medium' | 'low'> = { H: 'high', M: 'medium', L: 'low' };
    const obs: Observation[] = [];

    for (const line of text.split('\n')) {
      const parts = line.split('|').map(s => s.trim());
      if (parts.length < 4) continue;

      const kind = kindMap[parts[0]];
      const priority = prioMap[parts[1]];
      if (!kind || !priority) continue;

      const entities = parts[2].split(',').map(e => e.trim().replace(/^@/, '')).filter(Boolean);
      const content = parts.slice(3).join('|');

      const confMatch = content.match(/confidence:\s*([\d.]+)/i);

      obs.push({
        kind,
        timestamp: new Date(),
        entities,
        content: content.replace(/\(confidence:.*?\)/i, '').trim(),
        source,
        priority,
        confidence: confMatch ? parseFloat(confMatch[1]) : undefined,
      });
    }

    return obs;
  }
}


path: tsconfig.json
content: {
  "compilerOptions": {
    "target": "ES2020",
    "module": "CommonJS",
    "lib": ["ES2020"],
    "declaration": true,
    "declarationMap": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "sourceMap": true,
    "noImplicitAny": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist", "**/*.test.ts"]
}


Security scan verdicts — ClawScan: suspicious | static analysis: suspicious | VirusTotal: suspicious | SkillSpector: suspicious (MEDIUM)