From 3960764522b02bbd49f4e3c664963e259f611e0a Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 8 Apr 2026 08:57:06 -0400 Subject: [PATCH 1/2] feat(paperclip): replace library with Paperclip plugin (v0.2.0) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the @vectorize-io/hindsight-paperclip npm library with a proper Paperclip plugin. Works with all adapter types (Claude, Codex, Cursor, HTTP, Process) via the event system — no code changes required by operators. - Auto-recalls on agent.run.started, auto-retains on agent.run.finished - hindsight_recall and hindsight_retain agent tools for mid-run access - onValidateConfig with live connectivity check - 15 tests passing --- hindsight-docs/docs-integrations/paperclip.md | 176 +- .../pages/changelog/integrations/paperclip.md | 15 + hindsight-integrations/paperclip/README.md | 170 +- .../paperclip/esbuild.config.mjs | 27 + .../paperclip/package-lock.json | 2670 +++++++++-------- hindsight-integrations/paperclip/package.json | 30 +- hindsight-integrations/paperclip/src/bank.ts | 42 +- .../paperclip/src/client.ts | 74 +- .../paperclip/src/config.ts | 49 - hindsight-integrations/paperclip/src/index.ts | 36 - .../paperclip/src/manifest.ts | 102 + .../paperclip/src/middleware.ts | 97 - .../paperclip/src/recall.ts | 79 - .../paperclip/src/retain.ts | 58 - .../paperclip/src/worker.ts | 255 ++ .../paperclip/tests/bank.test.ts | 44 - .../paperclip/tests/plugin.spec.ts | 338 +++ .../paperclip/tests/recall.test.ts | 118 - .../paperclip/tests/retain.test.ts | 112 - 19 files changed, 2271 insertions(+), 2221 deletions(-) create mode 100644 hindsight-integrations/paperclip/esbuild.config.mjs delete mode 100644 hindsight-integrations/paperclip/src/config.ts delete mode 100644 hindsight-integrations/paperclip/src/index.ts create mode 100644 hindsight-integrations/paperclip/src/manifest.ts delete mode 100644 hindsight-integrations/paperclip/src/middleware.ts delete mode 100644 hindsight-integrations/paperclip/src/recall.ts delete mode 100644 hindsight-integrations/paperclip/src/retain.ts create mode 100644 hindsight-integrations/paperclip/src/worker.ts delete mode 100644 hindsight-integrations/paperclip/tests/bank.test.ts create mode 100644 hindsight-integrations/paperclip/tests/plugin.spec.ts delete mode 100644 hindsight-integrations/paperclip/tests/recall.test.ts delete mode 100644 hindsight-integrations/paperclip/tests/retain.test.ts diff --git a/hindsight-docs/docs-integrations/paperclip.md b/hindsight-docs/docs-integrations/paperclip.md index 79a954a3a8..15bd0a759a 100644 --- a/hindsight-docs/docs-integrations/paperclip.md +++ b/hindsight-docs/docs-integrations/paperclip.md @@ -1,163 +1,87 @@ --- sidebar_position: 11 title: "Paperclip Persistent Memory with Hindsight | Integration Guide" -description: "Add long-term memory to Paperclip agents with Hindsight. Retain, recall, and reflect memories across sessions using the Paperclip integration." +description: "Add long-term memory to all Paperclip agents with Hindsight. Install once as a plugin — every agent gets automatic recall before runs and retain after runs." --- # Paperclip Persistent memory for [Paperclip AI](https://github.com/paperclipai/paperclip) agents using [Hindsight](https://hindsight.vectorize.io). -Paperclip agents start every heartbeat cold — no memory of prior sessions, decisions, or patterns. The `@vectorize-io/hindsight-paperclip` package gives them long-term memory that persists across heartbeats and sessions. +Install the `@vectorize-io/hindsight-paperclip` plugin once. Every agent in your Paperclip instance automatically gets long-term memory that persists across runs, companies, and restarts — no code changes required. -## Quick Start +## Installation ```bash -npm install @vectorize-io/hindsight-paperclip +pnpm paperclipai plugin install @vectorize-io/hindsight-paperclip ``` -```typescript -import { recall, retain, loadConfig } from '@vectorize-io/hindsight-paperclip' - -const config = loadConfig() // reads HINDSIGHT_API_URL, HINDSIGHT_API_TOKEN - -// Before the heartbeat — inject context from prior sessions -const memories = await recall({ - companyId, - agentId, - query: `${task.title}\n${task.description}`, -}, config) - -if (memories) { - systemPrompt = `Past context:\n${memories}\n\n${systemPrompt}` -} - -// After the heartbeat — store what the agent did -await retain({ - companyId, - agentId, - content: agentOutput, - documentId: runId, -}, config) -``` +Then configure in **Settings → Plugins → Hindsight Memory**. -Get an API key at [Hindsight Cloud](https://ui.hindsight.vectorize.io/signup). +## Prerequisites -## How It Works +Either: +```bash +# Self-hosted +pip install hindsight-all +export HINDSIGHT_API_LLM_API_KEY=your-openai-key +hindsight-api ``` -Paperclip Heartbeat - │ - ▼ - recall() ← Query Hindsight for prior context - │ - ▼ - Agent executes ← Prompt enriched with memories - │ - ▼ - retain() ← Store output for future heartbeats -``` - -Memory is isolated per company and agent by default (`paperclip::{companyId}::{agentId}`), matching Paperclip's multi-tenant model. - -## HTTP Adapter Integration -For agents running as HTTP webhook servers, use the Express middleware: +Or [Hindsight Cloud](https://ui.hindsight.vectorize.io/signup) — no self-hosting required. -```typescript -import express from 'express' -import { createMemoryMiddleware, loadConfig } from '@vectorize-io/hindsight-paperclip' -import type { HindsightRequest } from '@vectorize-io/hindsight-paperclip' - -const app = express() -app.use(express.json()) -app.use(createMemoryMiddleware(loadConfig())) - -app.post('/heartbeat', async (req, res) => { - const { memories } = (req as HindsightRequest).hindsight - const { context } = req.body - - const prompt = memories - ? `Past context:\n${memories}\n\nCurrent task: ${context.taskDescription}` - : `Task: ${context.taskDescription}` +## How It Works - const output = await runYourAgent(prompt) - res.json({ output }) // output is auto-retained by middleware -}) ``` +agent.run.started + └─ recall(issueTitle + description) + └─ cached in plugin state for this run -The middleware reads `agentId`, `companyId`, `runId`, and `context.taskDescription` from Paperclip's HTTP adapter request body, then auto-retains the agent's `output` field after each response. - -## Process Adapter Integration +agent running… + ├─ hindsight_recall(query) → returns cached context or live recall + └─ hindsight_retain(content) → stores immediately -For agents running as scripts via Paperclip's Process adapter: - -```typescript -import { recall, retain, loadConfig } from '@vectorize-io/hindsight-paperclip' - -const config = loadConfig() -const { PAPERCLIP_AGENT_ID, PAPERCLIP_COMPANY_ID, PAPERCLIP_RUN_ID } = process.env - -const memories = await recall({ - agentId: PAPERCLIP_AGENT_ID!, - companyId: PAPERCLIP_COMPANY_ID!, - query: process.env.TASK_DESCRIPTION ?? '', -}, config) - -if (memories) { - console.log(`[Memory Context]\n${memories}`) -} - -// ... agent executes ... - -await retain({ - agentId: PAPERCLIP_AGENT_ID!, - companyId: PAPERCLIP_COMPANY_ID!, - content: agentOutput, - documentId: PAPERCLIP_RUN_ID!, -}, config) +agent.run.finished + └─ retain(output) → stored with runId as document_id ``` -## Bank ID Isolation +Memory is keyed to `companyId` + `agentId` — never to the run ID — so it accumulates across every run. -By default, each company+agent pair gets its own memory bank: - -| Setting | Bank ID format | -|---|---| -| Default | `paperclip::{companyId}::{agentId}` | -| Company-only | `paperclip::{companyId}` | -| Agent-only | `paperclip::{agentId}` | -| Custom prefix | `{prefix}::{companyId}::{agentId}` | +## Configuration -```typescript -// Shared memory across all agents in a company -loadConfig({ bankGranularity: ['company'] }) +| Field | Default | Description | +|-------|---------|-------------| +| `hindsightApiUrl` | `http://localhost:8888` | Hindsight server URL | +| `hindsightApiKeyRef` | — | Paperclip secret name holding Hindsight Cloud API key | +| `bankGranularity` | `["company", "agent"]` | Memory isolation: per company+agent, per company, or per agent | +| `recallBudget` | `mid` | `low` = fastest, `mid` = balanced, `high` = most thorough | +| `autoRetain` | `true` | Automatically retain run output after every run | -// Agent's global memory across all companies -loadConfig({ bankGranularity: ['agent'] }) +## Bank ID Format -// Custom prefix -loadConfig({ bankIdPrefix: 'myapp' }) +``` +paperclip::{companyId}::{agentId} ← default (company + agent granularity) +paperclip::{companyId} ← company granularity (shared across agents) +paperclip::{agentId} ← agent granularity (agent memory across companies) ``` -## Configuration +## Agent Tools + +Agents can call these tools directly during a run: -| Option | Env Variable | Default | Description | -|---|---|---|---| -| `hindsightApiUrl` | `HINDSIGHT_API_URL` | Required | Hindsight server URL | -| `hindsightApiToken` | `HINDSIGHT_API_TOKEN` | — | API token for Hindsight Cloud | -| `bankGranularity` | — | `['company', 'agent']` | Which IDs to include in the bank ID | -| `bankIdPrefix` | — | `'paperclip'` | Prefix for bank IDs | -| `recallBudget` | — | `'mid'` | Search depth: `low`, `mid`, or `high` | -| `recallMaxTokens` | — | `1024` | Max tokens in recalled memory block | -| `retainContext` | — | `'paperclip'` | Provenance label stored with memories | -| `timeoutMs` | — | `15000` | Request timeout in milliseconds | +**`hindsight_recall(query)`** — search memory for relevant context. Called automatically at run start; agents can also call it mid-run for targeted queries. -## Skill File +**`hindsight_retain(content)`** — store a fact or decision immediately, without waiting for run end. -A markdown skill file is included at `src/skills/hindsight.md`. Inject it into your agent's system prompt to give the agent direct access to Hindsight's REST API via `curl` for mid-task recall and retention. +## Adapter Compatibility -## Requirements +Works with all Paperclip adapter types via the event system: -- Node.js 20+ (uses native `fetch`, no external HTTP dependencies) -- Hindsight server (self-hosted or [Hindsight Cloud](https://hindsight.vectorize.io)) +| Adapter | Supported | +|---------|-----------| +| Claude | ✓ | +| Codex | ✓ | +| Cursor | ✓ | +| HTTP | ✓ | +| Process | ✓ | diff --git a/hindsight-docs/src/pages/changelog/integrations/paperclip.md b/hindsight-docs/src/pages/changelog/integrations/paperclip.md index 24e4f95e96..45d5db03b7 100644 --- a/hindsight-docs/src/pages/changelog/integrations/paperclip.md +++ b/hindsight-docs/src/pages/changelog/integrations/paperclip.md @@ -10,6 +10,21 @@ For the source code, see [`hindsight-integrations/paperclip`](https://github.com ← [Back to main changelog](/changelog) +## [0.2.0](https://github.com/vectorize-io/hindsight/tree/integrations/paperclip/v0.2.0) + +**Breaking Changes** + +- Rewritten as a proper Paperclip plugin (installed via `pnpm paperclipai plugin install`). No code changes required — memory hooks run automatically via the event system. +- Works with all adapter types (Claude, Codex, Cursor, HTTP, Process). Previously required manual `recall()`/`retain()` calls and only supported HTTP adapter agents. + +**Features** + +- `agent.run.started` hook: auto-recalls context keyed to issue title + description +- `agent.run.finished` hook: auto-retains agent output with `runId` as document ID +- `hindsight_recall` and `hindsight_retain` agent tools for mid-run memory access +- `onValidateConfig`: live connectivity check when operator saves settings +- Configurable bank granularity (company+agent, company-only, agent-only) + ## [0.1.1](https://github.com/vectorize-io/hindsight/tree/integrations/paperclip/v0.1.1) **Features** diff --git a/hindsight-integrations/paperclip/README.md b/hindsight-integrations/paperclip/README.md index ed408e1886..dca083997e 100644 --- a/hindsight-integrations/paperclip/README.md +++ b/hindsight-integrations/paperclip/README.md @@ -1,153 +1,91 @@ # @vectorize-io/hindsight-paperclip -Persistent memory for [Paperclip AI](https://github.com/paperclipai/paperclip) agents using [Hindsight](https://hindsight.vectorize.io). +Persistent long-term memory for Paperclip agents via [Hindsight](https://github.com/vectorize-io/hindsight). -Paperclip agents start every heartbeat cold — no memory of prior sessions, decisions, or patterns. This package gives them long-term memory that persists across heartbeats and sessions. +Install once. Every agent in your Paperclip instance gets memory that persists across runs, companies, and restarts. -## How It Works - -1. **Before each heartbeat**: `recall()` queries Hindsight for context relevant to the current task and injects it into the agent's prompt -2. **After each heartbeat**: `retain()` stores the agent's output so future heartbeats can reference it +## What It Does -Memory is isolated per company and agent by default (`paperclip::{companyId}::{agentId}`), matching Paperclip's multi-tenant model. +- **Before each run** — recalls relevant memories from past runs and caches them for the agent +- **After each run** — retains the agent's output to Hindsight automatically +- **Agent tools** — `hindsight_recall` and `hindsight_retain` tools for agents to query and store memory mid-run ## Installation ```bash -npm install @vectorize-io/hindsight-paperclip +pnpm paperclipai plugin install @vectorize-io/hindsight-paperclip ``` -## Configuration - -Set environment variables (or pass as options to `loadConfig()`): +Then configure in **Settings → Plugins → Hindsight Memory**. -| Variable | Description | Default | -|---|---|---| -| `HINDSIGHT_API_URL` | Hindsight server URL | Required | -| `HINDSIGHT_API_TOKEN` | API token for Hindsight Cloud | — | +## Prerequisites -## Usage +Either: -### HTTP Adapter Agents (Express middleware) +```bash +# Self-hosted (runs locally) +pip install hindsight-all +export HINDSIGHT_API_LLM_API_KEY=your-openai-key +hindsight-api +``` -```typescript -import express from 'express' -import { createMemoryMiddleware, loadConfig } from '@vectorize-io/hindsight-paperclip' -import type { HindsightRequest } from '@vectorize-io/hindsight-paperclip' +Or [Hindsight Cloud](https://ui.hindsight.vectorize.io/signup) — no self-hosting required. -const app = express() -app.use(express.json()) -app.use(createMemoryMiddleware(loadConfig())) +## Configuration -app.post('/heartbeat', async (req, res) => { - const { memories, runId } = (req as HindsightRequest).hindsight - const { context } = req.body +| Field | Default | Description | +|-------|---------|-------------| +| `hindsightApiUrl` | `http://localhost:8888` | Hindsight server URL | +| `hindsightApiKeyRef` | — | Paperclip secret name holding Hindsight Cloud API key | +| `bankGranularity` | `["company", "agent"]` | Memory isolation: per company+agent, per company, or per agent | +| `recallBudget` | `mid` | `low` = fastest, `mid` = balanced, `high` = most thorough | +| `autoRetain` | `true` | Automatically retain run output after every run | - const prompt = memories - ? `Past context:\n${memories}\n\nCurrent task: ${context.taskDescription}` - : `Task: ${context.taskDescription}` +## Bank ID Format - const output = await runYourAgent(prompt) - res.json({ output }) // middleware auto-retains output -}) +``` +paperclip::{companyId}::{agentId} ← default (company + agent granularity) +paperclip::{companyId} ← company granularity (shared across agents) +paperclip::{agentId} ← agent granularity (agent memory across companies) ``` -The middleware reads `agentId`, `companyId`, `runId`, and `context.taskDescription` from the Paperclip HTTP adapter request body automatically. - -### Process Adapter Scripts - -```typescript -import { recall, retain, loadConfig } from '@vectorize-io/hindsight-paperclip' +## Agent Tools -const config = loadConfig() -const { PAPERCLIP_AGENT_ID, PAPERCLIP_COMPANY_ID, PAPERCLIP_RUN_ID } = process.env +Agents can call these tools directly during a run: -// Recall before executing -const memories = await recall({ - agentId: PAPERCLIP_AGENT_ID!, - companyId: PAPERCLIP_COMPANY_ID!, - query: process.env.TASK_DESCRIPTION ?? '', -}, config) +**`hindsight_recall(query)`** — search memory for relevant context. Called automatically at run start; agents can also call it mid-run for targeted queries. -if (memories) { - console.log(`[Memory Context]\n${memories}`) -} +**`hindsight_retain(content)`** — store a fact or decision immediately, without waiting for run end. -// ... agent does its work ... +## How It Works -// Retain after -await retain({ - agentId: PAPERCLIP_AGENT_ID!, - companyId: PAPERCLIP_COMPANY_ID!, - content: agentOutput, - documentId: PAPERCLIP_RUN_ID!, -}, config) ``` +agent.run.started + └─ recall(issueTitle + description) + └─ store in plugin state for this run (instant lookup by tools) -### Direct Function Usage - -```typescript -import { recall, retain, loadConfig } from '@vectorize-io/hindsight-paperclip' - -const config = loadConfig({ - hindsightApiUrl: 'https://api.hindsight.vectorize.io', - hindsightApiToken: process.env.HINDSIGHT_API_TOKEN, -}) +agent running… + ├─ hindsight_recall(query) → returns cached context or live recall + └─ hindsight_retain(content) → stores immediately -const memories = await recall( - { companyId, agentId, query: `${task.title}\n${task.description}` }, - config -) - -if (memories) { - systemPrompt = `Past context:\n${memories}\n\n${systemPrompt}` -} +agent.run.finished + └─ retain(output) → stored in Hindsight with runId as document_id ``` -## Bank ID Isolation +Memory is keyed to `companyId` + `agentId`, never to the Paperclip session or run ID — so it survives across any number of runs. -By default, each company+agent pair gets its own memory bank: +## Development +```bash +npm install +npm run build +npm test ``` -paperclip::{companyId}::{agentId} -``` - -You can change the isolation granularity: - -```typescript -// Shared memory across all agents in a company -loadConfig({ bankGranularity: ['company'] }) -// → "paperclip::{companyId}" - -// Agent's global memory across all companies -loadConfig({ bankGranularity: ['agent'] }) -// → "paperclip::{agentId}" -// Custom prefix -loadConfig({ bankIdPrefix: 'myapp' }) -// → "myapp::{companyId}::{agentId}" -``` +Local install into a running Paperclip instance: -## Configuration Reference - -```typescript -interface PaperclipMemoryConfig { - hindsightApiUrl: string // HINDSIGHT_API_URL — required - hindsightApiToken?: string // HINDSIGHT_API_TOKEN - bankGranularity?: ('company' | 'agent')[] // default: ['company', 'agent'] - bankIdPrefix?: string // default: 'paperclip' - recallBudget?: 'low' | 'mid' | 'high' // default: 'mid' - recallMaxTokens?: number // default: 1024 - retainContext?: string // default: 'paperclip' - timeoutMs?: number // default: 15000 -} +```bash +curl -X POST http://127.0.0.1:3100/api/plugins/install \ + -H "Content-Type: application/json" \ + -d '{"packageName":"/absolute/path/to/hindsight-integrations/paperclip","isLocalPath":true}' ``` - -## Skill File - -An agent-readable skill file is included at `src/skills/hindsight.md`. Inject it into your agent's system prompt or as a Paperclip skill to give the agent direct access to Hindsight's REST API via `curl`. - -## Requirements - -- Node.js 20+ (uses native `fetch`) -- Hindsight server (self-hosted or [Hindsight Cloud](https://hindsight.vectorize.io)) diff --git a/hindsight-integrations/paperclip/esbuild.config.mjs b/hindsight-integrations/paperclip/esbuild.config.mjs new file mode 100644 index 0000000000..df8295ae91 --- /dev/null +++ b/hindsight-integrations/paperclip/esbuild.config.mjs @@ -0,0 +1,27 @@ +import esbuild from "esbuild"; + +const watch = process.argv.includes("--watch"); + +const sharedConfig = { + bundle: true, + platform: "node", + target: "node20", + format: "esm", + external: ["@paperclipai/plugin-sdk"], +}; + +const builds = [ + { entryPoints: ["src/manifest.ts"], outfile: "dist/manifest.js" }, + { entryPoints: ["src/worker.ts"], outfile: "dist/worker.js" }, +]; + +if (watch) { + const contexts = await Promise.all( + builds.map((b) => esbuild.context({ ...sharedConfig, ...b })), + ); + await Promise.all(contexts.map((ctx) => ctx.watch())); + console.log("Watching for changes…"); +} else { + await Promise.all(builds.map((b) => esbuild.build({ ...sharedConfig, ...b }))); + console.log("Build complete."); +} diff --git a/hindsight-integrations/paperclip/package-lock.json b/hindsight-integrations/paperclip/package-lock.json index 8772289385..97b1c9096b 100644 --- a/hindsight-integrations/paperclip/package-lock.json +++ b/hindsight-integrations/paperclip/package-lock.json @@ -1,30 +1,24 @@ { - "name": "@vectorize-io/hindsight-paperclip", + "name": "paperclip-plugin-hindsight", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "@vectorize-io/hindsight-paperclip", + "name": "paperclip-plugin-hindsight", "version": "0.1.0", "license": "MIT", + "dependencies": { + "@paperclipai/plugin-sdk": "^2026.403.0" + }, "devDependencies": { - "@types/express": "^5.0.0", "@types/node": "^20.0.0", - "express": "^5.0.0", + "esbuild": "^0.25.0", "typescript": "^5.3.0", "vitest": "^4.0.0" }, "engines": { "node": ">=20" - }, - "peerDependencies": { - "express": ">=4" - }, - "peerDependenciesMeta": { - "express": { - "optional": true - } } }, "node_modules/@emnapi/core": { @@ -34,7 +28,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" @@ -47,7 +40,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -59,53 +51,67 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "tslib": "^2.4.0" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.2.tgz", - "integrity": "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw==", + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" + "os": [ + "android" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@oxc-project/types": { - "version": "0.122.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz", - "integrity": "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==", + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==", + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", "cpu": [ - "arm64" + "x64" ], "dev": true, "license": "MIT", @@ -114,13 +120,13 @@ "android" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" } }, - "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==", + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", "cpu": [ "arm64" ], @@ -131,13 +137,13 @@ "darwin" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" } }, - "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.12.tgz", - "integrity": "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==", + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", "cpu": [ "x64" ], @@ -148,13 +154,30 @@ "darwin" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" } }, - "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.12.tgz", - "integrity": "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==", + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", "cpu": [ "x64" ], @@ -165,13 +188,13 @@ "freebsd" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" } }, - "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.12.tgz", - "integrity": "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==", + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", "cpu": [ "arm" ], @@ -182,13 +205,13 @@ "linux" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" } }, - "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==", + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", "cpu": [ "arm64" ], @@ -199,15 +222,15 @@ "linux" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" } }, - "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.12.tgz", - "integrity": "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==", + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", "cpu": [ - "arm64" + "ia32" ], "dev": true, "license": "MIT", @@ -216,15 +239,15 @@ "linux" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" } }, - "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==", + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", "cpu": [ - "ppc64" + "loong64" ], "dev": true, "license": "MIT", @@ -233,15 +256,15 @@ "linux" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" } }, - "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==", + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", "cpu": [ - "s390x" + "mips64el" ], "dev": true, "license": "MIT", @@ -250,15 +273,15 @@ "linux" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" } }, - "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.12.tgz", - "integrity": "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==", + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", "cpu": [ - "x64" + "ppc64" ], "dev": true, "license": "MIT", @@ -267,15 +290,15 @@ "linux" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" } }, - "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.12.tgz", - "integrity": "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==", + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", "cpu": [ - "x64" + "riscv64" ], "dev": true, "license": "MIT", @@ -284,47 +307,47 @@ "linux" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" } }, - "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.12.tgz", - "integrity": "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==", + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", "cpu": [ - "arm64" + "s390x" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "openharmony" + "linux" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" } }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.12.tgz", - "integrity": "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==", + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", "cpu": [ - "wasm32" + "x64" ], "dev": true, "license": "MIT", "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^1.1.1" - }, + "os": [ + "linux" + ], "engines": { - "node": ">=14.0.0" + "node": ">=18" } }, - "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.12.tgz", - "integrity": "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==", + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", "cpu": [ "arm64" ], @@ -332,16 +355,16 @@ "license": "MIT", "optional": true, "os": [ - "win32" + "netbsd" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" } }, - "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.12.tgz", - "integrity": "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==", + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", "cpu": [ "x64" ], @@ -349,512 +372,635 @@ "license": "MIT", "optional": true, "os": [ - "win32" + "netbsd" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" } }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.12.tgz", - "integrity": "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==", + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", "optional": true, - "dependencies": { - "tslib": "^2.4.0" + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@types/body-parser": { - "version": "1.19.6", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", - "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@types/chai": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", - "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@types/deep-eql": "*", - "assertion-error": "^2.0.1" + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", - "dependencies": { - "@types/node": "*" + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@types/deep-eql": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", - "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true, "license": "MIT" }, - "node_modules/@types/express": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", - "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.2.tgz", + "integrity": "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw==", "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^5.0.0", - "@types/serve-static": "^2" + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" } }, - "node_modules/@types/express-serve-static-core": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", - "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", + "node_modules/@oxc-project/types": { + "version": "0.123.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.123.0.tgz", + "integrity": "sha512-YtECP/y8Mj1lSHiUWGSRzy/C6teUKlS87dEfuVKT09LgQbUsBW1rNg+MiJ4buGu3yuADV60gbIvo9/HplA56Ew==", "dev": true, "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" + "funding": { + "url": "https://github.com/sponsors/Boshen" } }, - "node_modules/@types/http-errors": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", - "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "20.19.37", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.37.tgz", - "integrity": "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/qs": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz", - "integrity": "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/send": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", - "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*" - } - }, - "node_modules/@vitest/expect": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.2.tgz", - "integrity": "sha512-gbu+7B0YgUJ2nkdsRJrFFW6X7NTP44WlhiclHniUhxADQJH5Szt9mZ9hWnJPJ8YwOK5zUOSSlSvyzRf0u1DSBQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@standard-schema/spec": "^1.1.0", - "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.2", - "@vitest/utils": "4.1.2", - "chai": "^6.2.2", - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/mocker": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.2.tgz", - "integrity": "sha512-Ize4iQtEALHDttPRCmN+FKqOl2vxTiNUhzobQFFt/BM1lRUTG7zRCLOykG/6Vo4E4hnUdfVLo5/eqKPukcWW7Q==", - "dev": true, + "node_modules/@paperclipai/plugin-sdk": { + "version": "2026.403.0", + "resolved": "https://registry.npmjs.org/@paperclipai/plugin-sdk/-/plugin-sdk-2026.403.0.tgz", + "integrity": "sha512-8WmsX1rGk1ubG41l26pvEyuaLBQgdWQ/s3QL9XMU3rcwItkMKGhopy9b4uPbwJMMv3gbRiaXRa2JJz3e+P3OgA==", "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.2", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.21" + "@paperclipai/shared": "2026.403.0", + "zod": "^3.24.2" }, - "funding": { - "url": "https://opencollective.com/vitest" + "bin": { + "paperclip-plugin-dev-server": "dist/dev-cli.js" }, "peerDependencies": { - "msw": "^2.4.9", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + "react": ">=18" }, "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { + "react": { "optional": true } } }, - "node_modules/@vitest/pretty-format": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.2.tgz", - "integrity": "sha512-dwQga8aejqeuB+TvXCMzSQemvV9hNEtDDpgUKDzOmNQayl2OG241PSWeJwKRH3CiC+sESrmoFd49rfnq7T4RnA==", - "dev": true, + "node_modules/@paperclipai/shared": { + "version": "2026.403.0", + "resolved": "https://registry.npmjs.org/@paperclipai/shared/-/shared-2026.403.0.tgz", + "integrity": "sha512-wAwG5Bv0JEg0Itqf41niH5hAyV/aDA7n5ZRlHdMfmfMA4pcLKxZUyRhduFVJvgHtAW3UJwR/QA2rGQxxrzJrTw==", "license": "MIT", "dependencies": { - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" + "zod": "^3.24.2" } }, - "node_modules/@vitest/runner": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.2.tgz", - "integrity": "sha512-Gr+FQan34CdiYAwpGJmQG8PgkyFVmARK8/xSijia3eTFgVfpcpztWLuP6FttGNfPLJhaZVP/euvujeNYar36OQ==", + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.13.tgz", + "integrity": "sha512-5ZiiecKH2DXAVJTNN13gNMUcCDg4Jy8ZjbXEsPnqa248wgOVeYRX0iqXXD5Jz4bI9BFHgKsI2qmyJynstbmr+g==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@vitest/utils": "4.1.2", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@vitest/snapshot": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.2.tgz", - "integrity": "sha512-g7yfUmxYS4mNxk31qbOYsSt2F4m1E02LFqO53Xpzg3zKMhLAPZAjjfyl9e6z7HrW6LvUdTwAQR3HHfLjpko16A==", + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.13.tgz", + "integrity": "sha512-tz/v/8G77seu8zAB3A5sK3UFoOl06zcshEzhUO62sAEtrEuW/H1CcyoupOrD+NbQJytYgA4CppXPzlrmp4JZKA==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "4.1.2", - "@vitest/utils": "4.1.2", - "magic-string": "^0.30.21", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@vitest/spy": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.2.tgz", - "integrity": "sha512-DU4fBnbVCJGNBwVA6xSToNXrkZNSiw59H8tcuUspVMsBDBST4nfvsPsEHDHGtWRRnqBERBQu7TrTKskmjqTXKA==", + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.13.tgz", + "integrity": "sha512-8DakphqOz8JrMYWTJmWA+vDJxut6LijZ8Xcdc4flOlAhU7PNVwo2MaWBF9iXjJAPo5rC/IxEFZDhJ3GC7NHvug==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "funding": { - "url": "https://opencollective.com/vitest" + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@vitest/utils": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.2.tgz", - "integrity": "sha512-xw2/TiX82lQHA06cgbqRKFb5lCAy3axQ4H4SoUFhUsg+wztiet+co86IAMDtF6Vm1hc7J6j09oh/rgDn+JdKIQ==", + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.13.tgz", + "integrity": "sha512-4wBQFfjDuXYN/SVI8inBF3Aa+isq40rc6VMFbk5jcpolUBTe5cYnMsHZ51nFWsx3PVyyNN3vgoESki0Hmr/4BA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "4.1.2", - "convert-source-map": "^2.0.0", - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/accepts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", - "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.13.tgz", + "integrity": "sha512-JW/e4yPIXLms+jmnbwwy5LA/LxVwZUWLN8xug+V200wzaVi5TEGIWQlh8o91gWYFxW609euI98OCCemmWGuPrw==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "dependencies": { - "mime-types": "^3.0.0", - "negotiator": "^1.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.6" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.13.tgz", + "integrity": "sha512-ZfKWpXiUymDnavepCaM6KG/uGydJ4l2nBmMxg60Ci4CbeefpqjPWpfaZM7PThOhk2dssqBAcwLc6rAyr0uTdXg==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/body-parser": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", - "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.13.tgz", + "integrity": "sha512-bmRg3O6Z0gq9yodKKWCIpnlH051sEfdVwt+6m5UDffAQMUUqU0xjnQqqAUm+Gu7ofAAly9DqiQDtKu2nPDEABA==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "bytes": "^3.1.2", - "content-type": "^1.0.5", - "debug": "^4.4.3", - "http-errors": "^2.0.0", - "iconv-lite": "^0.7.0", - "on-finished": "^2.4.1", - "qs": "^6.14.1", - "raw-body": "^3.0.1", - "type-is": "^2.0.1" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.13.tgz", + "integrity": "sha512-8Wtnbw4k7pMYN9B/mOEAsQ8HOiq7AZ31Ig4M9BKn2So4xRaFEhtCSa4ZJaOutOWq50zpgR4N5+L/opnlaCx8wQ==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.8" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.13.tgz", + "integrity": "sha512-D/0Nlo8mQuxSMohNJUF2lDXWRsFDsHldfRRgD9bRgktj+EndGPj4DOV37LqDKPYS+osdyhZEH7fTakTAEcW7qg==", + "cpu": [ + "s390x" + ], "dev": true, "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.4" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.13.tgz", + "integrity": "sha512-eRrPvat2YaVQcwwKi/JzOP6MKf1WRnOCr+VaI3cTWz3ZoLcP/654z90lVCJ4dAuMEpPdke0n+qyAqXDZdIC4rA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/chai": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", - "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.13.tgz", + "integrity": "sha512-PsdONiFRp8hR8KgVjTWjZ9s7uA3uueWL0t74/cKHfM4dR5zXYv4AjB8BvA+QDToqxAFg4ZkcVEqeu5F7inoz5w==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/content-disposition": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", - "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.13.tgz", + "integrity": "sha512-hCNXgC5dI3TVOLrPT++PKFNZ+1EtS0mLQwfXXXSUD/+rGlB65gZDwN/IDuxLpQP4x8RYYHqGomlUXzpO8aVI2w==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.13.tgz", + "integrity": "sha512-viLS5C5et8NFtLWw9Sw3M/w4vvnVkbWkO7wSNh3C+7G1+uCkGpr6PcjNDSFcNtmXY/4trjPBqUfcOL+P3sWy/g==", + "cpu": [ + "wasm32" + ], "dev": true, "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.9.1", + "@emnapi/runtime": "1.9.1", + "@napi-rs/wasm-runtime": "^1.1.2" + }, "engines": { - "node": ">= 0.6" + "node": ">=14.0.0" } }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.13.tgz", + "integrity": "sha512-Fqa3Tlt1xL4wzmAYxGNFV36Hb+VfPc9PYU+E25DAnswXv3ODDu/yyWjQDbXMo5AGWkQVjLgQExuVu8I/UaZhPQ==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 0.6" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/cookie-signature": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", - "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.13.tgz", + "integrity": "sha512-/pLI5kPkGEi44TDlnbio3St/5gUFeN51YWNAk/Gnv6mEQBOahRBh52qVFVBpmrnU01n2yysvBML9Ynu7K4kGAQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=6.6.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.13.tgz", + "integrity": "sha512-3ngTAv6F/Py35BsYbeeLeecvhMKdsKm4AoOETVhAA+Qc8nrA2I0kF7oa93mE9qnIurngOSpMnQ0x2nQY2FPviA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", "dev": true, "license": "MIT", + "optional": true, "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" + "tslib": "^2.4.0" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.39", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.39.tgz", + "integrity": "sha512-orrrD74MBUyK8jOAD/r0+lfa1I2MO6I+vAkmAWzMYbCcgrN4lCrmK52gRFQq/JRxfYPfonkr4b0jcY7Olqdqbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.3.tgz", + "integrity": "sha512-CW8Q9KMtXDGHj0vCsqui0M5KqRsu0zm0GNDW7Gd3U7nZ2RFpPKSCpeCXoT+/+5zr1TNlsoQRDEz+LzZUyq6gnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.3", + "@vitest/utils": "4.1.3", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "node_modules/@vitest/pretty-format": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.3.tgz", + "integrity": "sha512-hYqqwuMbpkkBodpRh4k4cQSOELxXky1NfMmQvOfKvV8zQHz8x8Dla+2wzElkMkBvSAJX5TRGHJAQvK0TcOafwg==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.8" + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "node_modules/@vitest/runner": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.3.tgz", + "integrity": "sha512-VwgOz5MmT0KhlUj40h02LWDpUBVpflZ/b7xZFA25F29AJzIrE+SMuwzFf0b7t4EXdwRNX61C3B6auIXQTR3ttA==", "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8" + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.3", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "node_modules/@vitest/snapshot": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.3.tgz", + "integrity": "sha512-9l+k/J9KG5wPJDX9BcFFzhhwNjwkRb8RsnYhaT1vPY7OufxmQFc9sZzScRCPTiETzl37mrIWVY9zxzmdVeJwDQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" + "@vitest/pretty-format": "4.1.3", + "@vitest/utils": "4.1.3", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" }, - "engines": { - "node": ">= 0.4" + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "node_modules/@vitest/spy": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.3.tgz", + "integrity": "sha512-ujj5Uwxagg4XUIfAUyRQxAg631BP6e9joRiN99mr48Bg9fRs+5mdUElhOoZ6rP5mBr8Bs3lmrREnkrQWkrsTCw==", "dev": true, - "license": "MIT" + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "node_modules/@vitest/utils": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.3.tgz", + "integrity": "sha512-Pc/Oexse/khOWsGB+w3q4yzA4te7W4gpZZAvk+fr8qXfTURZUMj5i7kuxsNK5mP/dEB6ao3jfr0rs17fHhbHdw==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.8" + "dependencies": { + "@vitest/pretty-format": "4.1.3", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=12" } }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=18" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" } }, "node_modules/es-module-lexer": { @@ -864,26 +1010,48 @@ "dev": true, "license": "MIT" }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", "dev": true, + "hasInstallScript": true, "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" + "bin": { + "esbuild": "bin/esbuild" }, "engines": { - "node": ">= 0.4" + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" } }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "dev": true, - "license": "MIT" - }, "node_modules/estree-walker": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", @@ -894,16 +1062,6 @@ "@types/estree": "^1.0.0" } }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/expect-type": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", @@ -914,50 +1072,6 @@ "node": ">=12.0.0" } }, - "node_modules/express": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", - "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "accepts": "^2.0.0", - "body-parser": "^2.2.1", - "content-disposition": "^1.0.0", - "content-type": "^1.0.5", - "cookie": "^0.7.1", - "cookie-signature": "^1.2.1", - "debug": "^4.4.0", - "depd": "^2.0.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "finalhandler": "^2.1.0", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "merge-descriptors": "^2.0.0", - "mime-types": "^3.0.0", - "on-finished": "^2.4.1", - "once": "^1.4.0", - "parseurl": "^1.3.3", - "proxy-addr": "^2.0.7", - "qs": "^6.14.0", - "range-parser": "^1.2.1", - "router": "^2.2.0", - "send": "^1.1.0", - "serve-static": "^2.2.0", - "statuses": "^2.0.1", - "type-is": "^2.0.1", - "vary": "^1.1.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -976,48 +1090,6 @@ } } }, - "node_modules/finalhandler": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", - "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "on-finished": "^2.4.1", - "parseurl": "^1.3.3", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -1033,226 +1105,76 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", "dev": true, - "license": "MIT", + "license": "MPL-2.0", "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" + "detect-libc": "^2.0.3" }, "engines": { - "node": ">= 0.4" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" + "type": "opencollective", + "url": "https://opencollective.com/parcel" }, - "engines": { - "node": ">= 0.4" + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" } }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], "engines": { - "node": ">= 0.4" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">= 0.4" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/iconv-lite": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", - "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-promise": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", - "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" - } - }, - "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-darwin-x64": { @@ -1454,73 +1376,6 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/merge-descriptors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", - "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", - "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -1540,29 +1395,6 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/obug": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", @@ -1574,50 +1406,6 @@ ], "license": "MIT" }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-to-regexp": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.0.tgz", - "integrity": "sha512-PuseHIvAnz3bjrM2rGJtSgo1zjgxapTLZ7x2pjhzWwlp4SJQgK3f3iZIQwkpEnBaKz6seKBADpM4B4ySkuYypg==", - "dev": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", @@ -1674,420 +1462,784 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "node_modules/rolldown": { + "version": "1.0.0-rc.13", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.13.tgz", + "integrity": "sha512-bvVj8YJmf0rq4pSFmH7laLa6pYrhghv3PRzrCdRAr23g66zOKVJ4wkvFtgohtPLWmthgg8/rkaqRHrpUEh0Zbw==", "dev": true, "license": "MIT", "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" + "@oxc-project/types": "=0.123.0", + "@rolldown/pluginutils": "1.0.0-rc.13" }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/qs": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", - "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" + "bin": { + "rolldown": "bin/cli.mjs" }, "engines": { - "node": ">=0.6" + "node": "^20.19.0 || >=22.12.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.13", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.13", + "@rolldown/binding-darwin-x64": "1.0.0-rc.13", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.13", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.13", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.13", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.13", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.13", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.13", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.13", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.13", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.13", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.13", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.13", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.13" } }, - "node_modules/range-parser": { + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map-js": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, - "license": "MIT", + "license": "BSD-3-Clause", "engines": { - "node": ">= 0.6" + "node": ">=0.10.0" } }, - "node_modules/raw-body": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", - "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.0.0.tgz", + "integrity": "sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.1.tgz", + "integrity": "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==", "dev": true, "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.7.0", - "unpipe": "~1.0.0" - }, "engines": { - "node": ">= 0.10" + "node": ">=18" } }, - "node_modules/rolldown": { - "version": "1.0.0-rc.12", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.12.tgz", - "integrity": "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==", + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.122.0", - "@rolldown/pluginutils": "1.0.0-rc.12" - }, - "bin": { - "rolldown": "bin/cli.mjs" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.12", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.12", - "@rolldown/binding-darwin-x64": "1.0.0-rc.12", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.12", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.12", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.12", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.12", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12" - } - }, - "node_modules/router": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", - "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "depd": "^2.0.0", - "is-promise": "^4.0.0", - "parseurl": "^1.3.3", - "path-to-regexp": "^8.0.0" + "node": ">=12.0.0" }, - "engines": { - "node": ">= 18" + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" }, - "node_modules/send": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", - "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "node_modules/vitest": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.3.tgz", + "integrity": "sha512-DBc4Tx0MPNsqb9isoyOq00lHftVx/KIU44QOm2q59npZyLUkENn8TMFsuzuO+4U2FUa9rgbbPt3udrP25GcjXw==", "dev": true, "license": "MIT", "dependencies": { - "debug": "^4.4.3", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^2.0.0", - "http-errors": "^2.0.1", - "mime-types": "^3.0.2", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.2" + "@vitest/expect": "4.1.3", + "@vitest/mocker": "4.1.3", + "@vitest/pretty-format": "4.1.3", + "@vitest/runner": "4.1.3", + "@vitest/snapshot": "4.1.3", + "@vitest/spy": "4.1.3", + "@vitest/utils": "4.1.3", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" }, "engines": { - "node": ">= 18" + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.3", + "@vitest/browser-preview": "4.1.3", + "@vitest/browser-webdriverio": "4.1.3", + "@vitest/coverage-istanbul": "4.1.3", + "@vitest/coverage-v8": "4.1.3", + "@vitest/ui": "4.1.3", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } } }, - "node_modules/serve-static": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", - "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "node_modules/vitest/node_modules/@esbuild/aix-ppc64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz", + "integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", - "dependencies": { - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "parseurl": "^1.3.3", - "send": "^1.2.0" - }, + "optional": true, + "os": [ + "aix" + ], + "peer": true, "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "node": ">=18" } }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "node_modules/vitest/node_modules/@esbuild/android-arm": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz", + "integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==", + "cpu": [ + "arm" + ], "dev": true, - "license": "ISC" + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">=18" + } }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "node_modules/vitest/node_modules/@esbuild/android-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz", + "integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, + "optional": true, + "os": [ + "android" + ], + "peer": true, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "node_modules/vitest/node_modules/@esbuild/android-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz", + "integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, + "optional": true, + "os": [ + "android" + ], + "peer": true, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "node_modules/vitest/node_modules/@esbuild/darwin-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz", + "integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, + "optional": true, + "os": [ + "darwin" + ], + "peer": true, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "node_modules/vitest/node_modules/@esbuild/darwin-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz", + "integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, + "optional": true, + "os": [ + "darwin" + ], + "peer": true, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=18" } }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "node_modules/vitest/node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz", + "integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "ISC" + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">=18" + } }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "node_modules/vitest/node_modules/@esbuild/freebsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz", + "integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-arm": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz", + "integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz", + "integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-ia32": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz", + "integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-loong64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz", + "integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-mips64el": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz", + "integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-ppc64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz", + "integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-riscv64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz", + "integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-s390x": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz", + "integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/vitest/node_modules/@esbuild/linux-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz", + "integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==", + "cpu": [ + "x64" + ], "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "peer": true, "engines": { - "node": ">=0.10.0" + "node": ">=18" } }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true, - "license": "MIT" - }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "node_modules/vitest/node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz", + "integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "peer": true, "engines": { - "node": ">= 0.8" + "node": ">=18" } }, - "node_modules/std-env": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.0.0.tgz", - "integrity": "sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinybench": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyexec": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz", - "integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==", + "node_modules/vitest/node_modules/@esbuild/netbsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz", + "integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "peer": true, "engines": { "node": ">=18" } }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "node_modules/vitest/node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz", + "integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, + "optional": true, + "os": [ + "openbsd" + ], + "peer": true, "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" + "node": ">=18" } }, - "node_modules/tinyrainbow": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", - "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "node_modules/vitest/node_modules/@esbuild/openbsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz", + "integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "peer": true, "engines": { - "node": ">=14.0.0" + "node": ">=18" } }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "node_modules/vitest/node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz", + "integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==", + "cpu": [ + "arm64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "peer": true, "engines": { - "node": ">=0.6" + "node": ">=18" } }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD", - "optional": true - }, - "node_modules/type-is": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "node_modules/vitest/node_modules/@esbuild/sunos-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz", + "integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "content-type": "^1.0.5", - "media-typer": "^1.1.0", - "mime-types": "^3.0.0" - }, + "optional": true, + "os": [ + "sunos" + ], + "peer": true, "engines": { - "node": ">= 0.6" + "node": ">=18" } }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "node_modules/vitest/node_modules/@esbuild/win32-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz", + "integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, "engines": { - "node": ">=14.17" + "node": ">=18" } }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "node_modules/vitest/node_modules/@esbuild/win32-ia32": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz", + "integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==", + "cpu": [ + "ia32" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">=18" + } }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "node_modules/vitest/node_modules/@esbuild/win32-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz", + "integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "peer": true, "engines": { - "node": ">= 0.8" + "node": ">=18" } }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "node_modules/vitest/node_modules/@vitest/mocker": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.3.tgz", + "integrity": "sha512-XN3TrycitDQSzGRnec/YWgoofkYRhouyVQj4YNsJ5r/STCUFqMrP4+oxEv3e7ZbLi4og5kIHrZwekDJgw6hcjw==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.8" + "dependencies": { + "@vitest/spy": "4.1.3", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } } }, - "node_modules/vite": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.3.tgz", - "integrity": "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==", + "node_modules/vitest/node_modules/esbuild": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", + "integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "peer": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.0", + "@esbuild/android-arm": "0.28.0", + "@esbuild/android-arm64": "0.28.0", + "@esbuild/android-x64": "0.28.0", + "@esbuild/darwin-arm64": "0.28.0", + "@esbuild/darwin-x64": "0.28.0", + "@esbuild/freebsd-arm64": "0.28.0", + "@esbuild/freebsd-x64": "0.28.0", + "@esbuild/linux-arm": "0.28.0", + "@esbuild/linux-arm64": "0.28.0", + "@esbuild/linux-ia32": "0.28.0", + "@esbuild/linux-loong64": "0.28.0", + "@esbuild/linux-mips64el": "0.28.0", + "@esbuild/linux-ppc64": "0.28.0", + "@esbuild/linux-riscv64": "0.28.0", + "@esbuild/linux-s390x": "0.28.0", + "@esbuild/linux-x64": "0.28.0", + "@esbuild/netbsd-arm64": "0.28.0", + "@esbuild/netbsd-x64": "0.28.0", + "@esbuild/openbsd-arm64": "0.28.0", + "@esbuild/openbsd-x64": "0.28.0", + "@esbuild/openharmony-arm64": "0.28.0", + "@esbuild/sunos-x64": "0.28.0", + "@esbuild/win32-arm64": "0.28.0", + "@esbuild/win32-ia32": "0.28.0", + "@esbuild/win32-x64": "0.28.0" + } + }, + "node_modules/vitest/node_modules/vite": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.7.tgz", + "integrity": "sha512-P1PbweD+2/udplnThz3btF4cf6AgPky7kk23RtHUkJIU5BIxwPprhRGmOAHs6FTI7UiGbTNrgNP6jSYD6JaRnw==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.8", - "rolldown": "1.0.0-rc.12", + "rolldown": "1.0.0-rc.13", "tinyglobby": "^0.2.15" }, "bin": { @@ -2105,7 +2257,7 @@ "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", - "esbuild": "^0.27.0", + "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", @@ -2155,88 +2307,6 @@ } } }, - "node_modules/vitest": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.2.tgz", - "integrity": "sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/expect": "4.1.2", - "@vitest/mocker": "4.1.2", - "@vitest/pretty-format": "4.1.2", - "@vitest/runner": "4.1.2", - "@vitest/snapshot": "4.1.2", - "@vitest/spy": "4.1.2", - "@vitest/utils": "4.1.2", - "es-module-lexer": "^2.0.0", - "expect-type": "^1.3.0", - "magic-string": "^0.30.21", - "obug": "^2.1.1", - "pathe": "^2.0.3", - "picomatch": "^4.0.3", - "std-env": "^4.0.0-rc.1", - "tinybench": "^2.9.0", - "tinyexec": "^1.0.2", - "tinyglobby": "^0.2.15", - "tinyrainbow": "^3.1.0", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", - "why-is-node-running": "^2.3.0" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@opentelemetry/api": "^1.9.0", - "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.2", - "@vitest/browser-preview": "4.1.2", - "@vitest/browser-webdriverio": "4.1.2", - "@vitest/ui": "4.1.2", - "happy-dom": "*", - "jsdom": "*", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@opentelemetry/api": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser-playwright": { - "optional": true - }, - "@vitest/browser-preview": { - "optional": true - }, - "@vitest/browser-webdriverio": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - }, - "vite": { - "optional": false - } - } - }, "node_modules/why-is-node-running": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", @@ -2254,12 +2324,14 @@ "node": ">=8" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/hindsight-integrations/paperclip/package.json b/hindsight-integrations/paperclip/package.json index 1bc17ea50c..c66be76c88 100644 --- a/hindsight-integrations/paperclip/package.json +++ b/hindsight-integrations/paperclip/package.json @@ -1,16 +1,19 @@ { "name": "@vectorize-io/hindsight-paperclip", - "version": "0.1.1", - "description": "Persistent memory for Paperclip AI agents using Hindsight", + "version": "0.2.0", + "description": "Persistent long-term memory for Paperclip agents via Hindsight — recall before every heartbeat, retain after every run", "type": "module", - "main": "dist/index.js", - "types": "dist/index.d.ts", + "paperclipPlugin": { + "manifest": "./dist/manifest.js", + "worker": "./dist/worker.js" + }, "keywords": [ "paperclip", "hindsight", "memory", "agents", - "ai" + "ai", + "plugin" ], "author": "Vectorize ", "license": "MIT", @@ -24,25 +27,20 @@ "README.md" ], "scripts": { - "build": "tsc", - "dev": "tsc --watch", + "build": "node esbuild.config.mjs", + "dev": "node esbuild.config.mjs --watch", "clean": "rm -rf dist", "test": "vitest run tests", "test:watch": "vitest tests", + "typecheck": "tsc --noEmit", "prepublishOnly": "npm run clean && npm run build" }, - "peerDependencies": { - "express": ">=4" - }, - "peerDependenciesMeta": { - "express": { - "optional": true - } + "dependencies": { + "@paperclipai/plugin-sdk": "^2026.403.0" }, "devDependencies": { - "@types/express": "^5.0.0", "@types/node": "^20.0.0", - "express": "^5.0.0", + "esbuild": "^0.25.0", "typescript": "^5.3.0", "vitest": "^4.0.0" }, diff --git a/hindsight-integrations/paperclip/src/bank.ts b/hindsight-integrations/paperclip/src/bank.ts index c5e5960527..99ebd40b98 100644 --- a/hindsight-integrations/paperclip/src/bank.ts +++ b/hindsight-integrations/paperclip/src/bank.ts @@ -1,40 +1,30 @@ /** - * Bank ID derivation for Paperclip agents. + * Bank ID derivation — maps Paperclip company/agent identity onto Hindsight bank IDs. * - * Aligns Hindsight's memory bank model with Paperclip's company/agent isolation. + * Default format: "paperclip::{companyId}::{agentId}" + * + * bankGranularity: ['company'] → "paperclip::{companyId}" + * bankGranularity: ['agent'] → "paperclip::{agentId}" + * bankGranularity: ['company','agent'] → "paperclip::{companyId}::{agentId}" */ -import type { PaperclipMemoryConfig } from './config.js'; - export interface BankContext { companyId: string; agentId: string; } -/** - * Derive a Hindsight bank ID from Paperclip context. - * - * Default output: "paperclip::{companyId}::{agentId}" - * - * With bankGranularity: ['company'] → "paperclip::{companyId}" - * With bankGranularity: ['agent'] → "paperclip::{agentId}" - * With bankIdPrefix: '' → "{companyId}::{agentId}" - */ -export function deriveBankId(context: BankContext, config: PaperclipMemoryConfig): string { - const parts: string[] = []; - - if (config.bankIdPrefix) { - parts.push(config.bankIdPrefix); - } +export interface BankConfig { + bankGranularity?: Array<"company" | "agent">; +} - for (const field of config.bankGranularity ?? ['company', 'agent']) { - if (field === 'company') parts.push(context.companyId); - if (field === 'agent') parts.push(context.agentId); - } +export function deriveBankId(context: BankContext, config: BankConfig): string { + const granularity = config.bankGranularity ?? ["company", "agent"]; + const parts: string[] = ["paperclip"]; - if (parts.length === 0) { - throw new Error('Bank ID cannot be empty — bankGranularity or bankIdPrefix must be set'); + for (const field of granularity) { + if (field === "company") parts.push(context.companyId); + if (field === "agent") parts.push(context.agentId); } - return parts.join('::'); + return parts.join("::"); } diff --git a/hindsight-integrations/paperclip/src/client.ts b/hindsight-integrations/paperclip/src/client.ts index e752b12172..23e779381e 100644 --- a/hindsight-integrations/paperclip/src/client.ts +++ b/hindsight-integrations/paperclip/src/client.ts @@ -1,42 +1,32 @@ /** - * HTTP client for the Hindsight REST API. + * Minimal Hindsight HTTP client for use inside the plugin worker. * * Uses native fetch (Node 20+). No external dependencies. */ -import type { PaperclipMemoryConfig } from './config.js'; - export interface Memory { text: string; type?: string; - mentionedAt?: string; } export interface RecallResponse { results: Memory[]; } -export interface RetainResponse { - success: boolean; - bankId?: string; -} - export class HindsightClient { private readonly baseUrl: string; private readonly token: string | undefined; - private readonly timeoutMs: number; - constructor(config: PaperclipMemoryConfig) { - const url = config.hindsightApiUrl.trim(); - if (!url) throw new Error('hindsightApiUrl is required'); - this.baseUrl = url.replace(/\/$/, ''); - this.token = config.hindsightApiToken; - this.timeoutMs = config.timeoutMs ?? 15_000; + constructor(baseUrl: string, token?: string) { + const url = baseUrl.trim(); + if (!url) throw new Error("hindsightApiUrl is required"); + this.baseUrl = url.replace(/\/$/, ""); + this.token = token; } private headers(): Record { - const h: Record = { 'Content-Type': 'application/json' }; - if (this.token) h['Authorization'] = `Bearer ${this.token}`; + const h: Record = { "Content-Type": "application/json" }; + if (this.token) h["Authorization"] = `Bearer ${this.token}`; return h; } @@ -44,10 +34,9 @@ export class HindsightClient { method: string, path: string, body?: unknown, - timeoutMs?: number, ): Promise { const controller = new AbortController(); - const timer = setTimeout(() => controller.abort(), timeoutMs ?? this.timeoutMs); + const timer = setTimeout(() => controller.abort(), 15_000); try { const resp = await fetch(`${this.baseUrl}${path}`, { @@ -58,7 +47,7 @@ export class HindsightClient { }); if (!resp.ok) { - const text = await resp.text().catch(() => ''); + const text = await resp.text().catch(() => ""); throw new Error(`HTTP ${resp.status} from ${path}: ${text}`); } @@ -71,40 +60,30 @@ export class HindsightClient { async recall( bankId: string, query: string, - options?: { budget?: string; maxTokens?: number }, + budget = "mid", ): Promise { const path = `/v1/default/banks/${encodeURIComponent(bankId)}/memories/recall`; - return this.request('POST', path, { + return this.request("POST", path, { query, - budget: options?.budget ?? 'mid', - max_tokens: options?.maxTokens ?? 1024, + budget, + max_tokens: 1024, }); } async retain( bankId: string, content: string, - options?: { - documentId?: string; - context?: string; - metadata?: Record; - tags?: string[]; - }, - ): Promise { + documentId?: string, + metadata?: Record, + ): Promise { const path = `/v1/default/banks/${encodeURIComponent(bankId)}/memories`; - const item: Record = { content }; - if (options?.documentId) item['document_id'] = options.documentId; - if (options?.context) item['context'] = options.context; - if (options?.metadata) item['metadata'] = options.metadata; - if (options?.tags) item['tags'] = options.tags; - return this.request('POST', path, { items: [item], async: true }); - } - - async setBankMission(bankId: string, mission: string, retainMission?: string): Promise { - const path = `/v1/default/banks/${encodeURIComponent(bankId)}/config`; - const updates: Record = { reflect_mission: mission }; - if (retainMission) updates['retain_mission'] = retainMission; - await this.request('PATCH', path, { updates }); + const item: Record = { + content, + context: "paperclip", + }; + if (documentId) item["document_id"] = documentId; + if (metadata) item["metadata"] = metadata; + await this.request("POST", path, { items: [item], async: true }); } async health(): Promise { @@ -119,3 +98,8 @@ export class HindsightClient { } } } + +export function formatMemories(memories: Memory[]): string { + if (memories.length === 0) return ""; + return memories.map((m) => `- ${m.text}`).join("\n"); +} diff --git a/hindsight-integrations/paperclip/src/config.ts b/hindsight-integrations/paperclip/src/config.ts deleted file mode 100644 index df10fbdbf9..0000000000 --- a/hindsight-integrations/paperclip/src/config.ts +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Configuration for @vectorize-io/hindsight-paperclip. - * - * Loaded from explicit options first, then environment variables. - */ - -export type BankGranularity = 'company' | 'agent'; - -export interface PaperclipMemoryConfig { - /** Hindsight server URL. Required. env: HINDSIGHT_API_URL */ - hindsightApiUrl: string; - /** API token for Hindsight Cloud. env: HINDSIGHT_API_TOKEN */ - hindsightApiToken?: string; - /** - * Which dimensions to include in the bank ID. - * Default: ['company', 'agent'] → "paperclip::{companyId}::{agentId}" - */ - bankGranularity?: BankGranularity[]; - /** Prefix prepended to all bank IDs. Default: "paperclip" */ - bankIdPrefix?: string; - /** Recall search depth. Default: "mid" */ - recallBudget?: 'low' | 'mid' | 'high'; - /** Max tokens in the recalled memory block. Default: 1024 */ - recallMaxTokens?: number; - /** Provenance label stored with each retained document. Default: "paperclip" */ - retainContext?: string; - /** Request timeout in milliseconds. Default: 15000 */ - timeoutMs?: number; -} - -export function loadConfig(overrides?: Partial): PaperclipMemoryConfig { - const config: PaperclipMemoryConfig = { - hindsightApiUrl: process.env['HINDSIGHT_API_URL'] ?? '', - hindsightApiToken: process.env['HINDSIGHT_API_TOKEN'], - bankGranularity: ['company', 'agent'], - bankIdPrefix: 'paperclip', - recallBudget: 'mid', - recallMaxTokens: 1024, - retainContext: 'paperclip', - timeoutMs: 15_000, - ...overrides, - }; - if (!config.hindsightApiUrl) { - throw new Error( - 'hindsightApiUrl is required — set HINDSIGHT_API_URL or pass hindsightApiUrl to loadConfig()', - ); - } - return config; -} diff --git a/hindsight-integrations/paperclip/src/index.ts b/hindsight-integrations/paperclip/src/index.ts deleted file mode 100644 index 5f44b7b892..0000000000 --- a/hindsight-integrations/paperclip/src/index.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @vectorize-io/hindsight-paperclip - * - * Persistent memory for Paperclip AI agents using Hindsight. - * - * @example - * ```typescript - * import { recall, retain, loadConfig } from '@vectorize-io/hindsight-paperclip' - * - * const config = loadConfig() - * - * // Before heartbeat - * const memories = await recall({ companyId, agentId, query }, config) - * - * // After heartbeat - * await retain({ companyId, agentId, content: output, documentId: runId }, config) - * ``` - */ - -export { recall } from './recall.js'; -export type { RecallInput } from './recall.js'; - -export { retain } from './retain.js'; -export type { RetainInput } from './retain.js'; - -export { createMemoryMiddleware } from './middleware.js'; -export type { HindsightRequest } from './middleware.js'; - -export { deriveBankId } from './bank.js'; -export type { BankContext } from './bank.js'; - -export { loadConfig } from './config.js'; -export type { PaperclipMemoryConfig, BankGranularity } from './config.js'; - -export { HindsightClient } from './client.js'; -export type { Memory, RecallResponse, RetainResponse } from './client.js'; diff --git a/hindsight-integrations/paperclip/src/manifest.ts b/hindsight-integrations/paperclip/src/manifest.ts new file mode 100644 index 0000000000..3e3fadc1c2 --- /dev/null +++ b/hindsight-integrations/paperclip/src/manifest.ts @@ -0,0 +1,102 @@ +import type { PaperclipPluginManifestV1 } from "@paperclipai/plugin-sdk"; + +const manifest: PaperclipPluginManifestV1 = { + id: "paperclip-plugin-hindsight", + apiVersion: 1, + version: "0.2.0", + displayName: "Hindsight Memory", + author: "Vectorize ", + description: + "Persistent long-term memory for Paperclip agents. Automatically recalls relevant context before each run and retains agent output after — so every agent gets smarter over time.", + categories: ["automation"], + capabilities: [ + "events.subscribe", + "agent.tools.register", + "plugin.state.read", + "plugin.state.write", + "http.outbound", + "secrets.read-ref", + "agents.read", + ], + entrypoints: { + worker: "./dist/worker.js", + }, + instanceConfigSchema: { + type: "object", + required: ["hindsightApiUrl"], + properties: { + hindsightApiUrl: { + type: "string", + title: "Hindsight API URL", + description: + "Base URL of your Hindsight instance. Use http://localhost:8888 for self-hosted.", + default: "http://localhost:8888", + }, + hindsightApiKeyRef: { + type: "string", + title: "Hindsight API Key (secret ref)", + description: + "Name of the Paperclip secret holding your Hindsight Cloud API key. Leave empty for self-hosted.", + }, + bankGranularity: { + type: "array", + title: "Bank Granularity", + description: + "Controls memory isolation. Default ['company', 'agent'] gives each agent its own bank per company.", + items: { type: "string", enum: ["company", "agent"] }, + default: ["company", "agent"], + }, + recallBudget: { + type: "string", + title: "Recall Budget", + description: + "'low' is fastest, 'mid' balances speed and depth, 'high' is most thorough.", + enum: ["low", "mid", "high"], + default: "mid", + }, + autoRetain: { + type: "boolean", + title: "Auto-retain on Run Finished", + description: + "Automatically retain agent run output to Hindsight when a run completes.", + default: true, + }, + }, + }, + tools: [ + { + name: "hindsight_recall", + displayName: "Recall from Memory", + description: + "Search Hindsight long-term memory for context relevant to a query. Use this before starting a task to surface relevant past decisions, preferences, and knowledge.", + parametersSchema: { + type: "object", + required: ["query"], + properties: { + query: { + type: "string", + description: "What to search for in memory", + }, + }, + }, + }, + { + name: "hindsight_retain", + displayName: "Save to Memory", + description: + "Store important facts, decisions, or outcomes in Hindsight long-term memory for future runs.", + parametersSchema: { + type: "object", + required: ["content"], + properties: { + content: { + type: "string", + description: "The content to store in memory", + }, + }, + }, + }, + ], +}; + +export default manifest; diff --git a/hindsight-integrations/paperclip/src/middleware.ts b/hindsight-integrations/paperclip/src/middleware.ts deleted file mode 100644 index 6a7ef4d1bb..0000000000 --- a/hindsight-integrations/paperclip/src/middleware.ts +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Express middleware for Paperclip HTTP adapter agents. - * - * Automatically injects recalled memories into each request and - * retains the agent's output after each response. - * - * Paperclip HTTP adapter request shape: - * { - * runId: string, - * agentId: string, - * companyId: string, - * context: { taskId: string, taskDescription?: string, ... } - * } - */ - -import type { Request, Response, NextFunction } from 'express'; -import type { PaperclipMemoryConfig } from './config.js'; -import { recall } from './recall.js'; -import { retain } from './retain.js'; - -/** Augmented request with Hindsight memory context. */ -export interface HindsightRequest extends Request { - hindsight: { - memories: string; - companyId: string; - agentId: string; - runId: string; - }; -} - -/** - * Create Express middleware that auto-recalls before each heartbeat - * and auto-retains after each response. - * - * @example - * ```typescript - * import express from 'express' - * import { createMemoryMiddleware, loadConfig } from '@vectorize-io/hindsight-paperclip' - * - * const app = express() - * app.use(express.json()) - * app.use(createMemoryMiddleware(loadConfig())) - * - * app.post('/heartbeat', (req, res) => { - * const { memories, runId } = (req as HindsightRequest).hindsight - * const { context } = req.body - * - * const prompt = memories - * ? `Past context:\n${memories}\n\nCurrent task: ${context.taskDescription}` - * : `Task: ${context.taskDescription}` - * - * // ... run agent ... - * res.json({ output: agentOutput }) // auto-retained by middleware - * }) - * ``` - */ -export function createMemoryMiddleware(config: PaperclipMemoryConfig) { - return async (req: Request, res: Response, next: NextFunction): Promise => { - const { runId, agentId, companyId, context } = req.body ?? {}; - - if (!agentId || !companyId) { - next(); - return; - } - - const query: string = context?.taskDescription ?? context?.taskTitle ?? ''; - - // Pre-recall: inject memories into request - const memories = await recall({ companyId, agentId, query }, config); - (req as HindsightRequest).hindsight = { - memories, - companyId, - agentId, - runId: runId ?? '', - }; - - // Post-retain: wrap res.json to capture agent output - const originalJson = res.json.bind(res) as (body: unknown) => Response; - (res as Response).json = function (body: unknown): Response { - // Fire-and-forget retain (don't block response) - if (body && typeof body === 'object' && 'output' in body && runId) { - const output = (body as { output: unknown }).output; - if (typeof output === 'string' && output.trim()) { - retain( - { companyId, agentId, content: output, documentId: runId }, - config, - ).catch((err) => { - console.warn('[hindsight-paperclip] retain failed:', (err as Error).message); - }); - } - } - return originalJson(body); - }; - - next(); - }; -} diff --git a/hindsight-integrations/paperclip/src/recall.ts b/hindsight-integrations/paperclip/src/recall.ts deleted file mode 100644 index f37f83043f..0000000000 --- a/hindsight-integrations/paperclip/src/recall.ts +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Recall memories for a Paperclip agent heartbeat. - * - * Call this before the agent processes a task to inject relevant context - * from prior heartbeats and sessions. - */ - -import { HindsightClient } from './client.js'; -import type { PaperclipMemoryConfig } from './config.js'; -import { deriveBankId } from './bank.js'; - -export interface RecallInput { - /** Paperclip company ID — used to derive the bank ID. */ - companyId: string; - /** Paperclip agent ID — used to derive the bank ID. */ - agentId: string; - /** - * Query string for memory retrieval. Typically the task title + description. - * e.g. `${issue.title}\n${issue.description}` - */ - query: string; -} - -/** - * Retrieve relevant memories for the current Paperclip task. - * - * Returns a formatted string of memories to inject into the agent's prompt, - * or an empty string if no relevant memories are found. - * - * @example - * ```typescript - * const memories = await recall( - * { companyId, agentId, query: `${task.title}\n${task.description}` }, - * loadConfig() - * ) - * if (memories) { - * systemPrompt = `Past context:\n${memories}\n\n${systemPrompt}` - * } - * ``` - */ -export async function recall( - input: RecallInput, - config: PaperclipMemoryConfig, -): Promise { - const { companyId, agentId, query } = input; - - if (!query.trim()) return ''; - - const bankId = deriveBankId({ companyId, agentId }, config); - const client = new HindsightClient(config); - - let results: Array<{ text: string; type?: string; mentionedAt?: string }>; - try { - const response = await client.recall(bankId, query, { - budget: config.recallBudget, - maxTokens: config.recallMaxTokens, - }); - results = response.results; - } catch (err) { - console.warn('[hindsight-paperclip] recall failed:', (err as Error).message); - return ''; - } - - if (!results.length) return ''; - - return formatMemories(results); -} - -function formatMemories( - results: Array<{ text: string; type?: string; mentionedAt?: string }>, -): string { - return results - .map((r) => { - const typeStr = r.type ? ` [${r.type}]` : ''; - const dateStr = r.mentionedAt ? ` (${r.mentionedAt})` : ''; - return `- ${r.text}${typeStr}${dateStr}`; - }) - .join('\n\n'); -} diff --git a/hindsight-integrations/paperclip/src/retain.ts b/hindsight-integrations/paperclip/src/retain.ts deleted file mode 100644 index 52e78f14a8..0000000000 --- a/hindsight-integrations/paperclip/src/retain.ts +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Retain memories after a Paperclip agent heartbeat. - * - * Call this after the agent completes a task to store what it did - * so future heartbeats can recall the context. - */ - -import { HindsightClient } from './client.js'; -import type { PaperclipMemoryConfig } from './config.js'; -import { deriveBankId } from './bank.js'; - -export interface RetainInput { - /** Paperclip company ID — used to derive the bank ID. */ - companyId: string; - /** Paperclip agent ID — used to derive the bank ID. */ - agentId: string; - /** The agent's output or summary of what it did during the heartbeat. */ - content: string; - /** Paperclip run ID — used as document ID to prevent duplicate storage. */ - documentId: string; - /** Additional metadata to store with the memory. */ - metadata?: Record; -} - -/** - * Store the agent's output as a memory after a Paperclip task heartbeat. - * - * Fails silently — memory retention is an enhancement, not a requirement. - * - * @example - * ```typescript - * await retain( - * { companyId, agentId, content: agentOutput, documentId: runId }, - * loadConfig() - * ) - * ``` - */ -export async function retain( - input: RetainInput, - config: PaperclipMemoryConfig, -): Promise { - const { companyId, agentId, content, documentId, metadata } = input; - - if (!content.trim()) return; - - const bankId = deriveBankId({ companyId, agentId }, config); - const client = new HindsightClient(config); - - try { - await client.retain(bankId, content, { - documentId, - context: config.retainContext, - metadata: { companyId, agentId, ...metadata }, - }); - } catch (err) { - console.warn('[hindsight-paperclip] retain failed:', (err as Error).message); - } -} diff --git a/hindsight-integrations/paperclip/src/worker.ts b/hindsight-integrations/paperclip/src/worker.ts new file mode 100644 index 0000000000..fb49e93820 --- /dev/null +++ b/hindsight-integrations/paperclip/src/worker.ts @@ -0,0 +1,255 @@ +/** + * paperclip-plugin-hindsight — worker entrypoint. + * + * Gives Paperclip agents persistent long-term memory via Hindsight. + * + * Lifecycle: + * agent.run.started → recall relevant memories, store in plugin state for the run + * agent.run.finished → retain agent output to Hindsight (if autoRetain is enabled) + * + * Agent tools (callable mid-run): + * hindsight_recall(query) → search memory, returns relevant context + * hindsight_retain(content) → store content in memory immediately + */ + +import { definePlugin, runWorker } from "@paperclipai/plugin-sdk"; +import type { ToolRunContext } from "@paperclipai/plugin-sdk"; +import { HindsightClient, formatMemories } from "./client.js"; +import { deriveBankId } from "./bank.js"; + +interface PluginConfig { + hindsightApiUrl: string; + hindsightApiKeyRef?: string; + bankGranularity?: Array<"company" | "agent">; + recallBudget?: "low" | "mid" | "high"; + autoRetain?: boolean; +} + +interface RunStartedPayload { + agentId: string; + runId: string; + issueTitle?: string; + issueDescription?: string; +} + +interface RunFinishedPayload { + agentId: string; + runId: string; + output?: string; + result?: string; +} + +async function getConfig(ctx: { config: { get(): Promise> } }): Promise { + return (await ctx.config.get()) as unknown as PluginConfig; +} + +async function resolveApiKey( + ctx: { secrets: { resolve(ref: string): Promise } }, + config: PluginConfig, +): Promise { + if (!config.hindsightApiKeyRef) return undefined; + const resolved = await ctx.secrets.resolve(config.hindsightApiKeyRef); + return resolved ?? undefined; +} + +const plugin = definePlugin({ + async setup(ctx) { + ctx.logger.info("Hindsight memory plugin starting"); + + // --------------------------------------------------------------------------- + // agent.run.started — recall memories and cache them for this run + // --------------------------------------------------------------------------- + ctx.events.on("agent.run.started", async (event) => { + const payload = event.payload as RunStartedPayload; + const config = await getConfig(ctx); + const { agentId, runId, issueTitle, issueDescription } = payload; + const companyId = event.companyId; + + const query = [issueTitle, issueDescription].filter(Boolean).join("\n"); + if (!query.trim()) return; + + try { + const apiKey = await resolveApiKey(ctx, config); + const client = new HindsightClient(config.hindsightApiUrl, apiKey); + const bankId = deriveBankId({ companyId, agentId }, config); + + const response = await client.recall( + bankId, + query, + config.recallBudget ?? "mid", + ); + + const memories = formatMemories(response.results); + if (memories) { + await ctx.state.set( + { scopeKind: "run", scopeId: runId, stateKey: "recalled-memories" }, + memories, + ); + ctx.logger.info("Recalled memories for run", { + runId, + bankId, + count: response.results.length, + }); + } + } catch (err) { + // Non-fatal: agent runs without memory context. + ctx.logger.warn("Failed to recall memories on run start", { + runId, + error: String(err), + }); + } + }); + + // --------------------------------------------------------------------------- + // agent.run.finished — retain run output to Hindsight + // --------------------------------------------------------------------------- + ctx.events.on("agent.run.finished", async (event) => { + const payload = event.payload as RunFinishedPayload; + const config = await getConfig(ctx); + + if (config.autoRetain === false) return; + + const { agentId, runId, output, result } = payload; + const companyId = event.companyId; + const content = output ?? result; + + if (!content?.trim()) return; + + try { + const apiKey = await resolveApiKey(ctx, config); + const client = new HindsightClient(config.hindsightApiUrl, apiKey); + const bankId = deriveBankId({ companyId, agentId }, config); + + await client.retain(bankId, content, runId, { agentId, companyId, runId }); + ctx.logger.info("Retained run output to memory", { runId, bankId }); + } catch (err) { + ctx.logger.warn("Failed to retain run output", { + runId, + error: String(err), + }); + } + }); + + // --------------------------------------------------------------------------- + // Tool: hindsight_recall + // --------------------------------------------------------------------------- + ctx.tools.register( + "hindsight_recall", + { + displayName: "Recall from Memory", + description: + "Search Hindsight long-term memory for context relevant to a query.", + parametersSchema: { + type: "object", + required: ["query"], + properties: { + query: { type: "string", description: "What to search for" }, + }, + }, + }, + async (params: unknown, runCtx: ToolRunContext) => { + const { query } = params as { query: string }; + const config = await getConfig(ctx); + const bankId = deriveBankId( + { companyId: runCtx.companyId, agentId: runCtx.agentId }, + config, + ); + + // Return cached memories from run start if available + const cached = await ctx.state.get({ + scopeKind: "run", + scopeId: runCtx.runId, + stateKey: "recalled-memories", + }); + if (cached && typeof cached === "string") { + return { content: cached }; + } + + // Live recall fallback + try { + const apiKey = await resolveApiKey(ctx, config); + const client = new HindsightClient(config.hindsightApiUrl, apiKey); + const response = await client.recall(bankId, query, config.recallBudget ?? "mid"); + const memories = formatMemories(response.results); + return { content: memories || "No relevant memories found." }; + } catch (err) { + return { content: `Memory recall failed: ${String(err)}` }; + } + }, + ); + + // --------------------------------------------------------------------------- + // Tool: hindsight_retain + // --------------------------------------------------------------------------- + ctx.tools.register( + "hindsight_retain", + { + displayName: "Save to Memory", + description: + "Store important facts, decisions, or outcomes in Hindsight long-term memory for future runs.", + parametersSchema: { + type: "object", + required: ["content"], + properties: { + content: { + type: "string", + description: "The content to store in memory", + }, + }, + }, + }, + async (params: unknown, runCtx: ToolRunContext) => { + const { content } = params as { content: string }; + const config = await getConfig(ctx); + const bankId = deriveBankId( + { companyId: runCtx.companyId, agentId: runCtx.agentId }, + config, + ); + + try { + const apiKey = await resolveApiKey(ctx, config); + const client = new HindsightClient(config.hindsightApiUrl, apiKey); + await client.retain(bankId, content, undefined, { + agentId: runCtx.agentId, + companyId: runCtx.companyId, + runId: runCtx.runId, + }); + return { content: "Memory saved." }; + } catch (err) { + return { content: `Failed to save memory: ${String(err)}` }; + } + }, + ); + + ctx.logger.info("Hindsight memory plugin ready"); + }, + + async onHealth() { + return { status: "ok" }; + }, + + async onValidateConfig(config) { + const c = config as Partial; + if (!c.hindsightApiUrl?.trim()) { + return { ok: false, errors: ["hindsightApiUrl is required"] }; + } + + try { + const client = new HindsightClient(c.hindsightApiUrl); + const healthy = await client.health(); + if (!healthy) { + return { + ok: false, + errors: [`Cannot reach Hindsight at ${c.hindsightApiUrl}`], + }; + } + } catch (err) { + return { ok: false, errors: [`Connection failed: ${String(err)}`] }; + } + + return { ok: true }; + }, +}); + +export default plugin; +runWorker(plugin, import.meta.url); diff --git a/hindsight-integrations/paperclip/tests/bank.test.ts b/hindsight-integrations/paperclip/tests/bank.test.ts deleted file mode 100644 index a33d54308d..0000000000 --- a/hindsight-integrations/paperclip/tests/bank.test.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { deriveBankId } from '../src/bank.js'; -import { loadConfig } from '../src/config.js'; - -describe('deriveBankId', () => { - const ctx = { companyId: 'co-123', agentId: 'ag-456' }; - - const baseUrl = 'http://fake:9077'; - - it('default: paperclip::companyId::agentId', () => { - const config = loadConfig({ hindsightApiUrl: baseUrl }); - expect(deriveBankId(ctx, config)).toBe('paperclip::co-123::ag-456'); - }); - - it('company-only granularity', () => { - const config = loadConfig({ hindsightApiUrl: baseUrl, bankGranularity: ['company'] }); - expect(deriveBankId(ctx, config)).toBe('paperclip::co-123'); - }); - - it('agent-only granularity', () => { - const config = loadConfig({ hindsightApiUrl: baseUrl, bankGranularity: ['agent'] }); - expect(deriveBankId(ctx, config)).toBe('paperclip::ag-456'); - }); - - it('custom prefix', () => { - const config = loadConfig({ hindsightApiUrl: baseUrl, bankIdPrefix: 'myapp' }); - expect(deriveBankId(ctx, config)).toBe('myapp::co-123::ag-456'); - }); - - it('empty prefix with default granularity', () => { - const config = loadConfig({ hindsightApiUrl: baseUrl, bankIdPrefix: '' }); - expect(deriveBankId(ctx, config)).toBe('co-123::ag-456'); - }); - - it('throws when bank ID would be empty', () => { - const config = loadConfig({ hindsightApiUrl: baseUrl, bankIdPrefix: '', bankGranularity: [] }); - expect(() => deriveBankId(ctx, config)).toThrow('Bank ID cannot be empty'); - }); - - it('reversed granularity order', () => { - const config = loadConfig({ hindsightApiUrl: baseUrl, bankGranularity: ['agent', 'company'] }); - expect(deriveBankId(ctx, config)).toBe('paperclip::ag-456::co-123'); - }); -}); diff --git a/hindsight-integrations/paperclip/tests/plugin.spec.ts b/hindsight-integrations/paperclip/tests/plugin.spec.ts new file mode 100644 index 0000000000..8e02e3f5a5 --- /dev/null +++ b/hindsight-integrations/paperclip/tests/plugin.spec.ts @@ -0,0 +1,338 @@ +/** + * Tests for paperclip-plugin-hindsight. + * + * Uses @paperclipai/plugin-sdk's createTestHarness to simulate the Paperclip + * host environment without requiring a running Paperclip instance. + * + * Hindsight API calls are intercepted via global fetch mocking. + */ + +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { createTestHarness } from "@paperclipai/plugin-sdk"; +import manifest from "../src/manifest.js"; +import plugin from "../src/worker.js"; + +// --------------------------------------------------------------------------- +// Fetch mock helpers +// --------------------------------------------------------------------------- + +function mockFetch(responses: Array<{ url: string | RegExp; body: unknown; status?: number }>) { + return vi.fn(async (url: string) => { + const match = responses.find((r) => + typeof r.url === "string" ? url.includes(r.url) : r.url.test(url), + ); + if (!match) { + return new Response(JSON.stringify({ error: "unmatched url" }), { status: 404 }); + } + return new Response(JSON.stringify(match.body), { + status: match.status ?? 200, + }); + }); +} + +// --------------------------------------------------------------------------- +// Harness setup +// --------------------------------------------------------------------------- + +const DEFAULT_CONFIG = { + hindsightApiUrl: "http://localhost:8888", + bankGranularity: ["company", "agent"], + recallBudget: "mid", + autoRetain: true, +}; + +function buildHarness(config: Record = DEFAULT_CONFIG) { + return createTestHarness({ manifest, config, capabilities: manifest.capabilities }); +} + +async function setupPlugin(harness: ReturnType) { + await plugin.definition.setup(harness.ctx); +} + +// --------------------------------------------------------------------------- +// Bank ID derivation +// --------------------------------------------------------------------------- + +describe("bank ID derivation", () => { + it("default: company + agent", async () => { + const { deriveBankId } = await import("../src/bank.js"); + expect( + deriveBankId( + { companyId: "co-1", agentId: "ag-1" }, + { bankGranularity: ["company", "agent"] }, + ), + ).toBe("paperclip::co-1::ag-1"); + }); + + it("company only", async () => { + const { deriveBankId } = await import("../src/bank.js"); + expect( + deriveBankId( + { companyId: "co-1", agentId: "ag-1" }, + { bankGranularity: ["company"] }, + ), + ).toBe("paperclip::co-1"); + }); + + it("agent only", async () => { + const { deriveBankId } = await import("../src/bank.js"); + expect( + deriveBankId( + { companyId: "co-1", agentId: "ag-1" }, + { bankGranularity: ["agent"] }, + ), + ).toBe("paperclip::ag-1"); + }); +}); + +// --------------------------------------------------------------------------- +// agent.run.started — recall +// --------------------------------------------------------------------------- + +describe("agent.run.started", () => { + let fetchMock: ReturnType; + + beforeEach(() => { + fetchMock = mockFetch([ + { url: /recall/, body: { results: [{ text: "User prefers TypeScript" }] } }, + ]); + vi.stubGlobal("fetch", fetchMock); + }); + + afterEach(() => { + vi.unstubAllGlobals(); + }); + + it("calls recall and caches memories in plugin state", async () => { + const harness = buildHarness(); + await setupPlugin(harness); + + await harness.emit( + "agent.run.started", + { agentId: "ag-1", runId: "run-1", issueTitle: "Refactor auth module", issueDescription: "Migrate to JWT" }, + { companyId: "co-1" }, + ); + + const recallCall = fetchMock.mock.calls.find(([url]: [string]) => + url.includes("recall"), + ); + expect(recallCall).toBeDefined(); + expect(recallCall?.[0]).toContain("paperclip%3A%3Aco-1%3A%3Aag-1"); + + const state = harness.getState({ + scopeKind: "run", + scopeId: "run-1", + stateKey: "recalled-memories", + }); + expect(state).toContain("TypeScript"); + }); + + it("skips recall when no issue context provided", async () => { + const harness = buildHarness(); + await setupPlugin(harness); + + await harness.emit( + "agent.run.started", + { agentId: "ag-1", runId: "run-2" }, + { companyId: "co-1" }, + ); + + expect(fetchMock).not.toHaveBeenCalled(); + }); + + it("does not throw when Hindsight is unreachable", async () => { + vi.stubGlobal("fetch", vi.fn(async () => new Response("", { status: 503 }))); + const harness = buildHarness(); + await setupPlugin(harness); + + await expect( + harness.emit( + "agent.run.started", + { agentId: "ag-1", runId: "run-3", issueTitle: "Fix bug" }, + { companyId: "co-1" }, + ), + ).resolves.not.toThrow(); + }); +}); + +// --------------------------------------------------------------------------- +// agent.run.finished — auto-retain +// --------------------------------------------------------------------------- + +describe("agent.run.finished", () => { + let fetchMock: ReturnType; + + beforeEach(() => { + fetchMock = mockFetch([{ url: /memories$/, body: { success: true } }]); + vi.stubGlobal("fetch", fetchMock); + }); + + afterEach(() => { + vi.unstubAllGlobals(); + }); + + it("retains run output with runId as document ID", async () => { + const harness = buildHarness(); + await setupPlugin(harness); + + await harness.emit( + "agent.run.finished", + { agentId: "ag-1", runId: "run-1", output: "Refactored auth. Migrated to JWT with 24h expiry." }, + { companyId: "co-1" }, + ); + + const retainCall = fetchMock.mock.calls.find(([url]: [string]) => + /memories$/.test(url), + ); + expect(retainCall).toBeDefined(); + + const body = JSON.parse(retainCall?.[1]?.body as string) as { + items: Array<{ content: string; document_id?: string }>; + }; + expect(body.items[0]?.content).toContain("JWT"); + expect(body.items[0]?.document_id).toBe("run-1"); + }); + + it("skips retain when output is empty", async () => { + const harness = buildHarness(); + await setupPlugin(harness); + + await harness.emit( + "agent.run.finished", + { agentId: "ag-1", runId: "run-2", output: "" }, + { companyId: "co-1" }, + ); + + expect(fetchMock).not.toHaveBeenCalled(); + }); + + it("skips retain when autoRetain is false", async () => { + const harness = buildHarness({ ...DEFAULT_CONFIG, autoRetain: false }); + await setupPlugin(harness); + + await harness.emit( + "agent.run.finished", + { agentId: "ag-1", runId: "run-3", output: "Some output" }, + { companyId: "co-1" }, + ); + + expect(fetchMock).not.toHaveBeenCalled(); + }); +}); + +// --------------------------------------------------------------------------- +// hindsight_recall tool +// --------------------------------------------------------------------------- + +describe("hindsight_recall tool", () => { + afterEach(() => { + vi.unstubAllGlobals(); + }); + + it("returns cached memories from run start without additional API call", async () => { + vi.stubGlobal("fetch", vi.fn(async () => new Response("{}", { status: 200 }))); + const harness = buildHarness(); + await setupPlugin(harness); + + // Emit agent.run.started so recall fires and caches state + vi.stubGlobal( + "fetch", + mockFetch([{ url: /recall/, body: { results: [{ text: "User prefers dark mode" }] } }]), + ); + await harness.emit( + "agent.run.started", + { agentId: "ag-1", runId: "run-1", issueTitle: "Update UI" }, + { companyId: "co-1" }, + ); + + // Now recall tool should return cached state, not hit the API again + const callsBefore = (vi.mocked(fetch) as ReturnType).mock.calls.length; + const result = await harness.executeTool( + "hindsight_recall", + { query: "preferences" }, + { agentId: "ag-1", runId: "run-1", companyId: "co-1", projectId: "proj-1" }, + ); + + expect((result as { content: string }).content).toContain("dark mode"); + const callsAfter = (vi.mocked(fetch) as ReturnType).mock.calls.length; + // No new recall call — returned from cache + expect(callsAfter).toBe(callsBefore); + }); + + it("falls back to live recall when no cached state", async () => { + vi.stubGlobal( + "fetch", + mockFetch([{ url: /recall/, body: { results: [{ text: "Agent is a Python specialist" }] } }]), + ); + const harness = buildHarness(); + await setupPlugin(harness); + + const result = await harness.executeTool( + "hindsight_recall", + { query: "specialization" }, + { agentId: "ag-1", runId: "run-2", companyId: "co-1", projectId: "proj-1" }, + ); + + expect((result as { content: string }).content).toContain("Python specialist"); + }); +}); + +// --------------------------------------------------------------------------- +// hindsight_retain tool +// --------------------------------------------------------------------------- + +describe("hindsight_retain tool", () => { + afterEach(() => { + vi.unstubAllGlobals(); + }); + + it("stores content via Hindsight retain endpoint", async () => { + const fetchMock = mockFetch([{ url: /memories$/, body: { success: true } }]); + vi.stubGlobal("fetch", fetchMock); + const harness = buildHarness(); + await setupPlugin(harness); + + const result = await harness.executeTool( + "hindsight_retain", + { content: "Decision: use Postgres not MySQL" }, + { agentId: "ag-1", runId: "run-1", companyId: "co-1", projectId: "proj-1" }, + ); + + expect((result as { content: string }).content).toBe("Memory saved."); + const call = fetchMock.mock.calls.find(([url]: [string]) => /memories$/.test(url)); + const body = JSON.parse(call?.[1]?.body as string) as { + items: Array<{ content: string }>; + }; + expect(body.items[0]?.content).toContain("Postgres"); + }); +}); + +// --------------------------------------------------------------------------- +// onValidateConfig +// --------------------------------------------------------------------------- + +describe("onValidateConfig", () => { + it("fails when hindsightApiUrl is missing", async () => { + const result = await plugin.definition.onValidateConfig!({ hindsightApiUrl: "" }); + expect(result.ok).toBe(false); + expect(result.errors?.some((e) => e.includes("hindsightApiUrl"))).toBe(true); + }); + + it("fails when Hindsight is unreachable", async () => { + vi.stubGlobal("fetch", vi.fn(async () => new Response("", { status: 503 }))); + const result = await plugin.definition.onValidateConfig!({ + hindsightApiUrl: "http://localhost:8888", + }); + expect(result.ok).toBe(false); + vi.unstubAllGlobals(); + }); + + it("passes with a reachable Hindsight instance", async () => { + vi.stubGlobal("fetch", vi.fn(async () => new Response("{}", { status: 200 }))); + const result = await plugin.definition.onValidateConfig!({ + hindsightApiUrl: "http://localhost:8888", + }); + expect(result.ok).toBe(true); + vi.unstubAllGlobals(); + }); +}); diff --git a/hindsight-integrations/paperclip/tests/recall.test.ts b/hindsight-integrations/paperclip/tests/recall.test.ts deleted file mode 100644 index e07278e2fb..0000000000 --- a/hindsight-integrations/paperclip/tests/recall.test.ts +++ /dev/null @@ -1,118 +0,0 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { recall } from '../src/recall.js'; -import { loadConfig } from '../src/config.js'; - -// Mock fetch globally -const mockFetch = vi.fn(); -vi.stubGlobal('fetch', mockFetch); - -function makeRecallResponse(results: Array<{ text: string; type?: string; mentionedAt?: string }>) { - return { - ok: true, - status: 200, - json: async () => ({ results }), - text: async () => '', - } as unknown as Response; -} - -function makeErrorResponse(status: number, body = '') { - return { - ok: false, - status, - json: async () => { throw new Error('not json'); }, - text: async () => body, - } as unknown as Response; -} - -beforeEach(() => { - mockFetch.mockReset(); -}); - -const config = loadConfig({ hindsightApiUrl: 'http://fake:9077' }); -const input = { companyId: 'co-1', agentId: 'ag-1', query: 'what did I work on?' }; - -describe('recall()', () => { - it('returns empty string for blank query', async () => { - const result = await recall({ ...input, query: ' ' }, config); - expect(result).toBe(''); - expect(mockFetch).not.toHaveBeenCalled(); - }); - - it('formats memories as bullet list', async () => { - mockFetch.mockResolvedValue(makeRecallResponse([ - { text: 'Fixed the login bug', type: 'experience' }, - { text: 'Prefers TypeScript', type: 'preference' }, - ])); - const result = await recall(input, config); - expect(result).toContain('- Fixed the login bug [experience]'); - expect(result).toContain('- Prefers TypeScript [preference]'); - }); - - it('includes mentionedAt date when present', async () => { - mockFetch.mockResolvedValue(makeRecallResponse([ - { text: 'Deployed to prod', mentionedAt: '2024-01-15' }, - ])); - const result = await recall(input, config); - expect(result).toContain('(2024-01-15)'); - }); - - it('returns empty string when no results', async () => { - mockFetch.mockResolvedValue(makeRecallResponse([])); - const result = await recall(input, config); - expect(result).toBe(''); - }); - - it('gracefully degrades on HTTP error', async () => { - mockFetch.mockResolvedValue(makeErrorResponse(500, 'Internal Server Error')); - const result = await recall(input, config); - expect(result).toBe(''); - }); - - it('gracefully degrades on network error', async () => { - mockFetch.mockRejectedValue(new Error('ECONNREFUSED')); - const result = await recall(input, config); - expect(result).toBe(''); - }); - - it('calls the correct API path with bank ID', async () => { - mockFetch.mockResolvedValue(makeRecallResponse([])); - await recall(input, config); - const [url] = mockFetch.mock.calls[0] as [string, RequestInit]; - expect(url).toContain('/v1/default/banks/paperclip%3A%3Aco-1%3A%3Aag-1/memories/recall'); - }); - - it('sends query and budget in request body', async () => { - mockFetch.mockResolvedValue(makeRecallResponse([])); - await recall(input, config); - const [, init] = mockFetch.mock.calls[0] as [string, RequestInit]; - const body = JSON.parse(init.body as string); - expect(body.query).toBe('what did I work on?'); - expect(body.budget).toBe('mid'); - expect(body.max_tokens).toBe(1024); - }); - - it('uses custom budget and max_tokens from config', async () => { - const customConfig = loadConfig({ - hindsightApiUrl: 'http://fake:9077', - recallBudget: 'high', - recallMaxTokens: 2048, - }); - mockFetch.mockResolvedValue(makeRecallResponse([])); - await recall(input, customConfig); - const [, init] = mockFetch.mock.calls[0] as [string, RequestInit]; - const body = JSON.parse(init.body as string); - expect(body.budget).toBe('high'); - expect(body.max_tokens).toBe(2048); - }); - - it('sends Authorization header when token is set', async () => { - const authConfig = loadConfig({ - hindsightApiUrl: 'http://fake:9077', - hindsightApiToken: 'hsk_test123', - }); - mockFetch.mockResolvedValue(makeRecallResponse([])); - await recall(input, authConfig); - const [, init] = mockFetch.mock.calls[0] as [string, RequestInit]; - expect((init.headers as Record)['Authorization']).toBe('Bearer hsk_test123'); - }); -}); diff --git a/hindsight-integrations/paperclip/tests/retain.test.ts b/hindsight-integrations/paperclip/tests/retain.test.ts deleted file mode 100644 index f776a4944d..0000000000 --- a/hindsight-integrations/paperclip/tests/retain.test.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest'; -import { retain } from '../src/retain.js'; -import { loadConfig } from '../src/config.js'; - -const mockFetch = vi.fn(); -vi.stubGlobal('fetch', mockFetch); - -function makeRetainResponse() { - return { - ok: true, - status: 200, - json: async () => ({ success: true }), - text: async () => '', - } as unknown as Response; -} - -function makeErrorResponse(status: number) { - return { - ok: false, - status, - json: async () => { throw new Error('not json'); }, - text: async () => 'error', - } as unknown as Response; -} - -beforeEach(() => { - mockFetch.mockReset(); -}); - -const config = loadConfig({ hindsightApiUrl: 'http://fake:9077' }); -const input = { - companyId: 'co-1', - agentId: 'ag-1', - content: 'Fixed the authentication bug in login.ts', - documentId: 'run-abc123', -}; - -describe('retain()', () => { - it('does nothing for blank content', async () => { - await retain({ ...input, content: ' ' }, config); - expect(mockFetch).not.toHaveBeenCalled(); - }); - - it('calls the correct API path', async () => { - mockFetch.mockResolvedValue(makeRetainResponse()); - await retain(input, config); - const [url] = mockFetch.mock.calls[0] as [string, RequestInit]; - expect(url).toContain('/v1/default/banks/paperclip%3A%3Aco-1%3A%3Aag-1/memories'); - }); - - it('sends content in request body items array', async () => { - mockFetch.mockResolvedValue(makeRetainResponse()); - await retain(input, config); - const [, init] = mockFetch.mock.calls[0] as [string, RequestInit]; - const body = JSON.parse(init.body as string); - expect(body.items).toHaveLength(1); - expect(body.items[0].content).toBe('Fixed the authentication bug in login.ts'); - }); - - it('sends document_id to prevent duplicates', async () => { - mockFetch.mockResolvedValue(makeRetainResponse()); - await retain(input, config); - const [, init] = mockFetch.mock.calls[0] as [string, RequestInit]; - const body = JSON.parse(init.body as string); - expect(body.items[0].document_id).toBe('run-abc123'); - }); - - it('includes companyId and agentId in metadata', async () => { - mockFetch.mockResolvedValue(makeRetainResponse()); - await retain(input, config); - const [, init] = mockFetch.mock.calls[0] as [string, RequestInit]; - const body = JSON.parse(init.body as string); - expect(body.items[0].metadata.companyId).toBe('co-1'); - expect(body.items[0].metadata.agentId).toBe('ag-1'); - }); - - it('merges custom metadata with default metadata', async () => { - mockFetch.mockResolvedValue(makeRetainResponse()); - await retain({ ...input, metadata: { taskId: 'task-99' } }, config); - const [, init] = mockFetch.mock.calls[0] as [string, RequestInit]; - const body = JSON.parse(init.body as string); - expect(body.items[0].metadata.taskId).toBe('task-99'); - expect(body.items[0].metadata.companyId).toBe('co-1'); - }); - - it('sets context to retainContext from config', async () => { - mockFetch.mockResolvedValue(makeRetainResponse()); - await retain(input, config); - const [, init] = mockFetch.mock.calls[0] as [string, RequestInit]; - const body = JSON.parse(init.body as string); - expect(body.items[0].context).toBe('paperclip'); - }); - - it('gracefully degrades on HTTP error', async () => { - mockFetch.mockResolvedValue(makeErrorResponse(503)); - // Should not throw - await expect(retain(input, config)).resolves.toBeUndefined(); - }); - - it('gracefully degrades on network error', async () => { - mockFetch.mockRejectedValue(new Error('Network failure')); - await expect(retain(input, config)).resolves.toBeUndefined(); - }); - - it('sends async flag in request body', async () => { - mockFetch.mockResolvedValue(makeRetainResponse()); - await retain(input, config); - const [, init] = mockFetch.mock.calls[0] as [string, RequestInit]; - const body = JSON.parse(init.body as string); - expect(body.async).toBe(true); - }); -}); From 3b4c73cc241e507ebbada24f1a258c9604a612bb Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 15 Apr 2026 09:52:08 -0400 Subject: [PATCH 2/2] chore(paperclip): apply prettier formatting and update skills changelog --- hindsight-integrations/paperclip/README.md | 14 ++-- .../paperclip/esbuild.config.mjs | 4 +- .../paperclip/src/client.ts | 14 +--- .../paperclip/src/manifest.ts | 6 +- .../paperclip/src/worker.ts | 25 +++--- .../paperclip/tests/plugin.spec.ts | 83 +++++++++++-------- .../changelog/integrations/paperclip.md | 15 ++++ 7 files changed, 86 insertions(+), 75 deletions(-) diff --git a/hindsight-integrations/paperclip/README.md b/hindsight-integrations/paperclip/README.md index dca083997e..6f3a632c54 100644 --- a/hindsight-integrations/paperclip/README.md +++ b/hindsight-integrations/paperclip/README.md @@ -33,13 +33,13 @@ Or [Hindsight Cloud](https://ui.hindsight.vectorize.io/signup) — no self-hosti ## Configuration -| Field | Default | Description | -|-------|---------|-------------| -| `hindsightApiUrl` | `http://localhost:8888` | Hindsight server URL | -| `hindsightApiKeyRef` | — | Paperclip secret name holding Hindsight Cloud API key | -| `bankGranularity` | `["company", "agent"]` | Memory isolation: per company+agent, per company, or per agent | -| `recallBudget` | `mid` | `low` = fastest, `mid` = balanced, `high` = most thorough | -| `autoRetain` | `true` | Automatically retain run output after every run | +| Field | Default | Description | +| -------------------- | ----------------------- | -------------------------------------------------------------- | +| `hindsightApiUrl` | `http://localhost:8888` | Hindsight server URL | +| `hindsightApiKeyRef` | — | Paperclip secret name holding Hindsight Cloud API key | +| `bankGranularity` | `["company", "agent"]` | Memory isolation: per company+agent, per company, or per agent | +| `recallBudget` | `mid` | `low` = fastest, `mid` = balanced, `high` = most thorough | +| `autoRetain` | `true` | Automatically retain run output after every run | ## Bank ID Format diff --git a/hindsight-integrations/paperclip/esbuild.config.mjs b/hindsight-integrations/paperclip/esbuild.config.mjs index df8295ae91..f540165484 100644 --- a/hindsight-integrations/paperclip/esbuild.config.mjs +++ b/hindsight-integrations/paperclip/esbuild.config.mjs @@ -16,9 +16,7 @@ const builds = [ ]; if (watch) { - const contexts = await Promise.all( - builds.map((b) => esbuild.context({ ...sharedConfig, ...b })), - ); + const contexts = await Promise.all(builds.map((b) => esbuild.context({ ...sharedConfig, ...b }))); await Promise.all(contexts.map((ctx) => ctx.watch())); console.log("Watching for changes…"); } else { diff --git a/hindsight-integrations/paperclip/src/client.ts b/hindsight-integrations/paperclip/src/client.ts index 23e779381e..3d85fedbac 100644 --- a/hindsight-integrations/paperclip/src/client.ts +++ b/hindsight-integrations/paperclip/src/client.ts @@ -30,11 +30,7 @@ export class HindsightClient { return h; } - private async request( - method: string, - path: string, - body?: unknown, - ): Promise { + private async request(method: string, path: string, body?: unknown): Promise { const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), 15_000); @@ -57,11 +53,7 @@ export class HindsightClient { } } - async recall( - bankId: string, - query: string, - budget = "mid", - ): Promise { + async recall(bankId: string, query: string, budget = "mid"): Promise { const path = `/v1/default/banks/${encodeURIComponent(bankId)}/memories/recall`; return this.request("POST", path, { query, @@ -74,7 +66,7 @@ export class HindsightClient { bankId: string, content: string, documentId?: string, - metadata?: Record, + metadata?: Record ): Promise { const path = `/v1/default/banks/${encodeURIComponent(bankId)}/memories`; const item: Record = { diff --git a/hindsight-integrations/paperclip/src/manifest.ts b/hindsight-integrations/paperclip/src/manifest.ts index 3e3fadc1c2..21e99f49dd 100644 --- a/hindsight-integrations/paperclip/src/manifest.ts +++ b/hindsight-integrations/paperclip/src/manifest.ts @@ -49,16 +49,14 @@ const manifest: PaperclipPluginManifestV1 = { recallBudget: { type: "string", title: "Recall Budget", - description: - "'low' is fastest, 'mid' balances speed and depth, 'high' is most thorough.", + description: "'low' is fastest, 'mid' balances speed and depth, 'high' is most thorough.", enum: ["low", "mid", "high"], default: "mid", }, autoRetain: { type: "boolean", title: "Auto-retain on Run Finished", - description: - "Automatically retain agent run output to Hindsight when a run completes.", + description: "Automatically retain agent run output to Hindsight when a run completes.", default: true, }, }, diff --git a/hindsight-integrations/paperclip/src/worker.ts b/hindsight-integrations/paperclip/src/worker.ts index fb49e93820..85e41e0cd0 100644 --- a/hindsight-integrations/paperclip/src/worker.ts +++ b/hindsight-integrations/paperclip/src/worker.ts @@ -39,13 +39,15 @@ interface RunFinishedPayload { result?: string; } -async function getConfig(ctx: { config: { get(): Promise> } }): Promise { +async function getConfig(ctx: { + config: { get(): Promise> }; +}): Promise { return (await ctx.config.get()) as unknown as PluginConfig; } async function resolveApiKey( ctx: { secrets: { resolve(ref: string): Promise } }, - config: PluginConfig, + config: PluginConfig ): Promise { if (!config.hindsightApiKeyRef) return undefined; const resolved = await ctx.secrets.resolve(config.hindsightApiKeyRef); @@ -73,17 +75,13 @@ const plugin = definePlugin({ const client = new HindsightClient(config.hindsightApiUrl, apiKey); const bankId = deriveBankId({ companyId, agentId }, config); - const response = await client.recall( - bankId, - query, - config.recallBudget ?? "mid", - ); + const response = await client.recall(bankId, query, config.recallBudget ?? "mid"); const memories = formatMemories(response.results); if (memories) { await ctx.state.set( { scopeKind: "run", scopeId: runId, stateKey: "recalled-memories" }, - memories, + memories ); ctx.logger.info("Recalled memories for run", { runId, @@ -137,8 +135,7 @@ const plugin = definePlugin({ "hindsight_recall", { displayName: "Recall from Memory", - description: - "Search Hindsight long-term memory for context relevant to a query.", + description: "Search Hindsight long-term memory for context relevant to a query.", parametersSchema: { type: "object", required: ["query"], @@ -152,7 +149,7 @@ const plugin = definePlugin({ const config = await getConfig(ctx); const bankId = deriveBankId( { companyId: runCtx.companyId, agentId: runCtx.agentId }, - config, + config ); // Return cached memories from run start if available @@ -175,7 +172,7 @@ const plugin = definePlugin({ } catch (err) { return { content: `Memory recall failed: ${String(err)}` }; } - }, + } ); // --------------------------------------------------------------------------- @@ -203,7 +200,7 @@ const plugin = definePlugin({ const config = await getConfig(ctx); const bankId = deriveBankId( { companyId: runCtx.companyId, agentId: runCtx.agentId }, - config, + config ); try { @@ -218,7 +215,7 @@ const plugin = definePlugin({ } catch (err) { return { content: `Failed to save memory: ${String(err)}` }; } - }, + } ); ctx.logger.info("Hindsight memory plugin ready"); diff --git a/hindsight-integrations/paperclip/tests/plugin.spec.ts b/hindsight-integrations/paperclip/tests/plugin.spec.ts index 8e02e3f5a5..6d224363da 100644 --- a/hindsight-integrations/paperclip/tests/plugin.spec.ts +++ b/hindsight-integrations/paperclip/tests/plugin.spec.ts @@ -19,7 +19,7 @@ import plugin from "../src/worker.js"; function mockFetch(responses: Array<{ url: string | RegExp; body: unknown; status?: number }>) { return vi.fn(async (url: string) => { const match = responses.find((r) => - typeof r.url === "string" ? url.includes(r.url) : r.url.test(url), + typeof r.url === "string" ? url.includes(r.url) : r.url.test(url) ); if (!match) { return new Response(JSON.stringify({ error: "unmatched url" }), { status: 404 }); @@ -59,28 +59,22 @@ describe("bank ID derivation", () => { expect( deriveBankId( { companyId: "co-1", agentId: "ag-1" }, - { bankGranularity: ["company", "agent"] }, - ), + { bankGranularity: ["company", "agent"] } + ) ).toBe("paperclip::co-1::ag-1"); }); it("company only", async () => { const { deriveBankId } = await import("../src/bank.js"); expect( - deriveBankId( - { companyId: "co-1", agentId: "ag-1" }, - { bankGranularity: ["company"] }, - ), + deriveBankId({ companyId: "co-1", agentId: "ag-1" }, { bankGranularity: ["company"] }) ).toBe("paperclip::co-1"); }); it("agent only", async () => { const { deriveBankId } = await import("../src/bank.js"); expect( - deriveBankId( - { companyId: "co-1", agentId: "ag-1" }, - { bankGranularity: ["agent"] }, - ), + deriveBankId({ companyId: "co-1", agentId: "ag-1" }, { bankGranularity: ["agent"] }) ).toBe("paperclip::ag-1"); }); }); @@ -109,13 +103,16 @@ describe("agent.run.started", () => { await harness.emit( "agent.run.started", - { agentId: "ag-1", runId: "run-1", issueTitle: "Refactor auth module", issueDescription: "Migrate to JWT" }, - { companyId: "co-1" }, + { + agentId: "ag-1", + runId: "run-1", + issueTitle: "Refactor auth module", + issueDescription: "Migrate to JWT", + }, + { companyId: "co-1" } ); - const recallCall = fetchMock.mock.calls.find(([url]: [string]) => - url.includes("recall"), - ); + const recallCall = fetchMock.mock.calls.find(([url]: [string]) => url.includes("recall")); expect(recallCall).toBeDefined(); expect(recallCall?.[0]).toContain("paperclip%3A%3Aco-1%3A%3Aag-1"); @@ -134,14 +131,17 @@ describe("agent.run.started", () => { await harness.emit( "agent.run.started", { agentId: "ag-1", runId: "run-2" }, - { companyId: "co-1" }, + { companyId: "co-1" } ); expect(fetchMock).not.toHaveBeenCalled(); }); it("does not throw when Hindsight is unreachable", async () => { - vi.stubGlobal("fetch", vi.fn(async () => new Response("", { status: 503 }))); + vi.stubGlobal( + "fetch", + vi.fn(async () => new Response("", { status: 503 })) + ); const harness = buildHarness(); await setupPlugin(harness); @@ -149,8 +149,8 @@ describe("agent.run.started", () => { harness.emit( "agent.run.started", { agentId: "ag-1", runId: "run-3", issueTitle: "Fix bug" }, - { companyId: "co-1" }, - ), + { companyId: "co-1" } + ) ).resolves.not.toThrow(); }); }); @@ -177,13 +177,15 @@ describe("agent.run.finished", () => { await harness.emit( "agent.run.finished", - { agentId: "ag-1", runId: "run-1", output: "Refactored auth. Migrated to JWT with 24h expiry." }, - { companyId: "co-1" }, + { + agentId: "ag-1", + runId: "run-1", + output: "Refactored auth. Migrated to JWT with 24h expiry.", + }, + { companyId: "co-1" } ); - const retainCall = fetchMock.mock.calls.find(([url]: [string]) => - /memories$/.test(url), - ); + const retainCall = fetchMock.mock.calls.find(([url]: [string]) => /memories$/.test(url)); expect(retainCall).toBeDefined(); const body = JSON.parse(retainCall?.[1]?.body as string) as { @@ -200,7 +202,7 @@ describe("agent.run.finished", () => { await harness.emit( "agent.run.finished", { agentId: "ag-1", runId: "run-2", output: "" }, - { companyId: "co-1" }, + { companyId: "co-1" } ); expect(fetchMock).not.toHaveBeenCalled(); @@ -213,7 +215,7 @@ describe("agent.run.finished", () => { await harness.emit( "agent.run.finished", { agentId: "ag-1", runId: "run-3", output: "Some output" }, - { companyId: "co-1" }, + { companyId: "co-1" } ); expect(fetchMock).not.toHaveBeenCalled(); @@ -230,19 +232,22 @@ describe("hindsight_recall tool", () => { }); it("returns cached memories from run start without additional API call", async () => { - vi.stubGlobal("fetch", vi.fn(async () => new Response("{}", { status: 200 }))); + vi.stubGlobal( + "fetch", + vi.fn(async () => new Response("{}", { status: 200 })) + ); const harness = buildHarness(); await setupPlugin(harness); // Emit agent.run.started so recall fires and caches state vi.stubGlobal( "fetch", - mockFetch([{ url: /recall/, body: { results: [{ text: "User prefers dark mode" }] } }]), + mockFetch([{ url: /recall/, body: { results: [{ text: "User prefers dark mode" }] } }]) ); await harness.emit( "agent.run.started", { agentId: "ag-1", runId: "run-1", issueTitle: "Update UI" }, - { companyId: "co-1" }, + { companyId: "co-1" } ); // Now recall tool should return cached state, not hit the API again @@ -250,7 +255,7 @@ describe("hindsight_recall tool", () => { const result = await harness.executeTool( "hindsight_recall", { query: "preferences" }, - { agentId: "ag-1", runId: "run-1", companyId: "co-1", projectId: "proj-1" }, + { agentId: "ag-1", runId: "run-1", companyId: "co-1", projectId: "proj-1" } ); expect((result as { content: string }).content).toContain("dark mode"); @@ -262,7 +267,7 @@ describe("hindsight_recall tool", () => { it("falls back to live recall when no cached state", async () => { vi.stubGlobal( "fetch", - mockFetch([{ url: /recall/, body: { results: [{ text: "Agent is a Python specialist" }] } }]), + mockFetch([{ url: /recall/, body: { results: [{ text: "Agent is a Python specialist" }] } }]) ); const harness = buildHarness(); await setupPlugin(harness); @@ -270,7 +275,7 @@ describe("hindsight_recall tool", () => { const result = await harness.executeTool( "hindsight_recall", { query: "specialization" }, - { agentId: "ag-1", runId: "run-2", companyId: "co-1", projectId: "proj-1" }, + { agentId: "ag-1", runId: "run-2", companyId: "co-1", projectId: "proj-1" } ); expect((result as { content: string }).content).toContain("Python specialist"); @@ -295,7 +300,7 @@ describe("hindsight_retain tool", () => { const result = await harness.executeTool( "hindsight_retain", { content: "Decision: use Postgres not MySQL" }, - { agentId: "ag-1", runId: "run-1", companyId: "co-1", projectId: "proj-1" }, + { agentId: "ag-1", runId: "run-1", companyId: "co-1", projectId: "proj-1" } ); expect((result as { content: string }).content).toBe("Memory saved."); @@ -319,7 +324,10 @@ describe("onValidateConfig", () => { }); it("fails when Hindsight is unreachable", async () => { - vi.stubGlobal("fetch", vi.fn(async () => new Response("", { status: 503 }))); + vi.stubGlobal( + "fetch", + vi.fn(async () => new Response("", { status: 503 })) + ); const result = await plugin.definition.onValidateConfig!({ hindsightApiUrl: "http://localhost:8888", }); @@ -328,7 +336,10 @@ describe("onValidateConfig", () => { }); it("passes with a reachable Hindsight instance", async () => { - vi.stubGlobal("fetch", vi.fn(async () => new Response("{}", { status: 200 }))); + vi.stubGlobal( + "fetch", + vi.fn(async () => new Response("{}", { status: 200 })) + ); const result = await plugin.definition.onValidateConfig!({ hindsightApiUrl: "http://localhost:8888", }); diff --git a/skills/hindsight-docs/references/changelog/integrations/paperclip.md b/skills/hindsight-docs/references/changelog/integrations/paperclip.md index 1dbab3d9f5..f077771bd5 100644 --- a/skills/hindsight-docs/references/changelog/integrations/paperclip.md +++ b/skills/hindsight-docs/references/changelog/integrations/paperclip.md @@ -10,6 +10,21 @@ For the source code, see [`hindsight-integrations/paperclip`](https://github.com ← [Back to main changelog](../index.md) +## [0.2.0](https://github.com/vectorize-io/hindsight/tree/integrations/paperclip/v0.2.0) + +**Breaking Changes** + +- Rewritten as a proper Paperclip plugin (installed via `pnpm paperclipai plugin install`). No code changes required — memory hooks run automatically via the event system. +- Works with all adapter types (Claude, Codex, Cursor, HTTP, Process). Previously required manual `recall()`/`retain()` calls and only supported HTTP adapter agents. + +**Features** + +- `agent.run.started` hook: auto-recalls context keyed to issue title + description +- `agent.run.finished` hook: auto-retains agent output with `runId` as document ID +- `hindsight_recall` and `hindsight_retain` agent tools for mid-run memory access +- `onValidateConfig`: live connectivity check when operator saves settings +- Configurable bank granularity (company+agent, company-only, agent-only) + ## [0.1.2](https://github.com/vectorize-io/hindsight/tree/integrations/paperclip/v0.1.2) **Improvements**