From aeb0b5aad271e17eaef6752ef9a9c67a3a4a0bd3 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 26 Mar 2026 19:59:44 +0000 Subject: [PATCH 1/3] Add README, governance files, and fix license to Apache-2.0 - Create comprehensive README with app documentation, setup guide, architecture overview, and a section teaching about governance files - Add CONTRIBUTING.md with contribution guidelines and dev setup - Add CODE_OF_CONDUCT.md based on Contributor Covenant 2.1 - Add SECURITY.md with vulnerability reporting and security architecture - Fix package.json license field from "MIT" to "Apache-2.0" to match the actual LICENSE file - Update LICENSE copyright from placeholder to "2025 NeuroverseOS" https://claude.ai/code/session_01Fo4Up2mMrkuQv7bUS1PiqG --- CODE_OF_CONDUCT.md | 41 ++++++++++++ CONTRIBUTING.md | 60 +++++++++++++++++ LICENSE | 2 +- README.md | 160 +++++++++++++++++++++++++++++++++++++++++++++ SECURITY.md | 46 +++++++++++++ package.json | 2 +- 6 files changed, 309 insertions(+), 2 deletions(-) create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 README.md create mode 100644 SECURITY.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..0f76bad --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,41 @@ +# Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in the StarTalk community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behavior: + +- The use of sexualized language or imagery, and sexual attention or advances of any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information without explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the project team at **team@neuroverseos.com**. All complaints will be reviewed and investigated promptly and fairly. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), version 2.1. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..3d286c9 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,60 @@ +# Contributing to StarTalk + +Thank you for your interest in contributing to StarTalk! This document provides guidelines and information for contributors. + +## How to Contribute + +### Reporting Issues + +- Use [GitHub Issues](https://github.com/NeuroverseOS/startalk/issues) to report bugs or suggest features. +- Include steps to reproduce any bug, along with your environment details (Node version, OS, AI provider). +- Check existing issues before opening a new one to avoid duplicates. + +### Submitting Changes + +1. Fork the repository. +2. Create a feature branch from `main` (`git checkout -b feature/your-feature`). +3. Make your changes and test them locally with `npm run dev`. +4. Commit with clear, descriptive messages. +5. Push to your fork and open a pull request against `main`. + +### Pull Request Guidelines + +- Keep PRs focused on a single change. +- Describe what the PR does and why. +- Reference any related issues. +- Ensure your code follows the existing TypeScript style in the project. + +## Development Setup + +```bash +git clone https://github.com/NeuroverseOS/startalk.git +cd startalk +npm install +npm run dev +``` + +**Requirements:** +- Node.js 20+ +- A MentraOS app API key (`MENTRA_APP_API_KEY` environment variable) +- An AI provider API key (Anthropic or OpenAI, configured in app settings) + +## Areas of Contribution + +- **Zodiac sign definitions** (`src/signs/*.nv-world.md`): Refine traits, communication styles, compatibility, and mode directives. +- **Interaction modes**: Improve mode selection logic and response quality. +- **Governance integration**: Enhance trust scoring and guard evaluation. +- **People memory**: Improve the persistent people chart system. +- **Documentation**: Improve the README, guides, and inline comments. + +## Zodiac World File Format + +If you are contributing to sign definitions, each `.nv-world.md` file follows a structured format with frontmatter metadata and markdown sections for thesis, traits, communication, modes, compatibility, and tone. See any existing sign file in `src/signs/` as a reference. + +## Code of Conduct + +All contributors are expected to follow our [Code of Conduct](CODE_OF_CONDUCT.md). Please be respectful and constructive. + +## License + +By contributing to StarTalk, you agree that your contributions will be licensed under the [Apache License 2.0](LICENSE). diff --git a/LICENSE b/LICENSE index 261eeb9..a149a5a 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright 2025 NeuroverseOS Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..fa5b6a7 --- /dev/null +++ b/README.md @@ -0,0 +1,160 @@ +# StarTalk + +Astrological translator for MentraOS smart glasses. Set your signs. Tap. Get cosmic perspective. + +StarTalk is an AI-powered app that translates real-time social moments through the lens of your astrological chart. Running on MentraOS smart glasses, it gives you cosmic insight into what's happening around you — who you're talking to, how your signs interact, and what the stars have to say about the moment. + +## How It Works + +1. **Set your chart** in app settings: pick your Sun Sign (core identity) and optionally your Rising Sign (how you come across). +2. **Tap or say "star"** to get a cosmic read on the current moment. +3. **Tap again within 30 seconds** to expand or follow up. +4. **Long press** to dismiss. + +StarTalk auto-selects the best interaction mode for each moment: + +| Mode | When It Fires | Example | +|---|---|---| +| **Translate** | Understanding someone else's behavior | "That's classic Taurus — they're not ignoring you, they're processing." | +| **Reflect** | Understanding your own behavior | "Your Cancer moon is holding onto that comment. Let it go." | +| **Challenge** | Falling into shadow traits | "You're avoiding the conversation. That's your Libra dodge." | +| **Teach** | Learning the dynamic at play | "Fire signs speak to act. Water signs speak to connect." | +| **Direct** | Need a clear recommendation | "Say it now. Aries energy rewards directness." | + +## Features + +### World Stacking + +StarTalk's core innovation. Your Sun Sign defines *who you are*; your Rising Sign defines *how you present*. StarTalk combines both into a unified personality lens, weighting the sun as dominant and the rising as a modifier. A Cancer with Aries rising gets emotionally deep reads delivered with bold directness. + +### People Memory + +Tell StarTalk about the people in your life: +- "Sophie is a Cancer with Aries rising" +- "talking to Sophie" (recalls her chart automatically) +- "talking to a Taurus" (anonymous sign-based translation) + +People are persisted across sessions via MentraOS SimpleStorage. + +### Ambient Context + +With explicit opt-in (and bystander acknowledgment), StarTalk can listen to nearby conversation and factor it into reads. The audio buffer is configurable (1, 2, or 5 minutes) and is held in memory only — never persisted or transmitted beyond the AI call. + +### AI Provider Choice + +StarTalk uses a bring-your-own-key model. Choose between: +- **Anthropic Claude** (Claude Haiku 4.5) +- **OpenAI GPT** (GPT-4o Mini) + +No data is retained. Your API key goes directly to your chosen provider. + +## Getting Started + +### Prerequisites + +- Node.js 20+ +- A MentraOS app API key +- An Anthropic or OpenAI API key + +### Install and Run + +```bash +git clone https://github.com/NeuroverseOS/startalk.git +cd startalk +npm install +npm run dev +``` + +Set the `MENTRA_APP_API_KEY` environment variable before starting. + +### Docker + +```bash +docker build -t startalk . +docker run -p 3001:3001 -e MENTRA_APP_API_KEY= startalk +``` + +The container runs as a non-root user on port 3001 with a built-in health check at `/health`. + +## Project Structure + +``` +startalk/ + src/ + server.ts Main MentraOS app server + sign-loader.ts Zodiac world parser and prompt builder + signs/ + aries.nv-world.md Zodiac sign world definitions (x12) + taurus.nv-world.md + ... + app_config.json MentraOS settings schema + mentra.app.json App metadata and permissions + Dockerfile Multi-stage container build +``` + +### Zodiac World Files + +Each sign is defined in a `.nv-world.md` file — the NeuroverseOS world definition format. These structured markdown files contain frontmatter metadata (element, modality, ruling planet, dates) followed by sections for thesis, traits, communication style, the five interaction modes, compatibility, and tone. The sign-loader parses these at startup and caches them for prompt building. + +## NeuroverseOS Governance Integration + +StarTalk integrates with the [NeuroverseOS governance framework](https://github.com/NeuroverseOS/neuroverseos-governance), which provides three layers of runtime safety: + +- **Guard Engine** (`evaluateGuard()`): Checks all AI inputs and outputs against platform content rules before anything is displayed on the glasses. +- **World Simulation** (`simulateWorld()`): Evaluates trust scores based on session behavior (AI call volume, activation patterns). Trust gates adjust response length invisibly to the user — from full 50-word reads at high trust down to glance-only 15-word reads at low trust, with responses disabled entirely if trust drops to revoked levels. +- **Governed Executor** (`MentraGovernedExecutor`): Hooks into the MentraOS app lifecycle with callbacks for blocking or pausing actions that violate rules. + +The app declares its AI usage transparently in `mentra.app.json`, including that AI is opt-in only, keys are user-provided, and data retention is none. + +## Repository Governance Files + +Open-source projects benefit from clear governance — a set of files that define how the project is licensed, how people can contribute, what behavior is expected, and how security issues should be handled. GitHub recognizes these files and surfaces them in the repository's community profile. Here is how StarTalk uses each one: + +### LICENSE + +The [LICENSE](LICENSE) file contains the full text of the **Apache License 2.0**. This is the legal foundation of the project. It grants users a perpetual, royalty-free right to use, modify, and distribute StarTalk (including for commercial purposes), while requiring attribution and notice of changes. It also includes an explicit patent grant, which provides additional legal protection for contributors and users. The Apache 2.0 license was chosen for its balance of openness and legal clarity. + +### CONTRIBUTING.md + +The [CONTRIBUTING.md](CONTRIBUTING.md) file is the guide for anyone who wants to help improve StarTalk. It covers how to report bugs, submit pull requests, set up a development environment, and which areas of the codebase welcome contributions (sign definitions, interaction modes, governance integration, people memory, and documentation). Having clear contribution guidelines lowers the barrier to entry and keeps the project's quality consistent. + +### CODE_OF_CONDUCT.md + +The [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) sets behavioral expectations for everyone participating in the StarTalk community — contributors, maintainers, and users interacting in project spaces. It is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), the most widely adopted code of conduct in open source. It defines what positive participation looks like, what is not acceptable, and how violations are handled. + +### SECURITY.md + +The [SECURITY.md](SECURITY.md) file tells security researchers how to responsibly disclose vulnerabilities. Instead of opening public issues (which would expose the vulnerability to everyone), reporters email the team directly. The file also documents StarTalk's security architecture: BYO-key model with no key storage, opt-in ambient listening with bystander acknowledgment, governance-layer content filtering, and non-root Docker deployment. + +### Why Governance Files Matter + +These four files together form the governance foundation of an open-source project: + +- **LICENSE** answers: "Can I use this, and under what terms?" +- **CONTRIBUTING.md** answers: "How do I help?" +- **CODE_OF_CONDUCT.md** answers: "What behavior is expected?" +- **SECURITY.md** answers: "What do I do if I find a vulnerability?" + +Without them, potential contributors don't know if they're welcome, users don't know their legal rights, and security researchers don't know how to reach you. GitHub tracks these files as part of your repository's "Community Standards" and will prompt you to add any that are missing. + +## Tech Stack + +- **Runtime**: Node.js 20+ / TypeScript +- **Framework**: MentraOS SDK (`@mentra/sdk`) +- **AI**: Anthropic Claude SDK, OpenAI SDK (user-selected) +- **Governance**: NeuroverseOS governance framework +- **Deployment**: Docker (multi-stage, non-root) + +## License + +StarTalk is licensed under the [Apache License 2.0](LICENSE). + +``` +Copyright 2025 NeuroverseOS + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 +``` diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..6c7f101 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,46 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | +| ------- | ------------------ | +| 1.0.x | Yes | + +## Reporting a Vulnerability + +If you discover a security vulnerability in StarTalk, please report it responsibly. + +**Do not open a public GitHub issue for security vulnerabilities.** + +Instead, email **team@neuroverseos.com** with: + +- A description of the vulnerability +- Steps to reproduce it +- The potential impact +- Any suggested fixes (if applicable) + +We will acknowledge receipt within 48 hours and aim to provide an initial assessment within 5 business days. + +## Security Considerations + +### AI Provider API Keys + +- StarTalk uses a BYO-key model. User API keys are passed directly to the configured AI provider (Anthropic or OpenAI) and are never stored, logged, or transmitted elsewhere. +- Keys are configured via the MentraOS app settings interface using the `secret` field type. + +### Ambient Listening + +- Ambient audio capture is opt-in only and disabled by default. +- Users must explicitly enable ambient mode **and** acknowledge the bystander notice before any audio processing occurs. +- Audio buffers are held in memory only and are not persisted to disk or transmitted beyond the AI provider call. + +### Governance and Content Safety + +- All AI inputs and outputs are checked through the NeuroverseOS governance framework (`evaluateGuard()`) before being displayed. +- Trust scoring (`simulateWorld()`) provides behavioral degradation if anomalous usage patterns are detected. +- The app declares `"data_retention": "none"` in its MentraOS manifest. + +### Docker Deployment + +- The container runs as a non-root user (`startalk`, uid 1001). +- The Dockerfile uses a multi-stage build to minimize the attack surface of the runtime image. diff --git a/package.json b/package.json index 5b43c23..c3300f0 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "neuroverseos" ], "author": "NeuroverseOS", - "license": "MIT", + "license": "Apache-2.0", "repository": { "type": "git", "url": "https://github.com/NeuroverseOS/startalk" From b85412b70f7acae442bcfabb1ae1ac68ba7b9d14 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 26 Mar 2026 20:05:33 +0000 Subject: [PATCH 2/3] Add tsconfig, vitest tests, CI workflow, and governance module - Add tsconfig.json for deterministic TypeScript builds (ES2022, NodeNext) - Add vitest with 78 tests across 3 test files: - governance.test.ts: gate transitions, word limits, trust degradation chain - sign-loader.test.ts: world loading, parsing, stacking, compatibility - patterns.test.ts: speech recognition regex validation - Extract governance logic into testable src/governance.ts module - Add type declarations for private platform dependencies (external.d.ts) - Add GitHub Actions CI workflow (typecheck, test, docker build) - Add .gitignore for node_modules and dist - Fix server.ts import path for NodeNext module resolution https://claude.ai/code/session_01Fo4Up2mMrkuQv7bUS1PiqG --- .github/workflows/ci.yml | 24 + .gitignore | 3 + package-lock.json | 2121 +++++++++++++++++++++++++++++ package.json | 8 +- src/__tests__/governance.test.ts | 180 +++ src/__tests__/patterns.test.ts | 155 +++ src/__tests__/sign-loader.test.ts | 204 +++ src/governance.ts | 98 ++ src/server.ts | 2 +- src/types/external.d.ts | 90 ++ tsconfig.json | 14 + 11 files changed, 2896 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 .gitignore create mode 100644 package-lock.json create mode 100644 src/__tests__/governance.test.ts create mode 100644 src/__tests__/patterns.test.ts create mode 100644 src/__tests__/sign-loader.test.ts create mode 100644 src/governance.ts create mode 100644 src/types/external.d.ts create mode 100644 tsconfig.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3527efe --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,24 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - run: npm ci + - run: npx tsc --noEmit + - run: npx vitest run + - run: docker build . diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f4e2c6d --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +dist/ +*.tsbuildinfo diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..a948ad2 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2121 @@ +{ + "name": "neuroverseos-startalk", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "neuroverseos-startalk", + "version": "1.0.0", + "license": "Apache-2.0", + "dependencies": { + "@anthropic-ai/sdk": "^0.52.0", + "@mentra/sdk": "^3.0.0", + "neuroverseos-governance": "github:NeuroverseOS/neuroverseos-governance", + "openai": "^4.86.0" + }, + "devDependencies": { + "tsx": "^4.21.0", + "typescript": "^5.7.0", + "vitest": "^3.1.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.52.0", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.52.0.tgz", + "integrity": "sha512-d4c+fg+xy9e46c8+YnrrgIQR45CZlAi7PwdzIfDXDM6ACxEZli1/fxhURsq30ZpMZy6LvSkr41jGq5aF5TD7rQ==", + "license": "MIT", + "bin": { + "anthropic-ai-sdk": "bin/cli" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", + "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz", + "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz", + "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz", + "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz", + "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz", + "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz", + "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz", + "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz", + "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz", + "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz", + "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz", + "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz", + "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz", + "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz", + "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz", + "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz", + "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz", + "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz", + "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz", + "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz", + "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz", + "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz", + "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz", + "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz", + "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz", + "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "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/@mentra/sdk": { + "version": "3.0.0" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.0.tgz", + "integrity": "sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.0.tgz", + "integrity": "sha512-u6JHLll5QKRvjciE78bQXDmqRqNs5M/3GVqZeMwvmjaNODJih/WIrJlFVEihvV0MiYFmd+ZyPr9wxOVbPAG2Iw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.0.tgz", + "integrity": "sha512-qEF7CsKKzSRc20Ciu2Zw1wRrBz4g56F7r/vRwY430UPp/nt1x21Q/fpJ9N5l47WWvJlkNCPJz3QRVw008fi7yA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.0.tgz", + "integrity": "sha512-WADYozJ4QCnXCH4wPB+3FuGmDPoFseVCUrANmA5LWwGmC6FL14BWC7pcq+FstOZv3baGX65tZ378uT6WG8ynTw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.0.tgz", + "integrity": "sha512-6b8wGHJlDrGeSE3aH5mGNHBjA0TTkxdoNHik5EkvPHCt351XnigA4pS7Wsj/Eo9Y8RBU6f35cjN9SYmCFBtzxw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.0.tgz", + "integrity": "sha512-h25Ga0t4jaylMB8M/JKAyrvvfxGRjnPQIR8lnCayyzEjEOx2EJIlIiMbhpWxDRKGKF8jbNH01NnN663dH638mA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.0.tgz", + "integrity": "sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.0.tgz", + "integrity": "sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.0.tgz", + "integrity": "sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.0.tgz", + "integrity": "sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.0.tgz", + "integrity": "sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.0.tgz", + "integrity": "sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.0.tgz", + "integrity": "sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.0.tgz", + "integrity": "sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.0.tgz", + "integrity": "sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.0.tgz", + "integrity": "sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.0.tgz", + "integrity": "sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.0.tgz", + "integrity": "sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.0.tgz", + "integrity": "sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.0.tgz", + "integrity": "sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.0.tgz", + "integrity": "sha512-pESDkos/PDzYwtyzB5p/UoNU/8fJo68vcXM9ZW2V0kjYayj1KaaUfi1NmTUTUpMn4UhU4gTuK8gIaFO4UGuMbA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.0.tgz", + "integrity": "sha512-hj1wFStD7B1YBeYmvY+lWXZ7ey73YGPcViMShYikqKT1GtstIKQAtfUI6yrzPjAy/O7pO0VLXGmUVWXQMaYgTQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.0.tgz", + "integrity": "sha512-SyaIPFoxmUPlNDq5EHkTbiKzmSEmq/gOYFI/3HHJ8iS/v1mbugVa7dXUzcJGQfoytp9DJFLhHH4U3/eTy2Bq4w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.0.tgz", + "integrity": "sha512-RdcryEfzZr+lAr5kRm2ucN9aVlCCa2QNq4hXelZxb8GG0NJSazq44Z3PCCc8wISRuCVnGs0lQJVX5Vp6fKA+IA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.0.tgz", + "integrity": "sha512-PrsWNQ8BuE00O3Xsx3ALh2Df8fAj9+cvvX9AIA6o4KpATR98c9mud4XtDWVvsEuyia5U4tVSTKygawyJkjm60w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", + "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/node-fetch": { + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz", + "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.4" + } + }, + "node_modules/@vitest/expect": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.2.4", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", + "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.2.4", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", + "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^4.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "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", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/chai": { + "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/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/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "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": ">=6" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "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" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.27.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz", + "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.4", + "@esbuild/android-arm": "0.27.4", + "@esbuild/android-arm64": "0.27.4", + "@esbuild/android-x64": "0.27.4", + "@esbuild/darwin-arm64": "0.27.4", + "@esbuild/darwin-x64": "0.27.4", + "@esbuild/freebsd-arm64": "0.27.4", + "@esbuild/freebsd-x64": "0.27.4", + "@esbuild/linux-arm": "0.27.4", + "@esbuild/linux-arm64": "0.27.4", + "@esbuild/linux-ia32": "0.27.4", + "@esbuild/linux-loong64": "0.27.4", + "@esbuild/linux-mips64el": "0.27.4", + "@esbuild/linux-ppc64": "0.27.4", + "@esbuild/linux-riscv64": "0.27.4", + "@esbuild/linux-s390x": "0.27.4", + "@esbuild/linux-x64": "0.27.4", + "@esbuild/netbsd-arm64": "0.27.4", + "@esbuild/netbsd-x64": "0.27.4", + "@esbuild/openbsd-arm64": "0.27.4", + "@esbuild/openbsd-x64": "0.27.4", + "@esbuild/openharmony-arm64": "0.27.4", + "@esbuild/sunos-x64": "0.27.4", + "@esbuild/win32-arm64": "0.27.4", + "@esbuild/win32-ia32": "0.27.4", + "@esbuild/win32-x64": "0.27.4" + } + }, + "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/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", + "license": "MIT" + }, + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "license": "MIT", + "dependencies": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "engines": { + "node": ">= 12.20" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-tsconfig": { + "version": "4.13.7", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.7.tgz", + "integrity": "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "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/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/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/neuroverseos-governance": { + "name": "@neuroverseos/governance", + "version": "0.4.0", + "resolved": "git+ssh://git@github.com/NeuroverseOS/neuroverseos-governance.git#7f31da4a38b70da54db567531fbfb0658825d510", + "license": "Apache-2.0", + "bin": { + "neuroverse": "dist/cli/neuroverse.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/openai": { + "version": "4.104.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.104.0.tgz", + "integrity": "sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==", + "license": "Apache-2.0", + "dependencies": { + "@types/node": "^18.11.18", + "@types/node-fetch": "^2.6.4", + "abort-controller": "^3.0.0", + "agentkeepalive": "^4.2.1", + "form-data-encoder": "1.7.2", + "formdata-node": "^4.3.2", + "node-fetch": "^2.6.7" + }, + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "ws": "^8.18.0", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "ws": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/openai/node_modules/@types/node": { + "version": "18.19.130", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", + "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/openai/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "license": "MIT" + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "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", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "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.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/rollup": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.0.tgz", + "integrity": "sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.0", + "@rollup/rollup-android-arm64": "4.60.0", + "@rollup/rollup-darwin-arm64": "4.60.0", + "@rollup/rollup-darwin-x64": "4.60.0", + "@rollup/rollup-freebsd-arm64": "4.60.0", + "@rollup/rollup-freebsd-x64": "4.60.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.0", + "@rollup/rollup-linux-arm-musleabihf": "4.60.0", + "@rollup/rollup-linux-arm64-gnu": "4.60.0", + "@rollup/rollup-linux-arm64-musl": "4.60.0", + "@rollup/rollup-linux-loong64-gnu": "4.60.0", + "@rollup/rollup-linux-loong64-musl": "4.60.0", + "@rollup/rollup-linux-ppc64-gnu": "4.60.0", + "@rollup/rollup-linux-ppc64-musl": "4.60.0", + "@rollup/rollup-linux-riscv64-gnu": "4.60.0", + "@rollup/rollup-linux-riscv64-musl": "4.60.0", + "@rollup/rollup-linux-s390x-gnu": "4.60.0", + "@rollup/rollup-linux-x64-gnu": "4.60.0", + "@rollup/rollup-linux-x64-musl": "4.60.0", + "@rollup/rollup-openbsd-x64": "4.60.0", + "@rollup/rollup-openharmony-arm64": "4.60.0", + "@rollup/rollup-win32-arm64-msvc": "4.60.0", + "@rollup/rollup-win32-ia32-msvc": "4.60.0", + "@rollup/rollup-win32-x64-gnu": "4.60.0", + "@rollup/rollup-win32-x64-msvc": "4.60.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "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/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": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "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" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "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": "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": { + "node": ">=14.0.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/tsx": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz", + "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.27.0", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "license": "MIT" + }, + "node_modules/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.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" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.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", + "sugarss": "^5.0.0", + "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/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/vitest": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", + "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.4", + "@vitest/mocker": "3.2.4", + "@vitest/pretty-format": "^3.2.4", + "@vitest/runner": "3.2.4", + "@vitest/snapshot": "3.2.4", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "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.2", + "std-env": "^3.9.0", + "tinybench": "^2.9.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": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.4", + "@vitest/ui": "3.2.4", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + } + } +} diff --git a/package.json b/package.json index c3300f0..782b26e 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,10 @@ "type": "module", "scripts": { "start": "tsx src/server.ts", - "dev": "tsx watch src/server.ts" + "dev": "tsx watch src/server.ts", + "test": "vitest run", + "test:watch": "vitest", + "typecheck": "tsc --noEmit" }, "dependencies": { "@mentra/sdk": "^3.0.0", @@ -15,7 +18,8 @@ }, "devDependencies": { "tsx": "^4.21.0", - "typescript": "^5.7.0" + "typescript": "^5.7.0", + "vitest": "^3.1.0" }, "keywords": [ "mentraos", diff --git a/src/__tests__/governance.test.ts b/src/__tests__/governance.test.ts new file mode 100644 index 0000000..b17ae51 --- /dev/null +++ b/src/__tests__/governance.test.ts @@ -0,0 +1,180 @@ +import { describe, it, expect } from 'vitest'; +import { + trustToGate, + gateAdjustments, + evaluateTrustFromMetrics, + stripModeTag, + WORDS_DEPTH, + WORDS_GLANCE, +} from '../governance.js'; + +// ─── Gate Transitions ───────────────────────────────────────────────────────── + +describe('trustToGate', () => { + it('returns ACTIVE at trust 100', () => { + expect(trustToGate(100)).toBe('ACTIVE'); + }); + + it('returns ACTIVE at trust 70 (boundary)', () => { + expect(trustToGate(70)).toBe('ACTIVE'); + }); + + it('returns DEGRADED at trust 69', () => { + expect(trustToGate(69)).toBe('DEGRADED'); + }); + + it('returns DEGRADED at trust 31', () => { + expect(trustToGate(31)).toBe('DEGRADED'); + }); + + it('returns SUSPENDED at trust 30 (boundary)', () => { + expect(trustToGate(30)).toBe('SUSPENDED'); + }); + + it('returns SUSPENDED at trust 11', () => { + expect(trustToGate(11)).toBe('SUSPENDED'); + }); + + it('returns REVOKED at trust 10 (boundary)', () => { + expect(trustToGate(10)).toBe('REVOKED'); + }); + + it('returns REVOKED at trust 0', () => { + expect(trustToGate(0)).toBe('REVOKED'); + }); + + it('returns REVOKED for negative trust', () => { + expect(trustToGate(-5)).toBe('REVOKED'); + }); +}); + +// ─── Word Limit Adjustments ────────────────────────────────────────────────── + +describe('gateAdjustments', () => { + it('ACTIVE gate returns full depth words (50)', () => { + expect(gateAdjustments('ACTIVE').maxWords).toBe(WORDS_DEPTH); + expect(gateAdjustments('ACTIVE').maxWords).toBe(50); + }); + + it('DEGRADED gate returns 60% of depth (30)', () => { + expect(gateAdjustments('DEGRADED').maxWords).toBe(Math.round(WORDS_DEPTH * 0.6)); + expect(gateAdjustments('DEGRADED').maxWords).toBe(30); + }); + + it('SUSPENDED gate returns glance words (15)', () => { + expect(gateAdjustments('SUSPENDED').maxWords).toBe(WORDS_GLANCE); + expect(gateAdjustments('SUSPENDED').maxWords).toBe(15); + }); + + it('REVOKED gate returns 0 (no responses)', () => { + expect(gateAdjustments('REVOKED').maxWords).toBe(0); + }); +}); + +// ─── Trust Degradation from Metrics ────────────────────────────────────────── + +describe('evaluateTrustFromMetrics', () => { + const baseMetrics = { + activations: 0, + aiCalls: 0, + aiFailures: 0, + dismissals: 0, + ambientSends: 0, + }; + + it('trust unchanged with fewer than 5 dismissals', () => { + expect(evaluateTrustFromMetrics(100, { ...baseMetrics, dismissals: 4 })).toBe(100); + }); + + it('trust degrades by 0.85 at exactly 5 dismissals', () => { + expect(evaluateTrustFromMetrics(100, { ...baseMetrics, dismissals: 5 })).toBe(85); + }); + + it('trust degrades by 0.85 at 10 dismissals', () => { + expect(evaluateTrustFromMetrics(100, { ...baseMetrics, dismissals: 10 })).toBe(85); + }); + + it('repeated evaluations compound degradation', () => { + let trust = 100; + const metrics = { ...baseMetrics, dismissals: 5 }; + trust = evaluateTrustFromMetrics(trust, metrics); + expect(trust).toBe(85); + trust = evaluateTrustFromMetrics(trust, metrics); + expect(trust).toBeCloseTo(72.25); + trust = evaluateTrustFromMetrics(trust, metrics); + expect(trust).toBeCloseTo(61.41, 1); + }); + + it('trust never goes below 0', () => { + expect(evaluateTrustFromMetrics(1, { ...baseMetrics, dismissals: 100 })).toBeGreaterThanOrEqual(0); + }); + + it('trust never exceeds 100', () => { + expect(evaluateTrustFromMetrics(100, baseMetrics)).toBeLessThanOrEqual(100); + }); + + it('full degradation chain: ACTIVE → DEGRADED → SUSPENDED → REVOKED', () => { + let trust = 100; + const metrics = { ...baseMetrics, dismissals: 5 }; + + // Each evaluation applies trust *= 0.85 + expect(trustToGate(trust)).toBe('ACTIVE'); // 100 + + trust = evaluateTrustFromMetrics(trust, metrics); // 85 + expect(trustToGate(trust)).toBe('ACTIVE'); + + trust = evaluateTrustFromMetrics(trust, metrics); // 72.25 + expect(trustToGate(trust)).toBe('ACTIVE'); + + trust = evaluateTrustFromMetrics(trust, metrics); // 61.41 + expect(trustToGate(trust)).toBe('DEGRADED'); + + // Keep degrading + for (let i = 0; i < 5; i++) { + trust = evaluateTrustFromMetrics(trust, metrics); + } + expect(trustToGate(trust)).toBe('SUSPENDED'); + + // Keep going to REVOKED + for (let i = 0; i < 10; i++) { + trust = evaluateTrustFromMetrics(trust, metrics); + } + expect(trustToGate(trust)).toBe('REVOKED'); + }); +}); + +// ─── Mode Tag Stripping ────────────────────────────────────────────────────── + +describe('stripModeTag', () => { + it('strips [MODE:translate] and returns mode', () => { + const result = stripModeTag('[MODE:translate]\nYour Cancer instinct is right here.'); + expect(result.mode).toBe('translate'); + expect(result.displayText).toBe('Your Cancer instinct is right here.'); + }); + + it('strips [MODE:direct] without trailing newline', () => { + const result = stripModeTag('[MODE:direct]Say it now.'); + expect(result.mode).toBe('direct'); + expect(result.displayText).toBe('Say it now.'); + }); + + it('returns null mode when no tag present', () => { + const result = stripModeTag('Just a normal response.'); + expect(result.mode).toBeNull(); + expect(result.displayText).toBe('Just a normal response.'); + }); + + it('only strips tag at the start of the text', () => { + const text = 'Some text [MODE:reflect] more text'; + const result = stripModeTag(text); + expect(result.mode).toBeNull(); + expect(result.displayText).toBe(text); + }); + + it('handles all five modes', () => { + for (const mode of ['translate', 'reflect', 'challenge', 'teach', 'direct']) { + const result = stripModeTag(`[MODE:${mode}]\nTest.`); + expect(result.mode).toBe(mode); + } + }); +}); diff --git a/src/__tests__/patterns.test.ts b/src/__tests__/patterns.test.ts new file mode 100644 index 0000000..e1699c2 --- /dev/null +++ b/src/__tests__/patterns.test.ts @@ -0,0 +1,155 @@ +import { describe, it, expect } from 'vitest'; + +/** + * Tests for the regex patterns used in server.ts for speech recognition. + * These patterns gate all user interactions — correctness is critical. + */ + +const STAR_TRIGGER_PATTERN = /\b(?:star\s*(?:talk)?|what\s+do\s+the\s+stars\s+say)\b/i; +const HELP_PATTERN = /^(?:help|show\s+me\s+commands|how\s+does\s+this\s+work)\b/i; +const RESET_PATTERN = /\b(?:new\s+(?:conversation|chat|call)|reset|start\s+over|clear)\b/i; +const OTHER_SIGN_PATTERN = /\b(?:talking\s+to\s+(?:a\s+)?|they(?:'re|\s+are)\s+(?:a\s+)?)(\w+)\b/i; +const PERSON_SIGN_PATTERN = /(\w+)\s+is\s+(?:a\s+)?(\w+)(?:\s+with\s+(\w+)\s+rising)?/i; +const TALKING_TO_PERSON_PATTERN = /\b(?:talking\s+to|with|meeting)\s+(\w+)\b/i; + +describe('STAR_TRIGGER_PATTERN', () => { + it('matches "star"', () => { + expect(STAR_TRIGGER_PATTERN.test('star')).toBe(true); + }); + + it('matches "star talk"', () => { + expect(STAR_TRIGGER_PATTERN.test('star talk')).toBe(true); + }); + + it('matches "what do the stars say"', () => { + expect(STAR_TRIGGER_PATTERN.test('what do the stars say')).toBe(true); + }); + + it('matches case-insensitive', () => { + expect(STAR_TRIGGER_PATTERN.test('Star')).toBe(true); + expect(STAR_TRIGGER_PATTERN.test('STAR TALK')).toBe(true); + }); + + it('matches star within a sentence', () => { + expect(STAR_TRIGGER_PATTERN.test('hey star what\'s up')).toBe(true); + }); + + it('does not match "starting"', () => { + expect(STAR_TRIGGER_PATTERN.test('starting the meeting')).toBe(false); + }); +}); + +describe('HELP_PATTERN', () => { + it('matches "help"', () => { + expect(HELP_PATTERN.test('help')).toBe(true); + }); + + it('matches "show me commands"', () => { + expect(HELP_PATTERN.test('show me commands')).toBe(true); + }); + + it('matches "how does this work"', () => { + expect(HELP_PATTERN.test('how does this work')).toBe(true); + }); + + it('only matches at start of string', () => { + expect(HELP_PATTERN.test('I need help')).toBe(false); + }); +}); + +describe('RESET_PATTERN', () => { + it('matches "new conversation"', () => { + expect(RESET_PATTERN.test('new conversation')).toBe(true); + }); + + it('matches "reset"', () => { + expect(RESET_PATTERN.test('reset')).toBe(true); + }); + + it('matches "start over"', () => { + expect(RESET_PATTERN.test('start over')).toBe(true); + }); + + it('matches "clear"', () => { + expect(RESET_PATTERN.test('clear')).toBe(true); + }); + + it('matches within a sentence', () => { + expect(RESET_PATTERN.test('I want to start over please')).toBe(true); + }); +}); + +describe('PERSON_SIGN_PATTERN', () => { + it('matches "Sophie is a Cancer"', () => { + const match = 'Sophie is a Cancer'.match(PERSON_SIGN_PATTERN); + expect(match).not.toBeNull(); + expect(match![1]).toBe('Sophie'); + expect(match![2]).toBe('Cancer'); + }); + + it('matches "Sophie is Cancer" (without article)', () => { + const match = 'Sophie is Cancer'.match(PERSON_SIGN_PATTERN); + expect(match).not.toBeNull(); + expect(match![1]).toBe('Sophie'); + expect(match![2]).toBe('Cancer'); + }); + + it('matches "Sophie is a Cancer with Aries rising"', () => { + const match = 'Sophie is a Cancer with Aries rising'.match(PERSON_SIGN_PATTERN); + expect(match).not.toBeNull(); + expect(match![1]).toBe('Sophie'); + expect(match![2]).toBe('Cancer'); + expect(match![3]).toBe('Aries'); + }); + + it('captures undefined rising when not provided', () => { + const match = 'Alex is a Leo'.match(PERSON_SIGN_PATTERN); + expect(match![3]).toBeUndefined(); + }); +}); + +describe('OTHER_SIGN_PATTERN', () => { + it('matches "talking to a Taurus"', () => { + const match = 'talking to a Taurus'.match(OTHER_SIGN_PATTERN); + expect(match).not.toBeNull(); + expect(match![1]).toBe('Taurus'); + }); + + it('matches "talking to Taurus" (no article)', () => { + const match = 'talking to Taurus'.match(OTHER_SIGN_PATTERN); + expect(match).not.toBeNull(); + expect(match![1]).toBe('Taurus'); + }); + + it('matches "they\'re a Leo"', () => { + const match = "they're a Leo".match(OTHER_SIGN_PATTERN); + expect(match).not.toBeNull(); + expect(match![1]).toBe('Leo'); + }); + + it('matches "they are a Gemini"', () => { + const match = 'they are a Gemini'.match(OTHER_SIGN_PATTERN); + expect(match).not.toBeNull(); + expect(match![1]).toBe('Gemini'); + }); +}); + +describe('TALKING_TO_PERSON_PATTERN', () => { + it('matches "talking to Sophie"', () => { + const match = 'talking to Sophie'.match(TALKING_TO_PERSON_PATTERN); + expect(match).not.toBeNull(); + expect(match![1]).toBe('Sophie'); + }); + + it('matches "meeting Alex"', () => { + const match = 'meeting Alex'.match(TALKING_TO_PERSON_PATTERN); + expect(match).not.toBeNull(); + expect(match![1]).toBe('Alex'); + }); + + it('matches "with Jordan"', () => { + const match = 'with Jordan'.match(TALKING_TO_PERSON_PATTERN); + expect(match).not.toBeNull(); + expect(match![1]).toBe('Jordan'); + }); +}); diff --git a/src/__tests__/sign-loader.test.ts b/src/__tests__/sign-loader.test.ts new file mode 100644 index 0000000..d982db1 --- /dev/null +++ b/src/__tests__/sign-loader.test.ts @@ -0,0 +1,204 @@ +import { describe, it, expect } from 'vitest'; +import { + loadSign, + getSignInfo, + buildStarTalkPrompt, + ALL_SIGNS, + SIGN_SHORT, + type SignId, +} from '../sign-loader.js'; + +// ─── Sign Data ──────────────────────────────────────────────────────────────── + +describe('ALL_SIGNS', () => { + it('contains exactly 12 signs', () => { + expect(ALL_SIGNS).toHaveLength(12); + }); + + it('every sign has id, name, short, dates, and symbol', () => { + for (const sign of ALL_SIGNS) { + expect(sign.id).toBeTruthy(); + expect(sign.name).toBeTruthy(); + expect(sign.short).toBeTruthy(); + expect(sign.dates).toBeTruthy(); + expect(sign.symbol).toBeTruthy(); + } + }); +}); + +describe('SIGN_SHORT', () => { + it('abbreviates long sign names', () => { + expect(SIGN_SHORT['sagittarius']).toBe('Sag'); + expect(SIGN_SHORT['capricorn']).toBe('Cap'); + expect(SIGN_SHORT['aquarius']).toBe('Aqua'); + }); + + it('keeps short names unchanged', () => { + expect(SIGN_SHORT['aries']).toBe('Aries'); + expect(SIGN_SHORT['leo']).toBe('Leo'); + }); +}); + +describe('getSignInfo', () => { + it('returns sign info for valid id', () => { + const info = getSignInfo('aries'); + expect(info).toBeDefined(); + expect(info!.name).toBe('Aries'); + expect(info!.symbol).toBe('The Ram'); + }); + + it('returns undefined for invalid id', () => { + expect(getSignInfo('notasign')).toBeUndefined(); + }); +}); + +// ─── World Loading & Parsing ────────────────────────────────────────────────── + +describe('loadSign', () => { + it('loads Aries with correct metadata', () => { + const aries = loadSign('aries'); + expect(aries.id).toBe('aries'); + expect(aries.name).toBe('Aries'); + expect(aries.element).toBe('fire'); + expect(aries.modality).toBe('cardinal'); + expect(aries.rulingPlanet).toBe('Mars'); + }); + + it('loads Cancer with correct element', () => { + const cancer = loadSign('cancer'); + expect(cancer.element).toBe('water'); + expect(cancer.modality).toBe('cardinal'); + }); + + it('parses thesis section', () => { + const aries = loadSign('aries'); + expect(aries.thesis).toContain('Aries leads'); + }); + + it('parses traits section', () => { + const aries = loadSign('aries'); + expect(aries.traits).toContain('takes_initiative'); + expect(aries.traits).toContain('speaks_directly'); + }); + + it('parses all five modes', () => { + const aries = loadSign('aries'); + expect(Object.keys(aries.modes)).toEqual( + expect.arrayContaining(['direct', 'translate', 'reflect', 'challenge', 'teach']), + ); + }); + + it('mode has name, tagline, and directives', () => { + const aries = loadSign('aries'); + const direct = aries.modes['direct']; + expect(direct.name).toBe('Direct'); + expect(direct.tagline).toBeTruthy(); + expect(direct.directives).toBeTruthy(); + }); + + it('parses compatibility', () => { + const aries = loadSign('aries'); + expect(aries.compatibility.bestWith).toContain('Leo'); + expect(aries.compatibility.tensionWith).toContain('Cancer'); + }); + + it('parses tone', () => { + const aries = loadSign('aries'); + expect(aries.tone.formality).toBe('casual'); + expect(aries.tone.confidence).toBe('assertive'); + }); + + it('caches loaded signs (returns same reference)', () => { + const first = loadSign('aries'); + const second = loadSign('aries'); + expect(first).toBe(second); + }); + + it('loads all 12 signs without errors', () => { + const signIds: SignId[] = [ + 'aries', 'taurus', 'gemini', 'cancer', 'leo', 'virgo', + 'libra', 'scorpio', 'sagittarius', 'capricorn', 'aquarius', 'pisces', + ]; + for (const id of signIds) { + const sign = loadSign(id); + expect(sign.id).toBeTruthy(); + expect(sign.name).toBeTruthy(); + expect(Object.keys(sign.modes).length).toBeGreaterThanOrEqual(5); + } + }); +}); + +// ─── World Stacking (Prompt Building) ───────────────────────────────────────── + +describe('buildStarTalkPrompt', () => { + it('builds prompt with sun sign only', () => { + const prompt = buildStarTalkPrompt({ sunSign: 'aries' }); + expect(prompt).toContain('Aries'); + expect(prompt).toContain('fire sign'); + expect(prompt).toContain('Mars'); + expect(prompt).not.toContain('Rising Influence'); + }); + + it('includes rising sign when different from sun', () => { + const prompt = buildStarTalkPrompt({ sunSign: 'cancer', risingSign: 'aries' }); + expect(prompt).toContain('Cancer'); + expect(prompt).toContain('Aries Rising'); + expect(prompt).toContain('Rising Influence'); + }); + + it('omits rising section when rising equals sun', () => { + const prompt = buildStarTalkPrompt({ sunSign: 'aries', risingSign: 'aries' }); + expect(prompt).not.toContain('Rising Influence'); + }); + + it('includes compatibility when otherSign provided', () => { + const prompt = buildStarTalkPrompt({ sunSign: 'aries' }, 'cancer'); + expect(prompt).toContain('Cancer'); + expect(prompt).toContain('Talking To'); + }); + + it('detects best_with compatibility', () => { + const prompt = buildStarTalkPrompt({ sunSign: 'aries' }, 'leo'); + expect(prompt).toContain('Natural Allies'); + }); + + it('detects tension_with compatibility', () => { + const prompt = buildStarTalkPrompt({ sunSign: 'aries' }, 'cancer'); + expect(prompt).toContain('Watch Points'); + }); + + it('shows neutral when no strong compatibility', () => { + const prompt = buildStarTalkPrompt({ sunSign: 'aries' }, 'virgo'); + expect(prompt).toContain('Neutral Ground'); + }); + + it('respects maxWords parameter', () => { + const prompt15 = buildStarTalkPrompt({ sunSign: 'aries' }, undefined, 15); + const prompt50 = buildStarTalkPrompt({ sunSign: 'aries' }, undefined, 50); + expect(prompt15).toContain('15 words'); + expect(prompt50).toContain('50 words'); + }); + + it('includes all five mode directives', () => { + const prompt = buildStarTalkPrompt({ sunSign: 'aries' }); + expect(prompt).toContain('TRANSLATE'); + expect(prompt).toContain('REFLECT'); + expect(prompt).toContain('CHALLENGE'); + expect(prompt).toContain('TEACH'); + expect(prompt).toContain('DIRECT'); + }); + + it('includes mode selection guidance', () => { + const prompt = buildStarTalkPrompt({ sunSign: 'aries' }); + expect(prompt).toContain('Mode Selection'); + expect(prompt).toContain('When in doubt, use TRANSLATE'); + }); + + it('includes constraints for glasses display', () => { + const prompt = buildStarTalkPrompt({ sunSign: 'aries' }); + expect(prompt).toContain('smart glasses'); + expect(prompt).toContain('No bullet points'); + expect(prompt).toContain('No markdown'); + expect(prompt).toContain('[MODE:'); + }); +}); diff --git a/src/governance.ts b/src/governance.ts new file mode 100644 index 0000000..43b3963 --- /dev/null +++ b/src/governance.ts @@ -0,0 +1,98 @@ +/** + * StarTalk Governance Logic — extracted for testability. + * + * Gate transitions and word limits are the core product integrity rules. + * These must be deterministic and testable without external dependencies. + */ + +// ─── Constants ──────────────────────────────────────────────────────────────── + +export const WORDS_GLANCE = 15; +export const WORDS_EXPAND = 30; +export const WORDS_FOLLOWUP = 35; +export const WORDS_DEPTH = 50; + +export const FOLLOW_UP_WINDOW_MS = 30_000; + +// ─── Gate Logic ─────────────────────────────────────────────────────────────── + +export type GovernanceGate = 'ACTIVE' | 'DEGRADED' | 'SUSPENDED' | 'REVOKED'; + +export interface GovernanceState { + sessionTrust: number; + gate: GovernanceGate; +} + +/** + * Determine the governance gate from a trust score. + * Trust starts at 100 per session and degrades based on usage patterns. + * + * ACTIVE (>=70): Full responses (50 words) + * DEGRADED (30-69): Reduced responses (30 words) + * SUSPENDED (11-30): Glance only (15 words) + * REVOKED (<=10): No responses (0 words) + */ +export function trustToGate(trust: number): GovernanceGate { + if (trust <= 10) return 'REVOKED'; + if (trust <= 30) return 'SUSPENDED'; + if (trust < 70) return 'DEGRADED'; + return 'ACTIVE'; +} + +/** + * Get word limit adjustments for a governance gate. + * This is invisible to the user — responses just get shorter/longer. + */ +export function gateAdjustments(gate: GovernanceGate): { maxWords: number } { + switch (gate) { + case 'ACTIVE': return { maxWords: WORDS_DEPTH }; + case 'DEGRADED': return { maxWords: Math.round(WORDS_DEPTH * 0.6) }; + case 'SUSPENDED': return { maxWords: WORDS_GLANCE }; + case 'REVOKED': return { maxWords: 0 }; + } +} + +// ─── Metrics-based Trust Evaluation ─────────────────────────────────────────── + +export interface SessionMetrics { + activations: number; + aiCalls: number; + aiFailures: number; + dismissals: number; + ambientSends: number; +} + +/** + * Simple trust degradation based on session metrics. + * This runs when governance world simulation is unavailable. + * + * Rule: after 5+ dismissals, trust *= 0.85 per evaluation. + * This provides a fallback when the full simulateWorld() engine isn't loaded. + */ +export function evaluateTrustFromMetrics( + currentTrust: number, + metrics: SessionMetrics, +): number { + let trust = currentTrust; + if (metrics.dismissals >= 5) { + trust *= 0.85; + } + return Math.max(0, Math.min(100, trust)); +} + +// ─── Mode Tag Parsing ───────────────────────────────────────────────────────── + +/** + * Strip [MODE:xxx] tag from AI response and return the mode + clean text. + * The mode tag is for internal tracking — the user never sees it. + */ +export function stripModeTag(text: string): { mode: string | null; displayText: string } { + const match = text.match(/^\[MODE:(\w+)\]\n?/); + if (match) { + return { + mode: match[1], + displayText: text.replace(/^\[MODE:\w+\]\n?/, ''), + }; + } + return { mode: null, displayText: text }; +} diff --git a/src/server.ts b/src/server.ts index 2273419..2fbf5b9 100644 --- a/src/server.ts +++ b/src/server.ts @@ -46,7 +46,7 @@ import { buildStarTalkPrompt, type SignId, type UserProfile, -} from './sign-loader'; +} from './sign-loader.js'; import { readFileSync } from 'fs'; import { resolve } from 'path'; diff --git a/src/types/external.d.ts b/src/types/external.d.ts new file mode 100644 index 0000000..4eb41bf --- /dev/null +++ b/src/types/external.d.ts @@ -0,0 +1,90 @@ +/** + * Type stubs for private/platform dependencies. + * These are provided by the MentraOS SDK and NeuroverseOS governance + * packages at runtime. The stubs allow typecheck and tests to pass + * in environments where those packages aren't installed. + */ + +declare module '@mentra/sdk' { + export class AppServer { + constructor(config: { packageName: string; apiKey: string; port: number }); + start(): void; + protected onSession(session: AppSession, sessionId: string, userId: string): Promise; + protected onStop(sessionId: string, userId: string, reason: string): Promise; + } + + export interface AppSession { + settings: { + get(key: string, defaultValue: T): T; + }; + layouts: { + showTextWall(text: string): void; + showDoubleTextWall(header: string, body: string): void; + }; + dashboard: { + content: { + writeToMain(text: string): void; + }; + }; + events: { + onButtonPress(handler: (data: ButtonPress) => void): void; + onTranscription(handler: (data: TranscriptionData) => void): void; + }; + storage: { + get(key: string): Promise; + set(key: string, value: unknown): Promise; + }; + } + + export interface ButtonPress { + pressType: 'short' | 'long'; + } + + export interface TranscriptionData { + text: string; + isFinal: boolean; + } +} + +declare module 'neuroverseos-governance/adapters/mentraos' { + export class MentraGovernedExecutor { + constructor(world: any, callbacks: { onBlock: (r: any) => void; onPause: (r: any) => void }, rules: any); + evaluate(intent: string, context: AppContext): { allowed: boolean }; + } + export const DEFAULT_USER_RULES: any; + export interface AppContext { + appId: string; + aiProviderDeclared: boolean; + declaredAIProviders: string[]; + dataRetentionOptedIn: boolean; + aiDataTypesSent: number; + glassesModel: string | undefined; + } +} + +declare module 'neuroverseos-governance/engine/guard-engine' { + export function evaluateGuard(event: any, world: any, options: { level: string }): { status: string; reason?: string }; +} + +declare module 'neuroverseos-governance/engine/simulate-engine' { + export function simulateWorld(world: any, options: { stateOverrides: Record }): { finalState: Record }; +} + +declare module 'neuroverseos-governance/types' { + export interface GuardEvent { + intent: string; + direction: 'input' | 'output'; + contentFields: Record; + } + export interface WorldDefinition { + [key: string]: any; + } +} + +declare module 'neuroverseos-governance/engine/bootstrap-parser' { + export function parseWorldMarkdown(md: string): { world: any; issues: Array<{ severity: string }> }; +} + +declare module 'neuroverseos-governance/engine/bootstrap-emitter' { + export function emitWorldDefinition(world: any): { world: any }; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..7e40f9b --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules"] +} From 8083617c8087a1be83ced36e499e2bb16e41f18d Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 26 Mar 2026 20:21:01 +0000 Subject: [PATCH 3/3] Add core invariant: symbolic interpretation only, never tactical advice MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit StarTalk must always interpret meaning through symbolic systems (astrology) and must never output tactical advice. This is the product boundary — StarTalk is a translator, not a coach. Adds validateCoreInvariant() which checks two rules: - hasSymbolic: response references astrological concepts (signs, elements, planets, modalities, traits, shadow, energy, chart) - hasTactical: response contains concrete directives ("you should buy", "step 1:", "action items:", "here's a plan") Both must hold: symbolic AND not tactical. Astrology-garnished tactical advice still fails. Generic non-symbolic responses also fail. 15 new tests covering valid symbolic reads, invalid tactical advice, tactical-wrapped-in-astrology, and missing-symbolic-framing cases. https://claude.ai/code/session_01Fo4Up2mMrkuQv7bUS1PiqG --- src/__tests__/governance.test.ts | 136 +++++++++++++++++++++++++++++++ src/governance.ts | 70 ++++++++++++++++ 2 files changed, 206 insertions(+) diff --git a/src/__tests__/governance.test.ts b/src/__tests__/governance.test.ts index b17ae51..43092e5 100644 --- a/src/__tests__/governance.test.ts +++ b/src/__tests__/governance.test.ts @@ -4,6 +4,7 @@ import { gateAdjustments, evaluateTrustFromMetrics, stripModeTag, + validateCoreInvariant, WORDS_DEPTH, WORDS_GLANCE, } from '../governance.js'; @@ -143,6 +144,141 @@ describe('evaluateTrustFromMetrics', () => { }); }); +// ─── Core Invariant: Symbolic Interpretation, Never Tactical ───────────────── + +describe('validateCoreInvariant', () => { + // ── VALID: symbolic interpretation ────────────────────────────────────── + + it('valid: astrological read with sign reference', () => { + const result = validateCoreInvariant( + "That's classic Aries energy — they charged ahead because waiting feels like dying to a fire sign." + ); + expect(result.valid).toBe(true); + expect(result.hasSymbolic).toBe(true); + expect(result.hasTactical).toBe(false); + }); + + it('valid: mode translate with compatibility', () => { + const result = validateCoreInvariant( + "Your Cancer instinct is to protect, but their Sagittarius nature needs room to run. Water meets fire." + ); + expect(result.valid).toBe(true); + }); + + it('valid: challenge mode with shadow trait', () => { + const result = validateCoreInvariant( + "You're retreating into your Scorpio shadow right now. That silence isn't protecting you — it's a wall." + ); + expect(result.valid).toBe(true); + }); + + it('valid: teach mode explaining dynamic', () => { + const result = validateCoreInvariant( + "Cardinal signs like Aries and Cancer both want to lead, but through different elements — fire charges, water flows." + ); + expect(result.valid).toBe(true); + }); + + it('valid: reflect mode with planetary reference', () => { + const result = validateCoreInvariant( + "Mars energy is driving you hard today. Where is that urgency actually coming from?" + ); + expect(result.valid).toBe(true); + }); + + // ── INVALID: tactical advice (no symbolic framing) ───────────────────── + + it('invalid: tactical advice without symbolic framing', () => { + const result = validateCoreInvariant( + "You should call your manager tomorrow morning and ask for a raise." + ); + expect(result.valid).toBe(false); + expect(result.hasTactical).toBe(true); + expect(result.hasSymbolic).toBe(false); + }); + + it('invalid: step-by-step plan', () => { + const result = validateCoreInvariant( + "Step 1: Open the conversation. Step 2: State your needs clearly." + ); + expect(result.valid).toBe(false); + expect(result.hasTactical).toBe(true); + }); + + it('invalid: action items', () => { + const result = validateCoreInvariant( + "Action items: send the email, book the meeting, update your resume." + ); + expect(result.valid).toBe(false); + expect(result.hasTactical).toBe(true); + }); + + it('invalid: here is a plan', () => { + const result = validateCoreInvariant( + "Here's a plan for your conversation with your boss this afternoon." + ); + expect(result.valid).toBe(false); + expect(result.hasTactical).toBe(true); + }); + + it('invalid: concrete recommendation', () => { + const result = validateCoreInvariant( + "My recommendation is to quit and find a new job before the end of the month." + ); + expect(result.valid).toBe(false); + expect(result.hasTactical).toBe(true); + }); + + // ── INVALID: tactical even WITH symbolic framing ─────────────────────── + + it('invalid: tactical wrapped in astrology still fails', () => { + const result = validateCoreInvariant( + "As a Taurus, you should buy that house. Your earth sign energy makes it the right investment." + ); + expect(result.valid).toBe(false); + expect(result.hasTactical).toBe(true); + expect(result.hasSymbolic).toBe(true); + expect(result.tacticalMatches.length).toBeGreaterThan(0); + }); + + it('invalid: step-by-step plan with astrological garnish', () => { + const result = validateCoreInvariant( + "Your Capricorn discipline is perfect for this. Step 1: update LinkedIn. Step 2: reach out to recruiters." + ); + expect(result.valid).toBe(false); + expect(result.hasTactical).toBe(true); + expect(result.hasSymbolic).toBe(true); + }); + + // ── INVALID: no symbolic framing at all ──────────────────────────────── + + it('invalid: generic response with no astrological content', () => { + const result = validateCoreInvariant( + "That sounds like a tough situation. Maybe just give it some time." + ); + expect(result.valid).toBe(false); + expect(result.hasSymbolic).toBe(false); + }); + + it('invalid: therapy-speak with no symbolic lens', () => { + const result = validateCoreInvariant( + "It's important to set boundaries and communicate your needs clearly." + ); + expect(result.valid).toBe(false); + expect(result.hasSymbolic).toBe(false); + }); + + // ── tacticalMatches reports what triggered ────────────────────────────── + + it('tacticalMatches lists the offending phrases', () => { + const result = validateCoreInvariant( + "You should call your boss and I suggest you apply for that role." + ); + expect(result.tacticalMatches.map(m => m.toLowerCase())).toContain('you should call'); + expect(result.tacticalMatches.length).toBeGreaterThanOrEqual(1); + }); +}); + // ─── Mode Tag Stripping ────────────────────────────────────────────────────── describe('stripModeTag', () => { diff --git a/src/governance.ts b/src/governance.ts index 43b3963..ea62099 100644 --- a/src/governance.ts +++ b/src/governance.ts @@ -80,6 +80,76 @@ export function evaluateTrustFromMetrics( return Math.max(0, Math.min(100, trust)); } +// ─── Core Invariant ────────────────────────────────────────────────────────── +// +// StarTalk MUST always interpret meaning through symbolic systems (astrology). +// StarTalk MUST NEVER output tactical advice (do X, buy Y, go to Z). +// +// This is the product boundary. StarTalk is a translator, not a coach. +// Every response should read the moment through the user's chart — not tell +// them what to do in concrete, non-symbolic terms. + +/** + * Tactical advice patterns — phrases that indicate the response has crossed + * from symbolic interpretation into concrete life coaching / directives. + * + * These are NOT astrological. They're actionable instructions that belong + * in a productivity app, not a cosmic translator. + */ +const TACTICAL_PATTERNS: RegExp[] = [ + /\byou should (?:go|buy|sell|call|email|text|apply|invest|quit|hire|fire|move to|sign up)\b/i, + /\bI (?:recommend|suggest|advise) (?:you )?(go|buy|sell|call|email|text|apply|invest|quit)\b/i, + /\bstep \d+[:.]/i, + /\bhere(?:'s| is) (?:a |your |the )?(?:plan|strategy|checklist|action item|to-do|roadmap)\b/i, + /\baction items?:/i, + /\bmy (?:recommendation|advice) is to\b/i, +]; + +/** + * Symbolic framing patterns — phrases that indicate the response IS + * interpreting through an astrological / symbolic lens. + */ +const SYMBOLIC_PATTERNS: RegExp[] = [ + /\b(?:aries|taurus|gemini|cancer|leo|virgo|libra|scorpio|sagittarius|capricorn|aquarius|pisces)\b/i, + /\b(?:fire|earth|air|water)\s+sign\b/i, + /\b(?:cardinal|fixed|mutable)\b/i, + /\b(?:sun|moon|rising|mercury|venus|mars|jupiter|saturn)\b/i, + /\b(?:energy|instinct|shadow|trait|nature|chart|cosmic|star)\b/i, +]; + +export interface InvariantResult { + valid: boolean; + hasTactical: boolean; + hasSymbolic: boolean; + tacticalMatches: string[]; +} + +/** + * Validate the core StarTalk invariant: + * - Must interpret through symbolic systems (astrology) + * - Must never output tactical advice + * + * Returns { valid: true } if the response is symbolic and not tactical. + * Returns { valid: false } with details if either invariant is violated. + */ +export function validateCoreInvariant(text: string): InvariantResult { + const tacticalMatches: string[] = []; + for (const pattern of TACTICAL_PATTERNS) { + const match = text.match(pattern); + if (match) tacticalMatches.push(match[0]); + } + + const hasSymbolic = SYMBOLIC_PATTERNS.some(p => p.test(text)); + const hasTactical = tacticalMatches.length > 0; + + return { + valid: hasSymbolic && !hasTactical, + hasTactical, + hasSymbolic, + tacticalMatches, + }; +} + // ─── Mode Tag Parsing ───────────────────────────────────────────────────────── /**