diff --git a/.corp-harness/site.json b/.corp-harness/site.json index adde15b..955662d 100644 --- a/.corp-harness/site.json +++ b/.corp-harness/site.json @@ -1,8 +1,8 @@ { "schema": "corporate-site-site/v1", "site_id": "corpos", - "corporate_program": null, - "corporate_handoff_sha256": null, + "corporate_program": "corpos-autonomous-company", + "corporate_handoff_sha256": "c0bb90e70fdfe705192e435869c30ab401339ec6b47c0576be34d7f48bae1975", "verify_argv": ["./scripts/harness/verify.sh"], "adversarial_argv": ["./scripts/harness/adversarial.sh"] } diff --git a/.env.example b/.env.example index 76e2f6a..9300ff1 100644 --- a/.env.example +++ b/.env.example @@ -2,23 +2,21 @@ # NEVER commit a real `.env` (it is gitignored). Keep secrets out of code, # logs, and agent output. -# ── Model providers ────────────────────────────────────────────────── -# Provide the key(s) for the provider(s) you actually use. -# OpenRouter is the default for agent reasoning (model: Owl Alpha). +# ── Runtime mode ───────────────────────────────────────────────────── +# local (default): approvals/kill do not require a shared token. +# shared: require DASHBOARD_API_TOKEN for approval and kill mutations. +# CORPOS_MODE=local +# DASHBOARD_API_TOKEN= + +# ── Data ───────────────────────────────────────────────────────────── +# Local libsql / SQLite file used by the firm store (default: data/company.db). +# CORPOS_DB=data/company.db + +# ── HTTP ───────────────────────────────────────────────────────────── +# PORT=3000 + +# ── Model providers (optional; SimulationProvider used in CI) ──────── # OPENROUTER_API_KEY= # OPENROUTER_MODEL=openrouter/owl-alpha -# OPENROUTER_REFERER=https://corpos.local -# OPENROUTER_TITLE=CorpOS # ANTHROPIC_API_KEY= # OPENAI_API_KEY= -# GOOGLE_API_KEY= -# GROQ_API_KEY= - -# ── MCP servers ────────────────────────────────────────────────────── -# Referenced from kilo.jsonc mcp.*.headers via ${VAR} interpolation. -# MCP_TOKEN= - -# ── Project ────────────────────────────────────────────────────────── -# APP_ENV=development -# LOG_LEVEL=info -# DATABASE_URL= diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b2b3f15..10f596f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,37 +10,41 @@ permissions: contents: read jobs: - lint: + verify: runs-on: ubuntu-latest - timeout-minutes: 5 + timeout-minutes: 15 + strategy: + fail-fast: false + matrix: + node-version: [22, 24] steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: - node-version: 20 + node-version: ${{ matrix.node-version }} cache: npm - - run: npm ci - - name: ESLint - run: npm run lint - - name: Prettier check - run: npm run format:check + - name: Activate packageManager npm + run: | + corepack enable + corepack prepare npm@10.9.2 --activate + - name: Site verify (build, typecheck, test, lint, format) + run: ./scripts/harness/verify.sh - build: + adversarial: runs-on: ubuntu-latest timeout-minutes: 10 - strategy: - fail-fast: false - matrix: - node-version: [20, 22, 24] + needs: verify steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v6 + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 with: - node-version: ${{ matrix.node-version }} + node-version: 22 cache: npm + - name: Activate packageManager npm + run: | + corepack enable + corepack prepare npm@10.9.2 --activate - run: npm ci - - name: Typecheck - run: npm run typecheck - - name: Test - run: npm test + - run: npm run build + - name: Site adversarial + run: ./scripts/harness/adversarial.sh diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index a335398..0c441e6 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -4,12 +4,16 @@ on: push: branches: [main] paths: - - "src/**" + - "apps/**" + - "packages/**" + - "scripts/**" - "test/**" pull_request: branches: [main] paths: - - "src/**" + - "apps/**" + - "packages/**" + - "scripts/**" - "test/**" schedule: - cron: "27 6 * * 1" @@ -29,15 +33,15 @@ jobs: steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - name: Initialize CodeQL - uses: github/codeql-action/init@v3 + uses: github/codeql-action/init@4187e74d05793876e9989daffde9c3e66b4acd07 # v3 with: languages: ${{ matrix.language }} queries: +security-and-quality - name: Autobuild - uses: github/codeql-action/autobuild@v3 + uses: github/codeql-action/autobuild@4187e74d05793876e9989daffde9c3e66b4acd07 # v3 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + uses: github/codeql-action/analyze@4187e74d05793876e9989daffde9c3e66b4acd07 # v3 with: category: "/language:${{ matrix.language }}" diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 6fe50b9..c6035e1 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,8 +1,7 @@ name: Deploy to Fly.io -# Deploys the control plane to Fly.io when a release is published. -# Requires the FLY_API_TOKEN repo secret (create with: flyctl tokens create deploy -a ). -# Skips automatically if the secret is absent. +# Optional release deploy for the reference demo. Skips cleanly when +# FLY_API_TOKEN is unset. Requires fly.toml + Dockerfile. on: release: types: [published] @@ -16,20 +15,27 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 15 steps: - - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - name: Abort if Fly token not configured - if: ${{ secrets.FLY_API_TOKEN == '' }} + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 + + - name: Check Fly token + id: fly + env: + FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} run: | - echo "FLY_API_TOKEN secret is not set — skipping deploy." - echo "Create it with: flyctl tokens create deploy -a " - exit 0 + if [ -z "${FLY_API_TOKEN}" ]; then + echo "configured=false" >> "$GITHUB_OUTPUT" + echo "FLY_API_TOKEN secret is not set — skipping deploy." + else + echo "configured=true" >> "$GITHUB_OUTPUT" + fi + # setup-flyctl only publishes @master (not version tags); pin the commit. - name: Set up Flyctl - if: ${{ secrets.FLY_API_TOKEN != '' }} - uses: superfly/flyctl-actions/setup-flyctl@1.5 + if: steps.fly.outputs.configured == 'true' + uses: superfly/flyctl-actions/setup-flyctl@ed8efb33836e8b2096c7fd3ba1c8afe303ebbff1 # master - name: Deploy - if: ${{ secrets.FLY_API_TOKEN != '' }} + if: steps.fly.outputs.configured == 'true' run: flyctl deploy --remote-only --strategy=rolling env: FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 56f968d..4719e3a 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -4,7 +4,9 @@ on: push: branches: [main] paths: - - "src/**" + - "apps/**" + - "packages/**" + - "scripts/**" - "test/**" - "package.json" - "package-lock.json" @@ -29,13 +31,13 @@ jobs: steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 - name: Set up QEMU - uses: docker/setup-qemu-action@v4 + uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 + uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4 - name: Log in to GHCR - uses: docker/login-action@v3 + uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.actor }} @@ -43,7 +45,7 @@ jobs: - name: Derive image metadata id: meta - uses: docker/metadata-action@v6 + uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | @@ -53,7 +55,7 @@ jobs: type=raw,value=latest,enable={{is_default_branch}} - name: Build and push - uses: docker/build-push-action@v6 + uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 with: context: . platforms: linux/amd64,linux/arm64 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 4b5ab02..a40b97c 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -30,7 +30,7 @@ jobs: publish_results: true - if: always() - uses: github/codeql-action/upload-sarif@v3 + uses: github/codeql-action/upload-sarif@4187e74d05793876e9989daffde9c3e66b4acd07 # v3 with: sarif_file: results.sarif category: scorecard diff --git a/.prettierignore b/.prettierignore index 34287d3..9382df7 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,6 +2,9 @@ dist/ node_modules/ data/ docs/ +_archives/ +_legacy_src/ +_legacy_test/ .kilo/ .playwright-mcp/ package-lock.json diff --git a/CONTEXT.md b/CONTEXT.md index e29650f..d28e855 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -1,74 +1,11 @@ -# Project context — loaded into every agent session via `instructions` in kilo.jsonc. +# Project context -# Keep this short and factual. It orients agents to THIS project specifically +CorpOS is the open reference implementation of an autonomous company: +firm model + work contracts + control plane. Simulation-first. -# (unlike AGENTS.md, which states universal rules). +Workspaces: `packages/core`, `packages/mcp-knowledge`, `apps/api`, `apps/console`. -## What this project is +Commands: `npm run dev`, `npm test`, `npm run scenario`, `npm run audit:verify`, +`./scripts/harness/verify.sh`, `./scripts/harness/adversarial.sh`. -`CorpOS` — an autonomous company operating system: a TypeScript multi-agent -runtime plus a control plane (policy engine, approval gates, spend caps, audit, -REST/WebSocket API, dashboard). Department "agents" (support, sales, ops, -finance, engineering) reason via an LLM, call permissioned tools, and coordinate -through a shared orchestrator. Every consequential action is policy-gated. - -## Architecture (in brief) - -- **Runtime layer** (`src/core/`): `types`, `llm`, `logger`, `event-bus`, `store` - (SQLite via better-sqlite3), `tool` (typed registry + permissions), `policy` - (allow/deny/approve decisions + spend tracking), `memory`, `agent` (reasoning - loop), `orchestrator` (lifecycle, dispatch, concurrency, retries), `app` - (composition root). -- **Agents** (`src/agents/`): one file per department; each declares a system - prompt, a tool subset, and owns its domain tools. -- **Tools** (`src/tools/`): CRM, comms, billing, knowledge, system packs. - Every tool declares a `ToolPermission` (category + optional cost cap + - `requiresApproval`). -- **Control plane** (`src/api/`): Express REST + WebSocket; serves the dashboard. - -## Key conventions - -- TypeScript, ESM (`"type": "module"`), strict. Node >= 20. -- 2-space indent, LF, no trailing whitespace (see `.editorconfig`). -- IDs via `nanoid`. JSON-schema for tool parameter validation; `zod` only where - ergonomic. -- No comments unless genuinely necessary. -- Consequential tools (spend, external comms, mutating) MUST route through the - policy engine and produce audit events. Nothing destructive runs autonomously. -- Agent reasoning loop is LLM-driven but fully reproducible under the - `SimulationProvider` (scripted responses) — all tests run without network. - -## Commands - -- Install: `npm install` -- Typecheck: `npm run typecheck` -- Build: `npm run build` -- Run server (API + dashboard): `npm run dev` (boots on `$PORT` or 3000) -- Run a recorded multi-agent scenario: `npm run scenario` -- Tests: `npm test` (vitest) - -## LLM provider - -Agents call **OpenRouter** via its OpenAI-compatible endpoint -(`https://openrouter.ai/api/v1`), model **Owl Alpha**, key from -`OPENROUTER_API_KEY` and slug from `OPENROUTER_MODEL` (default -`openrouter/owl-alpha` — set the real OpenRouter slug for Owl Alpha there). -Optional `OPENROUTER_REFERER` / `OPENROUTER_TITLE` set the OpenRouter -`HTTP-Referer` / `X-Title` headers. Z.AI and OpenAI remain supported as -alternatives (`provider: "zai"|"openai"`); the factory auto-detects by key. - -**Gotcha: no key is set in this environment.** The system runs -**simulation-first** — the `SimulationProvider` returns scripted/tool-driven -responses so the full system is demonstrable and tested without network. Provide -`OPENROUTER_API_KEY` to switch the default agent provider to live OpenRouter. -Never log keys. - -## Gotchas - -- Do NOT edit `package-lock.json` directly (permission-denied by design); run - `npm install` to change the lockfile. -- Agent Manager run-script derives a per-worktree `$PORT` from the worktree path; - parallel worktrees bind different ports — read `$PORT`, don't hardcode 3000. -- SQLite file (`data/company.db`) is created at runtime; do not commit it. -- The reasoning loop is non-deterministic under a live LLM; deterministic tests - rely on `SimulationProvider` scripts. +Site id: `corpos`. Program: `corpos-autonomous-company`. diff --git a/Dockerfile b/Dockerfile index 9103849..46ef1b8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,34 +1,12 @@ -# CorpOS control-plane image. -# -# Runs the app under tsx (the same ESM resolver used in dev). The codebase -# uses directory/extensionless imports that tsx resolves; a compiled image -# would require a bundler step, which is out of scope for this reference. -# Simulation-first: works with zero configuration (no API key required). - -FROM node:20-alpine AS base - +FROM node:22-bookworm-slim WORKDIR /app - -# Install dependencies first for better layer caching. -COPY package.json package-lock.json ./ -RUN npm ci && npm cache clean --force - -# Copy the application source (dashboard + runtime + tools + tests). -COPY . . - -# SQLite writes here at runtime; ensure the non-root user owns it. -RUN mkdir -p data && chown -R node:node /app - -ENV NODE_ENV=production \ - PORT=3000 \ - LOG_LEVEL=info - -USER node - +COPY package.json package-lock.json* .npmrc* ./ +COPY packages ./packages +COPY apps ./apps +COPY scripts ./scripts +COPY tsconfig*.json vitest.config.ts eslint.config.mjs ./ +RUN corepack enable && corepack prepare npm@10.9.2 --activate \ + && npm ci && npm run build +ENV PORT=3000 EXPOSE 3000 - -HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ - CMD wget -qO- "http://127.0.0.1:${PORT:-3000}/api/health" >/dev/null 2>&1 || exit 1 - -# No API key set → simulation mode. Set OPENROUTER_API_KEY (etc.) to go live. -CMD ["npx", "tsx", "src/index.ts"] +CMD ["npm", "run", "start"] diff --git a/README.md b/README.md index 009f45e..7e74ede 100644 --- a/README.md +++ b/README.md @@ -1,223 +1,45 @@ # CorpOS -> A reference architecture for an autonomous company — a TypeScript multi-agent runtime with a policy, approval, and audit control plane. +> Reference implementation of an **autonomous company** — firm model, work contracts, and a policy-gated control plane. -[![CI](https://github.com/SafetyMP/CorpOS/actions/workflows/ci.yml/badge.svg)](https://github.com/SafetyMP/CorpOS/actions/workflows/ci.yml) [![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE) -[![Node](https://img.shields.io/badge/node-%E2%89%A5%2020-green.svg)](package.json) -[![Tests](https://img.shields.io/badge/tests-38%20passing-brightgreen.svg)](test) +[![Node](https://img.shields.io/badge/node-%E2%89%A5%2022-green.svg)](package.json) -

- CorpOS control-plane dashboard -

+CorpOS shows how a firm operates when department agents do most work and **humans govern by exception**. Autonomy is earned from evidence, not granted in prompts. -CorpOS is a **greenfield, architecture-first** system: a clean, layered design for running an organization of LLM-driven agents where **every consequential action is policy-gated**. Department agents (support, sales, ops, finance, engineering) reason via an LLM, call permissioned tools, and coordinate through a shared orchestrator. Spend, external comms, and mutations never run autonomously — they route through an approval engine and produce audit events. - -It's built **simulation-first**: the entire multi-agent loop, tool-calling, approval gates, spend tracking, and dashboard are fully demonstrable and tested with **no LLM key and no network**. Drop in an OpenRouter key to switch the agents to live reasoning. - ---- - -## Design principles - -CorpOS is built as an open reference architecture, not a black box. The decisions that shape it: - -- **Architecture over implementation.** The value lives in the layers — a typed runtime, a permissioned tool registry, a policy chokepoint, and a control plane — not in any one agent or integration. -- **Policy as the single chokepoint.** There is exactly one place a consequential action is authorized. Tools can't bypass it; agents can't bypass it. Auditability is a side-effect of the design, not a feature bolted on. -- **Simulation-first = reproducible.** The reasoning loop is LLM-driven but fully deterministic under the `SimulationProvider`. The whole system builds and tests in CI with zero network and zero cost. -- **Human-in-the-loop by default.** Consequential actions pause for approval. Agents resume from the exact conversation state where they paused — no lost context, no re-prompting. -- **Boring, durable tech.** TypeScript, SQLite, Express, vanilla web. No agent framework lock-in; every primitive is readable in a single file. - -## Architecture - -``` -┌─────────────────────────── Control plane (src/api) ───────────────────────────┐ -│ Express REST (tasks, agents, approvals, spend, events) · WebSocket · dashboard │ -└──────────────────────────────────────┬────────────────────────────────────────┘ - │ -┌─────────────────────────────── Orchestrator (src/core) ───────────────────────┐ -│ lifecycle · dispatch · concurrency · retries · approval resume │ -└──────────────────────────────────────┬────────────────────────────────────────┘ - │ - ┌──────────────────────────┼───────────────────────────┐ - ▼ ▼ ▼ - Agent (loop) Policy engine Tool registry - reason → tool → observe allow / deny / approve typed, JSON-schema, - pause/resume on gates spend caps, approvals permission-gated - │ │ │ - └────────────── LLM provider ──────────── Store (SQLite) ─┘ - Simulation (default) tasks · events · approvals - OpenRouter / Z.AI / OpenAI spend · memory · audit -``` - -| Layer | Path | Responsibility | -| ----------------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------- | -| **Runtime** | `src/core/` | types, llm, logger(+audit), event-bus, store (SQLite), tool registry, policy engine, memory, agent loop, orchestrator | -| **Agents** | `src/agents/` | one file per department — system prompt + tool subset | -| **Tools** | `src/tools/` | CRM, comms, billing, knowledge, system packs over shared seeded state + agent-to-agent delegate | -| **Control plane** | `src/api/`, `src/dashboard/` | Express REST + WebSocket + static dashboard | +Read the thesis: [`docs/future-of-the-firm.md`](docs/future-of-the-firm.md). ## Quick start ```bash -git clone https://github.com/SafetyMP/CorpOS.git -cd CorpOS npm install -npm run dev # boot the server + dashboard on $PORT or 3000 -``` - -Open `http://localhost:3000/`. With no key set, agents run in **simulation** — click **▶ Run demo** to watch a curated 5-agent scenario auto-play through real approval gates. - -> **Node version:** requires Node 20+ (verified through Node 26). If `npm install` -> fails while compiling `better-sqlite3` (a V8-API error from node-gyp), you're on -> an unsupported Node — use Node 22 LTS. On supported versions the install uses a -> prebuilt binary, so no compiler is needed. - -### Live demo (one-click deploy) - -No clone required — run the control plane in the cloud in simulation mode, then -click **▶ Run demo**: - -[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/SafetyMP/CorpOS) - -Prefer Fly.io? It's one command (uses the published image): - -```bash -flyctl launch --image ghcr.io/safetymp/corpos:0.1.0 +npm run build +npm run dev # ops console on $PORT or 3000 ``` -Set `OPENROUTER_API_KEY` in either dashboard to switch the agents from -simulation to live reasoning. See [Deployment](#deployment) below. - -### Go live (OpenRouter / Owl Alpha) +Click **Run company day** for the multi-handoff demo (support → finance → ops), including an autonomous settle, an exception approval path, compensation, and trust unlock. ```bash -cp .env.example .env -# edit .env: -# OPENROUTER_API_KEY=sk-or-... -# OPENROUTER_MODEL= -npm run dev +npm test +npm run scenario +npm run audit:verify ``` -The header badge flips from `simulation` to `live · openrouter`, and agents reason against the real model. Z.AI and OpenAI are also supported (auto-detected by key). - -## Demo mode - -One click on **▶ Run demo** posts a curated 5-agent scenario — support refund, ops service restart, finance goodwill credit, sales follow-up, engineer diagnosis — and **auto-resolves every approval gate** so the full policy-gated loop plays out without manual steps. Deterministic in simulation; genuine reasoning when live. - -## How an action gets approved - -1. A task is enqueued; the orchestrator assigns it to an agent. -2. The agent reasons and emits tool calls. Each call is evaluated by the **policy engine**. -3. A `read` call runs; a `spend` / `communicate` / mutating call **pauses** and creates a pending approval. -4. A human approves via the dashboard or `POST /api/approvals/:id/decide`. -5. On approval the agent **resumes from where it paused** (conversation preserved), executes the action, records spend, and produces a final summary — all audited. - -## Commands - -| Command | What it does | -| ------------------- | ------------------------------------------------- | -| `npm install` | Install dependencies | -| `npm run dev` | Boot API + dashboard (tsx watch) | -| `npm run scenario` | Run a recorded deterministic multi-agent scenario | -| `npm test` | Run the vitest suite (deterministic, no network) | -| `npm run typecheck` | `tsc --noEmit` | -| `npm run build` | Compile to `dist/` | - -## REST + WebSocket API (selected) - -| Method | Path | Purpose | -| -------------- | --------------------------- | --------------------------------------- | -| `GET` | `/api/health` | Liveness + provider mode | -| `GET` | `/api/agents` | Registered agents + tools | -| `GET` / `POST` | `/api/tasks` | List / submit tasks | -| `GET` | `/api/approvals` | Pending approvals | -| `POST` | `/api/approvals/:id/decide` | `{decision:"approved"\|"rejected", by}` | -| `GET` | `/api/spend` | Spend ledger totals | -| `GET` | `/api/events?limit=50` | Recent events | -| `WS` | `/ws` | Live snapshot + event stream | - -```bash -curl -X POST http://localhost:3000/api/tasks \ - -H 'content-type: application/json' \ - -d '{"title":"Refund for Ada","description":"ada@example.com wants a $49 refund on sub_ada_pro.","assignedTo":"agent_support","priority":2}' -``` - -## Configuration - -Environment variables (auto-loaded from `.env` on boot — see `.env.example`): - -| Var | Default | Purpose | -| ----------------------------------------- | ---------------------- | --------------------------------------------- | -| `OPENROUTER_API_KEY` | — | Live OpenRouter key. Unset → simulation mode. | -| `OPENROUTER_MODEL` | `openrouter/owl-alpha` | Model slug. | -| `OPENROUTER_REFERER` / `OPENROUTER_TITLE` | — | OpenRouter attribution headers. | -| `OPENAI_API_KEY` / `ZAI_API_KEY` | — | Alternative providers (auto-detected). | -| `PORT` | `3000` | HTTP port. | -| `LOG_LEVEL` | `info` | `debug` / `info` / `warn` / `error`. | - -Never commit a real `.env` (it's gitignored). - -## Project structure - -``` -src/ - core/ runtime: types, llm, store, tool, policy, memory, agent, orchestrator, app - agents/ department agents (support, sales, finance, ops, engineer) - tools/ tool packs (crm, comms, billing, knowledge, system, delegate) + seeded state - api/ Express REST + WebSocket server - dashboard/ static dashboard (single-file HTML/CSS/JS) - index.ts composition root — wires core + agents + tools + server, loads .env - scenario.ts deterministic multi-agent demo -test/ vitest unit + e2e (deterministic via SimulationProvider) -.github/ CI workflow -docs/ screenshots / assets -``` - -## Tech stack - -TypeScript (ESM, strict) · Node ≥ 20 · better-sqlite3 · Express · ws · zod · nanoid · vitest · tsx. The dashboard is dependency-free vanilla HTML/CSS/JS. - -## Deployment - -A prebuilt multi-arch image is published to the GitHub Container Registry on -every push to `main` and every release: - -```bash -docker run --rm -p 3000:3000 ghcr.io/safetymp/corpos:latest -# → http://localhost:3000 (simulation mode, zero config) -``` - -Go live by passing a key: - -```bash -docker run --rm -p 3000:3000 \ - -e OPENROUTER_API_KEY=sk-or-... \ - -e OPENROUTER_MODEL= \ - ghcr.io/safetymp/corpos:latest -``` - -Managed options (both auto-detect the `Dockerfile`, both run the simulation -demo by default): +## Architecture -- **Render** — one click via the "Deploy to Render" button above; configured by - [`render.yaml`](render.yaml). -- **Fly.io** — `flyctl launch --image ghcr.io/safetymp/corpos:0.1.0`; configured - by [`fly.toml`](fly.toml), with a release-triggered - [deploy workflow](.github/workflows/deploy.yml) (needs `FLY_API_TOKEN`). +| Layer | Package / app | Responsibility | +| --------------------- | ----------------------- | ----------------------------------------------------- | +| Firm / work / control | `@corpos/core` | Contracts, gateway, policy, trust, audit, company day | +| MCP knowledge | `@corpos/mcp-knowledge` | Real local MCP server (stdio) | +| API | `@corpos/api` | Hono REST + SSE | +| Ops console | `@corpos/console` | Vite + Preact | -⚠️ **Do not deploy to a public URL without reading [SECURITY.md](SECURITY.md) -first** — the control plane has no authentication, and anyone who can reach it -can approve gated actions. For a public demo, keep it in simulation mode and -restart it periodically (SQLite state at `data/company.db` persists across restarts unless the volume is cleared). +Stack: Node ≥22 · TypeScript · npm workspaces · Hono · Drizzle + libsql · official MCP SDK · Preact. No Express, no `better-sqlite3`, no agent-framework lock-in. ## Security -CorpOS is a **reference architecture and research/demo project, not production-hardened.** Consequential actions are policy-gated by design, but the control plane has no authentication by default — optional `DASHBOARD_API_TOKEN` gates approval mutations. Do not expose it to untrusted networks. See [SECURITY.md](SECURITY.md) for the full posture and vulnerability reporting. - -## Contributing - -Contributions are welcome. The codebase is intentionally readable and layered — each core primitive fits in a single file. See [AGENTS.md](AGENTS.md) for the engineering rules and conventions the project follows (commit style, verification, code style) and [CONTEXT.md](CONTEXT.md) for architecture orientation. Run `npm test` before opening a PR; CI enforces typecheck + tests. +Reference architecture — not production-hardened. Set `CORPOS_MODE=shared` and `DASHBOARD_API_TOKEN` before exposing approvals. See [SECURITY.md](SECURITY.md). ## License -Apache License 2.0 — see [LICENSE](LICENSE) and [NOTICE](NOTICE). Copyright © 2026 SafetyMP. +Apache-2.0 — Copyright © 2026 SafetyMP. diff --git a/SECURITY.md b/SECURITY.md index c86f9f8..02e6774 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,86 +1,27 @@ # Security Policy -## Status: reference architecture / research demo +## Status: reference architecture -CorpOS is a **greenfield reference architecture** for policy-gated autonomous -agent systems. It is **not production-hardened** and must not be operated as -if it were. Treat it as a design artifact you can read, run locally, and learn -from — not as software safe to expose to untrusted users or to let act on real -money, real customers, or real infrastructure. +CorpOS is a **reference implementation of an autonomous company**, not a production SaaS. +Treat it as a design artifact you can run locally. -## What the design does guarantee +## Guarantees (within local trust boundary) -CorpOS is deliberately **policy-first**: +- ToolGateway is the sole chokepoint for consequential tools. +- Unknown tools fail closed. +- Exception HITL with TTL; kill switch; department capital caps. +- Hash-chained audit receipts (`npm run audit:verify`). -- **Single authorization chokepoint.** Every tool call flows through the - policy engine (`src/core/policy.ts`). Tools cannot execute consequential - actions by any path that bypasses it. -- **Consequential actions require approval by default.** Spend, external - communications, and mutating/system tools are gated behind human approval - (`requiresApproval`). Nothing destructive runs autonomously. -- **Spend caps.** Spend-category tools are bounded by configurable caps - checked against the spend ledger before execution. -- **Audit trail.** Policy decisions, approvals, spend, and the full event - stream are persisted to SQLite and emitted to the dashboard. +## Not provided -These properties hold **within the trust boundary** of a single operator -running the control plane locally. +- Full multi-tenant isolation as a security boundary. +- Production OIDC / TLS by default. +- Real payment or messaging providers. -## What it does NOT have (the trust boundary) +## Shared demo -Do **not** deploy this control plane where it is reachable by anyone you do -not fully trust. The current design has: +When `CORPOS_MODE=shared`, `DASHBOARD_API_TOKEN` is required for approval and kill mutations. -- **No authentication or authorization** on the REST/WebSocket API. Anyone - who can reach the port can list/submit tasks, read events, and — critically - — **approve any pending gate**, including spend and external-comms actions. -- **No rate limiting** on task submission or tool execution. -- **No transport security** (plain HTTP/WS, no TLS). -- **No PII handling, retention, or redaction controls.** Agent memory and - the event store persist whatever is passed into tasks. -- **No isolation between tenants** beyond the `tenantId` field, which is not - enforced as a security boundary. -- **Live LLM calls are non-deterministic.** Agents reason via a third-party - model; outputs are not constrained beyond the tool/permission surface. +## Reporting -The **simulation-first** default (no API key set) keeps all behavior local and -offline, which is the safe mode for exploration. - -## Recommended hardening before any shared deployment - -If you ever intend to run this beyond a single trusted machine, at minimum: - -1. Put the control plane behind **authenticated TLS** (reverse proxy + identity). -2. Bind the server to **localhost** or a private network only. -3. Add **authorization** so only designated operators can reach - `POST /api/approvals/:id/decide` and `POST /api/tasks`. -4. Add **rate limits** and per-tenant **spend ceilings** enforced server-side. -5. Run spend/comms tools against **sandboxed** providers, never real billing - or messaging systems, until the flow is fully reviewed. -6. Treat agent memory and the event store as **sensitive** (PII-class). - -## Reporting a vulnerability - -Found a security issue in the policy engine, tool registry, approval flow, or -any boundary that lets a gated action execute without authorization? - -- **Do not** open a public GitHub issue. -- Email the maintainer via the email listed on the - [SafetyMP GitHub profile](https://github.com/SafetyMP), or open a - [GitHub Security Advisory](https://github.com/SafetyMP/CorpOS/security/advisories/new) - using **"Report a vulnerability"**. -- Please include: the affected file/path, a minimal reproduction, and the - impact (e.g. "approval gate bypassed for `billing.issue_refund`"). - -Reports are appreciated and will be acknowledged. This is a reference -architecture, so fixes are best-effort and advisory rather than SLA-bound. - -## Scope - -In scope: the authorization chokepoint, the approval workflow, the tool -permission model, the spend ledger, and anything that lets a gated action run -without the intended human sign-off. - -Out of scope: general hardening already documented above as missing -(auth/rate-limiting/TLS) — these are known limitations of the reference -design, not undisclosed vulnerabilities. +Use GitHub Security Advisories on the CorpOS repository. diff --git a/apps/api/package.json b/apps/api/package.json new file mode 100644 index 0000000..5d6807a --- /dev/null +++ b/apps/api/package.json @@ -0,0 +1,23 @@ +{ + "name": "@corpos/api", + "version": "0.2.0", + "private": true, + "type": "module", + "scripts": { + "build": "tsc -p tsconfig.json", + "typecheck": "tsc -p tsconfig.json --noEmit", + "dev": "tsx watch src/index.ts", + "start": "tsx src/index.ts", + "scenario": "tsx src/index.ts --scenario" + }, + "dependencies": { + "@corpos/core": "0.2.0", + "@hono/node-server": "^1.14.4", + "drizzle-orm": "^0.45.2", + "hono": "^4.12.32" + }, + "devDependencies": { + "tsx": "^4.23.0", + "typescript": "^5.8.3" + } +} diff --git a/apps/api/src/app.ts b/apps/api/src/app.ts new file mode 100644 index 0000000..2af9a11 --- /dev/null +++ b/apps/api/src/app.ts @@ -0,0 +1,124 @@ +import { Hono } from "hono"; +import { cors } from "hono/cors"; +import { streamSSE } from "hono/streaming"; +import { + agents, + contracts, + controlState, + counterfactualReplay, + createCompany, + decideException, + departments, + exceptions, + runCompanyDay, + traces, + type Company, +} from "@corpos/core"; +import { eq } from "drizzle-orm"; + +function requireAuth(c: { req: { header: (n: string) => string | undefined } }): boolean { + if (process.env.CORPOS_MODE !== "shared") return true; + const expected = process.env.DASHBOARD_API_TOKEN?.trim(); + if (!expected) return false; + const header = c.req.header("authorization") ?? ""; + return header === `Bearer ${expected}`; +} + +export function buildApp(company: Company): Hono { + const app = new Hono(); + app.use("*", cors()); + + app.get("/api/health", (c) => + c.json({ + ok: true, + mode: process.env.OPENROUTER_API_KEY ? "live" : "simulation", + product: "autonomous-company-reference", + }), + ); + + app.get("/api/firm", async (c) => { + const depts = await company.db.select().from(departments); + const ag = await company.db.select().from(agents); + const ctrl = ( + await company.db.select().from(controlState).where(eq(controlState.id, "global")) + )[0]; + return c.json({ + departments: depts, + agents: ag, + killed: Boolean(ctrl?.killed), + auditHead: await company.audit.head(), + }); + }); + + app.get("/api/contracts", async (c) => c.json(await company.db.select().from(contracts))); + app.get("/api/exceptions", async (c) => + c.json((await company.db.select().from(exceptions)).filter((e) => e.state === "pending")), + ); + + app.post("/api/exceptions/:id/decide", async (c) => { + if (!requireAuth(c)) return c.json({ error: "dashboard authentication required" }, 401); + const body = await c.req.json<{ decision: "approved" | "rejected"; by?: string }>(); + await decideException(company, c.req.param("id"), body.decision, body.by ?? "operator"); + return c.json({ ok: true }); + }); + + app.post("/api/kill", async (c) => { + if (!requireAuth(c)) return c.json({ error: "dashboard authentication required" }, 401); + const body = await c.req.json<{ killed: boolean }>(); + await company.gateway.setKilled(Boolean(body.killed)); + return c.json({ ok: true, killed: body.killed }); + }); + + app.post("/api/company-day", async (c) => { + const { result, company: ephemeral } = await runCompanyDay({ dbPath: ":memory:" }); + ephemeral.close(); + return c.json(result); + }); + + app.get("/api/traces/:taskId", async (c) => { + const row = (await company.db.select().from(traces)).find( + (t) => t.taskId === c.req.param("taskId"), + ); + if (!row) return c.json({ error: "not found" }, 404); + return c.json({ ...row, steps: JSON.parse(row.stepsJson) }); + }); + + app.post("/api/traces/:taskId/counterfactual", async (c) => { + const row = (await company.db.select().from(traces)).find( + (t) => t.taskId === c.req.param("taskId"), + ); + if (!row) return c.json({ error: "not found" }, 404); + const body = await c.req.json<{ + rules?: { tool: string; effect?: "allow" | "deny" | "draft" | "approve"; reason?: string }[]; + maxAutonomousRisk?: number; + }>(); + const steps = JSON.parse(row.stepsJson); + const diffs = counterfactualReplay(steps, { + rules: body.rules ?? [{ tool: "*", effect: "deny", reason: "stricter pack" }], + maxAutonomousRisk: body.maxAutonomousRisk ?? 0, + }); + return c.json({ diffs }); + }); + + app.get("/api/events", (c) => + streamSSE(c, async (stream) => { + await stream.writeSSE({ + data: JSON.stringify({ + type: "snapshot", + firm: { + agents: await company.db.select().from(agents), + exceptions: (await company.db.select().from(exceptions)).filter( + (e) => e.state === "pending", + ), + }, + }), + }); + }), + ); + + return app; +} + +export async function createDefaultCompany(): Promise { + return createCompany({ dbPath: process.env.CORPOS_DB }); +} diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts new file mode 100644 index 0000000..2b8c967 --- /dev/null +++ b/apps/api/src/index.ts @@ -0,0 +1,26 @@ +import { serve } from "@hono/node-server"; +import { serveStatic } from "@hono/node-server/serve-static"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { buildApp, createDefaultCompany } from "./app.js"; +import { runCompanyDay } from "@corpos/core"; + +const scenario = process.argv.includes("--scenario"); +if (scenario) { + const { result, company } = await runCompanyDay({ dbPath: ":memory:" }); + console.log(JSON.stringify(result, null, 2)); + company.close(); + process.exit(result.ok ? 0 : 1); +} + +const company = await createDefaultCompany(); +const app = buildApp(company); + +const here = path.dirname(fileURLToPath(import.meta.url)); +const consoleDist = path.resolve(here, "../../console/dist"); +app.use("/*", serveStatic({ root: consoleDist })); + +const port = Number(process.env.PORT ?? 3000); +serve({ fetch: app.fetch, port }, () => { + console.log(`CorpOS ops console on http://localhost:${port}`); +}); diff --git a/apps/api/tsconfig.json b/apps/api/tsconfig.json new file mode 100644 index 0000000..c99ec7b --- /dev/null +++ b/apps/api/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src/**/*.ts"] +} diff --git a/apps/console/index.html b/apps/console/index.html new file mode 100644 index 0000000..347e35c --- /dev/null +++ b/apps/console/index.html @@ -0,0 +1,12 @@ + + + + + + CorpOS Ops Console + + +
+ + + diff --git a/apps/console/package.json b/apps/console/package.json new file mode 100644 index 0000000..8b27dfa --- /dev/null +++ b/apps/console/package.json @@ -0,0 +1,19 @@ +{ + "name": "@corpos/console", + "version": "0.2.0", + "private": true, + "type": "module", + "scripts": { + "build": "vite build", + "typecheck": "tsc -p tsconfig.json --noEmit", + "dev": "vite" + }, + "dependencies": { + "preact": "^10.26.9" + }, + "devDependencies": { + "@preact/preset-vite": "^2.10.2", + "typescript": "^5.8.3", + "vite": "^6.3.5" + } +} diff --git a/apps/console/src/main.tsx b/apps/console/src/main.tsx new file mode 100644 index 0000000..bdb5702 --- /dev/null +++ b/apps/console/src/main.tsx @@ -0,0 +1,134 @@ +import { render } from "preact"; +import { useEffect, useState } from "preact/hooks"; +import "./styles.css"; + +type Firm = { + departments: { id: string; name: string; capitalBudget: number; capitalSpent: number }[]; + agents: { + id: string; + role: string; + department: string; + owner: string; + maxAutonomousRisk: number; + trustScore: number; + }[]; + killed: boolean; + auditHead: string; +}; + +type Exception = { + id: string; + tool: string; + reason: string; + riskLevel: number; + agentId: string; +}; + +function App() { + const [firm, setFirm] = useState(null); + const [exceptions, setExceptions] = useState([]); + const [day, setDay] = useState | null>(null); + const [tab, setTab] = useState<"ops" | "governor">("ops"); + + const refresh = async () => { + const f = await fetch("/api/firm").then((r) => r.json()); + const e = await fetch("/api/exceptions").then((r) => r.json()); + setFirm(f); + setExceptions(e); + }; + + useEffect(() => { + void refresh(); + }, []); + + const runDay = async () => { + const r = await fetch("/api/company-day", { method: "POST" }).then((res) => res.json()); + setDay(r); + await refresh(); + }; + + return ( +
+
+
+

CorpOS

+

Autonomous company ops

+

+ Authority, capital, exceptions, and earned autonomy — humans govern by exception. +

+
+
+ + +
+
+ + {tab === "ops" ? ( +
+
+

Capital

+
    + {firm?.departments.map((d) => ( +
  • + {d.name} {d.capitalSpent}/{d.capitalBudget} +
  • + ))} +
+
+
+

Trust

+
    + {firm?.agents.map((a) => ( +
  • + {a.role} maxRisk={a.maxAutonomousRisk} (owner {a.owner}) +
  • + ))} +
+
+
+

Exception queue

+ {exceptions.length === 0 ? ( +

No pending exceptions

+ ) : ( +
    + {exceptions.map((e) => ( +
  • + L{e.riskLevel} {e.tool} — {e.reason} +
  • + ))} +
+ )} +
+
+

Company day

+ {day ?
{JSON.stringify(day, null, 2)}
:

Not run yet

} +
+
+ ) : ( +
+
+

Governor

+

+ Audit head: {firm?.auditHead ?? "—"} +

+

+ Counterfactual replay and audit verification are available via API / + npm run audit:verify. +

+

Kill switch: {firm?.killed ? "ENGAGED" : "off"}

+
+
+ )} +
+ ); +} + +render(, document.getElementById("app")!); diff --git a/apps/console/src/styles.css b/apps/console/src/styles.css new file mode 100644 index 0000000..84ade24 --- /dev/null +++ b/apps/console/src/styles.css @@ -0,0 +1,109 @@ +:root { + --bg: #0f1410; + --panel: #1a221c; + --ink: #e8f0e9; + --muted: #9aab9d; + --accent: #c4f542; + --line: #2c3a30; + font-family: "IBM Plex Sans", "Segoe UI", sans-serif; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + background: radial-gradient(1200px 600px at 10% -10%, #243528 0%, transparent 55%), var(--bg); + color: var(--ink); + min-height: 100vh; +} + +.shell { + max-width: 1100px; + margin: 0 auto; + padding: 2rem 1.25rem 4rem; +} + +header { + display: flex; + justify-content: space-between; + gap: 1.5rem; + align-items: flex-start; + margin-bottom: 2rem; +} + +.eyebrow { + letter-spacing: 0.14em; + text-transform: uppercase; + color: var(--accent); + font-size: 0.75rem; + margin: 0 0 0.35rem; +} + +h1 { + font-family: "IBM Plex Serif", Georgia, serif; + font-weight: 500; + font-size: clamp(1.8rem, 4vw, 2.6rem); + margin: 0 0 0.4rem; +} + +.lede { + color: var(--muted); + max-width: 36rem; + margin: 0; +} + +.actions { + display: flex; + gap: 0.5rem; +} + +button { + background: var(--accent); + color: #132015; + border: 0; + padding: 0.7rem 1rem; + font-weight: 600; + cursor: pointer; +} + +button.ghost { + background: transparent; + color: var(--ink); + border: 1px solid var(--line); +} + +.grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); + gap: 1rem; +} + +section { + background: color-mix(in oklab, var(--panel) 90%, black); + border: 1px solid var(--line); + padding: 1rem 1.1rem; +} + +h2 { + margin: 0 0 0.75rem; + font-size: 0.95rem; + letter-spacing: 0.04em; + text-transform: uppercase; + color: var(--muted); +} + +ul { + margin: 0; + padding-left: 1.1rem; +} + +.muted { + color: var(--muted); +} + +pre { + white-space: pre-wrap; + font-size: 0.8rem; +} diff --git a/apps/console/tsconfig.json b/apps/console/tsconfig.json new file mode 100644 index 0000000..7948183 --- /dev/null +++ b/apps/console/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "jsx": "react-jsx", + "jsxImportSource": "preact", + "strict": true, + "skipLibCheck": true, + "noEmit": true + }, + "include": ["src/**/*.tsx", "src/**/*.ts"] +} diff --git a/apps/console/vite.config.ts b/apps/console/vite.config.ts new file mode 100644 index 0000000..4fa35cc --- /dev/null +++ b/apps/console/vite.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from "vite"; +import preact from "@preact/preset-vite"; + +export default defineConfig({ + plugins: [preact()], + build: { + outDir: "dist", + emptyOutDir: true, + }, + server: { + proxy: { "/api": "http://localhost:3000" }, + }, +}); diff --git a/docs/adr/0000-stack-bootstrap.md b/docs/adr/0000-stack-bootstrap.md new file mode 100644 index 0000000..9a1e28c --- /dev/null +++ b/docs/adr/0000-stack-bootstrap.md @@ -0,0 +1,3 @@ +# ADR-00 Stack bootstrap + +Adopt npm workspaces (`packages/core`, `packages/mcp-knowledge`, `apps/api`, `apps/console`), Hono, Drizzle+libsql, official MCP SDK, Vite+Preact. Retire Express and `better-sqlite3`. diff --git a/docs/adr/0001-firm-model.md b/docs/adr/0001-firm-model.md new file mode 100644 index 0000000..2c9467c --- /dev/null +++ b/docs/adr/0001-firm-model.md @@ -0,0 +1,3 @@ +# ADR-01 Firm model + +Departments with capital budgets, agents with owner/principal/trust, SLA timers, exception queues persisted in libsql. diff --git a/docs/adr/0002-work-contracts.md b/docs/adr/0002-work-contracts.md new file mode 100644 index 0000000..e600d30 --- /dev/null +++ b/docs/adr/0002-work-contracts.md @@ -0,0 +1,3 @@ +# ADR-02 Work contracts + +Contracts are the primary unit of work; handoffs are obligations; draft→settle for consequential tools; compensators on settle. diff --git a/docs/adr/0003-control-plane.md b/docs/adr/0003-control-plane.md new file mode 100644 index 0000000..aee8efd --- /dev/null +++ b/docs/adr/0003-control-plane.md @@ -0,0 +1,3 @@ +# ADR-03 Control plane + +ToolGateway is the sole chokepoint; knowledge uses real MCP stdio; fail-closed unknown tools; durable exceptions; kill switch. diff --git a/docs/adr/0004-trust-compensation.md b/docs/adr/0004-trust-compensation.md new file mode 100644 index 0000000..cb3ae2f --- /dev/null +++ b/docs/adr/0004-trust-compensation.md @@ -0,0 +1,3 @@ +# ADR-04 Trust + compensation + +Earned autonomy via trust ledger; saga-style compensators reverse settled L3+ actions. diff --git a/docs/adr/0005-company-day.md b/docs/adr/0005-company-day.md new file mode 100644 index 0000000..c04b5fd --- /dev/null +++ b/docs/adr/0005-company-day.md @@ -0,0 +1,3 @@ +# ADR-05 Company day + ops console + +Primary demo is a multi-handoff company day; Preact console shows capital, trust, exceptions. diff --git a/docs/adr/0006-insight-surface.md b/docs/adr/0006-insight-surface.md new file mode 100644 index 0000000..c1ef01c --- /dev/null +++ b/docs/adr/0006-insight-surface.md @@ -0,0 +1,3 @@ +# ADR-06 Insight surface + +`docs/future-of-the-firm.md`, README thesis, Governor tools (counterfactual + hash-chained audit). diff --git a/docs/future-of-the-firm.md b/docs/future-of-the-firm.md new file mode 100644 index 0000000..ca2b72f --- /dev/null +++ b/docs/future-of-the-firm.md @@ -0,0 +1,26 @@ +# Future of the Firm + +CorpOS is a **reference implementation of an autonomous company**. The thesis: + +> Autonomy is an organizational design problem. Future companies are policy + authority + capital + trust + exception queues — not chatbots with tools. + +## Three layers + +1. **Firm model** — org graph, owners, department capital, SLAs, exception queues. +2. **Work model** — work contracts, handoffs with obligations, draft→settle, compensating actions. +3. **Control plane** — MCP-gated tools, fail-closed risk ladder, durable HITL, kill/budgets, run traces. + +Humans govern **by exception**. Autonomy is **earned** from evidence (trust ledger), not granted in prompts. + +## 20-minute path + +1. `npm install && npm run build && npm run dev` +2. Open the ops console; click **Run company day** +3. Watch handoffs, one autonomous settle, one exception, trust unlock +4. Optionally open Governor view / run `npm run audit:verify` + +## Stack appendix + +Node 22+ · TypeScript · npm workspaces · Hono · Drizzle + libsql · official MCP TypeScript SDK · Vite + Preact · custom agent loop · SimulationProvider CI. + +Rejected for this reference: Temporal, Next.js, agent frameworks as core, `better-sqlite3`, Python rewrite. diff --git a/eslint.config.mjs b/eslint.config.mjs index e104c7b..c4ff451 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,39 +1,27 @@ -import js from "@eslint/js"; +import eslint from "@eslint/js"; import tseslint from "typescript-eslint"; import prettier from "eslint-config-prettier"; -import globals from "globals"; export default tseslint.config( + eslint.configs.recommended, + ...tseslint.configs.recommended, + prettier, { ignores: [ - "dist/**", - "node_modules/**", - "data/**", - "docs/**", - ".kilo/**", - ".playwright-mcp/**", - ".github/**", + "**/dist/**", + "**/node_modules/**", + "_legacy_src/**", + "_legacy_test/**", + "_archives/**", + "scripts/**/*.mjs", ], }, - js.configs.recommended, - ...tseslint.configs.recommended, { - languageOptions: { - ecmaVersion: 2022, - sourceType: "module", - globals: { - ...globals.node, - }, - }, rules: { "@typescript-eslint/no-unused-vars": [ - "warn", - { argsIgnorePattern: "^_", varsIgnorePattern: "^_", caughtErrorsIgnorePattern: "^_" }, + "error", + { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }, ], - "@typescript-eslint/no-explicit-any": "off", - "no-empty": ["error", { allowEmptyCatch: true }], - "no-unused-private-class-members": "off", }, }, - prettier, ); diff --git a/evidence/site-delivery/adversarial-stdout.txt b/evidence/site-delivery/adversarial-stdout.txt new file mode 100644 index 0000000..6837031 --- /dev/null +++ b/evidence/site-delivery/adversarial-stdout.txt @@ -0,0 +1 @@ +adversarial: ok diff --git a/evidence/site-delivery/company-day-scenario.json b/evidence/site-delivery/company-day-scenario.json new file mode 100644 index 0000000..c948c84 --- /dev/null +++ b/evidence/site-delivery/company-day-scenario.json @@ -0,0 +1,11 @@ +{ + "contractId": "ctr_g16siuyoYE", + "handoffs": 2, + "autonomousSettles": 1, + "exceptionSettles": 1, + "compensated": 1, + "trustAfter": 2, + "slaExceptions": 1, + "auditHead": "913d51c35e75fe4c148b6f893b2add53975564e8033874f24ce8be8737280e5e", + "ok": true +} diff --git a/evidence/site-delivery/receipt.json b/evidence/site-delivery/receipt.json new file mode 100644 index 0000000..76b1871 --- /dev/null +++ b/evidence/site-delivery/receipt.json @@ -0,0 +1,32 @@ +{ + "schema": "corpos-site-delivery-receipt/v1", + "program_id": "corpos-autonomous-company", + "site_id": "corpos", + "captured_at": "2026-07-26T02:49:43.402794+00:00", + "node": "v26.4.0", + "surfaces": { + "firm_work_control": "packages/core/src", + "api": "apps/api/src", + "console": "apps/console/src", + "mcp_knowledge": "packages/mcp-knowledge/src", + "adrs": "docs/adr", + "essay": "docs/future-of-the-firm.md" + }, + "company_day": { + "contractId": "ctr_g16siuyoYE", + "handoffs": 2, + "autonomousSettles": 1, + "exceptionSettles": 1, + "compensated": 1, + "trustAfter": 2, + "slaExceptions": 1, + "auditHead": "913d51c35e75fe4c148b6f893b2add53975564e8033874f24ce8be8737280e5e", + "ok": true + }, + "adversarial_stdout": "adversarial: ok", + "stack_forbidden_absent": { + "better-sqlite3": true, + "express_import": true + }, + "verification_scripts": "scripts/harness" +} diff --git a/package-lock.json b/package-lock.json index 47f5f71..2eeb87f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,78 +1,68 @@ { "name": "corpos", - "version": "0.1.0", + "version": "0.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "corpos", - "version": "0.1.0", + "version": "0.2.0", "license": "Apache-2.0", + "workspaces": [ + "packages/*", + "apps/*" + ], "dependencies": { - "better-sqlite3": "^12.11.1", - "express": "^5.2.1", - "nanoid": "^5.0.7", - "ws": "^8.18.0", - "zod": "^4.4.3" + "@corpos/core": "0.2.0", + "drizzle-orm": "^0.45.2" }, "devDependencies": { "@eslint/js": "^9.39.4", - "@types/better-sqlite3": "^7.6.11", - "@types/express": "^5.0.6", - "@types/node": "^26.0.1", - "@types/ws": "^8.5.12", + "@types/node": "^22.15.30", "eslint": "^10.6.0", "eslint-config-prettier": "^10.1.8", "globals": "^17.7.0", "prettier": "^3.9.3", "tsx": "^4.23.0", - "typescript": "^6.0.3", + "typescript": "^5.8.3", "typescript-eslint": "^8.62.0", - "vitest": "^4.1.9" + "vitest": "^3.2.4" }, "engines": { - "node": ">=20", + "node": ">=22", "npm": ">=10.9.2 <11" } }, - "node_modules/@emnapi/core": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", - "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.2", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", - "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", - "dev": true, - "license": "MIT", - "optional": true, + "apps/api": { + "name": "@corpos/api", + "version": "0.2.0", "dependencies": { - "tslib": "^2.4.0" + "@corpos/core": "0.2.0", + "@hono/node-server": "^1.14.4", + "drizzle-orm": "^0.45.2", + "hono": "^4.12.32" + }, + "devDependencies": { + "tsx": "^4.23.0", + "typescript": "^5.8.3" } }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", - "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", - "dev": true, - "license": "MIT", - "optional": true, + "apps/console": { + "name": "@corpos/console", + "version": "0.2.0", "dependencies": { - "tslib": "^2.4.0" + "preact": "^10.26.9" + }, + "devDependencies": { + "@preact/preset-vite": "^2.10.2", + "typescript": "^5.8.3", + "vite": "^6.3.5" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", - "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "apps/console/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" ], @@ -86,10 +76,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", - "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "apps/console/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" ], @@ -103,10 +93,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/android-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", - "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "apps/console/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" ], @@ -120,10 +110,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/android-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", - "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "apps/console/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": [ "x64" ], @@ -137,10 +127,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", - "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "apps/console/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" ], @@ -154,10 +144,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", - "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "apps/console/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" ], @@ -171,10 +161,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", - "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "apps/console/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" ], @@ -188,10 +178,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", - "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "apps/console/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" ], @@ -205,10 +195,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/linux-arm": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", - "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "apps/console/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" ], @@ -222,10 +212,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", - "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "apps/console/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" ], @@ -239,10 +229,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", - "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "apps/console/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": [ "ia32" ], @@ -256,10 +246,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", - "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "apps/console/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": [ "loong64" ], @@ -273,10 +263,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", - "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "apps/console/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": [ "mips64el" ], @@ -290,10 +280,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", - "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "apps/console/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": [ "ppc64" ], @@ -307,10 +297,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", - "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "apps/console/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": [ "riscv64" ], @@ -324,10 +314,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", - "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "apps/console/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": [ "s390x" ], @@ -341,10 +331,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/linux-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", - "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "apps/console/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": [ "x64" ], @@ -358,10 +348,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", - "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "apps/console/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" ], @@ -375,10 +365,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", - "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "apps/console/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" ], @@ -392,10 +382,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", - "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "apps/console/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" ], @@ -409,10 +399,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", - "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "apps/console/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" ], @@ -426,10 +416,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", - "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "apps/console/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" ], @@ -443,10 +433,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", - "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "apps/console/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" ], @@ -460,10 +450,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", - "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "apps/console/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" ], @@ -477,10 +467,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", - "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "apps/console/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" ], @@ -494,10 +484,10 @@ "node": ">=18" } }, - "node_modules/@esbuild/win32-x64": { - "version": "0.28.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", - "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "apps/console/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" ], @@ -511,283 +501,529 @@ "node": ">=18" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", - "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "apps/console/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", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "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" + } + }, + "apps/console/node_modules/vite": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.3.tgz", + "integrity": "sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==", "dev": true, "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.4.3" + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } } }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" }, - "funding": { - "url": "https://opencollective.com/eslint" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", "dev": true, "license": "MIT", "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "node": ">=6.9.0" } }, - "node_modules/@eslint/config-array": { - "version": "0.23.5", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", - "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "@eslint/object-schema": "^3.0.5", - "debug": "^4.3.1", - "minimatch": "^10.2.4" + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "node_modules/@eslint/config-helpers": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz", - "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==", + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^1.2.1" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/@eslint/core": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", - "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "node_modules/@babel/generator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.15" + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" }, "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" + "node": ">=6.9.0" } }, - "node_modules/@eslint/js": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", - "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", + "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", "dev": true, "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "dependencies": { + "@babel/types": "^7.29.7" }, - "funding": { - "url": "https://eslint.org/donate" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@eslint/object-schema": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", - "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" + "node": ">=6.9.0" } }, - "node_modules/@eslint/plugin-kit": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", - "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "Apache-2.0", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", "dependencies": { - "@eslint/core": "^1.2.1", - "levn": "^0.4.1" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" + "node": ">=6.9.0" } }, - "node_modules/@humanfs/core": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", - "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", "dependencies": { - "@humanfs/types": "^0.15.0" + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { - "node": ">=18.18.0" + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@humanfs/node": { - "version": "0.16.8", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", - "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", "dependencies": { - "@humanfs/core": "^0.19.2", - "@humanfs/types": "^0.15.0", - "@humanwhocodes/retry": "^0.4.0" + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { - "node": ">=18.18.0" + "node": ">=6.9.0" } }, - "node_modules/@humanfs/types": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", - "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, "engines": { - "node": ">=18.18.0" + "node": ">=6.0.0" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.29.7.tgz", + "integrity": "sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, "engines": { - "node": ">=12.22" + "node": ">=6.9.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "node_modules/@babel/plugin-transform-react-jsx": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.29.7.tgz", + "integrity": "sha512-WsZulLVBUHXVj2cUcPVx6UE21TpalB6bHbSFErKT0Ib++ax24jjXe73FqlWvdylFOjiuPHYi6VCcgRad1ItN+A==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-syntax-jsx": "^7.29.7", + "@babel/types": "^7.29.7" + }, "engines": { - "node": ">=18.18" + "node": ">=6.9.0" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "peerDependencies": { + "@babel/core": "^7.0.0-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/@babel/plugin-transform-react-jsx-development": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.29.7.tgz", + "integrity": "sha512-Xfy3UVMF04+ypnFbkhvfqtmvwfe92qwQdbGZVonhE+6v35GzlofmOnA1szaZqzb9xYWr0nl1e5EMmzi0DNON1g==", "dev": true, - "license": "MIT" + "license": "MIT", + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", - "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { - "@tybys/wasm-util": "^0.10.3" + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", + "debug": "^4.3.1" }, - "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@oxc-project/types": { - "version": "0.137.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.137.0.tgz", - "integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==", + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", "dev": true, "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@rolldown/binding-android-arm64": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.3.tgz", - "integrity": "sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==", + "node_modules/@corpos/api": { + "resolved": "apps/api", + "link": true + }, + "node_modules/@corpos/console": { + "resolved": "apps/console", + "link": true + }, + "node_modules/@corpos/core": { + "resolved": "packages/core", + "link": true + }, + "node_modules/@corpos/mcp-knowledge": { + "resolved": "packages/mcp-knowledge", + "link": true + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", "cpu": [ - "arm64" + "ppc64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "android" + "aix" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" } }, - "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.3.tgz", - "integrity": "sha512-0NwgwsjM7LrsuVnXMK3koTpagBNOhloc/BNjKqZjv4V5zI5r13qx69uVhRx+o5Z0yy4Hzq+lpy7TAgUG/ocvrw==", + "node_modules/@esbuild/android-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", "cpu": [ - "arm64" + "arm" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "darwin" + "android" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" } }, - "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.3.tgz", - "integrity": "sha512-YtiBp4disu6V560loT6PjMdiRaWmVvDNrUunAalbiFx2ggeJwxdAsgZMcoGP17uyAsTwAj5V1niksxlHnVQ1Sw==", + "node_modules/@esbuild/android-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", "cpu": [ - "x64" + "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "darwin" + "android" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" } }, - "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.3.tgz", - "integrity": "sha512-yD3EkEdXk2LypPxnf/kSZHirarsI8gcPzc62SukhR9VJTyvV+F9Q/GxWNuCojc7sXyuVC4DxRGhdDK4X8VSsbw==", + "node_modules/@esbuild/android-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", "cpu": [ "x64" ], @@ -795,50 +1031,50 @@ "license": "MIT", "optional": true, "os": [ - "freebsd" + "android" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" } }, - "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.3.tgz", - "integrity": "sha512-c+8vieQbsD7HNAHKIA34w0GJ9FedFFuJGD+7E6vz7Q3uqAIugL5p45fhlsj4UaAsHpcmlqugBWMhA0/j7o0sIg==", + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", "cpu": [ - "arm" + "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" + "darwin" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" } }, - "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.3.tgz", - "integrity": "sha512-50jD0uUwLvur7Zz9LHz17kaAdTPjn5wN93hEgjvmYFRZwiR7ZJYovTd5ipyWJDAnXKvZ+wgc+/Ika6dwSF5OcA==", + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", "cpu": [ - "arm64" + "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" + "darwin" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" } }, - "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.3.tgz", - "integrity": "sha512-BO9+oPL8K9poZJBfYPsXNtYjPE5uM3qeehT3aFcW4LITOl+iSqhp0abzjR2nWBUNjIZeKXjAEWBZ64WjNoHd6w==", + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", "cpu": [ "arm64" ], @@ -846,35 +1082,35 @@ "license": "MIT", "optional": true, "os": [ - "linux" + "freebsd" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" } }, - "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.3.tgz", - "integrity": "sha512-f3VpLB1vQ0Eo6ecr/6cekLnvYMFF4YBFoVGkfkvPLq1bAkbAwHYQPZKoAmG6OJyTcxxoC+AvezGx/S1obNC0Mw==", + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", "cpu": [ - "ppc64" + "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" + "freebsd" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" } }, - "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.3.tgz", - "integrity": "sha512-AmurZ26Pqx/RI9N1gzEOCklkKXl927yjfXWUUS0O7Puh8ARM/Ob8qfrD3qnWksScdw6cSrW5PSHE9DyLu7+PtA==", + "node_modules/@esbuild/linux-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", "cpu": [ - "s390x" + "arm" ], "dev": true, "license": "MIT", @@ -883,15 +1119,15 @@ "linux" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" } }, - "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.3.tgz", - "integrity": "sha512-JJpqs8bRGITDOdbkNKnlojzBabbOHrqjSvDr0IVsZObE1lBcPjxItUEY9eWIDbxaJ3cGrXPWGfGkIxFijg/URg==", + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", "cpu": [ - "x64" + "arm64" ], "dev": true, "license": "MIT", @@ -900,15 +1136,15 @@ "linux" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" } }, - "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.3.tgz", - "integrity": "sha512-rSJcdjPxzA/by/6/rYs+v+bXU7UjvnbUWz8MJb6kh6+knqB1dCrtHg0uu7C/4haqJvqdkYHQ5IGn+tCH9GLW/g==", + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", "cpu": [ - "x64" + "ia32" ], "dev": true, "license": "MIT", @@ -917,124 +1153,1242 @@ "linux" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" } }, - "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.3.tgz", - "integrity": "sha512-hQ3/PYkDJICgevvyNcVrihVeqq7k1Pp3VZ9lY+dauAYUJKO+auqApvANhvR1An9BhmqYKvW2Mu1F9u4DXSMLxQ==", + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", "cpu": [ - "arm64" + "loong64" ], "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.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.3.tgz", - "integrity": "sha512-Elcv/BtML9lXrV6JuKITc/grN2kYV9gjsQpW8Jfw4ioK0TOkjBjye0nnyqQNy9STNaI20lXNaQBRrD5gSgR0Yg==", + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", "cpu": [ - "wasm32" + "mips64el" ], "dev": true, "license": "MIT", "optional": true, - "dependencies": { - "@emnapi/core": "1.11.1", - "@emnapi/runtime": "1.11.1", - "@napi-rs/wasm-runtime": "^1.1.6" - }, + "os": [ + "linux" + ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" } }, - "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.3.tgz", - "integrity": "sha512-2DrEfhluH9yhiaFApmsjsjwrSYbNcY1oFTzYSP1a535jDbV98zCFanA/96TBUd0iDFcxGmw9QRExwGCXz3U+/g==", + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", "cpu": [ - "arm64" + "ppc64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "win32" + "linux" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.3.tgz", - "integrity": "sha512-OL4OMk7UPXOeVGGd3qo5zJyPIljf4AFgk5QAkPPS+OoLuOOozhuaQGC18MxVTnw/06q93gShAJzlwnSCY9YtqA==", + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, "os": [ "win32" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18" } }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", - "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "cpu": [ + "ia32" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "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/win32-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT" + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", - "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "node_modules/@eslint/config-helpers": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.7.0.tgz", + "integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz", + "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", + "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@hono/node-server": { + "version": "1.19.15", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.15.tgz", + "integrity": "sha512-Za2ai6TLdKjUvnur+eenO6nuYYipVAEhyCAdaV8IRvmU9kK8crOZUSYvIXn72E4f8fJqyAbpcJuTsYYmZp9Deg==", + "license": "MIT", + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@libsql/client": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@libsql/client/-/client-0.17.4.tgz", + "integrity": "sha512-lYayFWasDV78A+TjlEhr6ubb3odBV6OHjb+wdp8VQcyWWAEIjuwbCHaraEUS4m4yWoo0BvZo96It4VdzZRmRWw==", + "license": "MIT", + "dependencies": { + "@libsql/core": "^0.17.4", + "@libsql/hrana-client": "^0.10.0", + "js-base64": "^3.7.5", + "libsql": "^0.5.28", + "promise-limit": "^2.7.0" + } + }, + "node_modules/@libsql/core": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@libsql/core/-/core-0.17.4.tgz", + "integrity": "sha512-LqF9gIvnJ38nmAH1y/ChizHqDO/MO1wLgA96XrraulEEbqXxLjleSH92YWTolbuJKgPUmGu4aJk9W3UnAcxLOQ==", + "license": "MIT", + "dependencies": { + "js-base64": "^3.7.5" + } + }, + "node_modules/@libsql/darwin-arm64": { + "version": "0.5.29", + "resolved": "https://registry.npmjs.org/@libsql/darwin-arm64/-/darwin-arm64-0.5.29.tgz", + "integrity": "sha512-K+2RIB1OGFPYQbfay48GakLhqf3ArcbHqPFu7EZiaUcRgFcdw8RoltsMyvbj5ix2fY0HV3Q3Ioa/ByvQdaSM0A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@libsql/darwin-x64": { + "version": "0.5.29", + "resolved": "https://registry.npmjs.org/@libsql/darwin-x64/-/darwin-x64-0.5.29.tgz", + "integrity": "sha512-OtT+KFHsKFy1R5FVadr8FJ2Bb1mghtXTyJkxv0trocq7NuHntSki1eUbxpO5ezJesDvBlqFjnWaYYY516QNLhQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@libsql/hrana-client": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@libsql/hrana-client/-/hrana-client-0.10.0.tgz", + "integrity": "sha512-OoA4EMqRAC7kn7V2P6EQqRcpZf2W+AjsNIyCizBg339Tq/aMC7sRnzs3SklderhmQWAqEzvv8A2vhxVmWpkVvw==", + "license": "MIT", + "dependencies": { + "@libsql/isomorphic-ws": "^0.1.5", + "js-base64": "^3.7.5" + } + }, + "node_modules/@libsql/isomorphic-ws": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/@libsql/isomorphic-ws/-/isomorphic-ws-0.1.5.tgz", + "integrity": "sha512-DtLWIH29onUYR00i0GlQ3UdcTRC6EP4u9w/h9LxpUZJWRMARk6dQwZ6Jkd+QdwVpuAOrdxt18v0K2uIYR3fwFg==", + "license": "MIT", + "dependencies": { + "@types/ws": "^8.5.4", + "ws": "^8.13.0" + } + }, + "node_modules/@libsql/linux-arm-gnueabihf": { + "version": "0.5.29", + "resolved": "https://registry.npmjs.org/@libsql/linux-arm-gnueabihf/-/linux-arm-gnueabihf-0.5.29.tgz", + "integrity": "sha512-CD4n4zj7SJTHso4nf5cuMoWoMSS7asn5hHygsDuhRl8jjjCTT3yE+xdUvI4J7zsyb53VO5ISh4cwwOtf6k2UhQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@libsql/linux-arm-musleabihf": { + "version": "0.5.29", + "resolved": "https://registry.npmjs.org/@libsql/linux-arm-musleabihf/-/linux-arm-musleabihf-0.5.29.tgz", + "integrity": "sha512-2Z9qBVpEJV7OeflzIR3+l5yAd4uTOLxklScYTwpZnkm2vDSGlC1PRlueLaufc4EFITkLKXK2MWBpexuNJfMVcg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@libsql/linux-arm64-gnu": { + "version": "0.5.29", + "resolved": "https://registry.npmjs.org/@libsql/linux-arm64-gnu/-/linux-arm64-gnu-0.5.29.tgz", + "integrity": "sha512-gURBqaiXIGGwFNEaUj8Ldk7Hps4STtG+31aEidCk5evMMdtsdfL3HPCpvys+ZF/tkOs2MWlRWoSq7SOuCE9k3w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@libsql/linux-arm64-musl": { + "version": "0.5.29", + "resolved": "https://registry.npmjs.org/@libsql/linux-arm64-musl/-/linux-arm64-musl-0.5.29.tgz", + "integrity": "sha512-fwgYZ0H8mUkyVqXZHF3mT/92iIh1N94Owi/f66cPVNsk9BdGKq5gVpoKO+7UxaNzuEH1roJp2QEwsCZMvBLpqg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@libsql/linux-x64-gnu": { + "version": "0.5.29", + "resolved": "https://registry.npmjs.org/@libsql/linux-x64-gnu/-/linux-x64-gnu-0.5.29.tgz", + "integrity": "sha512-y14V0vY0nmMC6G0pHeJcEarcnGU2H6cm21ZceRkacWHvQAEhAG0latQkCtoS2njFOXiYIg+JYPfAoWKbi82rkg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@libsql/linux-x64-musl": { + "version": "0.5.29", + "resolved": "https://registry.npmjs.org/@libsql/linux-x64-musl/-/linux-x64-musl-0.5.29.tgz", + "integrity": "sha512-gquqwA/39tH4pFl+J9n3SOMSymjX+6kZ3kWgY3b94nXFTwac9bnFNMffIomgvlFaC4ArVqMnOZD3nuJ3H3VO1w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@libsql/win32-x64-msvc": { + "version": "0.5.29", + "resolved": "https://registry.npmjs.org/@libsql/win32-x64-msvc/-/win32-x64-msvc-0.5.29.tgz", + "integrity": "sha512-4/0CvEdhi6+KjMxMaVbFM2n2Z44escBRoEYpR+gZg64DdetzGnYm8mcNLcoySaDJZNaBd6wz5DNdgRmcI4hXcg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", + "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@modelcontextprotocol/sdk/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/@neon-rs/load": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@neon-rs/load/-/load-0.0.4.tgz", + "integrity": "sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw==", + "license": "MIT" + }, + "node_modules/@preact/preset-vite": { + "version": "2.10.6", + "resolved": "https://registry.npmjs.org/@preact/preset-vite/-/preset-vite-2.10.6.tgz", + "integrity": "sha512-ZZ5RIT2ZVXg8UxRA8VZpoT8CdpDG7RFIqOT4bELqNBp85+HKvQBbgmh3gsoW1UOQXx26TLe+ZRNKI7q281Kckw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-transform-react-jsx": "^7.27.1", + "@babel/plugin-transform-react-jsx-development": "^7.27.1", + "@prefresh/vite": "^2.4.11", + "@rollup/pluginutils": "^5.0.0", + "babel-plugin-transform-hook-names": "^1.0.2", + "debug": "^4.4.3", + "magic-string": "^0.30.21", + "picocolors": "^1.1.1", + "vite-prerender-plugin": "^0.5.8", + "zimmerframe": "^1.1.4" + }, + "peerDependencies": { + "@babel/core": "7.x", + "vite": "2.x || 3.x || 4.x || 5.x || 6.x || 7.x || 8.x" + } + }, + "node_modules/@prefresh/babel-plugin": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@prefresh/babel-plugin/-/babel-plugin-0.5.3.tgz", + "integrity": "sha512-57LX2SHs4BX2s1IwCjNzTE2OJeEepRCNf1VTEpbNcUyHfMO68eeOWGDIt4ob9aYlW6PEWZ1SuwNikuoIXANDtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@prefresh/core": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/@prefresh/core/-/core-1.5.10.tgz", + "integrity": "sha512-7yPTFbG56sutaFu8krp3B4a200KOFUvrtlllKWRuLjsYXo9UUucHOZRcer+gtgMkFTpv6ob8TGcTwA32bSwa1w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "preact": "^10.0.0 || ^11.0.0-0" + } + }, + "node_modules/@prefresh/utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@prefresh/utils/-/utils-1.2.1.tgz", + "integrity": "sha512-vq/sIuN5nYfYzvyayXI4C2QkprfNaHUQ9ZX+3xLD8nL3rWyzpxOm1+K7RtMbhd+66QcaISViK7amjnheQ/4WZw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@prefresh/vite": { + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/@prefresh/vite/-/vite-2.4.12.tgz", + "integrity": "sha512-FY1fzXpUjiuosznMV0YM7XAOPZjB5FIdWS0W24+XnlxYkt9hNAwwsiKYn+cuTEoMtD/ZVazS5QVssBr9YhpCQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.22.1", + "@prefresh/babel-plugin": "^0.5.2", + "@prefresh/core": "^1.5.0", + "@prefresh/utils": "^1.2.0", + "@rollup/pluginutils": "^4.2.1" + }, + "peerDependencies": { + "preact": "^10.4.0 || ^11.0.0-0", + "vite": ">=2.0.0" + } + }, + "node_modules/@prefresh/vite/node_modules/@rollup/pluginutils": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", + "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "estree-walker": "^2.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/@prefresh/vite/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", + "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz", + "integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz", + "integrity": "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz", + "integrity": "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz", + "integrity": "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz", + "integrity": "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz", + "integrity": "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz", + "integrity": "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz", + "integrity": "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz", + "integrity": "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz", + "integrity": "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz", + "integrity": "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz", + "integrity": "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz", + "integrity": "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz", + "integrity": "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz", + "integrity": "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz", + "integrity": "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz", + "integrity": "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz", + "integrity": "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz", + "integrity": "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz", + "integrity": "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz", + "integrity": "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz", + "integrity": "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz", + "integrity": "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } + "os": [ + "win32" + ] }, - "node_modules/@types/better-sqlite3": { - "version": "7.6.13", - "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.13.tgz", - "integrity": "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==", + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz", + "integrity": "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@types/node": "*" - } + "optional": true, + "os": [ + "win32" + ] }, - "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/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz", + "integrity": "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } + "optional": true, + "os": [ + "win32" + ] }, "node_modules/@types/chai": { "version": "5.2.3", @@ -1047,16 +2401,6 @@ "assertion-error": "^2.0.1" } }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/deep-eql": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", @@ -1078,38 +2422,6 @@ "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==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^5.0.0", - "@types/serve-static": "^2" - } - }, - "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==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "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/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -1118,72 +2430,35 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-26.0.1.tgz", - "integrity": "sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~8.3.0" - } - }, - "node_modules/@types/qs": { - "version": "6.15.1", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz", - "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==", - "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, + "version": "22.20.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz", + "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==", "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": "*" + "undici-types": "~6.21.0" } }, "node_modules/@types/ws": { "version": "8.18.1", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", - "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.62.0.tgz", - "integrity": "sha512-o+mpz7EYiMzXoySXiKmzlabIvTVqUuK5yLrAedRPRDA0IpPFMUV1IXt6OqljIxX/kumN6EjUYp41Hqelh6p/Dw==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.65.0.tgz", + "integrity": "sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.62.0", - "@typescript-eslint/type-utils": "8.62.0", - "@typescript-eslint/utils": "8.62.0", - "@typescript-eslint/visitor-keys": "8.62.0", + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/type-utils": "8.65.0", + "@typescript-eslint/utils": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -1196,15 +2471,15 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.62.0", + "@typescript-eslint/parser": "^8.65.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", + "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", "dev": true, "license": "MIT", "engines": { @@ -1212,16 +2487,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.62.0.tgz", - "integrity": "sha512-dzHeT2gySzZtLDsuqxU9AkYgIsQoHAHtRBpOqM+Ofzx1Bwrd2RcCjQJ+6iQbsHOIR6NS33bF2W1k3blN1zLDrA==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.65.0.tgz", + "integrity": "sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.62.0", - "@typescript-eslint/types": "8.62.0", - "@typescript-eslint/typescript-estree": "8.62.0", - "@typescript-eslint/visitor-keys": "8.62.0", + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", "debug": "^4.4.3" }, "engines": { @@ -1237,14 +2512,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.62.0.tgz", - "integrity": "sha512-wexnCqiTg7BOGtbLDftYpRWlmLq4xfoMd7BKFR6Y75sZS3QmRKLdN3yWLhmIYgqMmP/OXWpj3H8odkb5nGURCQ==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz", + "integrity": "sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.62.0", - "@typescript-eslint/types": "^8.62.0", + "@typescript-eslint/tsconfig-utils": "^8.65.0", + "@typescript-eslint/types": "^8.65.0", "debug": "^4.4.3" }, "engines": { @@ -1259,14 +2534,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.62.0.tgz", - "integrity": "sha512-1lX38kNxXIRb8mEc3lbq5mdHq1Pf2+U0nFU65KfT18mtPxxl0fvjuEE92mHuXPuCtElJhOrddOpyMlM3Z0umEA==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz", + "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.62.0", - "@typescript-eslint/visitor-keys": "8.62.0" + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1277,9 +2552,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.62.0.tgz", - "integrity": "sha512-y2GAdB6ykaXUvuspbYnizQc4oDDz0Tz/Yc7iWrXf9mx8vm/L/0vLHCe0tS2boG96Zy+DivnVDQ9ZUEWoHqqx1g==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz", + "integrity": "sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==", "dev": true, "license": "MIT", "engines": { @@ -1294,15 +2569,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.62.0.tgz", - "integrity": "sha512-+g5O3j0w2ldzC86Pv6fvbO/xhAonbJFIdf/MKQ1d30gndlsVzUOE83ldfSE15Qrl9fhFjK6AovHs5Wpp6vx86w==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.65.0.tgz", + "integrity": "sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.62.0", - "@typescript-eslint/typescript-estree": "8.62.0", - "@typescript-eslint/utils": "8.62.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/utils": "8.65.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -1319,9 +2594,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.62.0.tgz", - "integrity": "sha512-KvAclkktORPvM54TgLgA4z9HIV1M8zOgw9ZVNXl9f/8dLYfXYX1wkMXP7qmabpijQRV5bHJLOmoyGQbLMaUYeg==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", + "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", "dev": true, "license": "MIT", "engines": { @@ -1333,16 +2608,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.62.0.tgz", - "integrity": "sha512-+hVbNxtW64pIcZWDPGbyaKF7vp2IBTVY5ma1blwwksrjdsbdqqEKvJWMGbBofei4F6Dovx1M0RJgoFeNu2279A==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz", + "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.62.0", - "@typescript-eslint/tsconfig-utils": "8.62.0", - "@typescript-eslint/types": "8.62.0", - "@typescript-eslint/visitor-keys": "8.62.0", + "@typescript-eslint/project-service": "8.65.0", + "@typescript-eslint/tsconfig-utils": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -1361,16 +2636,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.62.0.tgz", - "integrity": "sha512-82r66fi9zYwZ+mTq3vKgwjbZ1PVk/DJzrXFLpG6RnBbdvH8TEGVHIs9H4d2drhkOzf0syZuD/OZvvlu6GDbP4g==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.65.0.tgz", + "integrity": "sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.62.0", - "@typescript-eslint/types": "8.62.0", - "@typescript-eslint/typescript-estree": "8.62.0" + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1385,13 +2660,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.62.0.tgz", - "integrity": "sha512-CY3uyFSRbcQv3nnSv8S0+lDftMVz6P963PoRlxrV7ew/Md564g9ut60PYzdLM5qW4jFn93GBF+Soi90ISAN+GQ==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", + "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.62.0", + "@typescript-eslint/types": "8.65.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -1403,40 +2678,39 @@ } }, "node_modules/@vitest/expect": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz", - "integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.7.tgz", + "integrity": "sha512-E8eBXaKibuvH2pSZErOjdVb5vF4PbKYcrnluBTYxEk1l/VhhwZg1kZQsdtjq+CsF5CFydf2Rdkz7jDHKSisi3w==", "dev": true, "license": "MIT", "dependencies": { - "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", - "chai": "^6.2.2", - "tinyrainbow": "^3.1.0" + "@vitest/spy": "3.2.7", + "@vitest/utils": "3.2.7", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/mocker": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz", - "integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.7.tgz", + "integrity": "sha512-Trr0hYO9CM3Wj6ksWHRhK9IZpIY6wTMO5u/MqXurMxT57sWBaOPEtP3Oq60ihZuh5JsiagKfz95OcxdEP6dBrA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.9", + "@vitest/spy": "3.2.7", "estree-walker": "^3.0.3", - "magic-string": "^0.30.21" + "magic-string": "^0.30.17" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "msw": "^2.4.9", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "peerDependenciesMeta": { "msw": { @@ -1447,43 +2721,53 @@ } } }, + "node_modules/@vitest/mocker/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/@vitest/pretty-format": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz", - "integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.7.tgz", + "integrity": "sha512-KUHlwqVu0sRlhCdyPdQ/wBoTfRahjUky1MubOmYw9fWfIZy1gNoHpuaaQBPAaMaVYdQYHJLurzj8ECCj5OwTqA==", "dev": true, "license": "MIT", "dependencies": { - "tinyrainbow": "^3.1.0" + "tinyrainbow": "^2.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/runner": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz", - "integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.7.tgz", + "integrity": "sha512-sB9y4ovltoQP+WaUPwmSxO9WIg9Ig694Di5PalVPsYHklAdE027mehpWF2SQSVq+k6sFgaivbTjTJwZLSHbedA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.9", - "pathe": "^2.0.3" + "@vitest/utils": "3.2.7", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz", - "integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.7.tgz", + "integrity": "sha512-7C+MwShwtBSI5Buwoyg3s/iY1eHL9PKAf+O1wVh/TdnjXUtkoL/9YQtre90i4MtNXM6edP1wJ2zOBpfCyhIS7g==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.9", - "@vitest/utils": "4.1.9", - "magic-string": "^0.30.21", + "@vitest/pretty-format": "3.2.7", + "magic-string": "^0.30.17", "pathe": "^2.0.3" }, "funding": { @@ -1491,25 +2775,28 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz", - "integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.7.tgz", + "integrity": "sha512-Q2eQGI6d2L/hBtZ0qNuKcAGid68XK6cv1xsoaIma6PaJhHPoqcEJhYpXZ/5myCMqkNgtP6UKuBhbc0nHKnrkuQ==", "dev": true, "license": "MIT", + "dependencies": { + "tinyspy": "^4.0.3" + }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/utils": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz", - "integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.7.tgz", + "integrity": "sha512-x6BDOd7dyo3PFLY3I9/HJ25X/6OurhGXk2/B9gOZNPF7XDVjeBK4k01lQE5uvDpbuheErh91qYuE1E2OEjK3Rw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.9", - "convert-source-map": "^2.0.0", - "tinyrainbow": "^3.1.0" + "@vitest/pretty-format": "3.2.7", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" }, "funding": { "url": "https://opencollective.com/vitest" @@ -1568,6 +2855,45 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, "node_modules/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", @@ -1578,6 +2904,16 @@ "node": ">=12" } }, + "node_modules/babel-plugin-transform-hook-names": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-hook-names/-/babel-plugin-transform-hook-names-1.0.2.tgz", + "integrity": "sha512-5gafyjyyBTTdX/tQQ0hRgu4AhNHG/hqWi0ZZmg2xvs2FgRkJXzDNKBZCyoYqgFkovfDrgM8OoKg8karoUvWeCw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@babel/core": "^7.12.10" + } + }, "node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", @@ -1588,58 +2924,17 @@ "node": "18 || 20 || >=22" } }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/better-sqlite3": { - "version": "12.11.1", - "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.11.1.tgz", - "integrity": "sha512-dq9AtApgg5PGFtBzPFSBl3HZQjHok5gaQCM6zh2Yk0aSmDCs1CbnVI8/HgASQkNKsWFpseIO9beg5xxpYhbIfA==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "bindings": "^1.5.0", - "prebuild-install": "^7.1.1" + "node_modules/baseline-browser-mapping": { + "version": "2.11.3", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.3.tgz", + "integrity": "sha512-sbT0Ui/CZwyAyy7icT1Gw5P1LKRlFaHwaF6tDCW5YHq2X5SeeZFphBuIagopSfwSSZq3sQcbmEL072yphxm7ew==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" }, "engines": { - "node": "20.x || 22.x || 23.x || 24.x || 25.x || 26.x" - } - }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "license": "MIT", - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" + "node": ">=6.0.0" } }, "node_modules/body-parser": { @@ -1679,41 +2974,58 @@ "url": "https://opencollective.com/express" } }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, "node_modules/brace-expansion": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", - "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "node_modules/browserslist": { + "version": "4.28.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz", + "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", + "dev": true, "funding": [ { - "type": "github", - "url": "https://github.com/sponsors/feross" + "type": "opencollective", + "url": "https://opencollective.com/browserslist" }, { - "type": "patreon", - "url": "https://www.patreon.com/feross" + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" }, { - "type": "consulting", - "url": "https://feross.org/support" + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "license": "MIT", "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "baseline-browser-mapping": "^2.10.44", + "caniuse-lite": "^1.0.30001806", + "electron-to-chromium": "^1.5.393", + "node-releases": "^2.0.51", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, "node_modules/bytes": { @@ -1725,6 +3037,16 @@ "node": ">= 0.8" } }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "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", @@ -1754,21 +3076,53 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/caniuse-lite": { + "version": "1.0.30001806", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", + "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, "node_modules/chai": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", - "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", "dev": true, "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, "engines": { "node": ">=18" } }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "license": "ISC" + "node_modules/check-error": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } }, "node_modules/content-disposition": { "version": "1.1.0", @@ -1817,11 +3171,27 @@ "node": ">=6.6.0" } }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -1832,6 +3202,36 @@ "node": ">= 8" } }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -1849,28 +3249,14 @@ } } }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "license": "MIT", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, "license": "MIT", "engines": { - "node": ">=4.0.0" + "node": ">=6" } }, "node_modules/deep-is": { @@ -1889,13 +3275,197 @@ "node": ">= 0.8" } }, - "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/detect-libc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/drizzle-orm": { + "version": "0.45.2", + "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.45.2.tgz", + "integrity": "sha512-kY0BSaTNYWnoDMVoyY8uxmyHjpJW1geOmBMdSSicKo9CIIWkSxMIj2rkeSR51b8KAPB7m+qysjuHme5nKP+E5Q==", "license": "Apache-2.0", - "engines": { - "node": ">=8" + "peerDependencies": { + "@aws-sdk/client-rds-data": ">=3", + "@cloudflare/workers-types": ">=4", + "@electric-sql/pglite": ">=0.2.0", + "@libsql/client": ">=0.10.0", + "@libsql/client-wasm": ">=0.10.0", + "@neondatabase/serverless": ">=0.10.0", + "@op-engineering/op-sqlite": ">=2", + "@opentelemetry/api": "^1.4.1", + "@planetscale/database": ">=1.13", + "@prisma/client": "*", + "@tidbcloud/serverless": "*", + "@types/better-sqlite3": "*", + "@types/pg": "*", + "@types/sql.js": "*", + "@upstash/redis": ">=1.34.7", + "@vercel/postgres": ">=0.8.0", + "@xata.io/client": "*", + "better-sqlite3": ">=7", + "bun-types": "*", + "expo-sqlite": ">=14.0.0", + "gel": ">=2", + "knex": "*", + "kysely": "*", + "mysql2": ">=2", + "pg": ">=8", + "postgres": ">=3", + "sql.js": ">=1", + "sqlite3": ">=5" + }, + "peerDependenciesMeta": { + "@aws-sdk/client-rds-data": { + "optional": true + }, + "@cloudflare/workers-types": { + "optional": true + }, + "@electric-sql/pglite": { + "optional": true + }, + "@libsql/client": { + "optional": true + }, + "@libsql/client-wasm": { + "optional": true + }, + "@neondatabase/serverless": { + "optional": true + }, + "@op-engineering/op-sqlite": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@prisma/client": { + "optional": true + }, + "@tidbcloud/serverless": { + "optional": true + }, + "@types/better-sqlite3": { + "optional": true + }, + "@types/pg": { + "optional": true + }, + "@types/sql.js": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@vercel/postgres": { + "optional": true + }, + "@xata.io/client": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "bun-types": { + "optional": true + }, + "expo-sqlite": { + "optional": true + }, + "gel": { + "optional": true + }, + "knex": { + "optional": true + }, + "kysely": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "pg": { + "optional": true + }, + "postgres": { + "optional": true + }, + "prisma": { + "optional": true + }, + "sql.js": { + "optional": true + }, + "sqlite3": { + "optional": true + } } }, "node_modules/dunder-proto": { @@ -1918,6 +3488,13 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, + "node_modules/electron-to-chromium": { + "version": "1.5.396", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.396.tgz", + "integrity": "sha512-yHiw2Y3C3H9U6TMbOfoWK/BPreiOPXRfTWPBwQBoZG6/8TB6eOPnsy5oaRYuatR7Fw2SJ4kKforgufeo7fq0EQ==", + "dev": true, + "license": "ISC" + }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", @@ -1927,13 +3504,17 @@ "node": ">= 0.8" } }, - "node_modules/end-of-stream": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "license": "MIT", - "dependencies": { - "once": "^1.4.0" + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, "node_modules/es-define-property": { @@ -1955,9 +3536,9 @@ } }, "node_modules/es-module-lexer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.2.0.tgz", - "integrity": "sha512-3lGxdTXCLfe1MYfTz1y2ksAAUM4NAOP6rPEjxGJVKO7TZ5+tvHCaQWGpC4Y3IXvW3ece0Cz1cIP4FWBxOnGCTQ==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", "dev": true, "license": "MIT" }, @@ -2015,6 +3596,16 @@ "@esbuild/win32-x64": "0.28.1" } }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -2035,9 +3626,9 @@ } }, "node_modules/eslint": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.6.0.tgz", - "integrity": "sha512-6lVbcqSodALYo+4ELD0heG6lFiFxnLMuLkiMi2qV8LMp54N8tE8FT1GMH+ev4Ti00nFjNze2+Su6DsV5OQW3Dg==", + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.8.0.tgz", + "integrity": "sha512-nuKKvN+oIBO0koN7Tm7dlkmnkc21mtt0QJLwAKzjLq14y6lRTdVG36MZHJ8eQHwdJMwZbQNMlPOYedMq/oVJvQ==", "dev": true, "license": "MIT", "workspaces": [ @@ -2047,7 +3638,7 @@ "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", "@eslint/config-array": "^0.23.5", - "@eslint/config-helpers": "^0.6.0", + "@eslint/config-helpers": "^0.7.0", "@eslint/core": "^1.2.1", "@eslint/plugin-kit": "^0.7.2", "@humanfs/node": "^0.16.6", @@ -2071,7 +3662,7 @@ "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "minimatch": "^10.2.4", + "minimatch": "^10.2.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, @@ -2196,14 +3787,11 @@ } }, "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } + "license": "MIT" }, "node_modules/esutils": { "version": "2.0.3", @@ -2224,13 +3812,25 @@ "node": ">= 0.6" } }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "license": "(MIT OR WTFPL)", + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, "engines": { - "node": ">=6" + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.0.tgz", + "integrity": "sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" } }, "node_modules/expect-type": { @@ -2286,11 +3886,29 @@ "url": "https://opencollective.com/express" } }, + "node_modules/express-rate-limit": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.6.0.tgz", + "integrity": "sha512-XKJXDsASUOo0LLtFwW5hCcQGH0N4WQc/Rn8/Pvoia+TJFOkkFPvrtW9lZOeeNcxQJspvOIERMwiRLsVFlhHEkA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "ip-address": "^10.2.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, "license": "MIT" }, "node_modules/fast-json-stable-stringify": { @@ -2307,6 +3925,22 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-uri": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", + "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -2338,12 +3972,6 @@ "node": ">=16.0.0" } }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "license": "MIT" - }, "node_modules/finalhandler": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", @@ -2397,9 +4025,9 @@ } }, "node_modules/flatted": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", - "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.3.tgz", + "integrity": "sha512-/zipXxyO6rGvuNGDiULY9MvEGSkb2gaG4GGH4ygMi0ZZzyMHdUZBmntJmx5x1G2VuPytCwGN4xsJP6cw+sK+vQ==", "dev": true, "license": "ISC" }, @@ -2421,12 +4049,6 @@ "node": ">= 0.8" } }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "license": "MIT" - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2451,6 +4073,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -2488,12 +4120,6 @@ "node": ">= 0.4" } }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "license": "MIT" - }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -2556,6 +4182,25 @@ "node": ">= 0.4" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/hono": { + "version": "4.12.32", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.32.tgz", + "integrity": "sha512-XcuyW9qE2kJn07PkecMOBd5Vq/hMy7mmGw+idz1yblbg9N17ijJODrvPkn7/dwL3Kulj8LcRJ69DLOWf91dRUg==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, "node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", @@ -2577,9 +4222,9 @@ } }, "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==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -2592,26 +4237,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -2638,11 +4263,14 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC" + "node_modules/ip-address": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.3.1.tgz", + "integrity": "sha512-1e9d3kb97NHJTIJDZW9rKqW2h6+dFa50Dy0fpPSMQp2ADje5gvKsXmdiK6dwY5t76TaTt5+P5N1Y/LoToIxP6g==", + "license": "MIT", + "engines": { + "node": ">= 12" + } }, "node_modules/ipaddr.js": { "version": "1.9.1", @@ -2684,315 +4312,146 @@ }, "node_modules/isexe": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "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" - }, + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jose": { + "version": "6.2.4", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.4.tgz", + "integrity": "sha512-N8acGzVsQy6M/fjFcxtysNc4Q379TcM5dM/qKkNtsHFji88yANnXTr7BLeP75iPnFwBfQzM/jg2BZ9+HZrHCZA==", + "license": "MIT", "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "url": "https://github.com/sponsors/panva" } }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", - "cpu": [ - "x64" - ], + "node_modules/js-base64": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.9.1.tgz", + "integrity": "sha512-U73qptcvf/HIOauFOmqT3a0mDUp0MYlfd15oqoe9kqZt5XhiXVb+HG09sLvI9PQ9tZIBFS4nlErai8zbWazP0g==", + "license": "BSD-3-Clause" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } + "license": "MIT" }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", - "cpu": [ - "x64" - ], + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "engines": { + "node": ">=6" } }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", - "cpu": [ - "arm" - ], + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } + "license": "MIT" }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", - "cpu": [ - "arm64" - ], + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } + "license": "MIT" }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", - "cpu": [ - "arm64" - ], + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "license": "BSD-2-Clause" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } + "license": "MIT" }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", - "cpu": [ - "x64" - ], + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" + "license": "MIT", + "bin": { + "json5": "lib/cli.js" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "engines": { + "node": ">=6" } }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", - "cpu": [ - "x64" - ], + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" } }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", - "cpu": [ - "arm64" - ], + "node_modules/kolorist": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", + "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" + "license": "MIT" + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "engines": { + "node": ">= 0.8.0" } }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "node_modules/libsql": { + "version": "0.5.29", + "resolved": "https://registry.npmjs.org/libsql/-/libsql-0.5.29.tgz", + "integrity": "sha512-8lMP8iMgiBzzoNbAPQ59qdVcj6UaE/Vnm+fiwX4doX4Narook0a4GPKWBEv+CR8a1OwbfkgL18uBfBjWdF0Fzg==", "cpu": [ - "x64" + "x64", + "arm64", + "wasm32", + "arm" ], - "dev": true, - "license": "MPL-2.0", - "optional": true, + "license": "MIT", "os": [ + "darwin", + "linux", "win32" ], - "engines": { - "node": ">= 12.0.0" + "dependencies": { + "@neon-rs/load": "^0.0.4", + "detect-libc": "2.0.2" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "optionalDependencies": { + "@libsql/darwin-arm64": "0.5.29", + "@libsql/darwin-x64": "0.5.29", + "@libsql/linux-arm-gnueabihf": "0.5.29", + "@libsql/linux-arm-musleabihf": "0.5.29", + "@libsql/linux-arm64-gnu": "0.5.29", + "@libsql/linux-arm64-musl": "0.5.29", + "@libsql/linux-x64-gnu": "0.5.29", + "@libsql/linux-x64-musl": "0.5.29", + "@libsql/win32-x64-msvc": "0.5.29" } }, "node_modules/locate-path": { @@ -3011,6 +4470,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -3031,12 +4507,16 @@ } }, "node_modules/media-typer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", - "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz", + "integrity": "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==", "license": "MIT", "engines": { "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/merge-descriptors": { @@ -3076,18 +4556,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/minimatch": { "version": "10.2.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", @@ -3104,21 +4572,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "license": "MIT" - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -3126,9 +4579,10 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "5.1.16", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.16.tgz", - "integrity": "sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ==", + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", + "dev": true, "funding": [ { "type": "github", @@ -3137,18 +4591,12 @@ ], "license": "MIT", "bin": { - "nanoid": "bin/nanoid.js" + "nanoid": "bin/nanoid.cjs" }, "engines": { - "node": "^18 || >=20" + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/napi-build-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", - "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", - "license": "MIT" - }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -3165,16 +4613,47 @@ "node": ">= 0.6" } }, - "node_modules/node-abi": { - "version": "3.92.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.92.0.tgz", - "integrity": "sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==", + "node_modules/node-html-parser": { + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-6.1.13.tgz", + "integrity": "sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-select": "^5.1.0", + "he": "1.2.0" + } + }, + "node_modules/node-releases": { + "version": "2.0.51", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz", + "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", + "dev": true, "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "semver": "^7.3.5" + "boolbase": "^1.0.0" }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=0.10.0" } }, "node_modules/object-inspect": { @@ -3189,20 +4668,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/obug": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.3.tgz", - "integrity": "sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==", - "dev": true, - "funding": [ - "https://github.com/sponsors/sxzz", - "https://opencollective.com/debug" - ], - "license": "MIT", - "engines": { - "node": ">=12.20.0" - } - }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -3297,7 +4762,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -3320,6 +4784,16 @@ "dev": true, "license": "MIT" }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -3328,9 +4802,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "dev": true, "license": "MIT", "engines": { @@ -3340,10 +4814,19 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, "node_modules/postcss": { - "version": "8.5.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz", - "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==", + "version": "8.5.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz", + "integrity": "sha512-g50586zr4bZmwFiTlflMu8E0bDTb5I5gertgwAKmsdUlTQIhZtunzUlD1WSzwcVWPoAVpsrA6vlfCD7oXvRwgg==", "dev": true, "funding": [ { @@ -3351,68 +4834,40 @@ "url": "https://opencollective.com/postcss/" }, { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.12", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss/node_modules/nanoid": { - "version": "3.3.15", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", - "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", - "dev": true, - "funding": [ + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, { "type": "github", "url": "https://github.com/sponsors/ai" } ], "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" + "dependencies": { + "nanoid": "^3.3.16", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + "node": "^10 || ^12 || >=14" } }, - "node_modules/prebuild-install": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", - "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", - "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", + "node_modules/preact": { + "version": "10.29.7", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.7.tgz", + "integrity": "sha512-DCHYrK/B10yUD3ZjLfhZ3WIE/9Vf9VFUODcRE2dRomTYDpJk6z6L9wecSfhfE6M9ZTHUdyQkoC46arIDhEV84Q==", "license": "MIT", - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^2.0.0", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" }, - "bin": { - "prebuild-install": "bin.js" + "peerDependencies": { + "preact-render-to-string": ">=5" }, - "engines": { - "node": ">=10" + "peerDependenciesMeta": { + "preact-render-to-string": { + "optional": true + } } }, "node_modules/prelude-ls": { @@ -3426,9 +4881,9 @@ } }, "node_modules/prettier": { - "version": "3.9.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.3.tgz", - "integrity": "sha512-HWmu+K+zvHNpaMfSnYeqdqrDbR16cuIXaPx8WoHaviQkDJh1/0BNtOZmHVQI5jc3wXv0H1yXc9wjvFdXh+n3hQ==", + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", + "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", "dev": true, "license": "MIT", "bin": { @@ -3441,6 +4896,12 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/promise-limit": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/promise-limit/-/promise-limit-2.7.0.tgz", + "integrity": "sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw==", + "license": "ISC" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -3454,16 +4915,6 @@ "node": ">= 0.10" } }, - "node_modules/pump": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", - "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -3518,67 +4969,58 @@ "node": ">= 0.10" } }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, "engines": { - "node": ">= 6" + "node": ">=0.10.0" } }, - "node_modules/rolldown": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.3.tgz", - "integrity": "sha512-1F1eEtUBtFvcGm1HQ9TiUIUHPQG7mSAODrhIzjxoUEFuo8OcbrGLiVLkevNgj84TE4lnHvnumwFjhJO5Eu135g==", + "node_modules/rollup": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz", + "integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.137.0", - "@rolldown/pluginutils": "^1.0.0" + "@types/estree": "1.0.9" }, "bin": { - "rolldown": "bin/cli.mjs" + "rollup": "dist/bin/rollup" }, "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=18.0.0", + "npm": ">=8.0.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.1.3", - "@rolldown/binding-darwin-arm64": "1.1.3", - "@rolldown/binding-darwin-x64": "1.1.3", - "@rolldown/binding-freebsd-x64": "1.1.3", - "@rolldown/binding-linux-arm-gnueabihf": "1.1.3", - "@rolldown/binding-linux-arm64-gnu": "1.1.3", - "@rolldown/binding-linux-arm64-musl": "1.1.3", - "@rolldown/binding-linux-ppc64-gnu": "1.1.3", - "@rolldown/binding-linux-s390x-gnu": "1.1.3", - "@rolldown/binding-linux-x64-gnu": "1.1.3", - "@rolldown/binding-linux-x64-musl": "1.1.3", - "@rolldown/binding-openharmony-arm64": "1.1.3", - "@rolldown/binding-wasm32-wasi": "1.1.3", - "@rolldown/binding-win32-arm64-msvc": "1.1.3", - "@rolldown/binding-win32-x64-msvc": "1.1.3" + "@rollup/rollup-android-arm-eabi": "4.62.2", + "@rollup/rollup-android-arm64": "4.62.2", + "@rollup/rollup-darwin-arm64": "4.62.2", + "@rollup/rollup-darwin-x64": "4.62.2", + "@rollup/rollup-freebsd-arm64": "4.62.2", + "@rollup/rollup-freebsd-x64": "4.62.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.2", + "@rollup/rollup-linux-arm-musleabihf": "4.62.2", + "@rollup/rollup-linux-arm64-gnu": "4.62.2", + "@rollup/rollup-linux-arm64-musl": "4.62.2", + "@rollup/rollup-linux-loong64-gnu": "4.62.2", + "@rollup/rollup-linux-loong64-musl": "4.62.2", + "@rollup/rollup-linux-ppc64-gnu": "4.62.2", + "@rollup/rollup-linux-ppc64-musl": "4.62.2", + "@rollup/rollup-linux-riscv64-gnu": "4.62.2", + "@rollup/rollup-linux-riscv64-musl": "4.62.2", + "@rollup/rollup-linux-s390x-gnu": "4.62.2", + "@rollup/rollup-linux-x64-gnu": "4.62.2", + "@rollup/rollup-linux-x64-musl": "4.62.2", + "@rollup/rollup-openbsd-x64": "4.62.2", + "@rollup/rollup-openharmony-arm64": "4.62.2", + "@rollup/rollup-win32-arm64-msvc": "4.62.2", + "@rollup/rollup-win32-ia32-msvc": "4.62.2", + "@rollup/rollup-win32-x64-gnu": "4.62.2", + "@rollup/rollup-win32-x64-msvc": "4.62.2", + "fsevents": "~2.3.2" } }, "node_modules/router": { @@ -3597,26 +5039,6 @@ "node": ">= 18" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -3627,6 +5049,7 @@ "version": "7.8.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -3690,7 +5113,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -3703,7 +5125,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -3788,49 +5209,24 @@ "dev": true, "license": "ISC" }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], + "node_modules/simple-code-frame": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/simple-code-frame/-/simple-code-frame-1.3.0.tgz", + "integrity": "sha512-MB4pQmETUBlNs62BBeRjIFGeuy/x6gGKh7+eRUemn1rCFhqo7K+4slPqsyizCbcbYLnaYqaoZ2FWsZ/jN06D8w==", + "dev": true, "license": "MIT", "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" + "kolorist": "^1.6.0" + } + }, + "node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" } }, "node_modules/source-map-js": { @@ -3843,6 +5239,16 @@ "node": ">=0.10.0" } }, + "node_modules/stack-trace": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-1.0.0.tgz", + "integrity": "sha512-H6D7134xi6qONvh7ZHKgviXf+rd3vhGBSvebPZCaUkd8zvQ+7PtDw6CljPTe4cXWNf2IKZGNqw6VJXSb9IgBpA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -3860,58 +5266,32 @@ } }, "node_modules/std-env": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", - "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", "dev": true, "license": "MIT" }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/tar-fs": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.5.tgz", - "integrity": "sha512-OboTd8mmMhZDNPV+UjQcK9yKAatXu2aJ+r1w4im1Otd4M4fl2hwvdoXUxIYHFTHWK/3y3FarBP70v3vwmGlOxw==", - "license": "MIT", - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "node_modules/strip-literal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", + "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", + "dev": true, "license": "MIT", "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" + "js-tokens": "^9.0.1" }, - "engines": { - "node": ">=6" + "funding": { + "url": "https://github.com/sponsors/antfu" } }, + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -3920,14 +5300,11 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", - "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } + "license": "MIT" }, "node_modules/tinyglobby": { "version": "0.2.17", @@ -3946,10 +5323,30 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, "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==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", + "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", "dev": true, "license": "MIT", "engines": { @@ -3978,18 +5375,10 @@ "typescript": ">=4.8.4" } }, - "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/tsx": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.0.tgz", - "integrity": "sha512-eUdUIaCr963q2h5u3+QwvYp0+eqPvn+egeqZUm0hwERCqqx1E3kK5ehbGCvqSE5MQAULr67ww0cA3jKc3YkM1w==", + "version": "4.23.1", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.1.tgz", + "integrity": "sha512-GQHnkIfxyx1wYCOS/wonik5MVRZU9hi1TEZmzGZSCJB1y9YgoZ8H6itNE/u4suE+yLmOzuE4E5S4TZ/ZX2wcWQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4005,18 +5394,6 @@ "fsevents": "~2.3.3" } }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -4062,9 +5439,9 @@ } }, "node_modules/typescript": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", - "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "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": { @@ -4076,16 +5453,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.62.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.62.0.tgz", - "integrity": "sha512-8QxXi+ZACKX0kaqO4gY8kn0RSD9gFfaHDWwjqtEN48aWCBkX4MJaufWN+c3BzlrXLOxfywDL8CaoqUwcRq4j4Q==", + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.65.0.tgz", + "integrity": "sha512-/ggrHAwyjENDusvyxbuqxAC2dTnZg/Z8F+fgQtYIz+L6n/9HfSlEZcFGV/NsMNa6CkGk0xUjUAFwC0vHOflvIA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.62.0", - "@typescript-eslint/parser": "8.62.0", - "@typescript-eslint/typescript-estree": "8.62.0", - "@typescript-eslint/utils": "8.62.0" + "@typescript-eslint/eslint-plugin": "8.65.0", + "@typescript-eslint/parser": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/utils": "8.65.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4100,10 +5477,9 @@ } }, "node_modules/undici-types": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", - "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", - "dev": true, + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "license": "MIT" }, "node_modules/unpipe": { @@ -4115,6 +5491,37 @@ "node": ">= 0.8" } }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -4125,12 +5532,6 @@ "punycode": "^2.1.0" } }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -4141,17 +5542,18 @@ } }, "node_modules/vite": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.0.tgz", - "integrity": "sha512-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q==", + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", + "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", "dev": true, "license": "MIT", "dependencies": { - "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", - "postcss": "^8.5.15", - "rolldown": "~1.1.2", - "tinyglobby": "^0.2.17" + "esbuild": "^0.27.0 || ^0.28.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" }, "bin": { "vite": "bin/vite.js" @@ -4167,10 +5569,9 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.3.0", - "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", + "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", @@ -4183,18 +5584,15 @@ "@types/node": { "optional": true }, - "@vitejs/devtools": { - "optional": true - }, - "esbuild": { - "optional": true - }, "jiti": { "optional": true }, "less": { "optional": true }, + "lightningcss": { + "optional": true + }, "sass": { "optional": true }, @@ -4218,80 +5616,107 @@ } } }, + "node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite-prerender-plugin": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/vite-prerender-plugin/-/vite-prerender-plugin-0.5.13.tgz", + "integrity": "sha512-IKSpYkzDBsKAxa05naRbj7GvNVMSdww/Z/E89oO3xndz+gWnOBOKOAbEXv7qDhktY/j3vHgJmoV1pPzqU2tx9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "kolorist": "^1.8.0", + "magic-string": "0.x >= 0.26.0", + "node-html-parser": "^6.1.12", + "simple-code-frame": "^1.3.0", + "source-map": "^0.7.4", + "stack-trace": "^1.0.0-pre2" + }, + "peerDependencies": { + "vite": "5.x || 6.x || 7.x || 8.x" + } + }, "node_modules/vitest": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz", - "integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.7.tgz", + "integrity": "sha512-KrxIJ62Fd89gfysR4WotlgZABiz2dqFPgqGzX7s+CwsqLFomRH7777ZcrOD6+WVAh7khPQP41A+BKbpcJFrdEg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.9", - "@vitest/mocker": "4.1.9", - "@vitest/pretty-format": "4.1.9", - "@vitest/runner": "4.1.9", - "@vitest/snapshot": "4.1.9", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", - "es-module-lexer": "^2.0.0", - "expect-type": "^1.3.0", - "magic-string": "^0.30.21", - "obug": "^2.1.1", + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.7", + "@vitest/mocker": "3.2.7", + "@vitest/pretty-format": "^3.2.7", + "@vitest/runner": "3.2.7", + "@vitest/snapshot": "3.2.7", + "@vitest/spy": "3.2.7", + "@vitest/utils": "3.2.7", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", "pathe": "^2.0.3", - "picomatch": "^4.0.3", - "std-env": "^4.0.0-rc.1", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", "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", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "bin": { "vitest": "vitest.mjs" }, "engines": { - "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^18.0.0 || ^20.0.0 || >=22.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.9", - "@vitest/browser-preview": "4.1.9", - "@vitest/browser-webdriverio": "4.1.9", - "@vitest/coverage-istanbul": "4.1.9", - "@vitest/coverage-v8": "4.1.9", - "@vitest/ui": "4.1.9", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.7", + "@vitest/ui": "3.2.7", "happy-dom": "*", - "jsdom": "*", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + "jsdom": "*" }, "peerDependenciesMeta": { "@edge-runtime/vm": { "optional": true }, - "@opentelemetry/api": { + "@types/debug": { "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": { + "@vitest/browser": { "optional": true }, "@vitest/ui": { @@ -4302,9 +5727,6 @@ }, "jsdom": { "optional": true - }, - "vite": { - "optional": false } } }, @@ -4312,7 +5734,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -4358,9 +5779,9 @@ "license": "ISC" }, "node_modules/ws": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", - "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "version": "8.21.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz", + "integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -4378,6 +5799,13 @@ } } }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -4391,14 +5819,76 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zimmerframe": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz", + "integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==", + "dev": true, + "license": "MIT" + }, "node_modules/zod": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", - "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "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" } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25.28 || ^4" + } + }, + "packages/core": { + "name": "@corpos/core", + "version": "0.2.0", + "dependencies": { + "@libsql/client": "^0.17.4", + "@modelcontextprotocol/sdk": "^1.29.0", + "drizzle-orm": "^0.45.2", + "nanoid": "^5.0.7", + "zod": "^3.25.76" + }, + "devDependencies": { + "typescript": "^5.8.3" + } + }, + "packages/core/node_modules/nanoid": { + "version": "5.1.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.16.tgz", + "integrity": "sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, + "packages/mcp-knowledge": { + "name": "@corpos/mcp-knowledge", + "version": "0.2.0", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.29.0", + "zod": "^3.25.76" + }, + "bin": { + "corpos-mcp-knowledge": "dist/server.js" + }, + "devDependencies": { + "typescript": "^5.8.3" + } } } } diff --git a/package.json b/package.json index 6492edc..1ae2675 100644 --- a/package.json +++ b/package.json @@ -1,68 +1,46 @@ { "name": "corpos", - "version": "0.1.0", + "version": "0.2.0", "private": true, - "description": "CorpOS — an autonomous company operating system: a TypeScript multi-agent runtime with a policy, approval, and audit control plane.", + "description": "CorpOS — reference implementation of an autonomous company", "author": "SafetyMP", "license": "Apache-2.0", "type": "module", - "homepage": "https://github.com/SafetyMP/CorpOS", - "repository": { - "type": "git", - "url": "https://github.com/SafetyMP/CorpOS.git" - }, - "bugs": { - "url": "https://github.com/SafetyMP/CorpOS/issues" - }, - "keywords": [ - "multi-agent", - "llm", - "autonomous-agents", - "agent-orchestration", - "policy-engine", - "approval-workflow", - "openrouter", - "ai", - "control-plane", - "typescript" + "workspaces": [ + "packages/*", + "apps/*" ], "engines": { - "node": ">=20", - "npm": ">=10.9.2 <11" + "node": ">=22" }, "packageManager": "npm@10.9.2", "scripts": { - "build": "tsc -p tsconfig.json", - "typecheck": "tsc -p tsconfig.json --noEmit", + "build": "npm run build -w @corpos/core && npm run build -w @corpos/mcp-knowledge && npm run build -w @corpos/api && npm run build -w @corpos/console", + "typecheck": "npm run typecheck -w @corpos/core && npm run typecheck -w @corpos/mcp-knowledge && npm run typecheck -w @corpos/api && npm run typecheck -w @corpos/console", + "dev": "npm run dev -w @corpos/api", + "start": "npm run start -w @corpos/api", + "scenario": "npm run scenario -w @corpos/api", + "test": "vitest run", + "test:watch": "vitest", "lint": "eslint .", "format": "prettier --write .", "format:check": "prettier --check .", - "dev": "tsx watch src/index.ts", - "start": "tsx src/index.ts", - "scenario": "tsx src/index.ts --scenario", - "test": "vitest run", - "test:watch": "vitest" + "audit:verify": "tsx scripts/audit-verify.ts" }, "dependencies": { - "better-sqlite3": "^12.11.1", - "express": "^5.2.1", - "nanoid": "^5.0.7", - "ws": "^8.18.0", - "zod": "^4.4.3" + "@corpos/core": "0.2.0", + "drizzle-orm": "^0.45.2" }, "devDependencies": { "@eslint/js": "^9.39.4", - "@types/better-sqlite3": "^7.6.11", - "@types/express": "^5.0.6", - "@types/node": "^26.0.1", - "@types/ws": "^8.5.12", + "@types/node": "^22.15.30", "eslint": "^10.6.0", "eslint-config-prettier": "^10.1.8", "globals": "^17.7.0", "prettier": "^3.9.3", "tsx": "^4.23.0", - "typescript": "^6.0.3", + "typescript": "^5.8.3", "typescript-eslint": "^8.62.0", - "vitest": "^4.1.9" + "vitest": "^3.2.4" } } diff --git a/packages/core/package.json b/packages/core/package.json new file mode 100644 index 0000000..28f14f9 --- /dev/null +++ b/packages/core/package.json @@ -0,0 +1,28 @@ +{ + "name": "@corpos/core", + "version": "0.2.0", + "private": true, + "type": "module", + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "scripts": { + "build": "tsc -p tsconfig.json", + "typecheck": "tsc -p tsconfig.json --noEmit" + }, + "dependencies": { + "@libsql/client": "^0.17.4", + "@modelcontextprotocol/sdk": "^1.29.0", + "drizzle-orm": "^0.45.2", + "nanoid": "^5.0.7", + "zod": "^3.25.76" + }, + "devDependencies": { + "typescript": "^5.8.3" + } +} diff --git a/packages/core/src/audit.ts b/packages/core/src/audit.ts new file mode 100644 index 0000000..a0a514f --- /dev/null +++ b/packages/core/src/audit.ts @@ -0,0 +1,79 @@ +import { createHash } from "node:crypto"; +import { asc, desc, eq } from "drizzle-orm"; +import type { Db } from "./db.js"; +import { auditChain } from "./schema.js"; +import { newId } from "./id.js"; +import { now } from "./types.js"; + +function canonical(value: unknown): string { + return JSON.stringify(value); +} + +export function hashEntry(prevHash: string, payload: unknown): string { + return createHash("sha256") + .update(prevHash) + .update("\0") + .update(canonical(payload)) + .digest("hex"); +} + +export class AuditLog { + constructor(private db: Db) {} + + async append(eventType: string, payload: unknown): Promise<{ seq: number; entryHash: string }> { + const last = ( + await this.db.select().from(auditChain).orderBy(desc(auditChain.seq)).limit(1) + )[0]; + const prevHash = last?.entryHash ?? "genesis"; + const seq = (last?.seq ?? 0) + 1; + const ts = now(); + const body = { eventType, payload, seq, ts }; + const entryHash = hashEntry(prevHash, body); + await this.db.insert(auditChain).values({ + id: newId("aud"), + seq, + prevHash, + entryHash, + eventType, + payloadJson: JSON.stringify(payload), + createdAt: ts, + }); + return { seq, entryHash }; + } + + async head(): Promise { + const last = ( + await this.db.select().from(auditChain).orderBy(desc(auditChain.seq)).limit(1) + )[0]; + return last?.entryHash ?? "genesis"; + } + + async verify(): Promise<{ ok: boolean; error?: string }> { + const rows = await this.db.select().from(auditChain).orderBy(asc(auditChain.seq)); + let prev = "genesis"; + for (const row of rows) { + if (row.prevHash !== prev) return { ok: false, error: `prevHash break at seq ${row.seq}` }; + const recomputed = hashEntry(prev, { + eventType: row.eventType, + payload: JSON.parse(row.payloadJson), + seq: row.seq, + ts: row.createdAt, + }); + if (recomputed !== row.entryHash) { + return { ok: false, error: `entryHash mismatch at seq ${row.seq}` }; + } + prev = row.entryHash; + } + return { ok: true }; + } + + async forgeMiddle(): Promise { + const rows = await this.db.select().from(auditChain).orderBy(asc(auditChain.seq)); + if (rows.length < 2) return; + const mid = rows[Math.floor(rows.length / 2)]!; + await this.db + .update(auditChain) + .set({ payloadJson: JSON.stringify({ forged: true }) }) + .where(eq(auditChain.id, mid.id)); + } +} diff --git a/packages/core/src/company-day.ts b/packages/core/src/company-day.ts new file mode 100644 index 0000000..fe8a995 --- /dev/null +++ b/packages/core/src/company-day.ts @@ -0,0 +1,171 @@ +import { eq } from "drizzle-orm"; +import { + checkSlaBreaches, + createCompany, + decideException, + openContract, + runAgentTurn, + type Company, +} from "./company.js"; +import { SimulationProvider, tc } from "./llm.js"; +import { agents, contracts, exceptions } from "./schema.js"; +import { mcpKnowledgeSearch } from "./mcp-client.js"; + +export interface CompanyDayResult { + contractId: string; + handoffs: number; + autonomousSettles: number; + exceptionSettles: number; + compensated: number; + trustAfter: number; + slaExceptions: number; + auditHead: string; + ok: boolean; +} + +function countHandoffs(steps: unknown[]): number { + return steps.filter( + (s) => + typeof s === "object" && s && "tool" in s && (s as { tool: string }).tool === "agent.handoff", + ).length; +} + +export async function runCompanyDay(opts?: { + dbPath?: string; + withMcp?: boolean; + serverCommand?: { command: string; args: string[] }; +}): Promise<{ company: Company; result: CompanyDayResult }> { + const mcpInvoke = opts?.withMcp + ? async (name: string, args: Record) => { + if (name === "knowledge.search") { + return mcpKnowledgeSearch(String(args.query ?? ""), opts.serverCommand); + } + return { ok: false, error: "not mcp" }; + } + : undefined; + + const company = await createCompany({ dbPath: opts?.dbPath ?? ":memory:", mcpInvoke }); + + const provider = new SimulationProvider({ + "Support Agent": [ + [tc("crm.lookup", { email: "ada@example.com" })], + [tc("knowledge.search", { query: "refund" })], + [ + tc("agent.handoff", { + toAgent: "agent_finance", + obligation: "Settle $49 refund for ada@example.com", + }), + ], + ], + "Finance Agent": [ + [ + tc("billing.issue_refund", { + subscription: "sub_ada_pro", + amount: 49, + customer: "ada@example.com", + }), + ], + [ + tc("agent.handoff", { + toAgent: "agent_ops", + obligation: "Confirm service health after refund", + }), + ], + ], + "Ops Agent": [[tc("ops.restart_service", { service: "billing-api" })]], + }); + + const { contractId, taskId } = await openContract(company, { + title: "Customer refund day", + intake: "ada@example.com wants a $49 refund on sub_ada_pro", + assignees: ["agent_support", "agent_finance", "agent_ops"], + slaMinutes: 240, + }); + + let handoffs = 0; + let autonomousSettles = 0; + let exceptionSettles = 0; + + const support = await runAgentTurn(company, provider, { + agentId: "agent_support", + taskId, + contractId, + tenantId: "default", + systemPrompt: "You are Support Agent for CorpOS.", + userPrompt: "Handle the refund intake and hand off to finance.", + }); + handoffs += countHandoffs(support.steps); + + await company.db + .update(agents) + .set({ maxAutonomousRisk: 3 }) + .where(eq(agents.id, "agent_finance")); + + const finance = await runAgentTurn(company, provider, { + agentId: "agent_finance", + taskId, + contractId, + tenantId: "default", + systemPrompt: "You are Finance Agent for CorpOS.", + userPrompt: "Settle the refund obligation and hand off to ops.", + }); + if (finance.steps.some((s) => typeof s === "object" && s && "draftSettled" in s)) { + autonomousSettles++; + } + handoffs += countHandoffs(finance.steps); + + const ops = await runAgentTurn(company, provider, { + agentId: "agent_ops", + taskId, + contractId, + tenantId: "default", + systemPrompt: "You are Ops Agent for CorpOS.", + userPrompt: "Restart billing-api if needed.", + }); + + if (ops.awaitingExceptionId) { + await decideException(company, ops.awaitingExceptionId, "approved", "carol@corpos.local"); + const ex = ( + await company.db.select().from(exceptions).where(eq(exceptions.id, ops.awaitingExceptionId)) + )[0]!; + const tool = company.gateway.get(ex.tool)!; + await tool.execute(JSON.parse(ex.argsJson), { + agentId: "agent_ops", + taskId, + contractId, + tenantId: "default", + }); + exceptionSettles++; + await company.trust.recordAccept("agent_support"); + await company.trust.recordAccept("agent_support"); + } + + const compensated = await company.gateway.compensate(contractId); + + const short = await openContract(company, { + title: "Expiring SLA", + intake: "tiny", + assignees: ["agent_support"], + slaMinutes: 0, + }); + await company.db + .update(contracts) + .set({ slaDueAt: new Date(Date.now() - 1000).toISOString() }) + .where(eq(contracts.id, short.contractId)); + const slaExceptions = await checkSlaBreaches(company); + + const trustAfter = (await company.trust.get("agent_support"))?.maxAutonomousRisk ?? 0; + + const result: CompanyDayResult = { + contractId, + handoffs, + autonomousSettles, + exceptionSettles, + compensated, + trustAfter, + slaExceptions, + auditHead: await company.audit.head(), + ok: handoffs >= 2 && autonomousSettles >= 1 && exceptionSettles >= 1 && trustAfter >= 2, + }; + return { company, result }; +} diff --git a/packages/core/src/company.ts b/packages/core/src/company.ts new file mode 100644 index 0000000..d3c0bfc --- /dev/null +++ b/packages/core/src/company.ts @@ -0,0 +1,305 @@ +import { eq } from "drizzle-orm"; +import { openDb, type Db } from "./db.js"; +import { AuditLog } from "./audit.js"; +import { PolicyEngine } from "./policy.js"; +import { ToolGateway } from "./gateway.js"; +import { TrustLedger } from "./trust.js"; +import { createSeedTools, registryOf } from "./tools.js"; +import { + agents, + controlState, + departments, + contracts, + tasks, + exceptions, + traces, +} from "./schema.js"; +import { newId } from "./id.js"; +import { now, type ChatMessage, type LLMProvider, type ToolResult } from "./types.js"; +import type { Client } from "@libsql/client"; + +export interface Company { + client: Client; + db: Db; + audit: AuditLog; + policy: PolicyEngine; + gateway: ToolGateway; + trust: TrustLedger; + seed: { + refunds: Array<{ id: string; amount: number; customer: string }>; + messages: Array<{ to: string; body: string }>; + knowledge: Record; + }; + close: () => void; +} + +export async function createCompany(opts?: { + dbPath?: string; + mcpInvoke?: (name: string, args: Record) => Promise; +}): Promise { + const { client, db } = await openDb(opts?.dbPath); + const audit = new AuditLog(db); + const policy = new PolicyEngine(db, audit, "deny"); + policy.setRules([]); + const seed = { + refunds: [] as Array<{ id: string; amount: number; customer: string }>, + messages: [] as Array<{ to: string; body: string }>, + knowledge: { + refund: "Refunds under $100 may be issued after CRM lookup.", + sla: "Support SLA is 4 hours for refund requests.", + }, + }; + const tools = registryOf(createSeedTools(seed)); + const gateway = new ToolGateway(db, policy, audit, tools, opts?.mcpInvoke); + const trust = new TrustLedger(db, audit); + await seedFirm(db); + return { + client, + db, + audit, + policy, + gateway, + trust, + seed, + close: () => client.close(), + }; +} + +async function seedFirm(db: Db): Promise { + const depts = [ + { id: "support", name: "Support", capitalBudget: 500, capitalSpent: 0 }, + { id: "finance", name: "Finance", capitalBudget: 2000, capitalSpent: 0 }, + { id: "ops", name: "Operations", capitalBudget: 300, capitalSpent: 0 }, + ]; + for (const d of depts) { + if (!(await db.select().from(departments).where(eq(departments.id, d.id)))[0]) { + await db.insert(departments).values(d); + } + } + const agentRows = [ + { + id: "agent_support", + role: "Support Agent", + department: "support", + owner: "alice@corpos.local", + principal: "support-bot", + maxAutonomousRisk: 1, + trustScore: 0, + accepts: 0, + rejects: 0, + violations: 0, + }, + { + id: "agent_finance", + role: "Finance Agent", + department: "finance", + owner: "bob@corpos.local", + principal: "finance-bot", + maxAutonomousRisk: 1, + trustScore: 0, + accepts: 0, + rejects: 0, + violations: 0, + }, + { + id: "agent_ops", + role: "Ops Agent", + department: "ops", + owner: "carol@corpos.local", + principal: "ops-bot", + maxAutonomousRisk: 1, + trustScore: 0, + accepts: 0, + rejects: 0, + violations: 0, + }, + ]; + for (const a of agentRows) { + if (!(await db.select().from(agents).where(eq(agents.id, a.id)))[0]) { + await db.insert(agents).values(a); + } + } + if (!(await db.select().from(controlState).where(eq(controlState.id, "global")))[0]) { + await db + .insert(controlState) + .values({ id: "global", killed: 0, tokenBudget: 100000, tokensUsed: 0 }); + } +} + +export async function runAgentTurn( + company: Company, + provider: LLMProvider, + opts: { + agentId: string; + taskId: string; + contractId: string; + tenantId: string; + systemPrompt: string; + userPrompt: string; + }, +): Promise<{ + ok: boolean; + awaitingExceptionId?: string; + summary?: string; + steps: unknown[]; +}> { + const steps: unknown[] = []; + const messages: ChatMessage[] = [ + { role: "system", content: opts.systemPrompt }, + { role: "user", content: opts.userPrompt }, + ]; + + for (let i = 0; i < 8; i++) { + const response = await provider.complete({ + messages, + tools: company.gateway.list().map((t) => ({ + name: t.name, + description: t.description, + parameters: t.parameters, + })), + }); + messages.push(response.message); + if (!response.toolCalls.length) { + const summary = response.message.content || "Done"; + await company.db.insert(traces).values({ + id: newId("tr"), + contractId: opts.contractId, + taskId: opts.taskId, + stepsJson: JSON.stringify(steps), + createdAt: now(), + }); + return { ok: true, summary, steps }; + } + for (const call of response.toolCalls) { + const gw = await company.gateway.invoke(call.name, call.arguments, { + agentId: opts.agentId, + taskId: opts.taskId, + contractId: opts.contractId, + tenantId: opts.tenantId, + }); + steps.push({ tool: call.name, decision: gw.decision, result: gw.result }); + if (gw.decision.effect === "approve" && gw.decision.approvalId) { + await company.db + .update(exceptions) + .set({ + pauseJson: JSON.stringify({ + messages, + toolCallId: call.id, + tool: call.name, + args: call.arguments, + }), + }) + .where(eq(exceptions.id, gw.decision.approvalId)); + await company.db + .update(tasks) + .set({ state: "awaiting_exception" }) + .where(eq(tasks.id, opts.taskId)); + return { ok: false, awaitingExceptionId: gw.decision.approvalId, steps }; + } + if (gw.decision.effect === "draft" && gw.draftId) { + const settled = await company.gateway.settleDraft(gw.draftId, { + agentId: opts.agentId, + taskId: opts.taskId, + contractId: opts.contractId, + tenantId: opts.tenantId, + }); + messages.push({ + role: "tool", + toolCallId: call.id, + content: settled.note ?? "draft settled", + }); + steps.push({ draftSettled: gw.draftId, result: settled }); + continue; + } + messages.push({ + role: "tool", + toolCallId: call.id, + content: gw.result?.note ?? gw.decision.reason, + }); + } + } + return { ok: false, summary: "max steps", steps }; +} + +export async function openContract( + company: Company, + input: { title: string; intake: string; assignees: string[]; slaMinutes?: number }, +): Promise<{ contractId: string; taskId: string }> { + const contractId = newId("ctr"); + const taskId = newId("task"); + const t = now(); + const slaDueAt = input.slaMinutes + ? new Date(Date.now() + input.slaMinutes * 60_000).toISOString() + : null; + await company.db.insert(contracts).values({ + id: contractId, + tenantId: "default", + title: input.title, + intake: input.intake, + state: "open", + assignees: JSON.stringify(input.assignees), + obligations: JSON.stringify([]), + slaDueAt, + createdAt: t, + updatedAt: t, + }); + await company.db.insert(tasks).values({ + id: taskId, + contractId, + tenantId: "default", + title: input.title, + description: input.intake, + state: "queued", + assignedTo: input.assignees[0], + attempts: 0, + createdAt: t, + }); + await company.audit.append("contract.opened", { contractId, title: input.title }); + return { contractId, taskId }; +} + +export async function decideException( + company: Company, + exceptionId: string, + decision: "approved" | "rejected", + by: string, +): Promise { + const ex = (await company.db.select().from(exceptions).where(eq(exceptions.id, exceptionId)))[0]; + if (!ex || ex.state !== "pending") return; + await company.db + .update(exceptions) + .set({ state: decision, decidedAt: now(), decidedBy: by }) + .where(eq(exceptions.id, exceptionId)); + if (decision === "approved") await company.trust.recordAccept(ex.agentId); + else await company.trust.recordReject(ex.agentId); + await company.audit.append(`exception.${decision}`, { exceptionId, by }); +} + +export async function checkSlaBreaches(company: Company): Promise { + const t = now(); + let n = 0; + for (const c of await company.db.select().from(contracts)) { + if (c.slaDueAt && c.slaDueAt < t && c.state === "open") { + await company.db.insert(exceptions).values({ + id: newId("ex"), + tenantId: c.tenantId, + contractId: c.id, + taskId: "sla", + agentId: "system", + tool: "sla.breach", + argsJson: "{}", + reason: "SLA breached", + riskLevel: 2, + state: "pending", + ttlAt: new Date(Date.now() + 3600_000).toISOString(), + createdAt: t, + }); + await company.db + .update(contracts) + .set({ state: "sla_breach", updatedAt: t }) + .where(eq(contracts.id, c.id)); + n++; + } + } + return n; +} diff --git a/packages/core/src/counterfactual.ts b/packages/core/src/counterfactual.ts new file mode 100644 index 0000000..dba1901 --- /dev/null +++ b/packages/core/src/counterfactual.ts @@ -0,0 +1,41 @@ +import type { Effect, PolicyRule, RiskLevel } from "./types.js"; +import { globMatch } from "./policy.js"; + +export interface TraceStep { + tool?: string; + decision?: { effect: Effect; reason: string }; +} + +export interface CounterfactualDiff { + tool: string; + did: Effect; + would: Effect; + reason: string; +} + +export function counterfactualReplay( + steps: TraceStep[], + candidate: { rules: PolicyRule[]; maxAutonomousRisk: number; defaultDeny?: boolean }, +): CounterfactualDiff[] { + const diffs: CounterfactualDiff[] = []; + for (const step of steps) { + if (!step.tool || !step.decision) continue; + const rule = candidate.rules.filter((r) => globMatch(r.tool, step.tool!))[0]; + let would: Effect = rule?.effect ?? "deny"; + if (!rule) { + // Approximate ladder with risk unknown → treat as 3 + const risk = 3 as RiskLevel; + if (risk <= candidate.maxAutonomousRisk) would = "draft"; + else would = "approve"; + } + if (would !== step.decision.effect) { + diffs.push({ + tool: step.tool, + did: step.decision.effect, + would, + reason: rule?.reason ?? "candidate policy", + }); + } + } + return diffs; +} diff --git a/packages/core/src/db.ts b/packages/core/src/db.ts new file mode 100644 index 0000000..46fac0d --- /dev/null +++ b/packages/core/src/db.ts @@ -0,0 +1,140 @@ +import { createClient, type Client } from "@libsql/client"; +import { drizzle, type LibSQLDatabase } from "drizzle-orm/libsql"; +import * as schema from "./schema.js"; +import path from "node:path"; +import fs from "node:fs"; + +export type Db = LibSQLDatabase; + +export async function openDb(dbPath?: string): Promise<{ client: Client; db: Db }> { + const file = dbPath ?? process.env.CORPOS_DB ?? path.join(process.cwd(), "data", "company.db"); + const isMemory = file === ":memory:"; + if (!isMemory) fs.mkdirSync(path.dirname(file), { recursive: true }); + const url = isMemory ? ":memory:" : `file:${file}`; + const client = createClient({ url }); + const db = drizzle(client, { schema }); + await migrate(client); + return { client, db }; +} + +async function migrate(client: Client): Promise { + await client.executeMultiple(` + CREATE TABLE IF NOT EXISTS departments ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + capital_budget REAL NOT NULL DEFAULT 1000, + capital_spent REAL NOT NULL DEFAULT 0 + ); + CREATE TABLE IF NOT EXISTS agents ( + id TEXT PRIMARY KEY, + role TEXT NOT NULL, + department TEXT NOT NULL, + owner TEXT NOT NULL, + principal TEXT NOT NULL, + max_autonomous_risk INTEGER NOT NULL DEFAULT 1, + trust_score REAL NOT NULL DEFAULT 0, + accepts INTEGER NOT NULL DEFAULT 0, + rejects INTEGER NOT NULL DEFAULT 0, + violations INTEGER NOT NULL DEFAULT 0 + ); + CREATE TABLE IF NOT EXISTS contracts ( + id TEXT PRIMARY KEY, + tenant_id TEXT NOT NULL, + title TEXT NOT NULL, + intake TEXT NOT NULL, + state TEXT NOT NULL, + assignees TEXT NOT NULL, + obligations TEXT NOT NULL, + sla_due_at TEXT, + created_at TEXT NOT NULL, + updated_at TEXT NOT NULL + ); + CREATE TABLE IF NOT EXISTS tasks ( + id TEXT PRIMARY KEY, + contract_id TEXT NOT NULL, + tenant_id TEXT NOT NULL, + title TEXT NOT NULL, + description TEXT NOT NULL, + state TEXT NOT NULL, + assigned_to TEXT, + attempts INTEGER NOT NULL DEFAULT 0, + created_at TEXT NOT NULL, + started_at TEXT, + finished_at TEXT, + error TEXT, + result_summary TEXT + ); + CREATE TABLE IF NOT EXISTS exceptions ( + id TEXT PRIMARY KEY, + tenant_id TEXT NOT NULL, + contract_id TEXT NOT NULL, + task_id TEXT NOT NULL, + agent_id TEXT NOT NULL, + tool TEXT NOT NULL, + args_json TEXT NOT NULL, + reason TEXT NOT NULL, + risk_level INTEGER NOT NULL, + state TEXT NOT NULL, + ttl_at TEXT NOT NULL, + created_at TEXT NOT NULL, + decided_at TEXT, + decided_by TEXT, + pause_json TEXT + ); + CREATE TABLE IF NOT EXISTS drafts ( + id TEXT PRIMARY KEY, + tenant_id TEXT NOT NULL, + contract_id TEXT NOT NULL, + task_id TEXT NOT NULL, + agent_id TEXT NOT NULL, + tool TEXT NOT NULL, + args_json TEXT NOT NULL, + state TEXT NOT NULL, + created_at TEXT NOT NULL, + settled_at TEXT + ); + CREATE TABLE IF NOT EXISTS compensators ( + id TEXT PRIMARY KEY, + draft_id TEXT NOT NULL, + contract_id TEXT NOT NULL, + kind TEXT NOT NULL, + payload_json TEXT NOT NULL, + state TEXT NOT NULL, + created_at TEXT NOT NULL, + run_at TEXT + ); + CREATE TABLE IF NOT EXISTS spend_ledger ( + id TEXT PRIMARY KEY, + tenant_id TEXT NOT NULL, + department TEXT NOT NULL, + contract_id TEXT NOT NULL, + task_id TEXT NOT NULL, + tool TEXT NOT NULL, + amount REAL NOT NULL, + currency TEXT NOT NULL, + created_at TEXT NOT NULL + ); + CREATE TABLE IF NOT EXISTS audit_chain ( + id TEXT PRIMARY KEY, + seq INTEGER NOT NULL, + prev_hash TEXT NOT NULL, + entry_hash TEXT NOT NULL, + event_type TEXT NOT NULL, + payload_json TEXT NOT NULL, + created_at TEXT NOT NULL + ); + CREATE TABLE IF NOT EXISTS traces ( + id TEXT PRIMARY KEY, + contract_id TEXT NOT NULL, + task_id TEXT NOT NULL, + steps_json TEXT NOT NULL, + created_at TEXT NOT NULL + ); + CREATE TABLE IF NOT EXISTS control_state ( + id TEXT PRIMARY KEY, + killed INTEGER NOT NULL DEFAULT 0, + token_budget INTEGER NOT NULL DEFAULT 100000, + tokens_used INTEGER NOT NULL DEFAULT 0 + ); + `); +} diff --git a/packages/core/src/gateway.ts b/packages/core/src/gateway.ts new file mode 100644 index 0000000..1066ebf --- /dev/null +++ b/packages/core/src/gateway.ts @@ -0,0 +1,205 @@ +import { eq } from "drizzle-orm"; +import type { Db } from "./db.js"; +import type { AuditLog } from "./audit.js"; +import type { PolicyEngine } from "./policy.js"; +import type { Tool, ToolContext, ToolResult, PolicyDecision } from "./types.js"; +import type { ToolMap } from "./tools.js"; +import { compensators, controlState, departments, drafts, spendLedger, agents } from "./schema.js"; +import { newId } from "./id.js"; +import { now } from "./types.js"; + +export interface GatewayInvokeResult { + decision: PolicyDecision; + result?: ToolResult; + draftId?: string; +} + +export class ToolGateway { + constructor( + private db: Db, + private policy: PolicyEngine, + private audit: AuditLog, + private tools: ToolMap, + private mcpInvoke?: (name: string, args: Record) => Promise, + ) {} + + get(name: string): Tool | undefined { + return this.tools.get(name); + } + + list(): Tool[] { + return [...this.tools.values()]; + } + + async invoke( + name: string, + args: Record, + ctx: ToolContext, + ): Promise { + const tool = this.tools.get(name); + const decision = await this.policy.evaluate(tool, args, { + tenantId: ctx.tenantId, + agentId: ctx.agentId, + taskId: ctx.taskId, + contractId: ctx.contractId, + }); + await this.audit.append("gateway.decision", { + tool: name, + effect: decision.effect, + reason: decision.reason, + }); + + if (decision.effect === "deny") { + const agent = (await this.db.select().from(agents).where(eq(agents.id, ctx.agentId)))[0]; + if (agent) { + await this.db + .update(agents) + .set({ violations: agent.violations + 1 }) + .where(eq(agents.id, agent.id)); + } + return { decision }; + } + if (decision.effect === "approve") { + return { decision }; + } + + if (decision.effect === "draft") { + const draftId = decision.draftId ?? newId("draft"); + await this.db.insert(drafts).values({ + id: draftId, + tenantId: ctx.tenantId, + contractId: ctx.contractId, + taskId: ctx.taskId, + agentId: ctx.agentId, + tool: name, + argsJson: JSON.stringify(args), + state: "staged", + createdAt: now(), + }); + await this.audit.append("draft.staged", { draftId, tool: name }); + return { + decision, + draftId, + result: { ok: true, note: `Draft staged for ${name}`, draftId }, + }; + } + + return { decision, result: await this.execute(name, args, ctx) }; + } + + async settleDraft(draftId: string, ctx: ToolContext): Promise { + const draft = (await this.db.select().from(drafts).where(eq(drafts.id, draftId)))[0]; + if (!draft || draft.state !== "staged") return { ok: false, error: "draft not found" }; + const args = JSON.parse(draft.argsJson) as Record; + const result = await this.execute(draft.tool, args, ctx); + await this.db + .update(drafts) + .set({ state: "settled", settledAt: now() }) + .where(eq(drafts.id, draftId)); + if (result.compensator) { + await this.db.insert(compensators).values({ + id: newId("cmp"), + draftId, + contractId: draft.contractId, + kind: result.compensator, + payloadJson: JSON.stringify({ args, result }), + state: "ready", + createdAt: now(), + }); + } + await this.audit.append("draft.settled", { draftId, tool: draft.tool }); + return result; + } + + async compensate(contractId: string): Promise { + const rows = (await this.db.select().from(compensators)).filter( + (c) => c.contractId === contractId && c.state === "ready", + ); + let n = 0; + for (const c of rows) { + const payload = JSON.parse(c.payloadJson) as { + args: Record; + result: ToolResult; + }; + const tool = this.tools.get(c.kind); + if (!tool) continue; + await tool.execute( + { + ...payload.args, + refundId: (payload.result.data as { refundId?: string } | undefined)?.refundId, + amount: payload.args.amount, + }, + { + agentId: "system", + taskId: "compensate", + contractId, + tenantId: "default", + }, + ); + await this.db + .update(compensators) + .set({ state: "run", runAt: now() }) + .where(eq(compensators.id, c.id)); + n++; + await this.audit.append("compensate.run", { compensatorId: c.id, kind: c.kind }); + } + return n; + } + + async setKilled(killed: boolean): Promise { + const row = (await this.db.select().from(controlState).where(eq(controlState.id, "global")))[0]; + if (!row) { + await this.db.insert(controlState).values({ + id: "global", + killed: killed ? 1 : 0, + tokenBudget: 100000, + tokensUsed: 0, + }); + } else { + await this.db + .update(controlState) + .set({ killed: killed ? 1 : 0 }) + .where(eq(controlState.id, "global")); + } + await this.audit.append("kill.switch", { killed }); + } + + private async execute( + name: string, + args: Record, + ctx: ToolContext, + ): Promise { + if (name === "knowledge.search" && this.mcpInvoke) { + const result = await this.mcpInvoke(name, args); + await this.audit.append("tool.execute", { tool: name, via: "mcp", ok: result.ok }); + return result; + } + const tool = this.tools.get(name); + if (!tool) return { ok: false, error: `Unknown tool: ${name}` }; + const result = await tool.execute(args, ctx); + if (result.cost) { + const agent = (await this.db.select().from(agents).where(eq(agents.id, ctx.agentId)))[0]; + const deptId = agent?.department ?? "support"; + await this.db.insert(spendLedger).values({ + id: newId("spd"), + tenantId: ctx.tenantId, + department: deptId, + contractId: ctx.contractId, + taskId: ctx.taskId, + tool: name, + amount: result.cost.amount, + currency: result.cost.currency, + createdAt: now(), + }); + const dept = (await this.db.select().from(departments).where(eq(departments.id, deptId)))[0]; + if (dept) { + await this.db + .update(departments) + .set({ capitalSpent: dept.capitalSpent + result.cost.amount }) + .where(eq(departments.id, deptId)); + } + } + await this.audit.append("tool.execute", { tool: name, via: "local", ok: result.ok }); + return result; + } +} diff --git a/packages/core/src/id.ts b/packages/core/src/id.ts new file mode 100644 index 0000000..f68f00d --- /dev/null +++ b/packages/core/src/id.ts @@ -0,0 +1,5 @@ +import { nanoid } from "nanoid"; + +export function newId(prefix: string): string { + return `${prefix}_${nanoid(10)}`; +} diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts new file mode 100644 index 0000000..0026029 --- /dev/null +++ b/packages/core/src/index.ts @@ -0,0 +1,14 @@ +export * from "./types.js"; +export * from "./id.js"; +export * from "./db.js"; +export * from "./schema.js"; +export * from "./audit.js"; +export * from "./policy.js"; +export * from "./tools.js"; +export * from "./gateway.js"; +export * from "./trust.js"; +export * from "./llm.js"; +export * from "./company.js"; +export * from "./company-day.js"; +export * from "./counterfactual.js"; +export * from "./mcp-client.js"; diff --git a/packages/core/src/llm.ts b/packages/core/src/llm.ts new file mode 100644 index 0000000..c1ddd7d --- /dev/null +++ b/packages/core/src/llm.ts @@ -0,0 +1,43 @@ +import type { LLMProvider, LLMRequest, LLMResponse, ToolCall } from "./types.js"; +import { newId } from "./id.js"; + +/** Deterministic provider for CI / company-day. */ +export class SimulationProvider implements LLMProvider { + readonly id = "simulation"; + private scripts: Map; + + constructor(scripts: Record = {}) { + this.scripts = new Map(Object.entries(scripts)); + } + + async complete(request: LLMRequest): Promise { + const system = request.messages.find((m) => m.role === "system")?.content ?? ""; + const agentKey = + Object.keys(Object.fromEntries(this.scripts)).find((k) => system.includes(k)) ?? + [...this.scripts.keys()][0]; + const queue = agentKey ? this.scripts.get(agentKey) : undefined; + const step = queue?.shift() ?? []; + if (!step.length) { + return { + message: { role: "assistant", content: "Done." }, + toolCalls: [], + finishReason: "stop", + usage: { promptTokens: 10, completionTokens: 5 }, + }; + } + return { + message: { + role: "assistant", + content: "", + toolCalls: step, + }, + toolCalls: step, + finishReason: "tool_calls", + usage: { promptTokens: 20, completionTokens: 15 }, + }; + } +} + +export function tc(name: string, args: Record): ToolCall { + return { id: newId("tc"), name, arguments: args }; +} diff --git a/packages/core/src/mcp-client.ts b/packages/core/src/mcp-client.ts new file mode 100644 index 0000000..868f94e --- /dev/null +++ b/packages/core/src/mcp-client.ts @@ -0,0 +1,45 @@ +import { Client } from "@modelcontextprotocol/sdk/client/index.js"; +import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"; +import type { ToolResult } from "./types.js"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +export async function withKnowledgeMcp( + fn: ( + invoke: (name: string, args: Record) => Promise, + ) => Promise, + serverCommand?: { command: string; args: string[] }, +): Promise { + const here = path.dirname(fileURLToPath(import.meta.url)); + const defaultServer = path.resolve(here, "../../../mcp-knowledge/dist/server.js"); + const command = serverCommand?.command ?? process.execPath; + const args = serverCommand?.args ?? [defaultServer]; + + const transport = new StdioClientTransport({ command, args }); + const client = new Client({ name: "corpos-gateway", version: "0.2.0" }); + await client.connect(transport); + try { + const invoke = async (name: string, toolArgs: Record): Promise => { + const mcpName = name === "knowledge.search" ? "search" : name; + const result = await client.callTool({ name: mcpName, arguments: toolArgs }); + const text = Array.isArray(result.content) + ? result.content.map((c) => ("text" in c ? String(c.text) : JSON.stringify(c))).join("\n") + : JSON.stringify(result.content); + return { ok: !result.isError, note: text, data: result.content }; + }; + await fn(invoke); + } finally { + await client.close(); + } +} + +export async function mcpKnowledgeSearch( + query: string, + serverCommand?: { command: string; args: string[] }, +): Promise { + let out: ToolResult = { ok: false, error: "not run" }; + await withKnowledgeMcp(async (invoke) => { + out = await invoke("knowledge.search", { query }); + }, serverCommand); + return out; +} diff --git a/packages/core/src/policy.ts b/packages/core/src/policy.ts new file mode 100644 index 0000000..ff28629 --- /dev/null +++ b/packages/core/src/policy.ts @@ -0,0 +1,131 @@ +import type { Db } from "./db.js"; +import type { Effect, PolicyDecision, PolicyRule, RiskLevel, Tool } from "./types.js"; +import { agents, controlState, departments, exceptions } from "./schema.js"; +import { eq } from "drizzle-orm"; +import { newId } from "./id.js"; +import { now } from "./types.js"; +import type { AuditLog } from "./audit.js"; + +export function globMatch(pattern: string, name: string): boolean { + if (pattern === name || pattern === "*") return true; + if (pattern.endsWith(".*")) { + const prefix = pattern.slice(0, -2); + return name === prefix || name.startsWith(`${prefix}.`); + } + return false; +} + +function ladderEffect(risk: RiskLevel, maxAuto: number, requiresApproval?: boolean): Effect { + if (requiresApproval || risk >= 4) return "approve"; + if (risk >= 3) return risk <= maxAuto ? "draft" : "approve"; + if (risk === 2) return risk <= maxAuto ? "draft" : "approve"; + if (risk <= maxAuto) return "allow"; + return "approve"; +} + +export class PolicyEngine { + private rules: PolicyRule[] = []; + + constructor( + private db: Db, + private audit: AuditLog, + private defaultEffect: Effect = "deny", + ) {} + + setRules(rules: PolicyRule[]): void { + this.rules = rules.map((r) => ({ priority: 0, ...r })); + this.rules.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0)); + } + + async evaluate( + tool: Tool | undefined, + args: Record, + ctx: { tenantId: string; agentId: string; taskId: string; contractId: string }, + ): Promise { + const ctrl = ( + await this.db.select().from(controlState).where(eq(controlState.id, "global")) + )[0]; + if (ctrl?.killed) { + return { effect: "deny", reason: "kill switch engaged" }; + } + if (!tool) { + await this.audit.append("policy.deny", { tool: "unknown", reason: "unknown tool" }); + return { effect: "deny", reason: "unknown tool (fail-closed)" }; + } + + const agent = (await this.db.select().from(agents).where(eq(agents.id, ctx.agentId)))[0]; + const maxAuto = agent?.maxAutonomousRisk ?? 0; + + const matched = this.rules.filter((r) => globMatch(r.tool, tool.name))[0]; + if (matched?.effect === "deny") { + return { effect: "deny", reason: matched.reason ?? `Rule denies ${tool.name}` }; + } + + if (tool.permission.category === "spend") { + const dept = ( + await this.db + .select() + .from(departments) + .where(eq(departments.id, agent?.department ?? "")) + )[0]; + const amount = typeof args.amount === "number" ? args.amount : 0; + if (dept && dept.capitalSpent + amount > dept.capitalBudget) { + return { + effect: "deny", + reason: `department capital exceeded (${dept.capitalSpent + amount} > ${dept.capitalBudget})`, + }; + } + } + + const effect = + matched?.effect ?? + ladderEffect(tool.permission.riskLevel, maxAuto, tool.permission.requiresApproval); + + if (effect === "approve") { + const approvalId = newId("ex"); + const ttl = new Date(Date.now() + 60 * 60 * 1000).toISOString(); + await this.db.insert(exceptions).values({ + id: approvalId, + tenantId: ctx.tenantId, + contractId: ctx.contractId, + taskId: ctx.taskId, + agentId: ctx.agentId, + tool: tool.name, + argsJson: JSON.stringify(args), + reason: `${tool.name} requires exception approval`, + riskLevel: tool.permission.riskLevel, + state: "pending", + ttlAt: ttl, + createdAt: now(), + }); + await this.audit.append("exception.requested", { approvalId, tool: tool.name }); + return { + effect: "approve", + reason: `${tool.name} requires exception approval`, + approvalId, + }; + } + + if (effect === "draft") { + return { effect: "draft", reason: `${tool.name} stages as draft`, draftId: newId("draft") }; + } + + return { effect: effect === "allow" ? "allow" : this.defaultEffect, reason: "policy allow" }; + } + + async expireTtl(): Promise { + const pending = (await this.db.select().from(exceptions)).filter((e) => e.state === "pending"); + let n = 0; + const t = now(); + for (const e of pending) { + if (e.ttlAt < t) { + await this.db + .update(exceptions) + .set({ state: "rejected", decidedAt: t, decidedBy: "ttl" }) + .where(eq(exceptions.id, e.id)); + n++; + } + } + return n; + } +} diff --git a/packages/core/src/schema.ts b/packages/core/src/schema.ts new file mode 100644 index 0000000..9225adf --- /dev/null +++ b/packages/core/src/schema.ts @@ -0,0 +1,129 @@ +import { sqliteTable, text, integer, real } from "drizzle-orm/sqlite-core"; + +export const departments = sqliteTable("departments", { + id: text("id").primaryKey(), + name: text("name").notNull(), + capitalBudget: real("capital_budget").notNull().default(1000), + capitalSpent: real("capital_spent").notNull().default(0), +}); + +export const agents = sqliteTable("agents", { + id: text("id").primaryKey(), + role: text("role").notNull(), + department: text("department").notNull(), + owner: text("owner").notNull(), + principal: text("principal").notNull(), + maxAutonomousRisk: integer("max_autonomous_risk").notNull().default(1), + trustScore: real("trust_score").notNull().default(0), + accepts: integer("accepts").notNull().default(0), + rejects: integer("rejects").notNull().default(0), + violations: integer("violations").notNull().default(0), +}); + +export const contracts = sqliteTable("contracts", { + id: text("id").primaryKey(), + tenantId: text("tenant_id").notNull(), + title: text("title").notNull(), + intake: text("intake").notNull(), + state: text("state").notNull(), + assignees: text("assignees").notNull(), + obligations: text("obligations").notNull(), + slaDueAt: text("sla_due_at"), + createdAt: text("created_at").notNull(), + updatedAt: text("updated_at").notNull(), +}); + +export const tasks = sqliteTable("tasks", { + id: text("id").primaryKey(), + contractId: text("contract_id").notNull(), + tenantId: text("tenant_id").notNull(), + title: text("title").notNull(), + description: text("description").notNull(), + state: text("state").notNull(), + assignedTo: text("assigned_to"), + attempts: integer("attempts").notNull().default(0), + createdAt: text("created_at").notNull(), + startedAt: text("started_at"), + finishedAt: text("finished_at"), + error: text("error"), + resultSummary: text("result_summary"), +}); + +export const exceptions = sqliteTable("exceptions", { + id: text("id").primaryKey(), + tenantId: text("tenant_id").notNull(), + contractId: text("contract_id").notNull(), + taskId: text("task_id").notNull(), + agentId: text("agent_id").notNull(), + tool: text("tool").notNull(), + argsJson: text("args_json").notNull(), + reason: text("reason").notNull(), + riskLevel: integer("risk_level").notNull(), + state: text("state").notNull(), + ttlAt: text("ttl_at").notNull(), + createdAt: text("created_at").notNull(), + decidedAt: text("decided_at"), + decidedBy: text("decided_by"), + pauseJson: text("pause_json"), +}); + +export const drafts = sqliteTable("drafts", { + id: text("id").primaryKey(), + tenantId: text("tenant_id").notNull(), + contractId: text("contract_id").notNull(), + taskId: text("task_id").notNull(), + agentId: text("agent_id").notNull(), + tool: text("tool").notNull(), + argsJson: text("args_json").notNull(), + state: text("state").notNull(), + createdAt: text("created_at").notNull(), + settledAt: text("settled_at"), +}); + +export const compensators = sqliteTable("compensators", { + id: text("id").primaryKey(), + draftId: text("draft_id").notNull(), + contractId: text("contract_id").notNull(), + kind: text("kind").notNull(), + payloadJson: text("payload_json").notNull(), + state: text("state").notNull(), + createdAt: text("created_at").notNull(), + runAt: text("run_at"), +}); + +export const spendLedger = sqliteTable("spend_ledger", { + id: text("id").primaryKey(), + tenantId: text("tenant_id").notNull(), + department: text("department").notNull(), + contractId: text("contract_id").notNull(), + taskId: text("task_id").notNull(), + tool: text("tool").notNull(), + amount: real("amount").notNull(), + currency: text("currency").notNull(), + createdAt: text("created_at").notNull(), +}); + +export const auditChain = sqliteTable("audit_chain", { + id: text("id").primaryKey(), + seq: integer("seq").notNull(), + prevHash: text("prev_hash").notNull(), + entryHash: text("entry_hash").notNull(), + eventType: text("event_type").notNull(), + payloadJson: text("payload_json").notNull(), + createdAt: text("created_at").notNull(), +}); + +export const traces = sqliteTable("traces", { + id: text("id").primaryKey(), + contractId: text("contract_id").notNull(), + taskId: text("task_id").notNull(), + stepsJson: text("steps_json").notNull(), + createdAt: text("created_at").notNull(), +}); + +export const controlState = sqliteTable("control_state", { + id: text("id").primaryKey(), + killed: integer("killed").notNull().default(0), + tokenBudget: integer("token_budget").notNull().default(100000), + tokensUsed: integer("tokens_used").notNull().default(0), +}); diff --git a/packages/core/src/tools.ts b/packages/core/src/tools.ts new file mode 100644 index 0000000..90f6fea --- /dev/null +++ b/packages/core/src/tools.ts @@ -0,0 +1,161 @@ +import type { Tool, ToolContext, ToolResult } from "./types.js"; + +export type ToolMap = Map; + +export function createSeedTools(state: { + refunds: Array<{ id: string; amount: number; customer: string }>; + messages: Array<{ to: string; body: string }>; + knowledge: Record; +}): Tool[] { + return [ + { + name: "crm.lookup", + description: "Lookup a customer by email", + parameters: { + type: "object", + properties: { email: { type: "string" } }, + required: ["email"], + }, + permission: { category: "read", riskLevel: 0 }, + async execute(args): Promise { + return { + ok: true, + data: { email: args.email, plan: "pro", subscription: "sub_ada_pro" }, + note: `Found customer ${args.email}`, + }; + }, + }, + { + name: "knowledge.search", + description: "Search knowledge base (via MCP)", + parameters: { + type: "object", + properties: { query: { type: "string" } }, + required: ["query"], + }, + permission: { category: "read", riskLevel: 0 }, + async execute(args): Promise { + const q = String(args.query ?? "").toLowerCase(); + const hit = Object.entries(state.knowledge).find(([k]) => q.includes(k) || k.includes(q)); + return { + ok: true, + data: hit ? { article: hit[0], body: hit[1] } : { article: null }, + note: hit ? `KB: ${hit[0]}` : "No KB hit", + }; + }, + }, + { + name: "billing.issue_refund", + description: "Issue a refund (draft/settle)", + parameters: { + type: "object", + properties: { + subscription: { type: "string" }, + amount: { type: "number" }, + customer: { type: "string" }, + }, + required: ["subscription", "amount"], + }, + permission: { category: "spend", riskLevel: 3, costCap: 200, requiresApproval: false }, + async execute(args, _ctx: ToolContext): Promise { + const amount = Number(args.amount ?? 0); + const id = `ref_${state.refunds.length + 1}`; + state.refunds.push({ + id, + amount, + customer: String(args.customer ?? "unknown"), + }); + return { + ok: true, + data: { refundId: id, amount }, + note: `Refunded $${amount}`, + cost: { amount, currency: "USD" }, + compensator: "billing.reverse_refund", + }; + }, + }, + { + name: "billing.reverse_refund", + description: "Compensate a refund", + parameters: { + type: "object", + properties: { refundId: { type: "string" }, amount: { type: "number" } }, + required: ["refundId", "amount"], + }, + permission: { category: "spend", riskLevel: 3 }, + async execute(args): Promise { + const amount = Number(args.amount ?? 0); + state.refunds = state.refunds.filter((r) => r.id !== args.refundId); + return { + ok: true, + note: `Reversed refund ${args.refundId} ($${amount})`, + cost: { amount: -amount, currency: "USD" }, + }; + }, + }, + { + name: "comms.send_email", + description: "Send customer email", + parameters: { + type: "object", + properties: { to: { type: "string" }, body: { type: "string" } }, + required: ["to", "body"], + }, + permission: { category: "communicate", riskLevel: 4, requiresApproval: true }, + async execute(args): Promise { + state.messages.push({ to: String(args.to), body: String(args.body) }); + return { ok: true, note: `Emailed ${args.to}` }; + }, + }, + { + name: "ops.restart_service", + description: "Restart an internal service", + parameters: { + type: "object", + properties: { service: { type: "string" } }, + required: ["service"], + }, + permission: { category: "system", riskLevel: 4, requiresApproval: true }, + async execute(args): Promise { + return { ok: true, note: `Restarted ${args.service}`, compensator: "ops.mark_incident" }; + }, + }, + { + name: "ops.mark_incident", + description: "Mark compensation incident", + parameters: { + type: "object", + properties: { service: { type: "string" } }, + required: ["service"], + }, + permission: { category: "system", riskLevel: 2 }, + async execute(args): Promise { + return { ok: true, note: `Incident marked for ${args.service}` }; + }, + }, + { + name: "agent.handoff", + description: "Hand work to another department agent", + parameters: { + type: "object", + properties: { + toAgent: { type: "string" }, + obligation: { type: "string" }, + }, + required: ["toAgent", "obligation"], + }, + permission: { category: "delegate", riskLevel: 1 }, + async execute(args): Promise { + return { + ok: true, + data: { toAgent: args.toAgent, obligation: args.obligation }, + note: `Handed off to ${args.toAgent}: ${args.obligation}`, + }; + }, + }, + ]; +} + +export function registryOf(tools: Tool[]): ToolMap { + return new Map(tools.map((t) => [t.name, t])); +} diff --git a/packages/core/src/trust.ts b/packages/core/src/trust.ts new file mode 100644 index 0000000..afcbcfa --- /dev/null +++ b/packages/core/src/trust.ts @@ -0,0 +1,66 @@ +import { eq } from "drizzle-orm"; +import type { Db } from "./db.js"; +import { agents } from "./schema.js"; +import type { AuditLog } from "./audit.js"; + +export function computeMaxAutonomousRisk(stats: { + accepts: number; + rejects: number; + violations: number; +}): number { + if (stats.violations >= 3 || stats.rejects >= 3) return 0; + if (stats.accepts >= 2 && stats.rejects === 0) return 2; + if (stats.accepts >= 1) return 1; + return 1; +} + +export class TrustLedger { + constructor( + private db: Db, + private audit: AuditLog, + ) {} + + async recordAccept(agentId: string): Promise { + const agent = (await this.db.select().from(agents).where(eq(agents.id, agentId)))[0]; + if (!agent) return; + const accepts = agent.accepts + 1; + const maxAutonomousRisk = computeMaxAutonomousRisk({ + accepts, + rejects: agent.rejects, + violations: agent.violations, + }); + await this.db + .update(agents) + .set({ + accepts, + maxAutonomousRisk, + trustScore: accepts - agent.rejects - agent.violations, + }) + .where(eq(agents.id, agentId)); + await this.audit.append("trust.accept", { agentId, maxAutonomousRisk }); + } + + async recordReject(agentId: string): Promise { + const agent = (await this.db.select().from(agents).where(eq(agents.id, agentId)))[0]; + if (!agent) return; + const rejects = agent.rejects + 1; + const maxAutonomousRisk = computeMaxAutonomousRisk({ + accepts: agent.accepts, + rejects, + violations: agent.violations, + }); + await this.db + .update(agents) + .set({ + rejects, + maxAutonomousRisk, + trustScore: agent.accepts - rejects - agent.violations, + }) + .where(eq(agents.id, agentId)); + await this.audit.append("trust.reject", { agentId, maxAutonomousRisk }); + } + + async get(agentId: string) { + return (await this.db.select().from(agents).where(eq(agents.id, agentId)))[0]; + } +} diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts new file mode 100644 index 0000000..b8a63a5 --- /dev/null +++ b/packages/core/src/types.ts @@ -0,0 +1,106 @@ +export type ID = string; +export type ISODate = string; +export const now = (): ISODate => new Date().toISOString(); + +export type RiskLevel = 0 | 1 | 2 | 3 | 4 | 5; +export type Effect = "allow" | "deny" | "draft" | "approve"; +export type ToolCategory = "read" | "write" | "spend" | "communicate" | "system" | "delegate"; + +export interface ToolPermission { + category: ToolCategory; + riskLevel: RiskLevel; + costCap?: number; + requiresApproval?: boolean; +} + +export interface AgentIdentity { + id: ID; + role: string; + department: string; + owner: string; + principal: string; + tools: string[]; + maxSteps?: number; + model?: string; + systemPrompt: string; +} + +export interface ChatMessage { + role: "system" | "user" | "assistant" | "tool"; + content: string; + name?: string; + toolCallId?: string; + toolCalls?: ToolCall[]; +} + +export interface ToolCall { + id: ID; + name: string; + arguments: Record; +} + +export interface ToolSchema { + name: string; + description: string; + parameters: Record; +} + +export interface LLMRequest { + messages: ChatMessage[]; + tools?: ToolSchema[]; + temperature?: number; + maxTokens?: number; + model?: string; +} + +export interface LLMResponse { + message: ChatMessage; + toolCalls: ToolCall[]; + finishReason: "stop" | "tool_calls" | "length"; + usage: { promptTokens: number; completionTokens: number }; +} + +export interface LLMProvider { + readonly id: string; + complete(request: LLMRequest): Promise; +} + +export interface ToolResult { + ok: boolean; + data?: unknown; + error?: string; + note?: string; + cost?: { amount: number; currency: string }; + draftId?: string; + compensator?: string; +} + +export interface ToolContext { + agentId: ID; + taskId: ID; + contractId: ID; + tenantId: ID; +} + +export interface Tool { + name: string; + description: string; + parameters: Record; + permission: ToolPermission; + execute: (args: Record, ctx: ToolContext) => Promise; +} + +export interface PolicyDecision { + effect: Effect; + reason: string; + approvalId?: string; + draftId?: string; +} + +export interface PolicyRule { + tool: string; + effect?: Effect; + reason?: string; + spendCapPerRun?: number; + priority?: number; +} diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json new file mode 100644 index 0000000..c99ec7b --- /dev/null +++ b/packages/core/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src/**/*.ts"] +} diff --git a/packages/mcp-knowledge/package.json b/packages/mcp-knowledge/package.json new file mode 100644 index 0000000..ce30944 --- /dev/null +++ b/packages/mcp-knowledge/package.json @@ -0,0 +1,21 @@ +{ + "name": "@corpos/mcp-knowledge", + "version": "0.2.0", + "private": true, + "type": "module", + "bin": { + "corpos-mcp-knowledge": "./dist/server.js" + }, + "scripts": { + "build": "tsc -p tsconfig.json", + "typecheck": "tsc -p tsconfig.json --noEmit", + "start": "node dist/server.js" + }, + "dependencies": { + "@modelcontextprotocol/sdk": "^1.29.0", + "zod": "^3.25.76" + }, + "devDependencies": { + "typescript": "^5.8.3" + } +} diff --git a/packages/mcp-knowledge/src/server.ts b/packages/mcp-knowledge/src/server.ts new file mode 100644 index 0000000..7b5578b --- /dev/null +++ b/packages/mcp-knowledge/src/server.ts @@ -0,0 +1,22 @@ +#!/usr/bin/env node +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { z } from "zod"; + +const KB: Record = { + refund: "Refunds under $100 may be issued after CRM lookup.", + sla: "Support SLA is 4 hours for refund requests.", + policy: "Humans govern by exception; autonomy is earned.", +}; + +const server = new McpServer({ name: "corpos-knowledge", version: "0.2.0" }); + +server.tool("search", "Search CorpOS knowledge base", { query: z.string() }, async ({ query }) => { + const q = query.toLowerCase(); + const hit = Object.entries(KB).find(([k, v]) => q.includes(k) || v.toLowerCase().includes(q)); + const text = hit ? `${hit[0]}: ${hit[1]}` : "No KB hit"; + return { content: [{ type: "text", text }] }; +}); + +const transport = new StdioServerTransport(); +await server.connect(transport); diff --git a/packages/mcp-knowledge/tsconfig.json b/packages/mcp-knowledge/tsconfig.json new file mode 100644 index 0000000..c99ec7b --- /dev/null +++ b/packages/mcp-knowledge/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src/**/*.ts"] +} diff --git a/scripts/adversarial-run.mjs b/scripts/adversarial-run.mjs new file mode 100644 index 0000000..a2bc035 --- /dev/null +++ b/scripts/adversarial-run.mjs @@ -0,0 +1,39 @@ +import { createCompany, departments, agents } from "@corpos/core"; +import { eq } from "drizzle-orm"; + +const company = await createCompany({ dbPath: ":memory:" }); + +const bypass = await company.gateway.invoke( + "evil.shell", + {}, + { agentId: "agent_support", taskId: "t", contractId: "c", tenantId: "default" }, +); +if (bypass.decision.effect !== "deny") throw new Error("bypass cell failed"); + +await company.db.update(agents).set({ maxAutonomousRisk: 3 }).where(eq(agents.id, "agent_finance")); +await company.db + .update(departments) + .set({ capitalSpent: 1999, capitalBudget: 2000 }) + .where(eq(departments.id, "finance")); +const cap = await company.gateway.invoke( + "billing.issue_refund", + { subscription: "x", amount: 50, customer: "x" }, + { agentId: "agent_finance", taskId: "t", contractId: "c", tenantId: "default" }, +); +if (cap.decision.effect !== "deny") throw new Error("capital cell failed"); + +process.env.CORPOS_MODE = "shared"; +process.env.DASHBOARD_API_TOKEN = "secret"; +if (`Bearer ${process.env.DASHBOARD_API_TOKEN}` !== "Bearer secret") { + throw new Error("auth cell failed"); +} + +await company.audit.append("a", { n: 1 }); +await company.audit.append("b", { n: 2 }); +await company.audit.append("c", { n: 3 }); +if (!(await company.audit.verify()).ok) throw new Error("audit should pass"); +await company.audit.forgeMiddle(); +if ((await company.audit.verify()).ok) throw new Error("forge cell failed"); + +company.close(); +console.log("adversarial: ok"); diff --git a/scripts/audit-verify.ts b/scripts/audit-verify.ts new file mode 100644 index 0000000..342dd65 --- /dev/null +++ b/scripts/audit-verify.ts @@ -0,0 +1,8 @@ +import { createCompany } from "@corpos/core"; + +const company = await createCompany({ dbPath: process.env.CORPOS_DB ?? "data/company.db" }); +await company.audit.append("boot", { ok: true }); +const result = await company.audit.verify(); +console.log(result); +company.close(); +process.exit(result.ok ? 0 : 1); diff --git a/scripts/harness/adversarial.sh b/scripts/harness/adversarial.sh index 817840d..eeedcad 100755 --- a/scripts/harness/adversarial.sh +++ b/scripts/harness/adversarial.sh @@ -1,7 +1,5 @@ #!/usr/bin/env bash -# Authorized local adversarial probes (corp-site gate). -# Hermetic stub until a corporate handoff requires real probes. set -euo pipefail ROOT="$(cd "$(dirname "$0")/../.." && pwd)" cd "$ROOT" -echo "adversarial: ok (stub — no program-bound probes yet)" +node scripts/adversarial-run.mjs diff --git a/scripts/harness/verify.sh b/scripts/harness/verify.sh index fa854f5..de08bb4 100755 --- a/scripts/harness/verify.sh +++ b/scripts/harness/verify.sh @@ -1,25 +1,33 @@ #!/usr/bin/env bash -# Definition of Done — mirrors CI lint + build matrix jobs (no Docker). set -euo pipefail ROOT="$(cd "$(dirname "$0")/../.." && pwd)" cd "$ROOT" -if [[ -x ./scripts/check-stub-canary.sh ]]; then - ./scripts/check-stub-canary.sh -fi - if command -v corepack >/dev/null 2>&1; then corepack enable >/dev/null 2>&1 || true corepack prepare npm@10.9.2 --activate >/dev/null 2>&1 || true fi -echo "==> npm ci (expect packageManager npm@10.9.2)" +echo "==> npm ci" npm ci +echo "==> build" +npm run build + echo "==> typecheck + test + lint + format" npm run typecheck npm run test npm run lint npm run format:check -echo "verify: ok (ci/web parity)" +# Guardrails +if rg -n "from ['\"]express['\"]|require\\(['\"]express['\"]\\)" apps packages --glob '!**/node_modules/**' >/dev/null 2>&1; then + echo "verify: Express import forbidden" >&2 + exit 1 +fi +if rg -n "better-sqlite3" package.json packages/*/package.json apps/*/package.json >/dev/null 2>&1; then + echo "verify: better-sqlite3 forbidden" >&2 + exit 1 +fi + +echo "verify: ok" diff --git a/src/agents/engineer.ts b/src/agents/engineer.ts deleted file mode 100644 index ec0714a..0000000 --- a/src/agents/engineer.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { AgentDefinition } from "../core"; - -export const engineerAgent: AgentDefinition = { - id: "agent_engineer", - name: "Engineering Agent", - role: "Engineering", - tools: [ - "system.get_health", - "system.restart_service", - "kb.search", - "kb.get_article", - "comms.send_slack", - ], - systemPrompt: [ - "You are the Engineering agent.", - "You investigate and remediate technical issues using system health, restarts, the knowledge base, and Slack.", - "Confirm impact with system.get_health and kb.search before acting; correlate symptoms to a root cause.", - "Restarts require approval and route through policy automatically; prefer the least-disruptive remediation.", - "Finish with a concise summary of the root cause, the remediation applied, and recommended follow-up.", - ].join("\n"), -}; diff --git a/src/agents/finance.ts b/src/agents/finance.ts deleted file mode 100644 index 1dac2d6..0000000 --- a/src/agents/finance.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { AgentDefinition } from "../core"; - -export const financeAgent: AgentDefinition = { - id: "agent_finance", - name: "Finance Agent", - role: "Finance", - tools: [ - "billing.get_subscription", - "billing.issue_refund", - "billing.apply_credit", - "crm.lookup_contact", - "comms.send_slack", - ], - systemPrompt: [ - "You are the Finance agent.", - "You own billing accuracy: subscriptions, refunds, and credits.", - "Verify the account with crm.lookup_contact and billing.get_subscription before issuing any refund or credit.", - "Refunds and credits are spend-gated and require approval; never move money without verification and never exceed the configured cap.", - "Finish with a concise summary that states the exact amounts and references.", - ].join("\n"), -}; diff --git a/src/agents/index.ts b/src/agents/index.ts deleted file mode 100644 index 1936236..0000000 --- a/src/agents/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Agent, type AgentDeps, type AgentDefinition } from "../core"; -import { supportAgent } from "./support"; -import { salesAgent } from "./sales"; -import { financeAgent } from "./finance"; -import { opsAgent } from "./ops"; -import { engineerAgent } from "./engineer"; - -export { supportAgent, salesAgent, financeAgent, opsAgent, engineerAgent }; - -export const agentDefinitions: AgentDefinition[] = [ - supportAgent, - salesAgent, - financeAgent, - opsAgent, - engineerAgent, -]; - -export function createAgent(deps: AgentDeps): Agent { - return new Agent(deps); -} diff --git a/src/agents/ops.ts b/src/agents/ops.ts deleted file mode 100644 index 0b7b314..0000000 --- a/src/agents/ops.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { AgentDefinition } from "../core"; - -export const opsAgent: AgentDefinition = { - id: "agent_ops", - name: "Operations Agent", - role: "Operations", - tools: [ - "system.get_health", - "system.list_alerts", - "system.restart_service", - "kb.search", - "comms.send_slack", - "delegate.task", - ], - systemPrompt: [ - "You are the Operations agent.", - "You monitor system health, triage alerts, and coordinate service recovery.", - "Check system.get_health and system.list_alerts first to establish the current state before acting.", - "Restarts and notifications are consequential and route through policy automatically; delegate to Engineering when a deeper fix is needed.", - "Finish with a concise status summary and any actions taken or pending approval.", - ].join("\n"), -}; diff --git a/src/agents/sales.ts b/src/agents/sales.ts deleted file mode 100644 index c295669..0000000 --- a/src/agents/sales.ts +++ /dev/null @@ -1,22 +0,0 @@ -import type { AgentDefinition } from "../core"; - -export const salesAgent: AgentDefinition = { - id: "agent_sales", - name: "Sales Agent", - role: "Sales", - tools: [ - "crm.lookup_contact", - "crm.create_contact", - "crm.update_contact", - "crm.list_deals", - "comms.send_email", - "delegate.task", - ], - systemPrompt: [ - "You are the Sales agent.", - "Help prospects and customers progress deals, create contacts, and propose the right plan.", - "Use your CRM and email tools to look up context, update records, and follow up before you answer.", - "Outbound emails and handoffs are consequential and route through policy automatically; never misrepresent pricing or terms.", - "Finish with a concise summary and the recommended next step.", - ].join("\n"), -}; diff --git a/src/agents/support.ts b/src/agents/support.ts deleted file mode 100644 index bab5be3..0000000 --- a/src/agents/support.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { AgentDefinition } from "../core"; - -export const supportAgent: AgentDefinition = { - id: "agent_support", - name: "Support Agent", - role: "Customer Support", - tools: [ - "kb.search", - "kb.get_article", - "crm.lookup_contact", - "comms.send_email", - "comms.get_thread", - "billing.issue_refund", - "delegate.task", - ], - systemPrompt: [ - "You are the Customer Support agent.", - "Resolve customer questions about accounts, billing, refunds, and product usage.", - "Always gather facts first using your tools (kb.search, crm.lookup_contact, comms.get_thread, billing.issue_refund) before answering.", - "Consequential actions such as refunds and outbound emails route through policy automatically and may require human approval — do not attempt to bypass them.", - "Finish with a concise summary of what you found and what you did (or what needs approval).", - ].join("\n"), -}; diff --git a/src/api/auth.ts b/src/api/auth.ts deleted file mode 100644 index a5930c9..0000000 --- a/src/api/auth.ts +++ /dev/null @@ -1,39 +0,0 @@ -import express, { type Request, type Response, type NextFunction } from "express"; - -/** - * Bearer-token gate for dashboard mutating API routes. - * Set DASHBOARD_API_TOKEN in the environment; when unset, auth is disabled (dev only). - */ -export function requireDashboardAuth(req: Request, res: Response, next: NextFunction): void { - const expected = process.env.DASHBOARD_API_TOKEN?.trim(); - if (!expected) { - next(); - return; - } - const header = req.headers.authorization ?? ""; - const prefix = "Bearer "; - if (!header.startsWith(prefix)) { - res.status(401).json({ error: "dashboard authentication required" }); - return; - } - const token = header.slice(prefix.length).trim(); - if (token.length !== expected.length || !timingSafeEqual(token, expected)) { - res.status(401).json({ error: "dashboard authentication required" }); - return; - } - next(); -} - -function timingSafeEqual(a: string, b: string): boolean { - if (a.length !== b.length) return false; - let mismatch = 0; - for (let i = 0; i < a.length; i++) { - mismatch |= a.charCodeAt(i) ^ b.charCodeAt(i); - } - return mismatch === 0; -} - -export function installDashboardAuth(app: express.Application): void { - app.use("/api/approvals", requireDashboardAuth); - app.post("/api/approvals/:id/decide", requireDashboardAuth); -} diff --git a/src/api/index.ts b/src/api/index.ts deleted file mode 100644 index 787e46d..0000000 --- a/src/api/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { createHttpServer, type HttpServerHandles } from "./server"; diff --git a/src/api/server.ts b/src/api/server.ts deleted file mode 100644 index eb5f62a..0000000 --- a/src/api/server.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { createServer, type Server as HttpServer } from "node:http"; -import { dirname, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; - -import express from "express"; -import { WebSocketServer, WebSocket } from "ws"; - -import { newTask, type CompanyServices, type TaskState } from "../core"; -import { installDashboardAuth } from "./auth"; - -const __dirname = dirname(fileURLToPath(import.meta.url)); -const PUBLIC_DIR = resolve(__dirname, "../dashboard/public"); - -export interface HttpServerHandles { - app: express.Application; - server: HttpServer; - wss: WebSocketServer; -} - -export function createHttpServer(services: CompanyServices): HttpServerHandles { - const app = express(); - app.use(express.json()); - installDashboardAuth(app); - - // Suppress the browser's default favicon request (no asset shipped). - app.get("/favicon.ico", (_req, res) => { - res.status(204).end(); - }); - - app.get("/api/health", (_req, res) => { - res.json({ ok: true, ts: new Date().toISOString(), provider: services.provider.id }); - }); - - app.get("/api/tasks", (req, res) => { - const raw = req.query.state; - const state = typeof raw === "string" ? (raw as TaskState) : undefined; - res.json(services.store.listTasks(state)); - }); - - app.get("/api/tasks/:id", (req, res) => { - const task = services.store.getTask(req.params.id); - if (!task) { - res.status(404).json({ error: "task not found" }); - return; - } - res.json(task); - }); - - app.post("/api/tasks", async (req, res) => { - const body = req.body ?? {}; - const title = typeof body.title === "string" ? body.title : ""; - const description = typeof body.description === "string" ? body.description : ""; - if (!title || !description) { - res.status(400).json({ error: "title and description are required" }); - return; - } - const task = newTask({ - tenantId: typeof body.tenantId === "string" ? body.tenantId : "tenant_default", - title, - description, - createdBy: "human", - assignedTo: typeof body.assignedTo === "string" ? body.assignedTo : undefined, - priority: typeof body.priority === "number" ? body.priority : 0, - input: isRecord(body.input) ? body.input : undefined, - }); - await services.orchestrator.enqueue(task); - res.status(201).json(task); - }); - - app.get("/api/agents", (_req, res) => { - res.json(agentSummary(services)); - }); - - app.get("/api/events", (req, res) => { - const raw = req.query.limit; - const limit = typeof raw === "string" && /^\d+$/.test(raw) ? Number(raw) : 100; - res.json(services.store.recentEvents(limit)); - }); - - app.get("/api/approvals", (_req, res) => { - res.json(services.store.pendingApprovals()); - }); - - app.post("/api/approvals/:id/decide", async (req, res) => { - const id = req.params.id; - const body = req.body ?? {}; - const decision = body.decision; - let by = typeof body.by === "string" ? body.by : "human"; - if (decision !== "approved" && decision !== "rejected") { - res.status(400).json({ error: "decision must be 'approved' or 'rejected'" }); - return; - } - if (process.env.DASHBOARD_API_TOKEN?.trim()) { - by = "dashboard-operator"; - } - const approval = services.policy.decide(id, decision, by); - if (!approval) { - res.status(404).json({ error: "approval not found" }); - return; - } - if (decision === "approved") { - void services.orchestrator.resume(id).catch((err) => { - services.log.error("approval.resume.failed", { approvalId: id, error: String(err) }); - }); - } - res.json(approval); - }); - - app.get("/api/spend", (_req, res) => { - res.json(aggregateSpend(services)); - }); - - app.use(express.static(PUBLIC_DIR)); - - const server = createServer(app); - const wss = new WebSocketServer({ server, path: "/ws" }); - - wss.on("connection", (socket) => { - send(socket, { - type: "snapshot", - payload: { - tasks: services.store.listTasks(), - agents: agentSummary(services), - approvals: services.store.pendingApprovals(), - provider: services.provider.id, - }, - }); - - const unsubscribe = services.bus.onAll((event) => { - send(socket, event); - }); - - socket.on("close", () => unsubscribe()); - socket.on("error", () => unsubscribe()); - }); - - return { app, server, wss }; -} - -function send(socket: WebSocket, data: unknown): void { - if (socket.readyState === WebSocket.OPEN) { - socket.send(JSON.stringify(data)); - } -} - -function agentSummary(services: CompanyServices) { - return services.orchestrator.getAgents().map((a) => ({ - id: a.def.id, - name: a.def.name, - role: a.def.role, - tools: a.def.tools, - })); -} - -function isRecord(v: unknown): v is Record { - return typeof v === "object" && v !== null && !Array.isArray(v); -} - -function aggregateSpend(services: CompanyServices): { - total: number; - count: number; - byTool: Array<{ tool: string; total: number; count: number }>; - byTenant: Array<{ tenantId: string; total: number; count: number }>; -} { - const db = services.store.db; - const overall = db - .prepare("SELECT COALESCE(SUM(amount), 0) AS total, COUNT(*) AS count FROM spend") - .get() as { total: number; count: number } | undefined; - const byTool = db - .prepare( - "SELECT tool, COALESCE(SUM(amount), 0) AS total, COUNT(*) AS count FROM spend GROUP BY tool ORDER BY total DESC", - ) - .all() as Array<{ tool: string; total: number; count: number }>; - const byTenant = db - .prepare( - "SELECT tenant_id AS tenantId, COALESCE(SUM(amount), 0) AS total, COUNT(*) AS count FROM spend GROUP BY tenant_id ORDER BY total DESC", - ) - .all() as Array<{ tenantId: string; total: number; count: number }>; - return { - total: overall?.total ?? 0, - count: overall?.count ?? 0, - byTool, - byTenant, - }; -} diff --git a/src/core/agent.ts b/src/core/agent.ts deleted file mode 100644 index 1a729ca..0000000 --- a/src/core/agent.ts +++ /dev/null @@ -1,303 +0,0 @@ -import type { - AgentDefinition, - AgentRunResult, - AgentRunStep, - ChatMessage, - Logger, - LLMProvider, - Task, - TaskResult, -} from "./types"; -import { now } from "./types"; -import { EventBus } from "./event-bus"; -import type { Store } from "./store"; -import type { ToolRegistry } from "./tool"; -import type { PolicyEngine } from "./policy"; -import type { MemoryStore } from "./memory"; - -export interface AgentDeps { - def: AgentDefinition; - provider: LLMProvider; - tools: ToolRegistry; - policy: PolicyEngine; - memory: MemoryStore; - store: Store; - bus: EventBus; - log: Logger; -} - -export interface ResumeHandle { - approvalId: string; -} - -/** - * An LLM-driven agent. The run loop: - * reason → (optional) tool calls, each policy-gated → observe results → repeat - * It stops when the model returns no tool calls (final answer) or the step - * cap is hit. Consequential tool calls that require approval pause the run - * and return an approval id; the orchestrator resumes after a decision. - */ -export class Agent { - readonly id: string; - readonly def: AgentDefinition; - private deps: AgentDeps; - /** Conversation snapshots paused at an approval gate, keyed by approvalId. */ - private paused = new Map< - string, - { messages: ChatMessage[]; toolCallId: string; taskId: string } - >(); - - constructor(deps: AgentDeps) { - this.deps = deps; - this.id = deps.def.id; - this.def = deps.def; - } - - async run(task: Task, resume?: ResumeHandle): Promise { - const { provider, tools, policy, memory, store, bus, log, def } = this.deps; - const tenantId = task.tenantId; - const steps: AgentRunStep[] = []; - const logCtx = { agentId: def.id, taskId: task.id, tenantId }; - - store.updateTaskState(task.id, { - state: "running", - startedAt: task.startedAt ?? now(), - }); - await bus.emit( - "agent.started", - { agentId: def.id, role: def.role }, - { source: def.id, ...logCtx }, - ); - - const buildFresh = (): ChatMessage[] => { - const msgs: ChatMessage[] = [this.systemMessage(), this.taskMessage(task)]; - const recalled = memory.recall(tenantId, def.id, { query: task.title, limit: 5 }); - if (recalled.length) { - msgs.push({ - role: "user", - content: `Relevant memory:\n${recalled.map((m) => `- ${m.content}`).join("\n")}`, - }); - } - return msgs; - }; - - // Resume path: restore the full prior conversation so the agent CONTINUES - // from where it paused (not restart). Without this, every approval gate - // loses prior reasoning and the model re-requests the same gated tools. - let messages: ChatMessage[]; - if (resume) { - const snapshot = this.paused.get(resume.approvalId); - const approval = store.getApproval(resume.approvalId); - if (snapshot && approval) { - messages = [...snapshot.messages]; - if (approval.state === "approved") { - const result = await tools.invoke(approval.tool, approval.args, this.ctx(task)); - if (result.cost) - this.recordSpend(task, approval.tool, result.cost.amount, result.cost.currency); - memory.pushWorking(def.id, `${approval.tool} → ${result.note ?? "(ok)"}`); - messages.push( - this.toolResultMessage( - snapshot.toolCallId, - result.note ?? JSON.stringify(result.data ?? result), - ), - ); - steps.push({ ts: now(), results: [result] }); - } else { - messages.push({ - role: "user", - content: `Your request to use ${approval.tool} was ${approval.state}. Proceed without it.`, - }); - } - this.paused.delete(resume.approvalId); - } else { - // Fallback (e.g. process restarted mid-approval): rebuild fresh. - messages = buildFresh(); - } - } else { - messages = buildFresh(); - } - - const maxSteps = def.maxSteps ?? 8; - for (let step = 0; step < maxSteps; step++) { - const response = await provider.complete({ - messages, - tools: tools.schemasFor(def.tools), - model: def.model, - temperature: 0.2, - }); - - messages.push(response.message); - const stepEntry: AgentRunStep = { ts: now(), thought: response.message.content }; - - if (!response.toolCalls.length) { - // Final answer. - const result: TaskResult = { - summary: response.message.content || "(empty response)", - }; - steps.push(stepEntry); - await this.finish(task, result, steps, true); - return { taskId: task.id, ok: true, steps, result }; - } - - // Execute each tool call, gating through policy. - const results = []; - const decisions = []; - for (const call of response.toolCalls) { - const tool = tools.get(call.name); - if (!tool) { - const msg = `Tool not found: ${call.name}`; - messages.push(this.toolResultMessage(call.id, msg)); - results.push({ ok: false, error: msg }); - continue; - } - const decision = policy.evaluate(tool, call.arguments, { - tenantId, - agentId: def.id, - taskId: task.id, - }); - decisions.push({ tool: call.name, decision }); - - if (decision.effect === "deny") { - messages.push(this.toolResultMessage(call.id, `DENIED: ${decision.reason}`)); - results.push({ ok: false, error: decision.reason }); - await bus.emit( - "tool.denied", - { tool: call.name, reason: decision.reason }, - { source: def.id, ...logCtx }, - ); - continue; - } - if (decision.effect === "approve" && decision.approvalId) { - // Snapshot the conversation so resume continues from here. - this.paused.set(decision.approvalId, { - messages: [...messages], - toolCallId: call.id, - taskId: task.id, - }); - store.updateTaskState(task.id, { state: "awaiting_approval" }); - await bus.emit( - "agent.awaiting_approval", - { approvalId: decision.approvalId, tool: call.name }, - { source: def.id, ...logCtx }, - ); - steps.push({ ...stepEntry, policyDecisions: decisions }); - return { taskId: task.id, ok: false, steps, awaitingApprovalId: decision.approvalId }; - } - - await bus.emit( - "tool.call", - { tool: call.name, args: call.arguments }, - { source: def.id, ...logCtx }, - ); - const result = await tools.invoke(call.name, call.arguments, this.ctx(task)); - if (result.cost) { - this.recordSpend(task, call.name, result.cost.amount, result.cost.currency); - } - memory.pushWorking(def.id, `${call.name} → ${result.note ?? "(ok)"}`); - messages.push( - this.toolResultMessage(call.id, result.note ?? JSON.stringify(result.data ?? result)), - ); - results.push(result); - } - - stepEntry.toolCalls = response.toolCalls; - stepEntry.results = results; - stepEntry.policyDecisions = decisions; - steps.push(stepEntry); - log.debug("agent.step", { - agentId: def.id, - step, - tools: response.toolCalls.map((c) => c.name), - }); - } - - // Step cap exhausted. - const err = `exceeded max steps (${maxSteps})`; - await this.finish(task, undefined, steps, false, err); - return { taskId: task.id, ok: false, steps, error: err }; - } - - private systemMessage(): ChatMessage { - return { role: "system", content: this.deps.def.systemPrompt }; - } - - private taskMessage(task: Task): ChatMessage { - const input = task.input ? `\n\nInput: ${JSON.stringify(task.input)}` : ""; - return { - role: "user", - content: `Task: ${task.title}\n${task.description}${input}\n\nUse your tools to make progress, then give a final summary when done.`, - }; - } - - private toolResultMessage(toolCallId: string, content: string): ChatMessage { - return { role: "tool", toolCallId, content }; - } - - private ctx(task: Task) { - return { - agentId: this.def.id, - taskId: task.id, - tenantId: task.tenantId, - logger: this.deps.log, - }; - } - - private async recordSpend( - task: Task, - tool: string, - amount: number, - currency: string, - ): Promise { - const { store, def, bus } = this.deps; - store.recordSpend({ - id: `${tool}_${task.id}_${Date.now()}`, - tenantId: task.tenantId, - agentId: def.id, - taskId: task.id, - tool, - amount, - currency, - ref: task.title, - ts: now(), - }); - await bus.emit( - "spend.recorded", - { tool, amount, currency }, - { source: def.id, taskId: task.id, tenantId: task.tenantId, agentId: def.id }, - ); - } - - private async finish( - task: Task, - result: TaskResult | undefined, - steps: AgentRunStep[], - ok: boolean, - error?: string, - ): Promise { - const { store, memory, bus, def, log } = this.deps; - // Drop any paused snapshots for this terminal task to avoid leaks. - for (const [k, v] of this.paused) if (v.taskId === task.id) this.paused.delete(k); - store.updateTaskState(task.id, { - state: ok ? "succeeded" : "failed", - finishedAt: now(), - output: result, - error, - }); - if (result) { - memory.remember({ - tenantId: task.tenantId, - agentId: def.id, - kind: "decision", - content: `${task.title} → ${result.summary}`, - tags: [task.assignedTo ?? def.role], - }); - } - memory.clearWorking(def.id); - await bus.emit( - ok ? "agent.succeeded" : "agent.failed", - { taskId: task.id, steps: steps.length, error }, - { source: def.id, taskId: task.id, tenantId: task.tenantId, agentId: def.id }, - ); - log.audit(ok ? "agent.succeeded" : "agent.failed", { taskId: task.id, agentId: def.id }); - } -} diff --git a/src/core/app.ts b/src/core/app.ts deleted file mode 100644 index 821dae6..0000000 --- a/src/core/app.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { CoreLogger } from "./logger"; -import { EventBus } from "./event-bus"; -import { Store, newTask, type StoreOptions } from "./store"; -import { ToolRegistry } from "./tool"; -import { PolicyEngine, type PolicyRule } from "./policy"; -import { MemoryStore } from "./memory"; -import { Orchestrator } from "./orchestrator"; -import type { Agent, AgentDeps } from "./agent"; -import type { AgentDefinition, Logger, Task, Tool } from "./types"; -import type { LLMProvider } from "./types"; - -export interface CompanyConfig { - store?: StoreOptions; - concurrency?: number; - defaultEffect?: "allow" | "deny" | "approve"; - rules?: PolicyRule[]; - logLevel?: "debug" | "info" | "warn" | "error"; -} - -export interface CompanyServices { - store: Store; - bus: EventBus; - log: Logger; - tools: ToolRegistry; - policy: PolicyEngine; - memory: MemoryStore; - orchestrator: Orchestrator; - agents: Map; - provider: LLMProvider; - close(): void; - /** Build an agent from a definition and register it. */ - registerAgent(def: AgentDefinition, agentFactory: (deps: AgentDeps) => Agent): Agent; -} - -export interface Runtime { - services: CompanyServices; - /** Convenience: enqueue a task by description. */ - submit(partial: Parameters[0]): Promise; -} - -export function createCompany(opts: { - provider: LLMProvider; - config?: CompanyConfig; - agentFactory: (deps: AgentDeps) => Agent; - agents: AgentDefinition[]; - tools?: Tool[]; -}): Runtime { - const config = opts.config ?? {}; - const store = new Store(config.store ?? { path: "data/company.db" }); - const bus = new EventBus(5000); - const baseLog = new CoreLogger({ minLevel: config.logLevel, bus }); - const log = baseLog.child("company"); - - // Persist the full event stream to SQLite (audit backbone). - bus.onAll((event) => { - store.insertEvent(event); - }); - - const tools = new ToolRegistry(); - if (opts.tools) tools.registerAll(opts.tools); - - const policy = new PolicyEngine(store, bus, log.child("policy"), { - defaultEffect: config.defaultEffect, - }); - if (config.rules) policy.setRules(config.rules); - - const memory = new MemoryStore(store, log.child("memory")); - - const orchestrator = new Orchestrator({ - store, - bus, - log: log.child("orchestrator"), - concurrency: config.concurrency, - }); - - const agents = new Map(); - const registerAgent = (def: AgentDefinition): Agent => { - const deps: AgentDeps = { - def, - provider: opts.provider, - tools, - policy, - memory, - store, - bus, - log: log.child(`agent:${def.id}`), - }; - const agent = opts.agentFactory(deps); - agents.set(agent.id, agent); - orchestrator.register(agent); - return agent; - }; - - for (const def of opts.agents) registerAgent(def); - - const services: CompanyServices = { - store, - bus, - log, - tools, - policy, - memory, - orchestrator, - agents, - provider: opts.provider, - close: () => store.close(), - registerAgent: (def, factory) => { - const deps: AgentDeps = { - def, - provider: opts.provider, - tools, - policy, - memory, - store, - bus, - log: log.child(`agent:${def.id}`), - }; - const agent = factory(deps); - agents.set(agent.id, agent); - orchestrator.register(agent); - return agent; - }, - }; - - const runtime: Runtime = { - services, - submit: async (partial) => { - const task = newTask(partial); - await orchestrator.enqueue(task); - return task; - }, - }; - - orchestrator.start(); - return runtime; -} diff --git a/src/core/event-bus.ts b/src/core/event-bus.ts deleted file mode 100644 index 8b397e0..0000000 --- a/src/core/event-bus.ts +++ /dev/null @@ -1,66 +0,0 @@ -import type { Event, EventHandler } from "./types"; - -/** - * In-process typed event bus. Subscribers are notified synchronously by - * default; async handlers are awaited. A bounded ring buffer retains recent - * history for replay (dashboard, audit reconstruction, tests). - */ -export class EventBus { - private handlers = new Map>(); - private wildcard = new Set(); - private history: Event[] = []; - private capacity: number; - - constructor(capacity = 1000) { - this.capacity = capacity; - } - - on(type: string, handler: EventHandler): () => void { - const set = this.handlers.get(type) ?? new Set(); - set.add(handler as EventHandler); - this.handlers.set(type, set); - return () => set.delete(handler as EventHandler); - } - - /** Subscribe to every event regardless of type. */ - onAll(handler: EventHandler): () => void { - this.wildcard.add(handler); - return () => this.wildcard.delete(handler); - } - - async emit(type: string, payload: T, base?: Partial>): Promise { - const event: Event = { - id: crypto.randomUUID(), - type, - ts: new Date().toISOString(), - source: base?.source ?? "system", - payload, - tenantId: base?.tenantId, - taskId: base?.taskId, - agentId: base?.agentId, - }; - this.history.push(event as Event); - if (this.history.length > this.capacity) this.history.shift(); - - const direct = this.handlers.get(type); - const targets: EventHandler[] = [...(direct ? [...direct] : []), ...this.wildcard]; - for (const h of targets) { - try { - await h(event as Event); - } catch (err) { - // A failing handler must not break the bus or other handlers. - this.handlers - .get("__bus_error__") - ?.forEach((eh) => eh({ ...event, type: "__bus_error__", payload: err })); - } - } - } - - history_since(ts?: string): Event[] { - return ts ? this.history.filter((e) => e.ts > ts) : [...this.history]; - } - - filter(type?: string): Event[] { - return type ? this.history.filter((e) => e.type === type) : [...this.history]; - } -} diff --git a/src/core/id.ts b/src/core/id.ts deleted file mode 100644 index 170bbac..0000000 --- a/src/core/id.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { customAlphabet, nanoid } from "nanoid"; - -const short = customAlphabet("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 16); - -export const newId = (prefix = ""): string => (prefix ? `${prefix}_${short()}` : nanoid()); diff --git a/src/core/index.ts b/src/core/index.ts deleted file mode 100644 index b007350..0000000 --- a/src/core/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -export * from "./types"; -export { newId } from "./id"; -export { CoreLogger } from "./logger"; -export { EventBus } from "./event-bus"; -export { Store, newTask, type StoreOptions } from "./store"; -export { ToolRegistry, validateArgs, schemaOf } from "./tool"; -export { defineTool, type ToolSpec } from "./tool-builder"; -export { PolicyEngine, globMatch, type PolicyOptions } from "./policy"; -export { MemoryStore } from "./memory"; -export { - SimulationProvider, - HttpLLMProvider, - createProvider, - type ProviderConfig, - type ProviderKind, - type SimulationHandler, - type SimulationResponse, -} from "./llm"; -export { Agent, type AgentDeps, type ResumeHandle } from "./agent"; -export { Orchestrator, type OrchestratorDeps } from "./orchestrator"; -export { createCompany, type CompanyConfig, type CompanyServices, type Runtime } from "./app"; diff --git a/src/core/llm.ts b/src/core/llm.ts deleted file mode 100644 index 82e5b77..0000000 --- a/src/core/llm.ts +++ /dev/null @@ -1,325 +0,0 @@ -import type { - ChatMessage, - LLMProvider, - LLMRequest, - LLMResponse, - ToolCall, - ToolSchema, -} from "./types"; - -// ─── Simulation provider ───────────────────────────────────────────── -// Deterministic, offline provider. Two modes: -// 1. script: a queue of canned assistant messages consumed in order. -// 2. handler: a function that inspects the last user/assistant turn and -// returns the next response. This lets the simulation react to tool -// results so full multi-step agent runs are reproducible without network. - -export type SimulationResponse = { - content?: string; - toolCalls?: Array<{ name: string; arguments: Record }>; - finishReason?: LLMResponse["finishReason"]; -}; - -export type SimulationHandler = ( - req: LLMRequest, -) => SimulationResponse | Promise; - -export class SimulationProvider implements LLMProvider { - readonly id = "simulation"; - private script: SimulationResponse[] = []; - private handler?: SimulationHandler; - private cursor = 0; - - constructor(source: SimulationResponse[] | SimulationHandler) { - if (typeof source === "function") this.handler = source as SimulationHandler; - else this.script = source; - } - - async complete(req: LLMRequest): Promise { - const sim = this.handler - ? await this.handler(req) - : (this.script[this.cursor++] ?? { - content: "(no further scripted response)", - finishReason: "stop", - }); - - const toolCalls: ToolCall[] = (sim.toolCalls ?? []).map((tc, i) => ({ - id: `call_${this.cursor}_${i}_${Math.random().toString(36).slice(2, 8)}`, - name: tc.name, - arguments: tc.arguments, - })); - - const message: ChatMessage = { - role: "assistant", - content: sim.content ?? "", - toolCalls: toolCalls.length ? toolCalls : undefined, - }; - - return { - message, - toolCalls, - finishReason: toolCalls.length ? "tool_calls" : (sim.finishReason ?? "stop"), - usage: { - promptTokens: JSON.stringify(req.messages).length, - completionTokens: (sim.content ?? "").length, - }, - }; - } - - get remaining(): number { - return Math.max(0, this.script.length - this.cursor); - } -} - -// ─── OpenAI-compatible provider (Z.AI default) ─────────────────────── -// Calls /chat/completions with function-style tools. Works against Z.AI -// (https://open.bigmodel.cn/api/paas/v4) and any OpenAI-compatible base URL. - -export interface HttpProviderOptions { - apiKey: string; - baseUrl: string; - defaultModel: string; - /** Extra request headers (e.g. OpenRouter's HTTP-Referer / X-Title). */ - extraHeaders?: Record; - /** Override fetch (for tests / proxies). Defaults to global fetch. */ - fetchImpl?: typeof fetch; - timeoutMs?: number; -} - -export class HttpLLMProvider implements LLMProvider { - readonly id: string; - private opts: HttpProviderOptions; - - constructor(id: string, opts: HttpProviderOptions) { - this.id = id; - this.opts = { timeoutMs: 60_000, ...opts }; - } - - async complete(req: LLMRequest): Promise { - const fetchImpl = this.opts.fetchImpl ?? globalThis.fetch; - const model = req.model ?? this.opts.defaultModel; - const body: Record = { - model, - messages: req.messages.map(serializeMessage), - temperature: req.temperature ?? 0.2, - }; - if (req.maxTokens) body.max_tokens = req.maxTokens; - if (req.tools && req.tools.length) { - body.tools = req.tools.map(toOpenAITool); - body.tool_choice = "auto"; - } - - const url = `${this.opts.baseUrl.replace(/\/$/, "")}/chat/completions`; - const controller = new AbortController(); - const timer = setTimeout(() => controller.abort(), this.opts.timeoutMs); - try { - const res = await fetchImpl(url, { - method: "POST", - signal: controller.signal, - headers: { - "content-type": "application/json", - authorization: `Bearer ${this.opts.apiKey}`, - ...(this.opts.extraHeaders ?? {}), - }, - body: JSON.stringify(body), - }); - if (!res.ok) { - const text = await res.text().catch(() => ""); - throw new Error(`LLM HTTP ${res.status}: ${text.slice(0, 500)}`); - } - const json = (await res.json()) as { - choices: Array<{ - message: { - role: string; - content: string | null; - tool_calls?: Array<{ - id: string; - type: "function"; - function: { name: string; arguments: string }; - }>; - }; - finish_reason: string; - }>; - usage?: { prompt_tokens: number; completion_tokens: number }; - }; - const choice = json.choices[0]; - if (!choice) throw new Error("LLM returned no choices"); - - const toolCalls: ToolCall[] = (choice.message.tool_calls ?? []).map((tc) => ({ - id: tc.id, - name: tc.function.name, - arguments: safeParseArgs(tc.function.arguments), - })); - - const message: ChatMessage = { - role: "assistant", - content: choice.message.content ?? "", - toolCalls: toolCalls.length ? toolCalls : undefined, - }; - - return { - message, - toolCalls, - finishReason: - toolCalls.length || choice.finish_reason === "tool_calls" - ? "tool_calls" - : choice.finish_reason === "length" - ? "length" - : "stop", - usage: { - promptTokens: json.usage?.prompt_tokens ?? 0, - completionTokens: json.usage?.completion_tokens ?? 0, - }, - }; - } finally { - clearTimeout(timer); - } - } -} - -function serializeMessage(m: ChatMessage) { - if (m.role === "tool") { - return { - role: "tool", - tool_call_id: m.toolCallId, - content: m.content, - }; - } - return { - role: m.role, - content: m.content, - ...(m.toolCalls - ? { - tool_calls: m.toolCalls.map((tc) => ({ - id: tc.id, - type: "function" as const, - function: { name: tc.name, arguments: JSON.stringify(tc.arguments) }, - })), - } - : {}), - }; -} - -function toOpenAITool(t: ToolSchema) { - return { - type: "function" as const, - function: { - name: t.name, - description: t.description, - parameters: t.parameters, - }, - }; -} - -function safeParseArgs(raw: string): Record { - try { - return JSON.parse(raw) as Record; - } catch { - return { _raw: raw }; - } -} - -// ─── Factory ───────────────────────────────────────────────────────── - -export type ProviderKind = "simulation" | "http" | "zai" | "openai" | "openrouter"; - -export interface ProviderConfig { - provider?: ProviderKind; - simulation?: SimulationResponse[] | SimulationHandler; - apiKey?: string; - baseUrl?: string; - model?: string; -} - -interface HttpPreset { - keyEnv: string[]; - baseUrlEnv: string[]; - defaultBaseUrl: string; - modelEnv: string[]; - defaultModel: string; - extraHeaders?: Record; -} - -const PRESETS: Record<"zai" | "openai" | "openrouter", HttpPreset> = { - zai: { - keyEnv: ["ZAI_API_KEY", "OPENAI_API_KEY"], - baseUrlEnv: ["ZAI_BASE_URL", "OPENAI_BASE_URL"], - defaultBaseUrl: "https://open.bigmodel.cn/api/paas/v4", - modelEnv: ["ZAI_MODEL"], - defaultModel: "glm-4.6", - }, - openai: { - keyEnv: ["OPENAI_API_KEY"], - baseUrlEnv: ["OPENAI_BASE_URL"], - defaultBaseUrl: "https://api.openai.com/v1", - modelEnv: ["OPENAI_MODEL"], - defaultModel: "gpt-4o-mini", - }, - openrouter: { - keyEnv: ["OPENROUTER_API_KEY"], - baseUrlEnv: ["OPENROUTER_BASE_URL"], - defaultBaseUrl: "https://openrouter.ai/api/v1", - modelEnv: ["OPENROUTER_MODEL"], - // OpenRouter uses the vendor/model slug format. "Owl Alpha" maps to a - // slug you configure via OPENROUTER_MODEL; default kept as a sentinel. - defaultModel: "openrouter/owl-alpha", - extraHeaders: { - "HTTP-Referer": process.env.OPENROUTER_REFERER ?? "https://corpos.local", - "X-Title": process.env.OPENROUTER_TITLE ?? "CorpOS", - }, - }, -}; - -function resolveEnv(names: string[]): string | undefined { - for (const n of names) { - const v = process.env[n]; - if (v) return v; - } - return undefined; -} - -export function createProvider(cfg: ProviderConfig = {}): LLMProvider { - const explicit = cfg.provider; - if (explicit === "simulation" || (!explicit && !cfg.simulation && !cfg.apiKey)) { - if (cfg.simulation) return new SimulationProvider(cfg.simulation); - } - - const httpKind: "zai" | "openai" | "openrouter" | undefined = - explicit === "http" ? "openrouter" : (explicit as "zai" | "openai" | "openrouter" | undefined); - - // Determine which preset to use and whether a key is available. - let preset: HttpPreset | undefined; - if (httpKind) { - preset = PRESETS[httpKind]; - } else { - // Auto-detect by whichever key is present (openrouter wins over zai/openai). - preset = - [PRESETS.openrouter, PRESETS.zai, PRESETS.openai].find((p) => - p.keyEnv.some((e) => process.env[e]), - ) ?? undefined; - } - - const apiKey = cfg.apiKey ?? (preset ? resolveEnv(preset.keyEnv) : undefined); - - if (explicit && explicit !== "simulation" && !apiKey) { - throw new Error( - `LLM provider '${explicit}' requested but no API key found (checked: ${preset?.keyEnv.join(", ") ?? "none"}).`, - ); - } - - if (preset && apiKey) { - const baseUrl = cfg.baseUrl ?? resolveEnv(preset.baseUrlEnv) ?? preset.defaultBaseUrl; - const model = cfg.model ?? resolveEnv(preset.modelEnv) ?? preset.defaultModel; - return new HttpLLMProvider(explicit ?? "openrouter", { - apiKey, - baseUrl, - defaultModel: model, - extraHeaders: preset.extraHeaders, - }); - } - - // Fallback: a simulation that always finishes (safe default, no network). - return new SimulationProvider([ - { content: "Simulation provider active (no live LLM key). Finishing.", finishReason: "stop" }, - ]); -} diff --git a/src/core/logger.ts b/src/core/logger.ts deleted file mode 100644 index e88b6df..0000000 --- a/src/core/logger.ts +++ /dev/null @@ -1,86 +0,0 @@ -import type { ISODate, Logger, LogLevel } from "./types"; -import { now } from "./types"; -import type { EventBus } from "./event-bus"; - -interface LogEntry { - level: LogLevel; - scope: string; - msg: string; - meta?: Record; - ts: ISODate; -} - -const ORDER: Record = { - debug: 10, - info: 20, - warn: 30, - error: 40, -}; - -export class CoreLogger implements Logger { - private scope: string; - private minLevel: LogLevel; - private bus?: EventBus; - private sink: (entry: LogEntry) => void; - - constructor( - opts: { - scope?: string; - minLevel?: LogLevel; - bus?: EventBus; - sink?: (entry: LogEntry) => void; - } = {}, - ) { - this.scope = opts.scope ?? "app"; - this.minLevel = opts.minLevel ?? (process.env.LOG_LEVEL as LogLevel) ?? "info"; - this.bus = opts.bus; - this.sink = - opts.sink ?? - ((e) => { - const line = `[${e.ts}] ${e.level.toUpperCase()} ${e.scope}: ${e.msg}`; - const stream = e.level === "error" || e.level === "warn" ? process.stderr : process.stdout; - stream.write(e.meta ? `${line} ${JSON.stringify(e.meta)}\n` : `${line}\n`); - }); - } - - child(scope: string): Logger { - return new CoreLogger({ - scope: this.scope ? `${this.scope}:${scope}` : scope, - minLevel: this.minLevel, - bus: this.bus, - sink: this.sink, - }); - } - - private log(level: LogLevel, msg: string, meta?: Record): void { - if (ORDER[level] < ORDER[this.minLevel]) return; - const entry: LogEntry = { level, scope: this.scope, msg, meta, ts: now() }; - this.sink(entry); - this.bus?.emit("log", entry); - } - - debug(msg: string, meta?: Record): void { - this.log("debug", msg, meta); - } - info(msg: string, meta?: Record): void { - this.log("info", msg, meta); - } - warn(msg: string, meta?: Record): void { - this.log("warn", msg, meta); - } - error(msg: string, meta?: Record): void { - this.log("error", msg, meta); - } - - audit(action: string, meta?: Record): void { - const entry = { action, meta, ts: now(), scope: this.scope }; - this.bus?.emit("audit", entry); - this.sink({ - level: "info", - scope: `audit:${this.scope}`, - msg: action, - meta, - ts: entry.ts, - }); - } -} diff --git a/src/core/memory.ts b/src/core/memory.ts deleted file mode 100644 index e9c080d..0000000 --- a/src/core/memory.ts +++ /dev/null @@ -1,66 +0,0 @@ -import type { Logger, MemoryItem, MemoryKind } from "./types"; -import { now } from "./types"; -import { newId } from "./id"; -import type { Store } from "./store"; - -/** - * Two-tier memory: - * - working: ephemeral per-run scratch (cleared between runs) for the - * reasoning loop's transient notes. - * - long-term: durable facts/notes/decisions in the SQLite store, recalled - * by keyword + tags for future runs. - */ -export class MemoryStore { - private store: Store; - private log: Logger; - private working = new Map(); - - constructor(store: Store, log: Logger) { - this.store = store; - this.log = log; - } - - remember( - item: Omit & Partial>, - ): MemoryItem { - const full: MemoryItem = { - id: item.id ?? newId("mem"), - ts: item.ts ?? now(), - ...item, - }; - this.store.remember(full); - this.log.debug("memory.remember", { kind: full.kind, agentId: full.agentId }); - return full; - } - - recall( - tenantId: string, - agentId: string, - opts: { query?: string; limit?: number; tags?: string[] } = {}, - ): MemoryItem[] { - return this.store.recall(tenantId, agentId, opts); - } - - /** Working memory for the current run only. */ - pushWorking(agentId: string, content: string, kind: MemoryKind = "note"): void { - const list = this.working.get(agentId) ?? []; - list.push({ - id: newId("wm"), - tenantId: "working", - agentId, - kind, - content, - tags: [], - ts: now(), - }); - this.working.set(agentId, list); - } - - getWorking(agentId: string): MemoryItem[] { - return this.working.get(agentId) ?? []; - } - - clearWorking(agentId: string): void { - this.working.delete(agentId); - } -} diff --git a/src/core/orchestrator.ts b/src/core/orchestrator.ts deleted file mode 100644 index ddc19c9..0000000 --- a/src/core/orchestrator.ts +++ /dev/null @@ -1,198 +0,0 @@ -import type { Agent } from "./agent"; -import type { EventBus } from "./event-bus"; -import type { Logger, Task } from "./types"; -import { now } from "./types"; -import type { Store } from "./store"; - -export interface OrchestratorDeps { - store: Store; - bus: EventBus; - log: Logger; - concurrency?: number; - defaultAgentId?: string; -} - -/** - * Owns the agent lifecycle and the dispatch loop. Picks the next queued task, - * binds it to an agent (explicit `assignedTo`, else the default), runs it under - * a concurrency cap, retries on failure with backoff, and resumes tasks that - * paused for human approval. - */ -export class Orchestrator { - private agents = new Map(); - private deps: OrchestratorDeps; - private running = 0; - private concurrency: number; - private tickScheduled = false; - private started = false; - - constructor(deps: OrchestratorDeps) { - this.deps = deps; - this.concurrency = deps.concurrency ?? 4; - } - - register(agent: Agent): this { - this.agents.set(agent.id, agent); - return this; - } - - getAgents(): Agent[] { - return [...this.agents.values()]; - } - - agent(id: string): Agent | undefined { - return this.agents.get(id); - } - - start(): void { - this.started = true; - this.deps.log.info("orchestrator.started", { - agents: this.agents.size, - concurrency: this.concurrency, - }); - this.schedule(); - } - - stop(): void { - this.started = false; - } - - async enqueue(task: Task): Promise { - this.deps.store.insertTask(task); - await this.deps.bus.emit( - "task.queued", - { taskId: task.id, title: task.title }, - { source: task.createdBy, tenantId: task.tenantId, taskId: task.id }, - ); - this.schedule(); - return task; - } - - private schedule(): void { - if (!this.started) return; - if (this.tickScheduled) return; - this.tickScheduled = true; - queueMicrotask(() => { - this.tickScheduled = false; - void this.tick(); - }); - } - - private async tick(): Promise { - while (this.started && this.running < this.concurrency) { - const task = this.deps.store.nextQueued(); - if (!task) break; - const agent = this.resolveAgent(task); - if (!agent) { - this.deps.store.updateTaskState(task.id, { - state: "failed", - finishedAt: now(), - error: `no agent available for assignment '${task.assignedTo ?? "(none)"}'`, - }); - await this.deps.bus.emit( - "task.unassigned", - { taskId: task.id }, - { source: "orchestrator", tenantId: task.tenantId, taskId: task.id }, - ); - continue; - } - this.running++; - void this.runTask(task, agent); - } - } - - private resolveAgent(task: Task): Agent | undefined { - if (task.assignedTo) return this.agents.get(task.assignedTo); - if (this.deps.defaultAgentId) return this.agents.get(this.deps.defaultAgentId); - const first = this.getAgents()[0]; - return first; - } - - private async runTask(task: Task, agent: Agent): Promise { - const { store, bus, log } = this.deps; - const attempts = task.attempts + 1; - store.updateTaskState(task.id, { state: "assigned", assignedTo: agent.id, attempts }); - await bus.emit( - "task.assigned", - { taskId: task.id, agentId: agent.id }, - { source: "orchestrator", tenantId: task.tenantId, taskId: task.id, agentId: agent.id }, - ); - - try { - const result = await agent.run(task); - if (result.awaitingApprovalId) { - // Paused for approval; orchestrator resumes on decide(). - log.info("task.awaiting_approval", { - taskId: task.id, - approvalId: result.awaitingApprovalId, - }); - } else if (!result.ok) { - await this.maybeRetry(task, result.error ?? "run failed"); - } - } catch (err) { - const msg = err instanceof Error ? err.message : String(err); - log.error("task.threw", { taskId: task.id, error: msg }); - await this.maybeRetry(task, msg); - } finally { - this.running--; - this.schedule(); - } - } - - private async maybeRetry(task: Task, error: string): Promise { - const current = this.deps.store.getTask(task.id); - const attempts = current?.attempts ?? 1; - if (attempts < task.maxAttempts) { - this.deps.store.updateTaskState(task.id, { state: "queued", error }); - const backoff = Math.min(2000 * 2 ** (attempts - 1), 10_000); - this.deps.log.warn("task.retrying", { taskId: task.id, attempt: attempts + 1, backoff }); - setTimeout(() => this.schedule(), backoff); - } else { - this.deps.store.updateTaskState(task.id, { state: "failed", finishedAt: now(), error }); - await this.deps.bus.emit( - "task.failed", - { taskId: task.id, error }, - { source: "orchestrator", tenantId: task.tenantId, taskId: task.id }, - ); - } - } - - /** Resume a task paused on an approval gate, after a human decision. */ - async resume(approvalId: string): Promise { - const approval = this.deps.store.getApproval(approvalId); - if (!approval || approval.state === "pending") return; - const task = this.deps.store.getTask(approval.taskId); - if (!task || task.state !== "awaiting_approval") return; - const agent = this.agents.get(approval.agentId); - if (!agent) return; - - this.running++; - try { - const result = await agent.run(task, { approvalId }); - if (!result.ok && !result.awaitingApprovalId) { - await this.maybeRetry(task, result.error ?? "run failed after approval"); - } - } finally { - this.running--; - this.schedule(); - } - } - - /** Submit a task and await its terminal state (used by tests/scenarios). */ - async runToCompletion(task: Task, opts: { timeoutMs?: number } = {}): Promise { - if (!this.deps.store.getTask(task.id)) await this.enqueue(task); - const deadline = Date.now() + (opts.timeoutMs ?? 15_000); - while (Date.now() < deadline) { - const t = this.deps.store.getTask(task.id); - if (!t) return undefined; - if (t.state === "succeeded" || t.state === "failed" || t.state === "cancelled") return t; - if (t.state === "awaiting_approval") return t; - await sleep(25); - } - return this.deps.store.getTask(task.id); - } -} - -function sleep(ms: number): Promise { - return new Promise((r) => setTimeout(r, ms)); -} diff --git a/src/core/policy.ts b/src/core/policy.ts deleted file mode 100644 index 06da40a..0000000 --- a/src/core/policy.ts +++ /dev/null @@ -1,175 +0,0 @@ -import type { Approval, Effect, Logger, PolicyDecision, PolicyRule, Tool } from "./types"; -export type { PolicyRule } from "./types"; -import { newId } from "./id"; -import { EventBus } from "./event-bus"; -import type { Store } from "./store"; - -export interface PolicyOptions { - defaultEffect?: Effect; -} - -/** - * The policy engine is the single chokepoint every consequential action - * passes through. It evaluates a tool call against: - * 1. explicit rules (glob-matched on tool name, highest priority wins) - * 2. the tool's own permission (category / requiresApproval / costCap) - * 3. spend caps checked against the spend ledger - * and returns allow / deny / approve. - */ -export class PolicyEngine { - private rules: PolicyRule[] = []; - private store: Store; - private bus: EventBus; - private log: Logger; - private defaultEffect: Effect; - - constructor(store: Store, bus: EventBus, log: Logger, opts: PolicyOptions = {}) { - this.store = store; - this.bus = bus; - this.log = log; - this.defaultEffect = opts.defaultEffect ?? "allow"; - } - - addRule(rule: PolicyRule): void { - this.rules.push({ priority: 0, ...rule }); - this.rules.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0)); - } - - setRules(rules: PolicyRule[]): void { - this.rules = rules.map((r) => ({ priority: 0, ...r })); - this.rules.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0)); - } - - listRules(): PolicyRule[] { - return [...this.rules]; - } - - evaluate( - tool: Tool, - args: Record, - ctx: { tenantId: string; agentId: string; taskId: string }, - ): PolicyDecision { - const matched = this.matchingRules(tool.name); - const explicit = matched[0]; - - // 1. Explicit rule (highest priority) can force allow/deny/approve. - if (explicit && explicit.effect !== undefined) { - if (explicit.effect === "deny") { - return { effect: "deny", reason: explicit.reason ?? `Rule denies ${tool.name}` }; - } - } - - // 2. Deny destructive system tools unless explicitly allowed. - if (tool.permission.category === "system" && tool.permission.requiresApproval) { - return this.requireApproval(tool, args, ctx, "system tool requires approval"); - } - - // 3. Spend gating. - if (tool.permission.category === "spend" || explicit?.spendCapPerRun !== undefined) { - const cap = explicit?.spendCapPerRun ?? tool.permission.costCap ?? Infinity; - if (cap !== Infinity) { - const spent = this.store.spendForTask(ctx.tenantId, ctx.taskId); - const intended = extractCost(args) ?? 0; - if (spent + intended > cap) { - return { - effect: "deny", - reason: `spend cap ${cap} exceeded (spent ${spent}, intending ${intended})`, - }; - } - } - } - - // 4. Tools that require approval. - if (tool.permission.requiresApproval) { - return this.requireApproval(tool, args, ctx, `${tool.name} requires human approval`); - } - - // 5. Explicit approve rule. - if (explicit?.effect === "approve") { - return this.requireApproval( - tool, - args, - ctx, - explicit.reason ?? `${tool.name} requires approval`, - ); - } - - return { effect: this.defaultEffect, reason: "permitted by default policy" }; - } - - private matchingRules(toolName: string): PolicyRule[] { - return this.rules.filter((r) => globMatch(r.tool, toolName)); - } - - private requireApproval( - tool: Tool, - args: Record, - ctx: { tenantId: string; agentId: string; taskId: string }, - reason: string, - ): PolicyDecision { - const approval: Approval = { - id: newId("appr"), - tenantId: ctx.tenantId, - agentId: ctx.agentId, - taskId: ctx.taskId, - tool: tool.name, - args, - reason, - state: "pending", - cost: - tool.permission.costCap !== undefined - ? { amount: extractCost(args) ?? 0, currency: "USD" } - : undefined, - createdAt: new Date().toISOString(), - }; - this.store.insertApproval(approval); - void this.bus.emit("approval.requested", approval, { - source: "policy", - tenantId: ctx.tenantId, - taskId: ctx.taskId, - agentId: ctx.agentId, - }); - this.log.audit("approval.requested", { approvalId: approval.id, tool: tool.name }); - return { effect: "approve", reason, approvalId: approval.id }; - } - - /** Resolve a pending approval. Returns the resulting state. */ - decide( - approvalId: string, - decision: "approved" | "rejected", - decidedBy: string, - ): Approval | undefined { - const approval = this.store.getApproval(approvalId); - if (!approval || approval.state !== "pending") return approval; - this.store.setApprovalState(approvalId, decision, decidedBy); - void this.bus.emit( - `approval.${decision}`, - { approvalId, tool: approval.tool }, - { - source: "human", - tenantId: approval.tenantId, - taskId: approval.taskId, - agentId: approval.agentId, - }, - ); - this.log.audit(`approval.${decision}`, { approvalId, decidedBy }); - return { ...approval, state: decision, decidedAt: new Date().toISOString(), decidedBy }; - } -} - -function extractCost(args: Record): number | undefined { - const v = args["amount"] ?? args["cost"]; - if (typeof v === "number") return v; - return undefined; -} - -export function globMatch(pattern: string, name: string): boolean { - if (pattern === name) return true; - if (pattern === "*") return true; - // Support "billing.*" style single-segment prefix globs. - if (pattern.endsWith(".*")) { - const prefix = pattern.slice(0, -2); - return name === prefix || name.startsWith(`${prefix}.`); - } - return false; -} diff --git a/src/core/store.ts b/src/core/store.ts deleted file mode 100644 index fa869da..0000000 --- a/src/core/store.ts +++ /dev/null @@ -1,513 +0,0 @@ -import Database from "better-sqlite3"; -import { mkdirSync } from "node:fs"; -import { dirname, resolve } from "node:path"; -import type { - Approval, - ApprovalState, - Event, - MemoryItem, - SpendRecord, - Task, - TaskResult, - TaskState, -} from "./types"; -import { now } from "./types"; -import { newId } from "./id"; - -export interface StoreOptions { - path?: string; - inMemory?: boolean; -} - -const SCHEMA = ` -CREATE TABLE IF NOT EXISTS tasks ( - id TEXT PRIMARY KEY, - tenant_id TEXT NOT NULL, - title TEXT NOT NULL, - description TEXT NOT NULL, - assigned_to TEXT, - created_by TEXT NOT NULL, - state TEXT NOT NULL, - priority INTEGER NOT NULL DEFAULT 0, - depends_on TEXT, - input TEXT, - output TEXT, - attempts INTEGER NOT NULL DEFAULT 0, - max_attempts INTEGER NOT NULL DEFAULT 3, - created_at TEXT NOT NULL, - started_at TEXT, - finished_at TEXT, - error TEXT -); - -CREATE TABLE IF NOT EXISTS events ( - id TEXT PRIMARY KEY, - type TEXT NOT NULL, - ts TEXT NOT NULL, - source TEXT NOT NULL, - tenant_id TEXT, - task_id TEXT, - agent_id TEXT, - payload TEXT NOT NULL -); -CREATE INDEX IF NOT EXISTS idx_events_type ON events(type); -CREATE INDEX IF NOT EXISTS idx_events_ts ON events(ts); - -CREATE TABLE IF NOT EXISTS approvals ( - id TEXT PRIMARY KEY, - tenant_id TEXT NOT NULL, - agent_id TEXT NOT NULL, - task_id TEXT NOT NULL, - tool TEXT NOT NULL, - args TEXT NOT NULL, - reason TEXT NOT NULL, - state TEXT NOT NULL, - cost TEXT, - created_at TEXT NOT NULL, - decided_at TEXT, - decided_by TEXT -); - -CREATE TABLE IF NOT EXISTS spend ( - id TEXT PRIMARY KEY, - tenant_id TEXT NOT NULL, - agent_id TEXT NOT NULL, - task_id TEXT NOT NULL, - tool TEXT NOT NULL, - amount REAL NOT NULL, - currency TEXT NOT NULL, - ref TEXT NOT NULL, - ts TEXT NOT NULL -); -CREATE INDEX IF NOT EXISTS idx_spend_tenant_task ON spend(tenant_id, task_id); - -CREATE TABLE IF NOT EXISTS memory ( - id TEXT PRIMARY KEY, - tenant_id TEXT NOT NULL, - agent_id TEXT NOT NULL, - kind TEXT NOT NULL, - content TEXT NOT NULL, - tags TEXT NOT NULL, - ts TEXT NOT NULL -); -CREATE INDEX IF NOT EXISTS idx_memory_agent ON memory(tenant_id, agent_id); -`; - -type TaskRow = { - id: string; - tenant_id: string; - title: string; - description: string; - assigned_to: string | null; - created_by: string; - state: string; - priority: number; - depends_on: string | null; - input: string | null; - output: string | null; - attempts: number; - max_attempts: number; - created_at: string; - started_at: string | null; - finished_at: string | null; - error: string | null; -}; - -function rowToTask(r: TaskRow): Task { - return { - id: r.id, - tenantId: r.tenant_id, - title: r.title, - description: r.description, - assignedTo: r.assigned_to ?? undefined, - createdBy: r.created_by, - state: r.state as TaskState, - priority: r.priority, - dependsOn: r.depends_on ? (JSON.parse(r.depends_on) as string[]) : undefined, - input: r.input ? (JSON.parse(r.input) as Record) : undefined, - output: r.output ? (JSON.parse(r.output) as TaskResult) : undefined, - attempts: r.attempts, - maxAttempts: r.max_attempts, - createdAt: r.created_at, - startedAt: r.started_at ?? undefined, - finishedAt: r.finished_at ?? undefined, - error: r.error ?? undefined, - }; -} - -export class Store { - readonly db: Database.Database; - readonly path: string; - - constructor(opts: StoreOptions = {}) { - if (opts.inMemory) { - this.db = new Database(":memory:"); - this.path = ":memory:"; - } else { - this.path = resolve(opts.path ?? "data/company.db"); - mkdirSync(dirname(this.path), { recursive: true }); - this.db = new Database(this.path); - } - this.db.pragma("journal_mode = WAL"); - this.db.pragma("foreign_keys = ON"); - this.db.exec(SCHEMA); - } - - // ── Tasks ────────────────────────────────────────────────────────── - insertTask(task: Task): void { - this.db - .prepare( - `INSERT INTO tasks - (id, tenant_id, title, description, assigned_to, created_by, state, - priority, depends_on, input, output, attempts, max_attempts, - created_at, started_at, finished_at, error) - VALUES (@id,@tenant_id,@title,@description,@assigned_to,@created_by,@state, - @priority,@depends_on,@input,@output,@attempts,@max_attempts, - @created_at,@started_at,@finished_at,@error)`, - ) - .run({ - id: task.id, - tenant_id: task.tenantId, - title: task.title, - description: task.description, - assigned_to: task.assignedTo ?? null, - created_by: task.createdBy, - state: task.state, - priority: task.priority, - depends_on: task.dependsOn ? JSON.stringify(task.dependsOn) : null, - input: task.input ? JSON.stringify(task.input) : null, - output: null, - attempts: task.attempts, - max_attempts: task.maxAttempts, - created_at: task.createdAt, - started_at: task.startedAt ?? null, - finished_at: task.finishedAt ?? null, - error: task.error ?? null, - }); - } - - updateTaskState( - id: string, - patch: Partial< - Pick< - Task, - "state" | "assignedTo" | "attempts" | "startedAt" | "finishedAt" | "error" | "output" - > - >, - ): void { - const sets: string[] = ["state = @state"]; - const params: Record = { id, state: patch.state }; - if (patch.assignedTo !== undefined) { - sets.push("assigned_to = @assigned_to"); - params.assigned_to = patch.assignedTo; - } - if (patch.attempts !== undefined) { - sets.push("attempts = @attempts"); - params.attempts = patch.attempts; - } - if (patch.startedAt !== undefined) { - sets.push("started_at = @started_at"); - params.started_at = patch.startedAt; - } - if (patch.finishedAt !== undefined) { - sets.push("finished_at = @finished_at"); - params.finished_at = patch.finishedAt; - } - if (patch.error !== undefined) { - sets.push("error = @error"); - params.error = patch.error; - } - if (patch.output !== undefined) { - sets.push("output = @output"); - params.output = JSON.stringify(patch.output); - } - this.db.prepare(`UPDATE tasks SET ${sets.join(", ")} WHERE id = @id`).run(params); - } - - getTask(id: string): Task | undefined { - const row = this.db.prepare("SELECT * FROM tasks WHERE id = ?").get(id) as TaskRow | undefined; - return row ? rowToTask(row) : undefined; - } - - listTasks(state?: TaskState): Task[] { - const rows = state - ? (this.db - .prepare("SELECT * FROM tasks WHERE state = ? ORDER BY created_at") - .all(state) as TaskRow[]) - : (this.db.prepare("SELECT * FROM tasks ORDER BY created_at").all() as TaskRow[]); - return rows.map(rowToTask); - } - - nextQueued(): Task | undefined { - const row = this.db - .prepare( - `SELECT * FROM tasks - WHERE state = 'queued' - ORDER BY priority DESC, created_at - LIMIT 1`, - ) - .get() as TaskRow | undefined; - return row ? rowToTask(row) : undefined; - } - - // ── Events ───────────────────────────────────────────────────────── - insertEvent(e: Event): void { - this.db - .prepare( - `INSERT INTO events (id, type, ts, source, tenant_id, task_id, agent_id, payload) - VALUES (@id,@type,@ts,@source,@tenant_id,@task_id,@agent_id,@payload)`, - ) - .run({ - id: e.id, - type: e.type, - ts: e.ts, - source: e.source, - tenant_id: e.tenantId ?? null, - task_id: e.taskId ?? null, - agent_id: e.agentId ?? null, - payload: JSON.stringify(e.payload), - }); - } - - recentEvents(limit = 100): Event[] { - const rows = this.db - .prepare("SELECT * FROM events ORDER BY ts DESC LIMIT ?") - .all(limit) as Array<{ - id: string; - type: string; - ts: string; - source: string; - tenant_id: string | null; - task_id: string | null; - agent_id: string | null; - payload: string; - }>; - return rows.map((r) => ({ - id: r.id, - type: r.type, - ts: r.ts, - source: r.source, - payload: JSON.parse(r.payload), - tenantId: r.tenant_id ?? undefined, - taskId: r.task_id ?? undefined, - agentId: r.agent_id ?? undefined, - })); - } - - // ── Approvals ────────────────────────────────────────────────────── - insertApproval(a: Approval): void { - this.db - .prepare( - `INSERT INTO approvals - (id, tenant_id, agent_id, task_id, tool, args, reason, state, cost, - created_at, decided_at, decided_by) - VALUES (@id,@tenant_id,@agent_id,@task_id,@tool,@args,@reason,@state,@cost, - @created_at,@decided_at,@decided_by)`, - ) - .run({ - id: a.id, - tenant_id: a.tenantId, - agent_id: a.agentId, - task_id: a.taskId, - tool: a.tool, - args: JSON.stringify(a.args), - reason: a.reason, - state: a.state, - cost: a.cost ? JSON.stringify(a.cost) : null, - created_at: a.createdAt, - decided_at: a.decidedAt ?? null, - decided_by: a.decidedBy ?? null, - }); - } - - setApprovalState(id: string, state: ApprovalState, decidedBy: string): void { - this.db - .prepare( - `UPDATE approvals SET state = @state, decided_at = @decided_at, - decided_by = @decided_by WHERE id = @id`, - ) - .run({ id, state, decided_at: now(), decided_by: decidedBy }); - } - - getApproval(id: string): Approval | undefined { - const r = this.db.prepare("SELECT * FROM approvals WHERE id = ?").get(id) as - | { - id: string; - tenant_id: string; - agent_id: string; - task_id: string; - tool: string; - args: string; - reason: string; - state: string; - cost: string | null; - created_at: string; - decided_at: string | null; - decided_by: string | null; - } - | undefined; - if (!r) return undefined; - return { - id: r.id, - tenantId: r.tenant_id, - agentId: r.agent_id, - taskId: r.task_id, - tool: r.tool, - args: JSON.parse(r.args), - reason: r.reason, - state: r.state as ApprovalState, - cost: r.cost ? JSON.parse(r.cost) : undefined, - createdAt: r.created_at, - decidedAt: r.decided_at ?? undefined, - decidedBy: r.decided_by ?? undefined, - }; - } - - pendingApprovals(): Approval[] { - const rows = this.db - .prepare("SELECT * FROM approvals WHERE state = 'pending' ORDER BY created_at") - .all() as Array<{ - id: string; - tenant_id: string; - agent_id: string; - task_id: string; - tool: string; - args: string; - reason: string; - state: string; - cost: string | null; - created_at: string; - decided_at: string | null; - decided_by: string | null; - }>; - return rows.map((r) => ({ - id: r.id, - tenantId: r.tenant_id, - agentId: r.agent_id, - taskId: r.task_id, - tool: r.tool, - args: JSON.parse(r.args), - reason: r.reason, - state: r.state as ApprovalState, - cost: r.cost ? JSON.parse(r.cost) : undefined, - createdAt: r.created_at, - decidedAt: r.decided_at ?? undefined, - decidedBy: r.decided_by ?? undefined, - })); - } - - // ── Spend ────────────────────────────────────────────────────────── - recordSpend(s: SpendRecord): void { - this.db - .prepare( - `INSERT INTO spend (id, tenant_id, agent_id, task_id, tool, amount, currency, ref, ts) - VALUES (@id,@tenant_id,@agent_id,@task_id,@tool,@amount,@currency,@ref,@ts)`, - ) - .run({ - id: s.id, - tenant_id: s.tenantId, - agent_id: s.agentId, - task_id: s.taskId, - tool: s.tool, - amount: s.amount, - currency: s.currency, - ref: s.ref, - ts: s.ts, - }); - } - - spendForTask(tenantId: string, taskId: string): number { - const row = this.db - .prepare( - `SELECT COALESCE(SUM(amount), 0) AS total FROM spend - WHERE tenant_id = ? AND task_id = ?`, - ) - .get(tenantId, taskId) as { total: number }; - return row.total; - } - - // ── Memory ───────────────────────────────────────────────────────── - remember(m: MemoryItem): void { - this.db - .prepare( - `INSERT INTO memory (id, tenant_id, agent_id, kind, content, tags, ts) - VALUES (@id,@tenant_id,@agent_id,@kind,@content,@tags,@ts)`, - ) - .run({ - id: m.id, - tenant_id: m.tenantId, - agent_id: m.agentId, - kind: m.kind, - content: m.content, - tags: JSON.stringify(m.tags), - ts: m.ts, - }); - } - - recall( - tenantId: string, - agentId: string, - opts: { query?: string; limit?: number; tags?: string[] } = {}, - ): MemoryItem[] { - const limit = opts.limit ?? 25; - let sql = `SELECT * FROM memory WHERE tenant_id = ? AND agent_id = ?`; - const params: unknown[] = [tenantId, agentId]; - if (opts.tags && opts.tags.length) { - sql += ` AND ${opts.tags.map(() => `tags LIKE ?`).join(" OR ")}`; - for (const t of opts.tags) params.push(`%"${t}"%`); - } - if (opts.query) { - sql += ` AND content LIKE ?`; - params.push(`%${opts.query}%`); - } - sql += ` ORDER BY ts DESC LIMIT ?`; - params.push(limit); - const rows = this.db.prepare(sql).all(...params) as Array<{ - id: string; - tenant_id: string; - agent_id: string; - kind: string; - content: string; - tags: string; - ts: string; - }>; - return rows.map((r) => ({ - id: r.id, - tenantId: r.tenant_id, - agentId: r.agent_id, - kind: r.kind as MemoryItem["kind"], - content: r.content, - tags: JSON.parse(r.tags), - ts: r.ts, - })); - } - - close(): void { - this.db.close(); - } -} - -export const newTask = (init: { - tenantId: string; - title: string; - description: string; - createdBy?: string; - assignedTo?: string; - priority?: number; - input?: Record; - maxAttempts?: number; - dependsOn?: string[]; -}): Task => ({ - id: newId("task"), - tenantId: init.tenantId, - title: init.title, - description: init.description, - createdBy: init.createdBy ?? "system", - assignedTo: init.assignedTo, - state: "queued", - priority: init.priority ?? 0, - input: init.input, - attempts: 0, - maxAttempts: init.maxAttempts ?? 3, - createdAt: now(), -}); diff --git a/src/core/tool-builder.ts b/src/core/tool-builder.ts deleted file mode 100644 index 32e1aa8..0000000 --- a/src/core/tool-builder.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { Tool, ToolCategory, ToolHandler, ToolPermission } from "./types"; - -export interface ToolSpec { - name: string; - description: string; - /** JSON schema describing the parameters the agent may pass. */ - parameters: Record; - permission: Partial & { category: ToolCategory }; - execute: ToolHandler; -} - -/** - * Define a tool concisely. `category` is required; `costCap` and - * `requiresApproval` default based on category (spend/communicate default - * to approval; read defaults to no approval). - */ -export function defineTool(spec: ToolSpec): Tool { - const permission: ToolPermission = normalizePermission(spec.permission); - return { - name: spec.name, - description: spec.description, - parameters: spec.parameters, - permission, - execute: spec.execute, - }; -} - -function normalizePermission(p: ToolSpec["permission"]): ToolPermission { - const requiresApproval = - p.requiresApproval ?? (p.category === "spend" || p.category === "communicate"); - return { - category: p.category, - costCap: p.costCap, - requiresApproval, - }; -} diff --git a/src/core/tool.ts b/src/core/tool.ts deleted file mode 100644 index 438eecb..0000000 --- a/src/core/tool.ts +++ /dev/null @@ -1,155 +0,0 @@ -import type { Tool, ToolContext, ToolResult, ToolSchema } from "./types"; - -export { defineTool } from "./tool-builder"; - -export class ToolRegistry { - private tools = new Map(); - private aliases = new Map(); - - register(tool: Tool): this { - if (this.tools.has(tool.name)) { - throw new Error(`Tool already registered: ${tool.name}`); - } - this.tools.set(tool.name, tool); - return this; - } - - registerAll(tools: Tool[]): this { - for (const t of tools) this.register(t); - return this; - } - - /** Register a name that maps to an existing tool (e.g. a department alias). */ - alias(alias: string, canonical: string): this { - this.aliases.set(alias, canonical); - return this; - } - - get(name: string): Tool | undefined { - const canonical = this.aliases.get(name) ?? name; - return this.tools.get(canonical); - } - - has(name: string): boolean { - return this.get(name) !== undefined; - } - - list(): Tool[] { - return [...this.tools.values()]; - } - - /** Build the tool schemas the LLM should see for a given subset of names. */ - schemasFor(names: string[]): ToolSchema[] { - const out: ToolSchema[] = []; - for (const name of names) { - const tool = this.get(name); - if (tool) out.push(schemaOf(tool)); - } - return out; - } - - async invoke(name: string, args: Record, ctx: ToolContext): Promise { - const tool = this.get(name); - if (!tool) return { ok: false, error: `Unknown tool: ${name}` }; - const validation = validateArgs(tool, args); - if (!validation.ok) { - return { ok: false, error: `Invalid arguments for ${name}: ${validation.error}` }; - } - try { - return await tool.execute(args, ctx); - } catch (err) { - const msg = err instanceof Error ? err.message : String(err); - return { ok: false, error: `Tool ${name} threw: ${msg}` }; - } - } -} - -export function schemaOf(tool: Tool): ToolSchema { - return { - name: tool.name, - description: tool.description, - parameters: tool.parameters, - }; -} - -// ─── Minimal JSON-schema validator ─────────────────────────────────── -// Covers the subset tools actually use: type, required, properties, -// enum, items, additionalProperties. Avoids pulling ajv as a dependency. - -type ValidationResult = { ok: true } | { ok: false; error: string }; - -export function validateArgs(tool: Tool, args: Record): ValidationResult { - return validateValue(args, tool.parameters, "root"); -} - -function validateValue( - value: unknown, - schema: Record, - path: string, -): ValidationResult { - if (typeof schema !== "object" || schema === null) return { ok: true }; - - if (schema["type"] !== undefined) { - if (!matchesType(value, schema["type"] as string)) { - return { ok: false, error: `${path}: expected ${schema["type"]}, got ${typeof value}` }; - } - } - - if (schema["enum"] !== undefined) { - const allowed = schema["enum"] as unknown[]; - if (!allowed.includes(value)) { - return { - ok: false, - error: `${path}: ${JSON.stringify(value)} not in enum ${JSON.stringify(allowed)}`, - }; - } - } - - if (schema["type"] === "object" && typeof value === "object" && value !== null) { - const obj = value as Record; - const required = (schema["required"] as string[] | undefined) ?? []; - for (const key of required) { - if (!(key in obj)) return { ok: false, error: `${path}: missing required '${key}'` }; - } - const props = - (schema["properties"] as Record> | undefined) ?? {}; - for (const [key, sub] of Object.entries(props)) { - if (key in obj) { - const r = validateValue(obj[key], sub, `${path}.${key}`); - if (!r.ok) return r; - } - } - } - - if (schema["type"] === "array" && Array.isArray(value)) { - const items = schema["items"] as Record | undefined; - if (items) { - for (let i = 0; i < value.length; i++) { - const r = validateValue(value[i], items, `${path}[${i}]`); - if (!r.ok) return r; - } - } - } - - return { ok: true }; -} - -function matchesType(value: unknown, type: string): boolean { - switch (type) { - case "string": - return typeof value === "string"; - case "number": - case "integer": - return typeof value === "number" && (type !== "integer" || Number.isInteger(value)); - case "boolean": - return typeof value === "boolean"; - case "object": - return typeof value === "object" && value !== null && !Array.isArray(value); - case "array": - return Array.isArray(value); - case "null": - return value === null; - default: - return true; - } -} diff --git a/src/core/types.ts b/src/core/types.ts deleted file mode 100644 index 37aff6b..0000000 --- a/src/core/types.ts +++ /dev/null @@ -1,238 +0,0 @@ -export type ID = string; -export type ISODate = string; - -export const now = (): ISODate => new Date().toISOString(); - -// ─── Events ────────────────────────────────────────────────────────── -export interface Event { - id: ID; - type: string; - ts: ISODate; - source: string; - payload: T; - tenantId?: ID; - taskId?: ID; - agentId?: ID; -} - -export type EventHandler = (event: Event) => void | Promise; - -// ─── LLM ───────────────────────────────────────────────────────────── -export interface ChatMessage { - role: "system" | "user" | "assistant" | "tool"; - content: string; - name?: string; - toolCallId?: string; - toolCalls?: ToolCall[]; -} - -export interface ToolCall { - id: ID; - name: string; - arguments: Record; -} - -export interface ToolSchema { - name: string; - description: string; - parameters: Record; -} - -export interface LLMRequest { - messages: ChatMessage[]; - tools?: ToolSchema[]; - temperature?: number; - maxTokens?: number; - model?: string; -} - -export interface LLMResponse { - message: ChatMessage; - toolCalls: ToolCall[]; - finishReason: "stop" | "tool_calls" | "length"; - usage: { promptTokens: number; completionTokens: number }; -} - -export interface LLMProvider { - readonly id: string; - complete(request: LLMRequest): Promise; -} - -// ─── Tools ─────────────────────────────────────────────────────────── -export type ToolCategory = "read" | "write" | "spend" | "communicate" | "system" | "delegate"; - -export interface ToolPermission { - category: ToolCategory; - costCap?: number; - requiresApproval?: boolean; -} - -export interface ToolContext { - agentId: ID; - taskId: ID; - tenantId: ID; - logger: Logger; -} - -export interface ToolResult { - ok: boolean; - data?: unknown; - error?: string; - /** Cost incurred by this call, in the tool's currency (for spend-gated tools). */ - cost?: { amount: number; currency: string }; - /** Human-readable note surfaced back into the agent's reasoning context. */ - note?: string; -} - -export type ToolHandler = (args: Record, ctx: ToolContext) => Promise; - -export interface Tool { - name: string; - description: string; - parameters: Record; - permission: ToolPermission; - execute: ToolHandler; -} - -// ─── Policy ────────────────────────────────────────────────────────── -export type Effect = "allow" | "deny" | "approve"; - -export interface PolicyDecision { - effect: Effect; - reason: string; - approvalId?: ID; -} - -export interface PolicyRule { - id: ID; - /** Glob over tool name, e.g. "billing.*" or "crm.update_contact". */ - tool: string; - effect: Effect; - reason?: string; - /** Per-(tenant,category) spend ceiling checked against the spend ledger. */ - spendCapPerRun?: number; - priority?: number; -} - -export interface SpendRecord { - id: ID; - tenantId: ID; - agentId: ID; - taskId: ID; - tool: string; - amount: number; - currency: string; - ref: string; - ts: ISODate; -} - -// ─── Approvals ─────────────────────────────────────────────────────── -export type ApprovalState = "pending" | "approved" | "rejected" | "expired"; - -export interface Approval { - id: ID; - tenantId: ID; - agentId: ID; - taskId: ID; - tool: string; - args: Record; - reason: string; - state: ApprovalState; - cost?: { amount: number; currency: string }; - createdAt: ISODate; - decidedAt?: ISODate; - decidedBy?: string; -} - -// ─── Memory ────────────────────────────────────────────────────────── -export type MemoryKind = "fact" | "event" | "note" | "decision"; - -export interface MemoryItem { - id: ID; - tenantId: ID; - agentId: ID; - kind: MemoryKind; - content: string; - tags: string[]; - ts: ISODate; -} - -// ─── Tasks ─────────────────────────────────────────────────────────── -export type TaskState = - "queued" | "assigned" | "running" | "awaiting_approval" | "succeeded" | "failed" | "cancelled"; - -export interface TaskResult { - summary: string; - artifacts?: Record; -} - -export interface Task { - id: ID; - tenantId: ID; - title: string; - description: string; - assignedTo?: ID; - createdBy: string; - state: TaskState; - priority: number; - dependsOn?: ID[]; - input?: Record; - output?: TaskResult; - attempts: number; - maxAttempts: number; - createdAt: ISODate; - startedAt?: ISODate; - finishedAt?: ISODate; - error?: string; -} - -// ─── Agents ────────────────────────────────────────────────────────── -export type AgentState = "idle" | "busy" | "awaiting" | "stopped"; - -export interface AgentDefinition { - id: ID; - name: string; - role: string; - systemPrompt: string; - tools: string[]; - model?: string; - maxSteps?: number; - tenantId?: ID; -} - -export interface AgentRunStep { - ts: ISODate; - thought?: string; - toolCalls?: ToolCall[]; - results?: ToolResult[]; - policyDecisions?: Array<{ tool: string; decision: PolicyDecision }>; -} - -export interface AgentRunResult { - taskId: ID; - ok: boolean; - steps: AgentRunStep[]; - result?: TaskResult; - error?: string; - awaitingApprovalId?: ID; -} - -// ─── Logger ────────────────────────────────────────────────────────── -export type LogLevel = "debug" | "info" | "warn" | "error"; - -export interface Logger { - child(scope: string): Logger; - debug(msg: string, meta?: Record): void; - info(msg: string, meta?: Record): void; - warn(msg: string, meta?: Record): void; - error(msg: string, meta?: Record): void; - audit(action: string, meta?: Record): void; -} - -// ─── Tenancy ───────────────────────────────────────────────────────── -export interface Tenant { - id: ID; - name: string; - /** Default spend caps per category for this tenant. */ - spendCaps?: Partial>; -} diff --git a/src/dashboard/public/index.html b/src/dashboard/public/index.html deleted file mode 100644 index 9abf9d2..0000000 --- a/src/dashboard/public/index.html +++ /dev/null @@ -1,1348 +0,0 @@ - - - - - - CorpOS · control plane - - - -
-
- - CorpOS control plane -
-
- simulation - spend $0.00 - connecting… - -
-
- -
-
-
Tasks
-
0
-
-
-
Running
-
0
-
-
-
Awaiting
-
0
-
-
-
Succeeded
-
0
-
-
-
Approvals
-
0
-
-
-
Agents
-
0
-
-
- -
- -
- - -
-
-
Agents 0
-
-
no agents registered
-
-
- -
-
-
Tasks 0
-
-
no tasks yet
-
-
-
-
Activity feed
-
-
waiting for events…
-
-
-
-
-
- - - - diff --git a/src/index.ts b/src/index.ts deleted file mode 100644 index 5868f8b..0000000 --- a/src/index.ts +++ /dev/null @@ -1,183 +0,0 @@ -import { readFileSync } from "node:fs"; -import { createCompany, SimulationProvider, createProvider } from "./core"; -import { agentDefinitions, createAgent } from "./agents"; -import { allTools } from "./tools"; -import { createHttpServer } from "./api"; - -/** - * Minimal .env loader — populates process.env from a local .env file if - * present, without adding a dependency or editing the npm manifest. Real - * environment values always win (never overwrite with file values). - */ -function loadEnvFile(path = ".env"): void { - let text: string; - try { - text = readFileSync(path, "utf8"); - } catch { - return; - } - for (const rawLine of text.split("\n")) { - const line = rawLine.trim(); - if (!line || line.startsWith("#")) continue; - const eq = line.indexOf("="); - if (eq < 0) continue; - const key = line.slice(0, eq).trim(); - let val = line.slice(eq + 1).trim(); - if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) { - val = val.slice(1, -1); - } - if (process.env[key] === undefined) process.env[key] = val; - } -} - -function isScenario(): boolean { - return process.argv.includes("--scenario"); -} - -async function main(): Promise { - loadEnvFile(); - const tenant = "tenant_default"; - - // Simulation-first: use the live OpenRouter (Owl Alpha) provider only when a key is present. - const hasKey = Boolean(process.env.OPENROUTER_API_KEY); - const provider = hasKey - ? createProvider({ provider: "openrouter" }) - : new SimulationProvider(scriptedCompany()); - - const runtime = createCompany({ - provider, - config: { - store: { path: "data/company.db" }, - concurrency: 4, - logLevel: (process.env.LOG_LEVEL as "debug" | "info" | "warn" | "error") ?? "info", - }, - agentFactory: createAgent, - agents: agentDefinitions, - tools: allTools, - }); - - const { services } = runtime; - - if (isScenario()) { - const { runScenario } = await import("./scenario"); - await runScenario(runtime, tenant); - services.close(); - return; - } - - const port = Number(process.env.PORT ?? 3000); - const { server } = createHttpServer(services); - server.listen(port, () => { - services.log.info("server.listening", { port, provider: provider.id }); - console.log(`\n CorpOS control plane → http://localhost:${port}\n`); - }); - - const shutdown = (): void => { - services.log.info("server.shutdown"); - services.close(); - server.close(); - process.exit(0); - }; - process.on("SIGINT", shutdown); - process.on("SIGTERM", shutdown); -} - -main().catch((err) => { - console.error("fatal:", err); - process.exit(1); -}); - -/** - * Default simulation when no live LLM key is set. Dispatches per-agent so - * each department gets appropriate canned behavior with an independent step - * counter — e.g. ops diagnoses a degraded service rather than echoing the - * support refund flow. Uses real seeded data (Ada / sub_ada_pro). - */ -function scriptedCompany() { - const steps = new Map(); - const step = (key: string): number => { - const n = (steps.get(key) ?? 0) + 1; - steps.set(key, n); - return n; - }; - const has = (req: { tools?: Array<{ name: string }> }, name: string): boolean => - Boolean(req.tools?.some((t) => t.name === name)); - - return (req: { tools?: Array<{ name: string }> }) => { - // Operations / Engineering: diagnose a degraded service, propose restart. - if (has(req, "system.get_health")) { - const n = step("ops"); - if (n === 1) - return { - toolCalls: [{ name: "system.get_health", arguments: {} }], - }; - if (n === 2) - return { - toolCalls: [{ name: "system.list_alerts", arguments: {} }], - }; - if (n === 3 && has(req, "system.restart_service")) - return { - toolCalls: [ - { name: "system.restart_service", arguments: { serviceId: "svc_checkout_api" } }, - ], - }; - return { - content: - "checkout-api was degraded with elevated error rates; restart initiated and health recovering.", - }; - } - - // Finance: review a subscription and apply credit. - if (has(req, "billing.apply_credit")) { - const n = step("finance"); - if (n === 1) - return { - toolCalls: [ - { name: "billing.get_subscription", arguments: { subscriptionId: "sub_grace_team" } }, - ], - }; - if (n === 2) - return { - toolCalls: [ - { name: "billing.apply_credit", arguments: { contactId: "ct_grace", amount: 25 } }, - ], - }; - return { content: "Applied a $25 goodwill credit to Grace's Team subscription." }; - } - - // Sales: qualify a lead, update CRM. - if (has(req, "crm.list_deals")) { - const n = step("sales"); - if (n === 1) - return { - toolCalls: [{ name: "crm.list_deals", arguments: {} }], - }; - if (n === 2) - return { - toolCalls: [{ name: "crm.lookup_contact", arguments: { email: "grace@example.com" } }], - }; - return { content: "Grace's Team expansion deal is in negotiation; followed up via email." }; - } - - // Support (default): KB lookup → contact → approval-gated refund. - const n = step("support"); - if (n === 1) - return { - toolCalls: [{ name: "kb.search", arguments: { query: "refund policy" } }], - }; - if (n === 2) - return { - toolCalls: [{ name: "crm.lookup_contact", arguments: { email: "ada@example.com" } }], - }; - if (n === 3) - return { - toolCalls: [ - { - name: "billing.issue_refund", - arguments: { subscriptionId: "sub_ada_pro", amount: 49 }, - }, - ], - }; - return { content: "Refund of $49 initiated for Ada per the 30-day policy. Ticket closed." }; - }; -} diff --git a/src/scenario.ts b/src/scenario.ts deleted file mode 100644 index da31ad7..0000000 --- a/src/scenario.ts +++ /dev/null @@ -1,67 +0,0 @@ -import type { Runtime } from "./core"; - -/** - * Deterministic multi-agent scenario (no live LLM, no network). Demonstrates: - * - a support refund flow that hits an approval gate, - * - an ops agent diagnosing a degraded service, - * - inter-agent delegation shape. - * Run with `npm run scenario`. - */ -export async function runScenario(runtime: Runtime, tenant: string): Promise { - const { services } = runtime; - const log = services.log.child("scenario"); - - log.info("scenario.start", { provider: services.provider.id }); - - // 1. Support refund flow — hits billing.issue_refund approval gate. - const support = runtime.submit({ - tenantId: tenant, - title: "Refund request from Ada", - description: "Customer Ada (ada@example.com) requests a refund on sub_ada_pro.", - assignedTo: "agent_support", - createdBy: "human", - }); - - const supportTask = await services.orchestrator.runToCompletion(await support); - log.info("scenario.support.state", { state: supportTask?.state }); - - // 2. Ops diagnosis flow. - const ops = await runtime.submit({ - tenantId: tenant, - title: "Investigate degraded checkout service", - description: "Alerts show checkout-api degraded. Diagnose and propose action.", - assignedTo: "agent_ops", - createdBy: "system", - }); - await services.orchestrator.runToCompletion(ops); - - // 3. Auto-approve every pending gate so the scenario resolves cleanly - // (in production these wait for a human via the dashboard). - for (let round = 0; round < 4; round++) { - const pending = services.store.pendingApprovals(); - if (pending.length === 0) break; - for (const approval of pending) { - log.info("scenario.approval.decide", { id: approval.id, tool: approval.tool }); - services.policy.decide(approval.id, "approved", "scenario:human"); - await services.orchestrator.resume(approval.id); - } - } - - // 4. Report. - const tasks = services.store.listTasks(); - const events = services.store.recentEvents(); - const pending = services.store.pendingApprovals(); - - console.log("\n=== SCENARIO RESULT ==="); - console.log(`tasks: ${tasks.length}`); - for (const t of tasks) { - console.log( - ` - [${t.state}] ${t.title} → ${t.assignedTo} (output: ${t.output?.summary ?? "—"})`, - ); - } - console.log(`events: ${events.length}`); - console.log(`approvals: ${pending.length} pending`); - console.log("=======================\n"); - - log.info("scenario.done", { tasks: tasks.length, events: events.length }); -} diff --git a/src/tools/billing.ts b/src/tools/billing.ts deleted file mode 100644 index 11ba8c0..0000000 --- a/src/tools/billing.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { defineTool, newId, now } from "../core"; -import type { Tool } from "../core"; -import { state, asStr, asNum } from "./state"; - -const CREDIT_CEILING_USD = 100; - -function sumRefunds(subscriptionId: string): number { - return state.refunds - .filter((r) => r.subscriptionId === subscriptionId) - .reduce((total, r) => total + r.amount, 0); -} - -function sumCredits(contactId: string): number { - return state.credits - .filter((c) => c.contactId === contactId) - .reduce((total, c) => total + c.amount, 0); -} - -export function billingTools(): Tool[] { - return [ - defineTool({ - name: "billing.get_subscription", - description: "Look up the subscription for a contact.", - permission: { category: "read" }, - parameters: { - type: "object", - properties: { - contactId: { type: "string", description: "Contact id." }, - }, - required: ["contactId"], - }, - async execute(args) { - const contactId = asStr(args.contactId); - const sub = state.subscriptions.find((s) => s.contactId === contactId); - if (!sub) return { ok: false, error: `No subscription for contact ${contactId}` }; - return { - ok: true, - data: sub, - note: `Subscription ${sub.id} (${sub.plan}, ${sub.amount} ${sub.currency}/term).`, - }; - }, - }), - defineTool({ - name: "billing.issue_refund", - description: "Issue a refund against a subscription. Spend-gated and capped at $100.", - permission: { category: "spend", costCap: 100, requiresApproval: true }, - parameters: { - type: "object", - properties: { - subscriptionId: { type: "string" }, - amount: { type: "number", minimum: 0, description: "Refund amount in USD." }, - }, - required: ["subscriptionId", "amount"], - }, - async execute(args) { - const subscriptionId = asStr(args.subscriptionId); - const amount = asNum(args.amount); - if (!subscriptionId) return { ok: false, error: "subscriptionId is required" }; - if (amount === undefined || amount <= 0) { - return { ok: false, error: "amount must be a positive number" }; - } - const sub = state.subscriptions.find((s) => s.id === subscriptionId); - if (!sub) return { ok: false, error: `No subscription with id ${subscriptionId}` }; - const alreadyRefunded = sumRefunds(sub.id); - if (alreadyRefunded + amount > sub.amount) { - return { - ok: false, - error: `refund of ${amount} + already refunded ${alreadyRefunded} exceeds subscription charge ${sub.amount}`, - }; - } - const ref = newId("refund"); - state.refunds.push({ - id: ref, - subscriptionId: sub.id, - contactId: sub.contactId, - amount, - currency: "USD", - ts: now(), - }); - return { - ok: true, - data: { - ref, - subscriptionId: sub.id, - amount, - currency: "USD", - totalRefunded: alreadyRefunded + amount, - }, - cost: { amount, currency: "USD" }, - note: `Refund of $${amount} issued on subscription ${sub.id} (ref ${ref}).`, - }; - }, - }), - defineTool({ - name: "billing.apply_credit", - description: "Apply account credit to a contact. Spend-gated and capped at $100.", - permission: { category: "spend", costCap: 100, requiresApproval: true }, - parameters: { - type: "object", - properties: { - contactId: { type: "string" }, - amount: { type: "number", minimum: 0, description: "Credit amount in USD." }, - }, - required: ["contactId", "amount"], - }, - async execute(args) { - const contactId = asStr(args.contactId); - const amount = asNum(args.amount); - if (!contactId) return { ok: false, error: "contactId is required" }; - if (amount === undefined || amount <= 0) { - return { ok: false, error: "amount must be a positive number" }; - } - const contact = state.contacts.find((c) => c.id === contactId); - if (!contact) return { ok: false, error: `No contact with id ${contactId}` }; - const alreadyCredited = sumCredits(contact.id); - if (alreadyCredited + amount > CREDIT_CEILING_USD) { - return { - ok: false, - error: `credit of ${amount} + already credited ${alreadyCredited} exceeds credit ceiling ${CREDIT_CEILING_USD}`, - }; - } - const ref = newId("credit"); - state.credits.push({ - id: ref, - contactId: contact.id, - amount, - currency: "USD", - ts: now(), - }); - return { - ok: true, - data: { - ref, - contactId: contact.id, - contact: contact.name, - amount, - currency: "USD", - totalCredited: alreadyCredited + amount, - }, - cost: { amount, currency: "USD" }, - note: `Credit of $${amount} applied to ${contact.name} (ref ${ref}).`, - }; - }, - }), - ]; -} diff --git a/src/tools/comms.ts b/src/tools/comms.ts deleted file mode 100644 index e4fc02f..0000000 --- a/src/tools/comms.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { defineTool, newId, now } from "../core"; -import type { Tool } from "../core"; -import { state, asStr } from "./state"; - -export function commsTools(): Tool[] { - return [ - defineTool({ - name: "comms.send_email", - description: "Send an outbound email to a customer.", - permission: { category: "communicate" }, - parameters: { - type: "object", - properties: { - to: { type: "string", description: "Recipient email address." }, - subject: { type: "string" }, - body: { type: "string" }, - contactId: { type: "string" }, - }, - required: ["to", "subject", "body"], - }, - async execute(args) { - const to = asStr(args.to); - const subject = asStr(args.subject); - const body = asStr(args.body); - if (!to || !subject || !body) { - return { ok: false, error: "to, subject and body are required" }; - } - let contactId = asStr(args.contactId); - if (!contactId) { - const found = state.contacts.find((c) => c.email.toLowerCase() === to.toLowerCase()); - contactId = found?.id; - } - const record = { - id: newId("comm"), - channel: "email" as const, - direction: "outbound" as const, - to, - subject, - body, - contactId, - contactEmail: to, - threadId: newId("thread"), - ts: now(), - }; - state.commsLog.push(record); - return { ok: true, data: { id: record.id, to, subject }, note: `Email sent to ${to}.` }; - }, - }), - defineTool({ - name: "comms.send_slack", - description: "Post a message to an internal Slack channel.", - permission: { category: "communicate" }, - parameters: { - type: "object", - properties: { - channel: { type: "string", description: "Slack channel name, e.g. #ops." }, - message: { type: "string" }, - }, - required: ["channel", "message"], - }, - async execute(args) { - const channel = asStr(args.channel); - const message = asStr(args.message); - if (!channel || !message) { - return { ok: false, error: "channel and message are required" }; - } - const record = { - id: newId("comm"), - channel: "slack" as const, - direction: "outbound" as const, - to: channel, - body: message, - threadId: newId("thread"), - ts: now(), - }; - state.commsLog.push(record); - return { - ok: true, - data: { id: record.id, channel }, - note: `Slack message posted to ${channel}.`, - }; - }, - }), - defineTool({ - name: "comms.get_thread", - description: "Retrieve the communication history for a contact by email.", - permission: { category: "communicate" }, - parameters: { - type: "object", - properties: { - contactEmail: { type: "string", description: "Contact email to look up." }, - }, - required: ["contactEmail"], - }, - async execute(args) { - const email = (asStr(args.contactEmail) ?? "").toLowerCase(); - const contact = state.contacts.find((c) => c.email.toLowerCase() === email); - if (!contact) return { ok: false, error: `No contact with email ${email}` }; - const thread = state.commsLog - .filter( - (m) => m.contactId === contact.id || (m.contactEmail ?? "").toLowerCase() === email, - ) - .sort((a, b) => a.ts.localeCompare(b.ts)); - return { - ok: true, - data: { contact, thread }, - note: `${thread.length} message(s) in thread for ${contact.email}.`, - }; - }, - }), - ]; -} diff --git a/src/tools/crm.ts b/src/tools/crm.ts deleted file mode 100644 index a76691f..0000000 --- a/src/tools/crm.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { defineTool, newId, now } from "../core"; -import type { Tool } from "../core"; -import { state, asStr } from "./state"; - -export function crmTools(): Tool[] { - return [ - defineTool({ - name: "crm.lookup_contact", - description: "Look up a customer contact by email address.", - permission: { category: "read" }, - parameters: { - type: "object", - properties: { - email: { type: "string", description: "Contact email address." }, - }, - required: ["email"], - }, - async execute(args) { - const email = (asStr(args.email) ?? "").toLowerCase(); - const contact = state.contacts.find((c) => c.email.toLowerCase() === email); - if (!contact) return { ok: false, error: `No contact with email ${email}` }; - return { - ok: true, - data: contact, - note: `Found contact ${contact.name} (${contact.email}).`, - }; - }, - }), - defineTool({ - name: "crm.create_contact", - description: "Create a new customer contact record.", - permission: { category: "write" }, - parameters: { - type: "object", - properties: { - name: { type: "string" }, - email: { type: "string" }, - company: { type: "string" }, - plan: { type: "string" }, - }, - required: ["name", "email"], - }, - async execute(args) { - const name = asStr(args.name); - const email = asStr(args.email); - if (!name || !email) return { ok: false, error: "name and email are required" }; - if (state.contacts.some((c) => c.email.toLowerCase() === email.toLowerCase())) { - return { ok: false, error: `Contact already exists with email ${email}` }; - } - const contact = { - id: newId("ct"), - name, - email, - company: asStr(args.company), - plan: asStr(args.plan) ?? "Free", - status: "active", - createdAt: now(), - }; - state.contacts.push(contact); - return { ok: true, data: contact, note: `Created contact ${contact.name}.` }; - }, - }), - defineTool({ - name: "crm.update_contact", - description: "Update fields on an existing contact.", - permission: { category: "write" }, - parameters: { - type: "object", - properties: { - contactId: { type: "string" }, - name: { type: "string" }, - email: { type: "string" }, - company: { type: "string" }, - plan: { type: "string" }, - status: { type: "string" }, - }, - required: ["contactId"], - }, - async execute(args) { - const id = asStr(args.contactId); - const contact = state.contacts.find((c) => c.id === id); - if (!contact) return { ok: false, error: `No contact with id ${id}` }; - const name = asStr(args.name); - const email = asStr(args.email); - const company = asStr(args.company); - const plan = asStr(args.plan); - const status = asStr(args.status); - if (name) contact.name = name; - if (email) contact.email = email; - if (company) contact.company = company; - if (plan) contact.plan = plan; - if (status) contact.status = status; - return { ok: true, data: contact, note: `Updated contact ${contact.name}.` }; - }, - }), - defineTool({ - name: "crm.list_deals", - description: "List sales deals, optionally filtered by contact or stage.", - permission: { category: "read" }, - parameters: { - type: "object", - properties: { - contactId: { type: "string" }, - stage: { type: "string" }, - }, - }, - async execute(args) { - const contactId = asStr(args.contactId); - const stage = asStr(args.stage); - let deals = [...state.deals]; - if (contactId) deals = deals.filter((d) => d.contactId === contactId); - if (stage) deals = deals.filter((d) => d.stage.toLowerCase() === stage.toLowerCase()); - return { ok: true, data: deals, note: `${deals.length} deal(s) found.` }; - }, - }), - ]; -} diff --git a/src/tools/delegate.ts b/src/tools/delegate.ts deleted file mode 100644 index b0bd731..0000000 --- a/src/tools/delegate.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { defineTool } from "../core"; -import type { Tool } from "../core"; -import { asStr } from "./state"; - -export function delegateTool(): Tool { - return defineTool({ - name: "delegate.task", - description: - "Request handoff of a task to another agent. Requires approval; the orchestrator/app layer wires the actual delegation.", - permission: { category: "delegate", requiresApproval: true }, - parameters: { - type: "object", - properties: { - toAgentId: { type: "string", description: "Target agent id, e.g. agent_engineer." }, - title: { type: "string" }, - description: { type: "string" }, - }, - required: ["toAgentId", "title", "description"], - }, - async execute(args) { - const toAgentId = asStr(args.toAgentId) ?? "(unknown)"; - const title = asStr(args.title) ?? "(untitled)"; - return { - ok: true, - data: { toAgentId, title }, - note: `delegation requested (requires approval) — hand off "${title}" to ${toAgentId}; the orchestrator/app layer will create the task.`, - }; - }, - }); -} diff --git a/src/tools/index.ts b/src/tools/index.ts deleted file mode 100644 index c773d76..0000000 --- a/src/tools/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type { Tool } from "../core"; -import { knowledgeTools } from "./knowledge"; -import { crmTools } from "./crm"; -import { commsTools } from "./comms"; -import { billingTools } from "./billing"; -import { systemTools } from "./system"; -import { delegateTool } from "./delegate"; - -export { state, resetState } from "./state"; - -export const allTools: Tool[] = [ - ...knowledgeTools(), - ...crmTools(), - ...commsTools(), - ...billingTools(), - ...systemTools(), - delegateTool(), -]; diff --git a/src/tools/knowledge.ts b/src/tools/knowledge.ts deleted file mode 100644 index 3f3b5a3..0000000 --- a/src/tools/knowledge.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { defineTool } from "../core"; -import type { Tool } from "../core"; -import { state, asStr, asInt } from "./state"; - -export function knowledgeTools(): Tool[] { - return [ - defineTool({ - name: "kb.search", - description: "Search internal knowledge base articles by keyword.", - permission: { category: "read" }, - parameters: { - type: "object", - properties: { - query: { type: "string", description: "Keyword or phrase to search for." }, - limit: { type: "integer", minimum: 1, description: "Max results to return." }, - }, - required: ["query"], - }, - async execute(args) { - const query = (asStr(args.query) ?? "").trim().toLowerCase(); - if (!query) return { ok: false, error: "query is required" }; - const limit = asInt(args.limit) ?? 5; - const terms = query.split(/\s+/).filter(Boolean); - const matches = state.kbArticles - .filter((a) => { - const hay = `${a.title} ${a.summary} ${a.body} ${a.tags.join(" ")}`.toLowerCase(); - return terms.every((term) => hay.includes(term)); - }) - .slice(0, limit) - .map((a) => ({ id: a.id, title: a.title, summary: a.summary, tags: a.tags })); - return { - ok: true, - data: matches, - note: `${matches.length} article(s) matched "${query}".`, - }; - }, - }), - defineTool({ - name: "kb.get_article", - description: "Fetch a full knowledge base article by its id.", - permission: { category: "read" }, - parameters: { - type: "object", - properties: { - id: { type: "string", description: "Article id." }, - }, - required: ["id"], - }, - async execute(args) { - const id = asStr(args.id); - const article = state.kbArticles.find((a) => a.id === id); - if (!article) return { ok: false, error: `No KB article with id ${id}` }; - return { ok: true, data: article, note: `Loaded article "${article.title}".` }; - }, - }), - ]; -} diff --git a/src/tools/state.ts b/src/tools/state.ts deleted file mode 100644 index 19c48c7..0000000 --- a/src/tools/state.ts +++ /dev/null @@ -1,320 +0,0 @@ -export interface Contact { - id: string; - name: string; - email: string; - company?: string; - plan?: string; - status: string; - createdAt: string; -} - -export interface Deal { - id: string; - contactId: string; - title: string; - value: number; - currency: string; - stage: string; - createdAt: string; -} - -export interface Subscription { - id: string; - contactId: string; - plan: string; - status: string; - amount: number; - currency: string; - startedAt: string; - renewalAt?: string; -} - -export interface KbArticle { - id: string; - title: string; - tags: string[]; - summary: string; - body: string; -} - -export interface CommRecord { - id: string; - channel: "email" | "slack"; - direction: "inbound" | "outbound"; - to?: string; - subject?: string; - body: string; - contactId?: string; - contactEmail?: string; - threadId: string; - ts: string; -} - -export interface RefundRecord { - id: string; - subscriptionId: string; - contactId?: string; - amount: number; - currency: string; - ts: string; -} - -export interface CreditRecord { - id: string; - contactId: string; - amount: number; - currency: string; - ts: string; -} - -export type ServiceStatus = "healthy" | "degraded" | "down"; - -export interface ServiceHealth { - status: ServiceStatus; - latencyMs: number; - uptimePct: number; - lastCheck: string; -} - -export interface SystemService { - id: string; - name: string; - status: ServiceStatus; - health: ServiceHealth; -} - -export type AlertSeverity = "low" | "medium" | "high" | "critical"; - -export interface Alert { - id: string; - severity: AlertSeverity; - serviceId?: string; - message: string; - status: "open" | "acknowledged" | "resolved"; - createdAt: string; -} - -export interface CompanyState { - contacts: Contact[]; - deals: Deal[]; - subscriptions: Subscription[]; - kbArticles: KbArticle[]; - commsLog: CommRecord[]; - refunds: RefundRecord[]; - credits: CreditRecord[]; - systemServices: SystemService[]; - alerts: Alert[]; -} - -function freshState(): CompanyState { - return { - contacts: [ - { - id: "ct_ada", - name: "Ada Lovelace", - email: "ada@example.com", - company: "Analytical Engines Inc", - plan: "Pro", - status: "active", - createdAt: "2025-01-12T09:30:00.000Z", - }, - { - id: "ct_grace", - name: "Grace Hopper", - email: "grace@example.com", - company: "COBOL Corp", - plan: "Team", - status: "active", - createdAt: "2025-02-03T14:05:00.000Z", - }, - { - id: "ct_alan", - name: "Alan Turing", - email: "alan@example.com", - company: "Enigma Ltd", - plan: "Free", - status: "churned", - createdAt: "2024-11-21T18:42:00.000Z", - }, - ], - deals: [ - { - id: "deal_grace_team", - contactId: "ct_grace", - title: "COBOL Corp — Team plan expansion", - value: 12000, - currency: "USD", - stage: "negotiation", - createdAt: "2025-03-01T10:00:00.000Z", - }, - { - id: "deal_alan_reeng", - contactId: "ct_alan", - title: "Enigma Ltd — Re-engagement", - value: 4800, - currency: "USD", - stage: "lead", - createdAt: "2025-03-10T11:20:00.000Z", - }, - ], - subscriptions: [ - { - id: "sub_ada_pro", - contactId: "ct_ada", - plan: "Pro", - status: "active", - amount: 99, - currency: "USD", - startedAt: "2025-01-12T09:30:00.000Z", - renewalAt: "2026-01-12T09:30:00.000Z", - }, - { - id: "sub_grace_team", - contactId: "ct_grace", - plan: "Team", - status: "active", - amount: 499, - currency: "USD", - startedAt: "2025-02-03T14:05:00.000Z", - renewalAt: "2026-02-03T14:05:00.000Z", - }, - ], - kbArticles: [ - { - id: "kb_refund_policy", - title: "30-day refund policy", - tags: ["refund", "billing", "policy"], - summary: "Customers can request a full refund within 30 days of purchase or renewal.", - body: [ - "Refund policy:", - "- A full refund is available within 30 days of the initial purchase or a renewal.", - "- After 30 days, partial refunds may be issued as account credit at Finance's discretion.", - "- Refunds are returned to the original payment method within 5-7 business days.", - "- To issue a refund, use billing.issue_refund with the subscriptionId and amount; amounts over $100 require manager approval.", - "- Cancel the subscription first if the customer does not want future renewals.", - ].join("\n"), - }, - { - id: "kb_cancel_subscription", - title: "Canceling a subscription", - tags: ["subscription", "billing", "cancel"], - summary: "How to cancel a customer subscription and what happens to access.", - body: [ - "Cancellation:", - "- Set the subscription status to 'cancelled' to stop future renewals.", - "- The customer retains access until the current paid period ends.", - "- No further charges occur after cancellation; a confirmation email is recommended.", - ].join("\n"), - }, - ], - commsLog: [ - { - id: "comm_ada_welcome", - channel: "email", - direction: "outbound", - to: "ada@example.com", - subject: "Welcome to the Pro plan", - body: "Hi Ada, thanks for upgrading to Pro. Your subscription is active.", - contactId: "ct_ada", - contactEmail: "ada@example.com", - threadId: "thread_ada", - ts: "2025-01-12T09:35:00.000Z", - }, - { - id: "comm_ada_refund_q", - channel: "email", - direction: "inbound", - to: "support@example.com", - subject: "Question about a refund", - body: "Hi, I was double charged this month. Can I get a refund?", - contactId: "ct_ada", - contactEmail: "ada@example.com", - threadId: "thread_ada", - ts: "2025-03-15T08:10:00.000Z", - }, - ], - refunds: [], - credits: [], - systemServices: [ - { - id: "svc_api", - name: "API Gateway", - status: "healthy", - health: { - status: "healthy", - latencyMs: 42, - uptimePct: 99.98, - lastCheck: "2025-03-15T12:00:00.000Z", - }, - }, - { - id: "svc_db", - name: "Primary Database", - status: "degraded", - health: { - status: "degraded", - latencyMs: 880, - uptimePct: 99.5, - lastCheck: "2025-03-15T12:00:00.000Z", - }, - }, - { - id: "svc_dash", - name: "Dashboard", - status: "healthy", - health: { - status: "healthy", - latencyMs: 110, - uptimePct: 99.95, - lastCheck: "2025-03-15T12:00:00.000Z", - }, - }, - ], - alerts: [ - { - id: "alert_db_latency", - severity: "high", - serviceId: "svc_db", - message: "Primary database latency elevated (p95 > 800ms).", - status: "open", - createdAt: "2025-03-15T11:45:00.000Z", - }, - { - id: "alert_api_deploy", - severity: "low", - serviceId: "svc_api", - message: "API Gateway deploy completed.", - status: "resolved", - createdAt: "2025-03-14T22:10:00.000Z", - }, - ], - }; -} - -export const state: CompanyState = freshState(); - -export function resetState(): void { - const fresh = freshState(); - state.contacts = fresh.contacts; - state.deals = fresh.deals; - state.subscriptions = fresh.subscriptions; - state.kbArticles = fresh.kbArticles; - state.commsLog = fresh.commsLog; - state.refunds = fresh.refunds; - state.credits = fresh.credits; - state.systemServices = fresh.systemServices; - state.alerts = fresh.alerts; -} - -export function asStr(v: unknown): string | undefined { - return typeof v === "string" ? v : undefined; -} - -export function asNum(v: unknown): number | undefined { - return typeof v === "number" && Number.isFinite(v) ? v : undefined; -} - -export function asInt(v: unknown): number | undefined { - const n = asNum(v); - return n !== undefined && Number.isInteger(n) ? n : undefined; -} diff --git a/src/tools/system.ts b/src/tools/system.ts deleted file mode 100644 index 25a0181..0000000 --- a/src/tools/system.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { defineTool, now } from "../core"; -import type { Tool } from "../core"; -import { state, asStr } from "./state"; - -export function systemTools(): Tool[] { - return [ - defineTool({ - name: "system.get_health", - description: "Report current health of all managed services.", - permission: { category: "read" }, - parameters: { type: "object", properties: {} }, - async execute() { - const services = state.systemServices.map((s) => ({ - id: s.id, - name: s.name, - status: s.status, - health: s.health, - })); - const notHealthy = services.filter((s) => s.status !== "healthy").length; - return { - ok: true, - data: services, - note: notHealthy ? `${notHealthy} service(s) not healthy.` : "All services healthy.", - }; - }, - }), - defineTool({ - name: "system.list_alerts", - description: "List system alerts, optionally filtered by status.", - permission: { category: "read" }, - parameters: { - type: "object", - properties: { - status: { type: "string", enum: ["open", "acknowledged", "resolved"] }, - }, - }, - async execute(args) { - const status = asStr(args.status); - let alerts = [...state.alerts]; - if (status) alerts = alerts.filter((a) => a.status === status); - alerts.sort((a, b) => a.createdAt.localeCompare(b.createdAt)); - return { ok: true, data: alerts, note: `${alerts.length} alert(s).` }; - }, - }), - defineTool({ - name: "system.restart_service", - description: "Restart a managed service to recover from a degraded state. Requires approval.", - permission: { category: "system", requiresApproval: true }, - parameters: { - type: "object", - properties: { - serviceId: { type: "string", description: "Service id to restart." }, - }, - required: ["serviceId"], - }, - async execute(args) { - const serviceId = asStr(args.serviceId); - const service = state.systemServices.find((s) => s.id === serviceId); - if (!service) return { ok: false, error: `No service with id ${serviceId}` }; - service.status = "healthy"; - service.health = { - status: "healthy", - latencyMs: 38, - uptimePct: 99.99, - lastCheck: now(), - }; - for (const alert of state.alerts) { - if (alert.serviceId === serviceId && alert.status === "open") { - alert.status = "resolved"; - } - } - return { - ok: true, - data: { serviceId: service.id, name: service.name, status: service.status }, - note: `Restarted ${service.name}; health now healthy.`, - }; - }, - }), - ]; -} diff --git a/test/company-day.test.ts b/test/company-day.test.ts new file mode 100644 index 0000000..af9b6ff --- /dev/null +++ b/test/company-day.test.ts @@ -0,0 +1,78 @@ +import { describe, expect, it } from "vitest"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { + computeMaxAutonomousRisk, + counterfactualReplay, + createCompany, + runCompanyDay, +} from "@corpos/core"; + +describe("company day", () => { + it("runs multi-handoff day with autonomy, exception, compensation, sla, trust", async () => { + const { company, result } = await runCompanyDay({ dbPath: ":memory:" }); + expect(result.handoffs).toBeGreaterThanOrEqual(2); + expect(result.autonomousSettles).toBeGreaterThanOrEqual(1); + expect(result.exceptionSettles).toBeGreaterThanOrEqual(1); + expect(result.compensated).toBeGreaterThanOrEqual(1); + expect(result.slaExceptions).toBeGreaterThanOrEqual(1); + expect(result.trustAfter).toBeGreaterThanOrEqual(2); + expect(result.ok).toBe(true); + const verify = await company.audit.verify(); + expect(verify.ok).toBe(true); + company.close(); + }); + + it("fail-closed unknown tool and kill switch", async () => { + const company = await createCompany({ dbPath: ":memory:" }); + const denied = await company.gateway.invoke( + "nope.tool", + {}, + { agentId: "agent_support", taskId: "t", contractId: "c", tenantId: "default" }, + ); + expect(denied.decision.effect).toBe("deny"); + await company.gateway.setKilled(true); + const killed = await company.gateway.invoke( + "crm.lookup", + { email: "a@b.c" }, + { agentId: "agent_support", taskId: "t", contractId: "c", tenantId: "default" }, + ); + expect(killed.decision.effect).toBe("deny"); + company.close(); + }); + + it("trust mapping and counterfactual diffs", () => { + expect(computeMaxAutonomousRisk({ accepts: 2, rejects: 0, violations: 0 })).toBe(2); + expect(computeMaxAutonomousRisk({ accepts: 0, rejects: 3, violations: 0 })).toBe(0); + const diffs = counterfactualReplay( + [{ tool: "crm.lookup", decision: { effect: "allow", reason: "ok" } }], + { rules: [{ tool: "*", effect: "deny", reason: "strict" }], maxAutonomousRisk: 0 }, + ); + expect(diffs.length).toBeGreaterThanOrEqual(1); + }); + + it("audit chain detects forgery", async () => { + const company = await createCompany({ dbPath: ":memory:" }); + await company.audit.append("a", { n: 1 }); + await company.audit.append("b", { n: 2 }); + await company.audit.append("c", { n: 3 }); + expect((await company.audit.verify()).ok).toBe(true); + await company.audit.forgeMiddle(); + expect((await company.audit.verify()).ok).toBe(false); + company.close(); + }); + + it("knowledge path via real MCP subprocess", async () => { + const server = path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + "../packages/mcp-knowledge/dist/server.js", + ); + const { company, result } = await runCompanyDay({ + dbPath: ":memory:", + withMcp: true, + serverCommand: { command: process.execPath, args: [server] }, + }); + expect(result.handoffs).toBeGreaterThanOrEqual(2); + company.close(); + }); +}); diff --git a/test/event-bus.test.ts b/test/event-bus.test.ts deleted file mode 100644 index fbf246b..0000000 --- a/test/event-bus.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { describe, it, expect, beforeEach, vi } from "vitest"; -import { EventBus } from "../src/core/index"; - -describe("EventBus", () => { - let bus: EventBus; - - beforeEach(() => { - bus = new EventBus(); - }); - - it("delivers typed events to on() handlers", async () => { - const seen: unknown[] = []; - bus.on("task.queued", (e) => { - seen.push(e.payload); - }); - await bus.emit("task.queued", { id: "t1" }); - expect(seen).toEqual([{ id: "t1" }]); - }); - - it("unsubscribes when the returned off handle is called", async () => { - const seen: unknown[] = []; - const off = bus.on("x", (e) => { - seen.push(e.payload); - }); - off(); - await bus.emit("x", { n: 1 }); - expect(seen).toEqual([]); - }); - - it("delivers every event to onAll wildcard handlers", async () => { - const types: string[] = []; - bus.onAll((e) => { - types.push(e.type); - }); - await bus.emit("a", {}); - await bus.emit("b", {}); - expect(types).toEqual(["a", "b"]); - }); - - it("a throwing handler does not break other handlers", async () => { - const calls: string[] = []; - bus.on("evt", () => { - calls.push("first"); - throw new Error("boom"); - }); - bus.on("evt", () => { - calls.push("second"); - }); - await bus.emit("evt", {}); - expect(calls).toEqual(["first", "second"]); - }); - - it("history_since returns events strictly after the given timestamp", async () => { - vi.useFakeTimers(); - try { - vi.setSystemTime(new Date("2024-01-01T00:00:00.000Z")); - await bus.emit("early", { n: 1 }); - const boundary = new Date().toISOString(); - - vi.setSystemTime(new Date("2024-01-01T00:00:00.500Z")); - await bus.emit("late", { n: 2 }); - vi.setSystemTime(new Date("2024-01-01T00:00:01.000Z")); - await bus.emit("later", { n: 3 }); - - expect(bus.history_since(boundary).map((e) => e.type)).toEqual(["late", "later"]); - expect(bus.history_since().map((e) => e.type)).toEqual(["early", "late", "later"]); - } finally { - vi.useRealTimers(); - } - }); -}); diff --git a/test/helpers.ts b/test/helpers.ts deleted file mode 100644 index 7563ef4..0000000 --- a/test/helpers.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { Logger, ToolContext } from "../src/core/index"; - -export const silentLogger: Logger = { - child: () => silentLogger, - debug: () => {}, - info: () => {}, - warn: () => {}, - error: () => {}, - audit: () => {}, -}; - -export function mkCtx(overrides: Partial = {}): ToolContext { - return { - agentId: "agent_test", - taskId: "task_test", - tenantId: "tenant_test", - logger: silentLogger, - ...overrides, - }; -} diff --git a/test/orchestrator.test.ts b/test/orchestrator.test.ts deleted file mode 100644 index 9457ac9..0000000 --- a/test/orchestrator.test.ts +++ /dev/null @@ -1,171 +0,0 @@ -import { describe, it, expect } from "vitest"; -import { - createCompany, - SimulationProvider, - defineTool, - Agent, - type AgentDefinition, -} from "../src/core/index"; - -const tenant = "tenant_orch"; - -function lastMessage(req: { messages: Array<{ role: string; content?: string }> }) { - return req.messages[req.messages.length - 1]; -} - -describe("Orchestrator", () => { - it("runs a single-agent task to completion via the SimulationProvider", async () => { - const kbSearch = defineTool({ - name: "kb.search", - description: "Search the knowledge base.", - parameters: { - type: "object", - properties: { query: { type: "string" } }, - required: ["query"], - }, - permission: { category: "read" }, - execute: async (args) => ({ - ok: true, - data: { hits: [String(args.query)] }, - note: `hits for ${args.query}`, - }), - }); - - const provider = new SimulationProvider((req) => { - const last = lastMessage(req); - if (last?.role === "tool") return { content: "Done." }; - return { toolCalls: [{ name: "kb.search", arguments: { query: "policy" } }] }; - }); - - const def: AgentDefinition = { - id: "agent_test", - name: "Test", - role: "tester", - systemPrompt: "Use kb.search then answer.", - tools: ["kb.search"], - maxSteps: 4, - }; - - const runtime = createCompany({ - provider, - config: { store: { inMemory: true }, logLevel: "warn" }, - agentFactory: (deps) => new Agent(deps), - agents: [def], - tools: [kbSearch], - }); - - const task = await runtime.submit({ - tenantId: tenant, - title: "Look up policy", - description: "find the policy", - assignedTo: "agent_test", - }); - const finished = await runtime.services.orchestrator.runToCompletion(task); - expect(finished?.state).toBe("succeeded"); - expect(finished?.output?.summary).toBe("Done."); - runtime.services.close(); - }); - - it("retries a task after a transient provider failure", async () => { - let calls = 0; - const provider = new SimulationProvider(() => { - calls++; - if (calls === 1) throw new Error("transient provider failure"); - return { content: "Recovered and finished." }; - }); - - const def: AgentDefinition = { - id: "agent_retry", - name: "Retry", - role: "tester", - systemPrompt: "Answer directly.", - tools: [], - maxSteps: 2, - }; - - const runtime = createCompany({ - provider, - config: { store: { inMemory: true }, logLevel: "warn" }, - agentFactory: (deps) => new Agent(deps), - agents: [def], - }); - - const task = await runtime.submit({ - tenantId: tenant, - title: "Flaky job", - description: "fails once then succeeds", - assignedTo: "agent_retry", - maxAttempts: 3, - }); - const finished = await runtime.services.orchestrator.runToCompletion(task, { timeoutMs: 5000 }); - expect(finished?.state).toBe("succeeded"); - expect(finished?.attempts).toBe(2); - runtime.services.close(); - }); - - it("pauses on an approval-gated tool and resumes after approval", async () => { - const payout = defineTool({ - name: "billing.payout", - description: "Issue a payout.", - parameters: { - type: "object", - properties: { amount: { type: "number" } }, - required: ["amount"], - }, - permission: { category: "spend" }, - execute: async (args) => ({ - ok: true, - cost: { amount: Number(args.amount), currency: "USD" }, - note: `Payout of $${args.amount} sent`, - }), - }); - - const provider = new SimulationProvider((req) => { - const last = lastMessage(req); - if (last?.role === "tool" && /payout/i.test(last.content ?? "")) { - return { content: "Payout complete." }; - } - return { toolCalls: [{ name: "billing.payout", arguments: { amount: 50 } }] }; - }); - - const def: AgentDefinition = { - id: "agent_pay", - name: "Pay", - role: "finance", - systemPrompt: "Issue the payout.", - tools: ["billing.payout"], - maxSteps: 4, - }; - - const runtime = createCompany({ - provider, - config: { store: { inMemory: true }, logLevel: "warn" }, - agentFactory: (deps) => new Agent(deps), - agents: [def], - tools: [payout], - }); - - const task = await runtime.submit({ - tenantId: tenant, - title: "Send payout", - description: "pay the vendor", - assignedTo: "agent_pay", - }); - - const paused = await runtime.services.orchestrator.runToCompletion(task); - expect(paused?.state).toBe("awaiting_approval"); - - const pending = runtime.services.store.pendingApprovals(); - expect(pending).toHaveLength(1); - expect(pending[0]?.tool).toBe("billing.payout"); - const approvalId = pending[0]!.id; - - runtime.services.policy.decide(approvalId, "approved", "tester"); - await runtime.services.orchestrator.resume(approvalId); - - const finished = await runtime.services.orchestrator.runToCompletion(task); - expect(finished?.state).toBe("succeeded"); - expect(runtime.services.store.spendForTask(tenant, task.id)).toBe(50); - runtime.services.close(); - }); -}); diff --git a/test/policy.test.ts b/test/policy.test.ts deleted file mode 100644 index 90f1b62..0000000 --- a/test/policy.test.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { describe, it, expect, beforeEach } from "vitest"; -import { Store, EventBus, PolicyEngine, defineTool, globMatch, now } from "../src/core/index"; -import { silentLogger } from "./helpers"; - -const tenant = "tenant_a"; -const agentId = "agent_x"; -const taskId = "task_1"; -const ctx = { tenantId: tenant, agentId, taskId }; - -describe("PolicyEngine", () => { - let store: Store; - let bus: EventBus; - let policy: PolicyEngine; - - beforeEach(() => { - store = new Store({ inMemory: true }); - bus = new EventBus(); - policy = new PolicyEngine(store, bus, silentLogger, { defaultEffect: "allow" }); - }); - - describe("globMatch", () => { - it("matches exact names", () => { - expect(globMatch("billing.charge", "billing.charge")).toBe(true); - expect(globMatch("billing.charge", "billing.refund")).toBe(false); - }); - - it("matches single-segment prefix globs", () => { - expect(globMatch("billing.*", "billing.charge")).toBe(true); - expect(globMatch("billing.*", "billing")).toBe(true); - expect(globMatch("billing.*", "crm.update")).toBe(false); - }); - - it("matches the bare wildcard", () => { - expect(globMatch("*", "anything.at.all")).toBe(true); - }); - }); - - describe("rule effects", () => { - it("allows via a billing.* glob rule", () => { - const tool = defineTool({ - name: "billing.charge", - description: "charge", - parameters: { type: "object", properties: {} }, - permission: { category: "write", requiresApproval: false }, - execute: async () => ({ ok: true }), - }); - policy.addRule({ id: "r1", tool: "billing.*", effect: "allow" }); - expect(policy.evaluate(tool, {}, ctx).effect).toBe("allow"); - }); - - it("denies via an exact rule with a reason", () => { - const tool = defineTool({ - name: "crm.delete", - description: "delete", - parameters: { type: "object", properties: {} }, - permission: { category: "write", requiresApproval: false }, - execute: async () => ({ ok: true }), - }); - policy.addRule({ id: "r2", tool: "crm.delete", effect: "deny", reason: "destructive" }); - const d = policy.evaluate(tool, {}, ctx); - expect(d.effect).toBe("deny"); - expect(d.reason).toMatch(/destructive/); - }); - - it("forces approval via an explicit approve rule and creates a pending approval", () => { - const tool = defineTool({ - name: "data.export", - description: "export", - parameters: { type: "object", properties: {} }, - permission: { category: "read", requiresApproval: false }, - execute: async () => ({ ok: true }), - }); - policy.addRule({ - id: "r3", - tool: "data.export", - effect: "approve", - reason: "sensitive export", - }); - const d = policy.evaluate(tool, {}, ctx); - expect(d.effect).toBe("approve"); - expect(d.approvalId).toBeDefined(); - expect(store.pendingApprovals()).toHaveLength(1); - }); - }); - - describe("spend caps", () => { - const spendTool = defineTool({ - name: "billing.charge", - description: "charge", - parameters: { type: "object", properties: { amount: { type: "number" } } }, - permission: { category: "spend", requiresApproval: false, costCap: 100 }, - execute: async () => ({ ok: true }), - }); - - it("denies when a prior spend pushes the run over the cap", () => { - store.recordSpend({ - id: "s1", - tenantId: tenant, - agentId, - taskId, - tool: "billing.charge", - amount: 60, - currency: "USD", - ref: "prior", - ts: now(), - }); - const denied = policy.evaluate(spendTool, { amount: 50 }, ctx); - expect(denied.effect).toBe("deny"); - expect(denied.reason).toMatch(/spend cap/); - }); - - it("allows when the intended spend stays within the cap", () => { - store.recordSpend({ - id: "s1", - tenantId: tenant, - agentId, - taskId, - tool: "billing.charge", - amount: 60, - currency: "USD", - ref: "prior", - ts: now(), - }); - expect(policy.evaluate(spendTool, { amount: 30 }, ctx).effect).toBe("allow"); - }); - }); - - describe("approval gating", () => { - it("creates a pending approval for approval-gated tools and decide() flips it", () => { - const tool = defineTool({ - name: "billing.refund", - description: "refund", - parameters: { type: "object", properties: { amount: { type: "number" } } }, - permission: { category: "spend" }, - execute: async () => ({ ok: true }), - }); - - const d = policy.evaluate(tool, { amount: 25 }, ctx); - expect(d.effect).toBe("approve"); - expect(d.approvalId).toBeDefined(); - - const pending = store.pendingApprovals(); - expect(pending).toHaveLength(1); - expect(pending[0]?.tool).toBe("billing.refund"); - expect(pending[0]?.state).toBe("pending"); - - const approvalId = pending[0]!.id; - const decided = policy.decide(approvalId, "approved", "manager"); - expect(decided?.state).toBe("approved"); - expect(store.getApproval(approvalId)?.state).toBe("approved"); - expect(store.pendingApprovals()).toHaveLength(0); - }); - }); -}); diff --git a/test/scenario.test.ts b/test/scenario.test.ts deleted file mode 100644 index c44ebc1..0000000 --- a/test/scenario.test.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { describe, it, expect } from "vitest"; -import { - createCompany, - SimulationProvider, - defineTool, - Agent, - type AgentDefinition, -} from "../src/core/index"; - -const tenant = "tenant_scenario"; - -const kbSearch = defineTool({ - name: "kb.search", - description: "Search the knowledge base.", - parameters: { - type: "object", - properties: { query: { type: "string" } }, - required: ["query"], - }, - permission: { category: "read" }, - execute: async () => ({ - ok: true, - data: { policy: "refund-30d" }, - note: "KB: refunds permitted within 30 days", - }), -}); - -const crmLookup = defineTool({ - name: "crm.lookup_contact", - description: "Look up a contact.", - parameters: { - type: "object", - properties: { contactId: { type: "string" } }, - required: ["contactId"], - }, - permission: { category: "read" }, - execute: async () => ({ - ok: true, - data: { name: "alice" }, - note: "Contact found: alice, account in good standing", - }), -}); - -const issueRefund = defineTool({ - name: "billing.issue_refund", - description: "Issue a refund.", - parameters: { - type: "object", - properties: { - contactId: { type: "string" }, - amount: { type: "number" }, - }, - required: ["contactId", "amount"], - }, - permission: { category: "spend" }, - execute: async (args) => ({ - ok: true, - cost: { amount: Number(args.amount), currency: "USD" }, - note: `Refund of $${args.amount} issued to ${args.contactId}`, - }), -}); - -const provider = new SimulationProvider((req) => { - const last = req.messages[req.messages.length - 1]; - const text = last?.content ?? ""; - if (last?.role === "tool" && /refund of/i.test(text)) { - return { content: "Refund processed and the customer notified. Case closed." }; - } - if (last?.role === "tool" && /contact found/i.test(text)) { - return { - toolCalls: [{ name: "billing.issue_refund", arguments: { contactId: "c1", amount: 40 } }], - }; - } - if (last?.role === "tool" && /kb:|refunds permitted/i.test(text)) { - return { toolCalls: [{ name: "crm.lookup_contact", arguments: { contactId: "c1" } }] }; - } - return { toolCalls: [{ name: "kb.search", arguments: { query: "refund policy" } }] }; -}); - -const support: AgentDefinition = { - id: "agent_support", - name: "Support", - role: "Customer Support", - systemPrompt: "Resolve the refund request using your tools.", - tools: ["kb.search", "crm.lookup_contact", "billing.issue_refund"], - maxSteps: 6, -}; - -const finance: AgentDefinition = { - id: "agent_finance", - name: "Finance", - role: "Finance", - systemPrompt: "Manage billing and refunds.", - tools: ["billing.issue_refund"], - maxSteps: 4, -}; - -function assertSubsequence(types: string[], expected: string[]): void { - let i = 0; - for (const t of types) { - if (i < expected.length && t === expected[i]) i++; - } - expect(i).toBe(expected.length); -} - -describe("multi-agent refund scenario", () => { - it("routes a support refund through the approval gate to completion", async () => { - const runtime = createCompany({ - provider, - config: { store: { inMemory: true }, logLevel: "warn" }, - agentFactory: (deps) => new Agent(deps), - agents: [support, finance], - tools: [kbSearch, crmLookup, issueRefund], - }); - - expect(runtime.services.agents.size).toBe(2); - - const task = await runtime.submit({ - tenantId: tenant, - title: "Customer refund request", - description: "Customer c1 wants a refund.", - assignedTo: "agent_support", - }); - - const paused = await runtime.services.orchestrator.runToCompletion(task); - expect(paused?.state).toBe("awaiting_approval"); - - const pending = runtime.services.store.pendingApprovals(); - expect(pending).toHaveLength(1); - expect(pending[0]?.tool).toBe("billing.issue_refund"); - const approvalId = pending[0]!.id; - - runtime.services.policy.decide(approvalId, "approved", "manager"); - await runtime.services.orchestrator.resume(approvalId); - - const finished = await runtime.services.orchestrator.runToCompletion(task); - expect(finished?.state).toBe("succeeded"); - expect(runtime.services.store.spendForTask(tenant, task.id)).toBe(40); - - const types = runtime.services.bus.filter().map((e) => e.type); - assertSubsequence(types, [ - "task.queued", - "task.assigned", - "tool.call", - "approval.requested", - "approval.approved", - "agent.succeeded", - ]); - - runtime.services.close(); - }); -}); diff --git a/test/smoke.ts b/test/smoke.ts deleted file mode 100644 index 80c0a01..0000000 --- a/test/smoke.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { - Agent, - SimulationProvider, - createCompany, - defineTool, - type AgentDefinition, -} from "../src/core/index"; - -const tenant = "tenant_demo"; - -const echo = defineTool({ - name: "kb.search", - description: "Search the knowledge base.", - parameters: { - type: "object", - properties: { query: { type: "string" } }, - required: ["query"], - }, - permission: { category: "read" }, - execute: async (args) => ({ - ok: true, - data: { hits: [`result for ${args.query}`] }, - note: `Found 1 hit for "${args.query}"`, - }), -}); - -// Scripted: one tool call, then a final answer. -const provider = new SimulationProvider([ - { toolCalls: [{ name: "kb.search", arguments: { query: "refund policy" } }] }, - { content: "Resolved: refund issued per 30-day policy." }, -]); - -const def: AgentDefinition = { - id: "agent_support", - name: "Support", - role: "Customer Support", - systemPrompt: "You are a support agent. Use kb.search then answer.", - tools: ["kb.search"], - maxSteps: 4, -}; - -const runtime = createCompany({ - provider, - config: { store: { inMemory: true }, logLevel: "warn" }, - agentFactory: (deps) => new Agent(deps), - agents: [def], - tools: [echo], -}); - -const task = await runtime.submit({ - tenantId: tenant, - title: "Customer refund request", - description: "Customer wants a refund.", - assignedTo: "agent_support", -}); - -const finished = await runtime.services.orchestrator.runToCompletion(task); -console.log("FINAL STATE:", finished?.state); -console.log("OUTPUT:", finished?.output?.summary); -console.log("EVENTS:", runtime.services.store.recentEvents().length); -runtime.services.close(); -if (finished?.state !== "succeeded") { - console.error("SMOKE TEST FAILED"); - process.exit(1); -} -console.log("SMOKE TEST OK"); diff --git a/test/store.test.ts b/test/store.test.ts deleted file mode 100644 index 2f9f1ed..0000000 --- a/test/store.test.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { describe, it, expect, beforeEach, afterEach } from "vitest"; -import { Store, newTask, MemoryStore } from "../src/core/index"; -import { silentLogger } from "./helpers"; - -describe("Store", () => { - let store: Store; - - beforeEach(() => { - store = new Store({ inMemory: true }); - }); - afterEach(() => store.close()); - - it("inserts and retrieves a task", () => { - const t = newTask({ tenantId: "t", title: "Ship feature", description: "do it" }); - store.insertTask(t); - const got = store.getTask(t.id); - expect(got?.id).toBe(t.id); - expect(got?.state).toBe("queued"); - expect(got?.attempts).toBe(0); - }); - - it("updateTaskState transitions through the lifecycle", () => { - const t = newTask({ tenantId: "t", title: "x", description: "d" }); - store.insertTask(t); - - store.updateTaskState(t.id, { state: "running", startedAt: "2024-01-01T00:00:00Z" }); - expect(store.getTask(t.id)?.state).toBe("running"); - - store.updateTaskState(t.id, { - state: "succeeded", - finishedAt: "2024-01-01T00:00:01Z", - output: { summary: "done" }, - attempts: 2, - }); - const done = store.getTask(t.id); - expect(done?.state).toBe("succeeded"); - expect(done?.finishedAt).toBe("2024-01-01T00:00:01Z"); - expect(done?.output?.summary).toBe("done"); - expect(done?.attempts).toBe(2); - }); - - it("nextQueued returns the highest-priority queued task first", () => { - const low = newTask({ tenantId: "t", title: "low", description: "d", priority: 1 }); - const high = newTask({ tenantId: "t", title: "high", description: "d", priority: 9 }); - const mid = newTask({ tenantId: "t", title: "mid", description: "d", priority: 5 }); - store.insertTask(low); - store.insertTask(high); - store.insertTask(mid); - - expect(store.nextQueued()?.id).toBe(high.id); - store.updateTaskState(high.id, { state: "assigned" }); - expect(store.nextQueued()?.id).toBe(mid.id); - store.updateTaskState(mid.id, { state: "assigned" }); - expect(store.nextQueued()?.id).toBe(low.id); - store.updateTaskState(low.id, { state: "assigned" }); - expect(store.nextQueued()).toBeUndefined(); - }); - - it("inserts and reads back events via recentEvents (newest first)", () => { - store.insertEvent({ - id: "e1", - type: "task.queued", - ts: "2024-01-01T00:00:00Z", - source: "test", - payload: { a: 1 }, - }); - store.insertEvent({ - id: "e2", - type: "task.assigned", - ts: "2024-01-01T00:00:01Z", - source: "test", - payload: { a: 2 }, - }); - expect(store.recentEvents().map((e) => e.type)).toEqual(["task.assigned", "task.queued"]); - expect(store.recentEvents(1).map((e) => e.id)).toEqual(["e2"]); - }); - - it("advances the approval lifecycle (pending → approved)", () => { - store.insertApproval({ - id: "ap1", - tenantId: "t", - agentId: "a", - taskId: "tk", - tool: "billing.x", - args: { amount: 5 }, - reason: "test", - state: "pending", - createdAt: "2024-01-01T00:00:00Z", - }); - expect(store.pendingApprovals()).toHaveLength(1); - - store.setApprovalState("ap1", "approved", "tester"); - const a = store.getApproval("ap1"); - expect(a?.state).toBe("approved"); - expect(a?.decidedBy).toBe("tester"); - expect(a?.decidedAt).toBeDefined(); - expect(store.pendingApprovals()).toHaveLength(0); - }); - - it("sums spend records per task", () => { - const base = { - tenantId: "t", - agentId: "a", - taskId: "tk", - tool: "billing.x", - currency: "USD", - ref: "r", - }; - store.recordSpend({ id: "s1", ...base, amount: 10, ts: "2024-01-01T00:00:00Z" }); - store.recordSpend({ id: "s2", ...base, amount: 25, ts: "2024-01-01T00:00:01Z" }); - expect(store.spendForTask("t", "tk")).toBe(35); - }); - - it("remembers and recalls long-term memory via MemoryStore", () => { - const mem = new MemoryStore(store, silentLogger); - mem.remember({ - tenantId: "t", - agentId: "a", - kind: "fact", - content: "Refunds allowed within 30 days", - tags: ["refund", "policy"], - }); - mem.remember({ - tenantId: "t", - agentId: "a", - kind: "note", - content: "Customer prefers email", - tags: ["prefs"], - }); - - const byQuery = mem.recall("t", "a", { query: "Refunds" }); - expect(byQuery).toHaveLength(1); - expect(byQuery[0]?.content).toContain("Refunds"); - - const byTag = mem.recall("t", "a", { tags: ["prefs"] }); - expect(byTag).toHaveLength(1); - expect(byTag[0]?.tags).toContain("prefs"); - }); -}); diff --git a/test/tool.test.ts b/test/tool.test.ts deleted file mode 100644 index 4cc4a37..0000000 --- a/test/tool.test.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { describe, it, expect } from "vitest"; -import { defineTool, ToolRegistry, validateArgs, schemaOf } from "../src/core/index"; -import { mkCtx } from "./helpers"; - -const echo = defineTool({ - name: "echo", - description: "Echoes back its arguments.", - parameters: { - type: "object", - properties: { - name: { type: "string" }, - count: { type: "integer" }, - tier: { type: "string", enum: ["free", "pro"] }, - }, - required: ["name"], - }, - permission: { category: "read" }, - execute: async (args) => ({ ok: true, data: args }), -}); - -describe("ToolRegistry registration", () => { - it("registers tools and exposes them via list/get/has", () => { - const reg = new ToolRegistry(); - reg.register(echo); - expect(reg.has("echo")).toBe(true); - expect(reg.has("missing")).toBe(false); - expect(reg.get("echo")?.name).toBe("echo"); - expect(reg.list().map((t) => t.name)).toEqual(["echo"]); - }); - - it("rejects duplicate registration", () => { - const reg = new ToolRegistry(); - reg.register(echo); - expect(() => reg.register(echo)).toThrow(/already registered/); - }); - - it("resolves aliases to the canonical tool", () => { - const reg = new ToolRegistry(); - reg.register(echo); - reg.alias("support.echo", "echo"); - expect(reg.has("support.echo")).toBe(true); - expect(reg.get("support.echo")?.name).toBe("echo"); - }); - - it("schemasFor builds schemas only for known tools in the subset", () => { - const reg = new ToolRegistry(); - reg.register(echo); - const schemas = reg.schemasFor(["echo", "unknown"]); - expect(schemas).toHaveLength(1); - expect(schemas[0]?.name).toBe("echo"); - expect(schemas[0]?.parameters).toHaveProperty("properties"); - }); - - it("schemaOf returns name, description and parameters", () => { - const s = schemaOf(echo); - expect(s).toMatchObject({ name: "echo", description: expect.any(String) }); - expect(s.parameters).toBeDefined(); - }); -}); - -describe("validateArgs", () => { - it("accepts valid arguments", () => { - expect(validateArgs(echo, { name: "alice", count: 3, tier: "pro" })).toEqual({ ok: true }); - }); - - it("rejects a missing required field", () => { - const r = validateArgs(echo, { count: 3 }); - expect(r.ok).toBe(false); - expect((r as { error: string }).error).toMatch(/missing required 'name'/); - }); - - it("rejects a wrong-typed field", () => { - const r = validateArgs(echo, { name: "alice", count: "no" }); - expect(r.ok).toBe(false); - expect((r as { error: string }).error).toMatch(/expected integer/); - }); - - it("rejects a value outside the enum", () => { - const r = validateArgs(echo, { name: "alice", tier: "enterprise" }); - expect(r.ok).toBe(false); - expect((r as { error: string }).error).toMatch(/not in enum/); - }); -}); - -describe("ToolRegistry.invoke", () => { - it("validates and executes on success", async () => { - const reg = new ToolRegistry(); - reg.register(echo); - const res = await reg.invoke("echo", { name: "alice" }, mkCtx()); - expect(res.ok).toBe(true); - expect(res.data).toMatchObject({ name: "alice" }); - }); - - it("returns an error result for unknown tools", async () => { - const reg = new ToolRegistry(); - const res = await reg.invoke("nope", {}, mkCtx()); - expect(res.ok).toBe(false); - expect(res.error).toMatch(/Unknown tool/); - }); - - it("returns an error result for invalid arguments (never executes)", async () => { - const reg = new ToolRegistry(); - reg.register(echo); - const res = await reg.invoke("echo", {}, mkCtx()); - expect(res.ok).toBe(false); - expect(res.error).toMatch(/Invalid arguments/); - }); - - it("traps a throwing handler and reports the error", async () => { - const boom = defineTool({ - name: "boom", - description: "always throws", - parameters: { type: "object", properties: {} }, - permission: { category: "read" }, - execute: async () => { - throw new Error("kaboom"); - }, - }); - const reg = new ToolRegistry(); - reg.register(boom); - const res = await reg.invoke("boom", {}, mkCtx()); - expect(res.ok).toBe(false); - expect(res.error).toMatch(/threw/); - expect(res.error).toMatch(/kaboom/); - }); -}); diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 0000000..f30f535 --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "declaration": true, + "sourceMap": true, + "resolveJsonModule": true + } +} diff --git a/tsconfig.json b/tsconfig.json index 838220d..d40e753 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,24 +1,9 @@ { - "compilerOptions": { - "target": "ES2022", - "module": "ESNext", - "moduleResolution": "Bundler", - "lib": ["ES2022"], - "outDir": "dist", - "rootDir": "src", - "strict": true, - "noUncheckedIndexedAccess": true, - "noImplicitOverride": true, - "noFallthroughCasesInSwitch": true, - "exactOptionalPropertyTypes": false, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "declaration": false, - "sourceMap": true, - "skipLibCheck": true, - "types": ["node"] - }, - "include": ["src/**/*.ts"], - "exclude": ["node_modules", "dist", "test"] + "files": [], + "references": [ + { "path": "packages/core" }, + { "path": "packages/mcp-knowledge" }, + { "path": "apps/api" }, + { "path": "apps/console" } + ] } diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..5a1e427 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + include: ["test/**/*.test.ts"], + testTimeout: 60_000, + }, +});