diff --git a/.cursor/rules/agent-development.mdc b/.cursor/rules/agent-development.mdc index d75071229c..29027cdb89 100644 --- a/.cursor/rules/agent-development.mdc +++ b/.cursor/rules/agent-development.mdc @@ -23,7 +23,7 @@ Python agents are typically in folders named `agent-py/` or `agent/` and use the - **[langgraph-tutorial-quickstart/agent-py/](mdc:examples/langgraph-tutorial-quickstart/agent-py/)** - LangGraph integration ### JavaScript Agents -JavaScript agents are in folders named `agent-js/` and use the [sdk-js/](mdc:CopilotKit/packages/sdk-js/) package. +JavaScript agents are in folders named `agent-js/` and use the [sdk-js/](mdc:src/v1.x/packages/sdk-js/) package. #### JavaScript Agent Examples - **[coagents-starter/agent-js/](mdc:examples/coagents-starter/agent-js/)** - Basic JavaScript agent setup diff --git a/.cursor/rules/copilotkit-architecture.mdc b/.cursor/rules/copilotkit-architecture.mdc index e364d682cd..451cc7414a 100644 --- a/.cursor/rules/copilotkit-architecture.mdc +++ b/.cursor/rules/copilotkit-architecture.mdc @@ -6,18 +6,18 @@ alwaysApply: false # CopilotKit Architecture Overview ## Core Library Structure -The main CopilotKit library is located in [CopilotKit/](mdc:CopilotKit/) with the following key packages: +The main CopilotKit library is located in [CopilotKit/](mdc:src/v1.x/) with the following key packages: ### React Packages -- **[react-core/](mdc:CopilotKit/packages/react-core/)** - Core React components and hooks for CopilotKit -- **[react-textarea/](mdc:CopilotKit/packages/react-textarea/)** - Textarea component with AI integration -- **[react-ui/](mdc:CopilotKit/packages/react-ui/)** - UI components for copilot interfaces +- **[react-core/](mdc:src/v1.x/packages/react-core/)** - Core React components and hooks for CopilotKit +- **[react-textarea/](mdc:src/v1.x/packages/react-textarea/)** - Textarea component with AI integration +- **[react-ui/](mdc:src/v1.x/packages/react-ui/)** - UI components for copilot interfaces ### Runtime & SDK -- **[runtime/](mdc:CopilotKit/packages/runtime/)** - Core runtime for executing copilot actions -- **[runtime-client-gql/](mdc:CopilotKit/packages/runtime-client-gql/)** - GraphQL client for runtime communication -- **[sdk-js/](mdc:CopilotKit/packages/sdk-js/)** - JavaScript SDK for backend integration -- **[shared/](mdc:CopilotKit/packages/shared/)** - Shared utilities and types +- **[runtime/](mdc:src/v1.x/packages/runtime/)** - Core runtime for executing copilot actions +- **[runtime-client-gql/](mdc:src/v1.x/packages/runtime-client-gql/)** - GraphQL client for runtime communication +- **[sdk-js/](mdc:src/v1.x/packages/sdk-js/)** - JavaScript SDK for backend integration +- **[shared/](mdc:src/v1.x/packages/shared/)** - Shared utilities and types ### Python SDK - **[sdk-python/](mdc:sdk-python/)** - Python SDK with integrations for: @@ -39,7 +39,7 @@ All examples are in [examples/](mdc:examples/) and typically follow this pattern ## Development Workflow - Examples use the local packages via workspace references -- [package.json](mdc:CopilotKit/package.json) contains workspace configuration -- Scripts in [scripts/](mdc:CopilotKit/scripts/) for development, testing, and releases +- [package.json](mdc:src/v1.x/package.json) contains workspace configuration +- Scripts in [scripts/](mdc:src/v1.x/scripts/) for development, testing, and releases diff --git a/.cursor/rules/development-workflow.mdc b/.cursor/rules/development-workflow.mdc index 9c9ca89d89..84e97c361a 100644 --- a/.cursor/rules/development-workflow.mdc +++ b/.cursor/rules/development-workflow.mdc @@ -7,20 +7,20 @@ alwaysApply: false ## Workspace Setup CopilotKit uses a monorepo structure with: -- **[CopilotKit/package.json](mdc:CopilotKit/package.json)** - Main workspace configuration -- **[CopilotKit/packages/](mdc:CopilotKit/packages)** - Core library packages +- **[src/v1.x/package.json](mdc:src/v1.x/package.json)** - Main workspace configuration +- **[src/v1.x/packages/](mdc:src/v1.x/packages)** - Core library packages - **[examples/](mdc:examples)** - Example applications ## Development Scripts -Located in [CopilotKit/scripts/](mdc:CopilotKit/scripts): +Located in [src/v1.x/scripts/](mdc:src/v1.x/scripts): ### Development Scripts -- **[develop/](mdc:CopilotKit/scripts/develop)** - Development environment setup -- **[qa/](mdc:CopilotKit/scripts/qa)** - Quality assurance and testing scripts -- **[release/](mdc:CopilotKit/scripts/release)** - Release automation scripts +- **[develop/](mdc:src/v1.x/scripts/develop)** - Development environment setup +- **[qa/](mdc:src/v1.x/scripts/qa)** - Quality assurance and testing scripts +- **[release/](mdc:src/v1.x/scripts/release)** - Release automation scripts ### Documentation Scripts -- **[docs/](mdc:CopilotKit/scripts/docs)** - Documentation generation and management +- **[docs/](mdc:src/v1.x/scripts/docs)** - Documentation generation and management ## Package Management - Uses pnpm workspaces for package management @@ -39,11 +39,11 @@ Located in [CopilotKit/scripts/](mdc:CopilotKit/scripts): - **[snippets/](mdc:docs/snippets)** - Code snippets for documentation ## Configuration Files -- **[CopilotKit.code-workspace](mdc:CopilotKit/CopilotKit.code-workspace)** - VS Code workspace settings -- **[utilities/](mdc:CopilotKit/utilities)** - Shared utilities: - - [eslint-config-custom/](mdc:CopilotKit/utilities/eslint-config-custom) - ESLint configuration - - [tailwind-config/](mdc:CopilotKit/utilities/tailwind-config) - Tailwind CSS configuration - - [tsconfig/](mdc:CopilotKit/utilities/tsconfig) - TypeScript configurations +- **[CopilotKit.code-workspace](mdc:src/v1.x/CopilotKit.code-workspace)** - VS Code workspace settings +- **[utilities/](mdc:src/v1.x/utilities)** - Shared utilities: + - [eslint-config-custom/](mdc:src/v1.x/utilities/eslint-config-custom) - ESLint configuration + - [tailwind-config/](mdc:src/v1.x/utilities/tailwind-config) - Tailwind CSS configuration + - [tsconfig/](mdc:src/v1.x/utilities/tsconfig) - TypeScript configurations ## Infrastructure - **[infra/](mdc:infra)** - AWS CDK infrastructure code diff --git a/.cursor/rules/frontend-development.mdc b/.cursor/rules/frontend-development.mdc index 2e0000522a..132a4d745d 100644 --- a/.cursor/rules/frontend-development.mdc +++ b/.cursor/rules/frontend-development.mdc @@ -8,20 +8,20 @@ alwaysApply: false ## Core React Packages ### React Components -- **[react-core/](mdc:CopilotKit/packages/react-core/)** - Core React components and hooks +- **[react-core/](mdc:src/v1.x/packages/react-core/)** - Core React components and hooks - `CopilotProvider` - Main provider component - `useCopilotChat` - Chat functionality hook - `useCopilotAction` - Action definition hook - `useCopilotReadable` - State reading hook ### UI Components -- **[react-ui/](mdc:CopilotKit/packages/react-ui/)** - Pre-built UI components +- **[react-ui/](mdc:src/v1.x/packages/react-ui/)** - Pre-built UI components - `CopilotChat` - Chat interface component - `CopilotPopup` - Popup chat component - `CopilotSidebar` - Sidebar chat component ### Specialized Components -- **[react-textarea/](mdc:CopilotKit/packages/react-textarea/)** - AI-enhanced textarea +- **[react-textarea/](mdc:src/v1.x/packages/react-textarea/)** - AI-enhanced textarea - `CopilotTextarea` - Smart textarea with AI suggestions - Auto-completion and suggestions @@ -115,8 +115,8 @@ function MyComponent() { ## Integration with Agents ### Runtime Configuration -- **[runtime/](mdc:CopilotKit/packages/runtime/)** - Core runtime for agent communication -- **[runtime-client-gql/](mdc:CopilotKit/packages/runtime-client-gql/)** - GraphQL client +- **[runtime/](mdc:src/v1.x/packages/runtime/)** - Core runtime for agent communication +- **[runtime-client-gql/](mdc:src/v1.x/packages/runtime-client-gql/)** - GraphQL client ### Agent Communication - Frontend components communicate with agents via the runtime @@ -145,7 +145,7 @@ function MyComponent() { ### Styling and Theming - Uses Tailwind CSS for styling -- Shared configuration in [utilities/tailwind-config/](mdc:CopilotKit/utilities/tailwind-config/) +- Shared configuration in [utilities/tailwind-config/](mdc:src/v1.x/utilities/tailwind-config/) - Custom components in [components.json](mdc:docs/components.json) - UI components follow modern design patterns diff --git a/.cursor/rules/quick-reference.mdc b/.cursor/rules/quick-reference.mdc index 9da8093ead..5e6e18f6eb 100644 --- a/.cursor/rules/quick-reference.mdc +++ b/.cursor/rules/quick-reference.mdc @@ -9,8 +9,8 @@ alwaysApply: false ### Build a Simple Copilot - Start with [copilot-chat-with-your-data/](mdc:examples/copilot-chat-with-your-data) -- Use [react-core/](mdc:CopilotKit/packages/react-core) for basic integration -- Add [react-ui/](mdc:CopilotKit/packages/react-ui) for pre-built components +- Use [react-core/](mdc:src/v1.x/packages/react-core) for basic integration +- Add [react-ui/](mdc:src/v1.x/packages/react-ui) for pre-built components ### Create Multi-Agent Systems - Check [coagents-starter/](mdc:examples/coagents-starter) for basic setup @@ -27,18 +27,18 @@ alwaysApply: false - See [coagents-starter/agent-py/](mdc:examples/coagents-starter/agent-py) for example ### Develop JavaScript Agents -- Use [sdk-js/](mdc:CopilotKit/packages/sdk-js) package +- Use [sdk-js/](mdc:src/v1.x/packages/sdk-js) package - Check [coagents-starter/agent-js/](mdc:examples/coagents-starter/agent-js) for example ### Customize UI Components - Browse [registry/](mdc:registry) for reusable components - Check [docs/components/react/](mdc:docs/components/react) for documentation -- Use [react-ui/](mdc:CopilotKit/packages/react-ui) for pre-built components +- Use [react-ui/](mdc:src/v1.x/packages/react-ui) for pre-built components ### Set Up Development Environment -- Review [CopilotKit/package.json](mdc:CopilotKit/package.json) for workspace setup -- Use scripts in [CopilotKit/scripts/](mdc:CopilotKit/scripts) -- Check [utilities/](mdc:CopilotKit/utilities) for shared configurations +- Review [src/v1.x/package.json](mdc:src/v1.x/package.json) for workspace setup +- Use scripts in [src/v1.x/scripts/](mdc:src/v1.x/scripts) +- Check [utilities/](mdc:src/v1.x/utilities) for shared configurations ### Run Tests - Use [examples/e2e/](mdc:examples/e2e) for end-to-end testing @@ -50,9 +50,9 @@ alwaysApply: false - See [docs/content/docs/](mdc:docs/content/docs) for markdown files ## Key Files to Know -- **[CopilotKit/package.json](mdc:CopilotKit/package.json)** - Main workspace configuration +- **[src/v1.x/package.json](mdc:src/v1.x/package.json)** - Main workspace configuration - **[sdk-python/pyproject.toml](mdc:sdk-python/pyproject.toml)** - Python SDK configuration -- **[CopilotKit.code-workspace](mdc:CopilotKit/CopilotKit.code-workspace)** - VS Code workspace settings +- **[CopilotKit.code-workspace](mdc:src/v1.x/CopilotKit.code-workspace)** - VS Code workspace settings - **[examples/shared/](mdc:examples/shared)** - Shared utilities and templates ## Common Patterns @@ -63,7 +63,7 @@ alwaysApply: false ## Getting Started Checklist 1. Clone the repository -2. Navigate to [CopilotKit/](mdc:CopilotKit) directory +2. Navigate to [src/v1.x/](mdc:src/v1.x) directory 3. Run `npm install` to set up workspace 4. Choose an example from [examples/](mdc:examples) 5. Follow the example's README.md diff --git a/.cursor/rules/suggestions-development.mdc b/.cursor/rules/suggestions-development.mdc index 47442956b2..760beb5c87 100644 --- a/.cursor/rules/suggestions-development.mdc +++ b/.cursor/rules/suggestions-development.mdc @@ -31,16 +31,16 @@ Users can configure suggestions via `useCopilotChatSuggestions` hook which regis ## Core files - **@react-ui** - - [Suggestions.tsx](mdc:CopilotKit/packages/react-ui/src/components/chat/Suggestions.tsx) - How suggestions are rendered - - [Messages.tsx](mdc:CopilotKit/packages/react-ui/src/components/chat/Messages.tsx) - Includes relevant code for what renders [Suggestions.tsx](mdc:CopilotKit/packages/react-ui/src/components/chat/Suggestions.tsx) - - [Chat.tsx](mdc:CopilotKit/packages/react-ui/src/components/chat/Chat.tsx) - Includes relevant logic for our prebuilt components loading suggestions - - [use-copilot-chat-suggestions.tsx](mdc:CopilotKit/packages/react-ui/src/hooks/use-copilot-chat-suggestions.tsx) - How users specify the configuration for their suggestions - - [suggestions.css](mdc:CopilotKit/packages/react-ui/src/css/suggestions.css) - Styling for suggestions + - [Suggestions.tsx](mdc:src/v1.x/packages/react-ui/src/components/chat/Suggestions.tsx) - How suggestions are rendered + - [Messages.tsx](mdc:src/v1.x/packages/react-ui/src/components/chat/Messages.tsx) - Includes relevant code for what renders [Suggestions.tsx](mdc:src/v1.x/packages/react-ui/src/components/chat/Suggestions.tsx) + - [Chat.tsx](mdc:src/v1.x/packages/react-ui/src/components/chat/Chat.tsx) - Includes relevant logic for our prebuilt components loading suggestions + - [use-copilot-chat-suggestions.tsx](mdc:src/v1.x/packages/react-ui/src/hooks/use-copilot-chat-suggestions.tsx) - How users specify the configuration for their suggestions + - [suggestions.css](mdc:src/v1.x/packages/react-ui/src/css/suggestions.css) - Styling for suggestions - **@react-core** - - [copilot-context.tsx](mdc:CopilotKit/packages/react-core/src/context/copilot-context.tsx) - Where the actual suggestions are stored, the "provider" or "context" - - [use-copilot-chat.ts](mdc:CopilotKit/packages/react-core/src/hooks/use-copilot-chat.ts) - Hook that controls and contains logic for suggestions, often referred to as "headless UI" - - [suggestions.ts](mdc:CopilotKit/packages/react-core/src/utils/suggestions.ts) - Core suggestion generation logic with streaming validation and error handling - - [suggestions-constants.ts](mdc:CopilotKit/packages/react-core/src/utils/suggestions-constants.ts) - Retry configuration constants + - [copilot-context.tsx](mdc:src/v1.x/packages/react-core/src/context/copilot-context.tsx) - Where the actual suggestions are stored, the "provider" or "context" + - [use-copilot-chat.ts](mdc:src/v1.x/packages/react-core/src/hooks/use-copilot-chat.ts) - Hook that controls and contains logic for suggestions, often referred to as "headless UI" + - [suggestions.ts](mdc:src/v1.x/packages/react-core/src/utils/suggestions.ts) - Core suggestion generation logic with streaming validation and error handling + - [suggestions-constants.ts](mdc:src/v1.x/packages/react-core/src/utils/suggestions-constants.ts) - Retry configuration constants ## How Suggestions Work diff --git a/.github/workflows/cleanup-caches-after-pr-close.yml b/.github/workflows/cleanup_pr-caches.yml similarity index 93% rename from .github/workflows/cleanup-caches-after-pr-close.yml rename to .github/workflows/cleanup_pr-caches.yml index 5b56775094..a3cc178de8 100644 --- a/.github/workflows/cleanup-caches-after-pr-close.yml +++ b/.github/workflows/cleanup_pr-caches.yml @@ -1,11 +1,11 @@ -name: Cleanup caches after PR close +name: cleanup on: pull_request: types: - closed jobs: - cleanup: + pr-caches: runs-on: ubuntu-latest steps: - name: Cleanup diff --git a/.github/workflows/dojo-e2e.yml b/.github/workflows/e2e_dojo.yml similarity index 95% rename from .github/workflows/dojo-e2e.yml rename to .github/workflows/e2e_dojo.yml index 29680142e1..a34143e395 100644 --- a/.github/workflows/dojo-e2e.yml +++ b/.github/workflows/e2e_dojo.yml @@ -1,20 +1,22 @@ -name: e2e +name: e2e / dojo on: push: branches: [main] - paths-ignore: - - "docs/**" - - "README.md" + paths: + - "src/v1.x/**" + - "src/v2.x/**" + - ".github/workflows/e2e_dojo.yml" pull_request: branches: [main] - paths-ignore: - - "docs/**" - - "README.md" + paths: + - "src/v1.x/**" + - "src/v2.x/**" + - ".github/workflows/e2e_dojo.yml" jobs: dojo: - name: dojo / ${{ matrix.suite }} + name: ${{ matrix.suite }} runs-on: depot-ubuntu-24.04 timeout-minutes: 20 strategy: @@ -143,11 +145,11 @@ jobs: uses: astral-sh/setup-uv@v6 - name: Install cpk dependencies - working-directory: CopilotKit/CopilotKit + working-directory: CopilotKit/src/v1.x run: pnpm install --frozen-lockfile - name: Build cpk - working-directory: CopilotKit/CopilotKit + working-directory: CopilotKit/src/v1.x env: NODE_OPTIONS: --max-old-space-size=8192 TURBO_CONCURRENCY: 2 @@ -163,7 +165,7 @@ jobs: - name: Link cpk into ag-ui working-directory: CopilotKit - run: node ../ag-ui/apps/dojo/scripts/link-cpk.js . + run: node ../ag-ui/apps/dojo/scripts/link-cpk.js src/v1.x/packages - name: Prepare dojo for e2e working-directory: ag-ui/apps/dojo diff --git a/.github/workflows/preview-envs-deploy.yml b/.github/workflows/e2e_examples-previews-deploy.yml similarity index 96% rename from .github/workflows/preview-envs-deploy.yml rename to .github/workflows/e2e_examples-previews-deploy.yml index 800a9797be..091c5cde42 100644 --- a/.github/workflows/preview-envs-deploy.yml +++ b/.github/workflows/e2e_examples-previews-deploy.yml @@ -1,4 +1,4 @@ -name: Preview Environments +name: e2e / examples on: pull_request: @@ -16,7 +16,7 @@ jobs: build_images: if: ${{ !contains(github.event.pull_request.labels.*.name, 'automated-version-pr') }} runs-on: ubuntu-latest - name: Build Images + name: build strategy: matrix: app: @@ -116,19 +116,19 @@ jobs: - name: (UI, Local Deps) Install monorepo if: matrix.app.type == 'ui' || matrix.app.type == 'next-openai' - working-directory: ./CopilotKit + working-directory: ./src/v1.x run: pnpm install --frozen-lockfile - name: (UI, Local Deps) Build @copilotkit packages if: matrix.app.type == 'ui' || matrix.app.type == 'next-openai' - working-directory: ./CopilotKit + working-directory: ./src/v1.x run: pnpm run freshbuild - name: (UI, Local Deps) Link @copilotkit packages if: matrix.app.type == 'ui' working-directory: ./examples/${{ matrix.app.project }}/${{ matrix.app.type }} run: | - for package in $(ls -d ../../../CopilotKit/packages/*); do + for package in $(ls -d ../../../src/v1.x/packages/*); do pnpm link $package done @@ -141,7 +141,7 @@ jobs: - name: (next-openai) Build app if: matrix.app.type == 'next-openai' - working-directory: CopilotKit/examples/next-openai + working-directory: src/v1.x/examples/next-openai run: pnpm run example-build env: OPENAI_API_KEY: placeholder @@ -165,7 +165,7 @@ jobs: run: | docker buildx build \ -t 058264219587.dkr.ecr.us-east-1.amazonaws.com/coagents:next-openai-${{ github.run_id }} \ - -f ./CopilotKit/examples/next-openai/Dockerfile \ + -f ./src/v1.x/examples/next-openai/Dockerfile \ --platform=linux/amd64 \ --provenance=false \ --push \ @@ -204,7 +204,6 @@ jobs: deploy: runs-on: ubuntu-latest - name: Deploy Preview Environments needs: build_images outputs: e2e-urls: ${{ steps.export-e2e-urls.outputs.e2e-urls }} @@ -312,10 +311,10 @@ jobs: labels: ['preview'] }) - test: + playwright: + name: "playwright" if: github.event_name == 'pull_request' - name: End to End Tests - uses: ./.github/workflows/e2e.yml + uses: ./.github/workflows/e2e_examples-test.yml needs: deploy with: ref: "${{ github.event.pull_request.head.ref }}" diff --git a/.github/workflows/preview-envs-destroy.yml b/.github/workflows/e2e_examples-previews-destroy.yml similarity index 95% rename from .github/workflows/preview-envs-destroy.yml rename to .github/workflows/e2e_examples-previews-destroy.yml index 64acf4c2ed..222cad1bf2 100644 --- a/.github/workflows/preview-envs-destroy.yml +++ b/.github/workflows/e2e_examples-previews-destroy.yml @@ -1,12 +1,12 @@ -name: Destroy Preview Environments +name: e2e / examples on: pull_request: - types: [ closed ] + types: [closed] workflow_dispatch: inputs: pr_number: - description: 'Pull Request Number' + description: "Pull Request Number" required: true concurrency: @@ -42,7 +42,7 @@ jobs: aws configure set aws_access_key_id ${{ secrets.AWS_ACCESS_KEY_ID }} --profile default aws configure set aws_secret_access_key ${{ secrets.AWS_SECRET_ACCESS_KEY }} --profile default aws configure set region us-east-1 - + - uses: pnpm/action-setup@v4 with: version: 9 @@ -69,4 +69,4 @@ jobs: with: comment-tag: preview-status-update message: | - Preview environments destroyed for this pull request. \ No newline at end of file + Preview environments destroyed for this pull request. diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e_examples-test.yml similarity index 98% rename from .github/workflows/e2e.yml rename to .github/workflows/e2e_examples-test.yml index 8933d79ece..4ccf8000b7 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e_examples-test.yml @@ -1,4 +1,4 @@ -name: End to End Tests +name: e2e / examples on: workflow_call: @@ -17,8 +17,7 @@ on: required: true jobs: - e2e_tests: - name: E2E Tests + test: runs-on: ubuntu-latest timeout-minutes: 30 permissions: @@ -111,4 +110,4 @@ jobs: with: pr-number: ${{ inputs.pr_number }} comment-tag: test-run-status-update - file-path: ./examples/e2e/test-results/test-run-comment.md \ No newline at end of file + file-path: ./examples/e2e/test-results/test-run-comment.md diff --git a/.github/workflows/custom-prerelease.yml b/.github/workflows/publish_custom-pre.yml similarity index 83% rename from .github/workflows/custom-prerelease.yml rename to .github/workflows/publish_custom-pre.yml index 0dd888a54b..d789519b49 100644 --- a/.github/workflows/custom-prerelease.yml +++ b/.github/workflows/publish_custom-pre.yml @@ -1,4 +1,4 @@ -name: Custom Tag Pre-Release +name: publish on: workflow_dispatch: @@ -6,12 +6,11 @@ on: concurrency: ${{ github.workflow }}-${{ github.ref }} jobs: - release: - name: Pre-release + custom-pre: runs-on: ubuntu-latest defaults: run: - working-directory: "CopilotKit" + working-directory: "src/v1.x" timeout-minutes: 10 steps: - name: Checkout Repo @@ -39,4 +38,4 @@ jobs: - name: Publish snapshot run: ./scripts/release/publish-snapshot-release.sh env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} \ No newline at end of file + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/publish_release.yml similarity index 97% rename from .github/workflows/release.yml rename to .github/workflows/publish_release.yml index 51d2aaec59..970bc9213c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/publish_release.yml @@ -1,4 +1,4 @@ -name: Release +name: publish on: workflow_dispatch: @@ -10,11 +10,10 @@ concurrency: ${{ github.workflow }}-${{ github.ref }} jobs: release: - name: Release runs-on: ubuntu-latest defaults: run: - working-directory: "CopilotKit" + working-directory: "src/v1.x" timeout-minutes: 10 steps: - name: Checkout Repo @@ -117,6 +116,6 @@ jobs: with: tag_name: v${{ steps.new-version.outputs.version }} release_name: v${{ steps.new-version.outputs.version }} - body_path: ./CopilotKit/changelog.txt + body_path: ./src/v1.x/changelog.txt draft: false prerelease: false diff --git a/.github/workflows/danger.yml b/.github/workflows/static_danger.yml similarity index 91% rename from .github/workflows/danger.yml rename to .github/workflows/static_danger.yml index b724881e47..e289deca7d 100644 --- a/.github/workflows/danger.yml +++ b/.github/workflows/static_danger.yml @@ -1,10 +1,10 @@ -name: Danger +name: static on: pull_request: paths: - "sdk-python/copilotkit/langgraph_agent.py" - - "CopilotKit/packages/sdk-js/src/langgraph.ts" + - "src/v1.x/packages/sdk-js/src/langgraph.ts" jobs: danger: diff --git a/.github/workflows/quality.yml b/.github/workflows/static_quality.yml similarity index 60% rename from .github/workflows/quality.yml rename to .github/workflows/static_quality.yml index 7a6baaf80a..e9ca9d17a0 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/static_quality.yml @@ -1,31 +1,30 @@ -name: Quality +name: static on: push: branches: [main] paths-ignore: - - 'docs/**' - - 'README.md' - - 'examples/**' - - '.github/workflows/demos_preview.yml' - - '.github/workflows/release.yml' - - 'CopilotKit/packages/**/package.json' - - 'CopilotKit/packages/**/CHANGELOG.md' - - 'CopilotKit/.changeset/**' + - "docs/**" + - "README.md" + - "examples/**" + - ".github/workflows/demos_preview.yml" + - ".github/workflows/release.yml" + - "src/v1.x/packages/**/package.json" + - "src/v1.x/packages/**/CHANGELOG.md" + - "src/v1.x/.changeset/**" pull_request: - branches: [main] + branches: [main] paths-ignore: - - 'docs/**' - - 'README.md' - - 'examples/**' + - "docs/**" + - "README.md" + - "examples/**" jobs: prettier: - name: 'Prettier' runs-on: ubuntu-latest defaults: run: - working-directory: 'CopilotKit' + working-directory: "src/v1.x" steps: - name: Checkout @@ -40,9 +39,9 @@ jobs: uses: actions/setup-node@v3 with: node-version: 20.x - cache: 'pnpm' - cache-dependency-path: '**/pnpm-lock.yaml' - + cache: "pnpm" + cache-dependency-path: "**/pnpm-lock.yaml" + - name: Install dependencies run: pnpm install --frozen-lockfile @@ -50,11 +49,10 @@ jobs: run: npx turbo run check-prettier eslint: - name: 'ESLint' runs-on: ubuntu-latest defaults: run: - working-directory: CopilotKit + working-directory: src/v1.x steps: - name: Checkout uses: actions/checkout@v3 @@ -68,11 +66,11 @@ jobs: uses: actions/setup-node@v3 with: node-version: 20.x - cache: 'pnpm' - cache-dependency-path: '**/pnpm-lock.yaml' - + cache: "pnpm" + cache-dependency-path: "**/pnpm-lock.yaml" + - name: Install dependencies run: pnpm install --frozen-lockfile - name: Run ESLint check - run: npx turbo run lint \ No newline at end of file + run: npx turbo run lint diff --git a/.github/workflows/test-deploy-custom-copilotkit-version-to-cloud.yaml b/.github/workflows/test-deploy-custom-copilotkit-version-to-cloud.yaml deleted file mode 100644 index 6ac173e7e5..0000000000 --- a/.github/workflows/test-deploy-custom-copilotkit-version-to-cloud.yaml +++ /dev/null @@ -1,37 +0,0 @@ -name: Test Deploy Custom CopilotKit Version to Cloud - -on: - workflow_dispatch: - inputs: - version: - description: "Version to deploy (e.g. `1.5.12-next.0`)" - required: true - -jobs: - run: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - uses: actions/checkout@v3 - with: - repository: copilotkit/copilotcloud - ref: main - sparse-checkout: '.github/actions' - sparse-checkout-cone-mode: false - path: .github/cloud-actions - - - name: LS LA - run: | - ls -la .github - ls -la .github/cloud-actions - - # - name: Test Composite Action - # uses: copilotkit/copilotcloud/.github/workflows/test-deploy-custom-copilotkit-version-to-cloud.yml@main - # with: - # version: ${{ inputs.version }} - # secrets: - # aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID }} - # aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} diff --git a/.github/workflows/ci.yml b/.github/workflows/test_unit-v1.yml similarity index 55% rename from .github/workflows/ci.yml rename to .github/workflows/test_unit-v1.yml index 31214830d3..5350d3c930 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/test_unit-v1.yml @@ -1,44 +1,44 @@ -name: CI +name: test on: push: branches: [main] paths-ignore: - - 'docs/**' - - 'README.md' - - 'examples/**' - - '.github/workflows/demos_preview.yml' - - '.github/workflows/release.yml' - - 'CopilotKit/packages/**/package.json' - - 'CopilotKit/packages/**/CHANGELOG.md' - - 'CopilotKit/.changeset/**' + - "docs/**" + - "README.md" + - "examples/**" + - ".github/workflows/demos_preview.yml" + - ".github/workflows/release.yml" + - "src/v1.x/packages/**/package.json" + - "src/v1.x/packages/**/CHANGELOG.md" + - "src/v1.x/.changeset/**" pull_request: branches: [main] paths-ignore: - - 'docs/**' - - 'README.md' - - 'examples/**' + - "docs/**" + - "README.md" + - "examples/**" jobs: - test: - name: 'Test' + v1: + name: "unit / v1.x" runs-on: ubuntu-latest defaults: run: - working-directory: 'CopilotKit' + working-directory: "src/v1.x" strategy: matrix: - node-version: [20.x, 18.x] + node-version: [20.x, 22.x, 24.x] steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup pnpm uses: pnpm/action-setup@v4 with: version: "9.5" - + - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} diff --git a/.github/workflows/test_unit-v2.yml b/.github/workflows/test_unit-v2.yml new file mode 100644 index 0000000000..bb7210f0a9 --- /dev/null +++ b/.github/workflows/test_unit-v2.yml @@ -0,0 +1,45 @@ +name: test + +on: + push: + branches: [main] + paths: + - "src/v2.x/**" + - ".github/workflows/test_unit-v2.yml" + pull_request: + branches: [main] + paths: + - "src/v2.x/**" + - ".github/workflows/test_unit-v2.yml" +jobs: + v2: + name: "unit / v2.x" + runs-on: ubuntu-latest + defaults: + run: + working-directory: "src/v2.x" + strategy: + matrix: + node-version: [20.x, 22.x, 24.x] + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: "9.5" + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build + run: pnpm build + + - name: Run tests + run: pnpm test diff --git a/.husky/pre-commit b/.husky/pre-commit index 88641c6ff9..c234aa1390 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1 +1 @@ -cd CopilotKit && pnpm run precommit +cd src/v1.x && pnpm run precommit diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 63edd3a60e..31a4fafd7e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -73,7 +73,7 @@ pnpm install To make sure everything works, let’s build all packages once: ```jsx -cd CopilotKit +cd src/v1.x turbo run build ``` diff --git a/dangerfile.js b/dangerfile.js index 0588d5c8ba..6262596283 100644 --- a/dangerfile.js +++ b/dangerfile.js @@ -1,7 +1,7 @@ import { danger, warn } from "danger"; const file1 = "sdk-python/copilotkit/langgraph_agent.py"; -const file2 = "CopilotKit/packages/sdk-js/src/langgraph.ts"; +const file2 = "src/v1.x/packages/sdk-js/src/langgraph.ts"; // Get the list of modified files in the PR const changedFiles = danger.git.modified_files; diff --git a/docs/app/(home)/layout.tsx b/docs/app/(home)/layout.tsx index c145cc5f35..8bfc548065 100644 --- a/docs/app/(home)/layout.tsx +++ b/docs/app/(home)/layout.tsx @@ -13,6 +13,7 @@ import { ADKIcon, AgnoIcon, LlamaIndexIcon, + AgentSpecMarkIcon, PydanticAIIcon, AwsStrandsIcon, A2AIcon, @@ -217,6 +218,15 @@ export default function Layout({ children }: { children: ReactNode }) { bgGradient: "bg-black text-white", selectedStyle: "ring-gray-500 ring-2 rounded-sm", }, + { + title: "Open Agent Spec", + description: "Documentation for CoAgents with Open Agent Spec", + url: "/agent-spec", + icon: , + bgGradient: + "bg-gradient-to-b from-slate-700 to-slate-400 text-slate-100", + selectedStyle: "ring-slate-500/70 ring-2 rounded-sm", + }, ], }, ]} diff --git a/docs/components/content/iframe-switcher.tsx b/docs/components/content/iframe-switcher.tsx index 704359403b..71faf13af6 100644 --- a/docs/components/content/iframe-switcher.tsx +++ b/docs/components/content/iframe-switcher.tsx @@ -1,6 +1,6 @@ -import * as React from "react"; import { Tabs, + Tab, TabsContent, TabsList, TabsTrigger, @@ -57,3 +57,68 @@ export function IframeSwitcher({ ); } + +// Wrapper for multiple IframeSwitcher variants (e.g., different integrations) +interface IframeSwitcherVariant { + label: string; + exampleUrl: string; + codeUrl: string; +} + +interface IframeSwitcherGroupProps { + id?: string; + variants: IframeSwitcherVariant[]; + height?: string; + exampleLabel?: string; + codeLabel?: string; +} + +export function IframeSwitcherGroup({ + id, + variants, + height = "600px", + exampleLabel = "Example", + codeLabel = "Code", +}: IframeSwitcherGroupProps) { + const items = variants.map((v) => v.label); + const firstItem = items[0]; + + if (!firstItem || variants.length === 0) { + return null; + } + + // Single variant - render without outer tabs + if (variants.length === 1) { + const variant = variants[0]; + if (!variant) return null; + + return ( + + ); + } + + // Multiple variants - wrap with outer tabs + return ( + + {variants.map((variant) => ( + + + + ))} + + ); +} diff --git a/docs/components/content/index.ts b/docs/components/content/index.ts index 5401d87c3c..e67981490d 100644 --- a/docs/components/content/index.ts +++ b/docs/components/content/index.ts @@ -1 +1 @@ -export { IframeSwitcher } from "./iframe-switcher"; +export { IframeSwitcher, IframeSwitcherGroup } from "./iframe-switcher"; diff --git a/docs/content/docs/(root)/(other)/contributing/code-contributions/index.mdx b/docs/content/docs/(root)/(other)/contributing/code-contributions/index.mdx index 16ef161e6c..f20ae7e49f 100644 --- a/docs/content/docs/(root)/(other)/contributing/code-contributions/index.mdx +++ b/docs/content/docs/(root)/(other)/contributing/code-contributions/index.mdx @@ -22,7 +22,7 @@ This guide will help you get started as smoothly as possible. Then, clone your fork to your local machine: ```bash git clone https://github.com//CopilotKit - cd CopilotKit/CopilotKit + cd CopilotKit/src/v1.x ``` diff --git a/docs/content/docs/a2a/generative-ui/meta.json b/docs/content/docs/a2a/generative-ui/meta.json index 157b52f03b..c72c843607 100644 --- a/docs/content/docs/a2a/generative-ui/meta.json +++ b/docs/content/docs/a2a/generative-ui/meta.json @@ -1,5 +1,6 @@ { "title": "Generative UI", + "icon": "lucide/Paintbrush", "defaultOpen": true, "pages": ["declarative-a2ui"] } diff --git a/docs/content/docs/adk/generative-ui/meta.json b/docs/content/docs/adk/generative-ui/meta.json index 9360ddea96..b230d5f64d 100644 --- a/docs/content/docs/adk/generative-ui/meta.json +++ b/docs/content/docs/adk/generative-ui/meta.json @@ -1,5 +1,6 @@ { "title": "Generative UI", + "icon": "lucide/Paintbrush", "defaultOpen": true, "pages": ["backend-tools", "frontend-tools", "agentic"] } diff --git a/docs/content/docs/ag2/(other)/contributing/code-contributions/index.mdx b/docs/content/docs/ag2/(other)/contributing/code-contributions/index.mdx index 16ef161e6c..f20ae7e49f 100644 --- a/docs/content/docs/ag2/(other)/contributing/code-contributions/index.mdx +++ b/docs/content/docs/ag2/(other)/contributing/code-contributions/index.mdx @@ -22,7 +22,7 @@ This guide will help you get started as smoothly as possible. Then, clone your fork to your local machine: ```bash git clone https://github.com//CopilotKit - cd CopilotKit/CopilotKit + cd CopilotKit/src/v1.x ``` diff --git a/docs/content/docs/agent-spec/agentic-chat-ui.mdx b/docs/content/docs/agent-spec/agentic-chat-ui.mdx new file mode 100644 index 0000000000..560dddbdaf --- /dev/null +++ b/docs/content/docs/agent-spec/agentic-chat-ui.mdx @@ -0,0 +1,52 @@ +--- +title: Chat with an Agent +icon: "lucide/SendHorizontal" +description: Chat with your Agent Spec agent using CopilotKit's UI components. +--- + +import ConnectCopilotUI from "@/snippets/copilot-ui.mdx"; +import SelfHostingCopilotRuntimeCreateEndpoint from "@/snippets/self-hosting-copilot-runtime-create-endpoint.mdx"; +import SelfHostingCopilotRuntimeConfigureCopilotKitProvider from "@/snippets/self-hosting-copilot-runtime-configure-copilotkit-provider.mdx"; +import CopilotCloudConfigureCopilotKitProvider from "@/snippets/cloud/cloud-copilotkit-provider.mdx"; +import ComponentExamples from "@/snippets/component-examples.mdx"; +import { UserIcon, PaintbrushIcon, WrenchIcon, RepeatIcon } from "lucide-react"; +import { IframeSwitcherGroup } from "@/components/content" + + + +## What is this? + +Agentic chat UIs let your users interact with your agent. CopilotKit provides prebuilt components (like `CopilotChat` and `CopilotSidebar`) as well as headless hooks — you can start simple and grow to a fully custom app. + +If you've gone through the [quickstart](/agent-spec/quickstart), you already have an agentic chat UI set up — nothing else is required to begin chatting. + +## When should I use this? + +Use the agentic chat UI when you want a turnkey chat experience that: +- streams assistant text and tool calls/results, +- supports stateful conversations (thread/run lifecycle), +- can evolve into generative UI with custom renders. + + + +## Learn more + +- Agent Spec docs home: https://oracle.github.io/agent-spec/development/docs_home.html +- Reference sheet (formats): https://oracle.github.io/agent-spec/development/misc/reference_sheet.html diff --git a/docs/content/docs/agent-spec/custom-look-and-feel/bring-your-own-components.mdx b/docs/content/docs/agent-spec/custom-look-and-feel/bring-your-own-components.mdx new file mode 100644 index 0000000000..98fe17448e --- /dev/null +++ b/docs/content/docs/agent-spec/custom-look-and-feel/bring-your-own-components.mdx @@ -0,0 +1,7 @@ +--- +title: Custom Sub-Components +icon: "lucide/Puzzle" +--- +import BringYourOwnComponents from "@/snippets/shared/guides/custom-look-and-feel/bring-your-own-components.mdx"; + + \ No newline at end of file diff --git a/docs/content/docs/agent-spec/custom-look-and-feel/customize-built-in-ui-components.mdx b/docs/content/docs/agent-spec/custom-look-and-feel/customize-built-in-ui-components.mdx new file mode 100644 index 0000000000..aaeadede05 --- /dev/null +++ b/docs/content/docs/agent-spec/custom-look-and-feel/customize-built-in-ui-components.mdx @@ -0,0 +1,7 @@ +--- +title: "Styling Copilot UI" +icon: "lucide/Brush" +--- +import CustomizeBuiltInUIComponents from "@/snippets/shared/guides/custom-look-and-feel/customize-built-in-ui-components.mdx"; + + \ No newline at end of file diff --git a/docs/content/docs/agent-spec/custom-look-and-feel/headless-ui.mdx b/docs/content/docs/agent-spec/custom-look-and-feel/headless-ui.mdx new file mode 100644 index 0000000000..de5fa9adcf --- /dev/null +++ b/docs/content/docs/agent-spec/custom-look-and-feel/headless-ui.mdx @@ -0,0 +1,8 @@ +--- +title: "Fully Headless UI" +description: "Fully customize your Copilot's UI from the ground up using headless UI" +icon: "lucide/Settings" +--- +import HeadlessUI from "@/snippets/shared/guides/custom-look-and-feel/headless-ui.mdx"; + + \ No newline at end of file diff --git a/docs/content/docs/agent-spec/custom-look-and-feel/index.mdx b/docs/content/docs/agent-spec/custom-look-and-feel/index.mdx new file mode 100644 index 0000000000..7c059b39d0 --- /dev/null +++ b/docs/content/docs/agent-spec/custom-look-and-feel/index.mdx @@ -0,0 +1,42 @@ +--- +title: "Customize UI" +description: "Customize the look, feel, and functionality of CopilotKit's UI components." +icon: "lucide/Settings" +--- +import { MessageCircleIcon, BrushIcon, PuzzleIcon, SettingsIcon } from "lucide-react"; + +CopilotKit offers a variety of ways to create a UI interface for your Copilots and CoAgents. This ranges +from using our built-in UI components to fully customizing the UI with headless UI. + + + Prebuilt Copilot UI} + description="Get started quickly with CopilotKit's ready-to-use UI components." + href="./custom-look-and-feel/built-in-ui-components" + /> + Styling Copilot UI} + description="Customize the appearance of CopilotKit's pre-built components with your own styles." + href="./custom-look-and-feel/customize-built-in-ui-components" + /> + Custom Components} + description="Replace the Copilot UI components with your own while keeping the core functionality." + href="./custom-look-and-feel/bring-your-own-components" + /> + Fully Custom UI} + description="Build your UI from scratch using CopilotKit's hooks and core functionality." + href="./custom-look-and-feel/headless-ui" + /> + Markdown Rendering} + description="Modify CopilotKit's use of markdown to display elements within the assistant arrow text, such as source citing and reasoning steps." + href="./custom-look-and-feel/markdown-rendering" + /> + \ No newline at end of file diff --git a/docs/content/docs/agent-spec/custom-look-and-feel/markdown-rendering.mdx b/docs/content/docs/agent-spec/custom-look-and-feel/markdown-rendering.mdx new file mode 100644 index 0000000000..3f3ef50155 --- /dev/null +++ b/docs/content/docs/agent-spec/custom-look-and-feel/markdown-rendering.mdx @@ -0,0 +1,7 @@ +--- +title: Markdown rendering +icon: "lucide/MessageSquareCode" +--- +import MarkdownRendering from "@/snippets/shared/guides/custom-look-and-feel/markdown-rendering.mdx"; + + \ No newline at end of file diff --git a/docs/content/docs/agent-spec/custom-look-and-feel/meta.json b/docs/content/docs/agent-spec/custom-look-and-feel/meta.json new file mode 100644 index 0000000000..39661263b2 --- /dev/null +++ b/docs/content/docs/agent-spec/custom-look-and-feel/meta.json @@ -0,0 +1,11 @@ +{ + "title": "Custom Look and Feel", + "icon": "lucide/LayoutDashboard", + "pages": [ + "built-in-ui-components", + "customize-built-in-ui-components", + "bring-your-own-components", + "headless-ui", + "markdown-rendering" + ] +} \ No newline at end of file diff --git a/docs/content/docs/agent-spec/generative-ui/backend-tools.mdx b/docs/content/docs/agent-spec/generative-ui/backend-tools.mdx new file mode 100644 index 0000000000..0ed81d120c --- /dev/null +++ b/docs/content/docs/agent-spec/generative-ui/backend-tools.mdx @@ -0,0 +1,173 @@ +--- +title: Backend Tools +icon: "lucide/Server" +description: Render your agent's tool calls with custom UI components. +--- +import { Accordions, Accordion } from "fumadocs-ui/components/accordion"; +import { IframeSwitcherGroup } from "@/components/content" +import { Tabs, Tab } from "fumadocs-ui/components/tabs" +import DefaultToolRendering from "@/snippets/shared/guides/default-tool-rendering.mdx" +import RunAndConnect from "@/snippets/integrations/agent-spec/run-and-connect.mdx" + + + + + This example demonstrates the [implementation](#implementation) section applied in the CopilotKit feature viewer. + + +## What is this? + +Tools are a way for the LLM to call predefined, typically, deterministic functions. CopilotKit allows you to render these tools in the UI +as a custom component, which we call **Generative UI**. + +## When should I use this? + +Rendering tools in the UI is useful when you want to provide the user with feedback about what your agent is doing, specifically +when your agent is calling tools. CopilotKit allows you to fully customize how these tools are rendered in the chat. + +## Implementation + + + +### Run and connect your agent + + + +### Give your agent a tool to call + + + + ```python title="agent.py" + + # Create the agent + from pyagentspec.agent import Agent + from pyagentspec.llms import OpenAiCompatibleConfig + from pyagentspec.property import StringProperty + from pyagentspec.tools import ServerTool + from pyagentspec.serialization import AgentSpecSerializer + + llm = OpenAiCompatibleConfig( + name="my_llm", + model_id="gpt-4o-mini", + url="https://api.openai.com/v1", + ) + weather_tool = ServerTool( # ServerTool are backend tools in Agent Spec + name="get_weather", + description="Get the weather for a given location.", + inputs=[StringProperty(title="location", description="The location to get the weather forecast. Must be a city/town name.")], + outputs=[StringProperty(title="weather_result")], + ) + agent = Agent( + name="my_agent", + llm_config=llm, + system_prompt="Based on the weather forecast result and the user input, write a response to the user", + tools=[weather_tool], + human_in_the_loop=True, + ) + agentspec_json_config = AgentSpecSerializer().to_json(agent) + + async def get_weather(location: str = "Everywhere ever") -> str: + """Get the weather for a given location. Ensure location is fully spelled out.""" + return f"The weather in {location} is sunny." + + # Start the server + from fastapi import APIRouter, FastAPI + from ag_ui_agentspec.agent import AgentSpecAgent + from ag_ui_agentspec.endpoint import add_agentspec_fastapi_endpoint + + + runtime = "langgraph" # you can also choose "wayflow" + router = APIRouter() + add_agentspec_fastapi_endpoint( + app=router, + agentspec_agent=AgentSpecAgent( + agentspec_json_config, + runtime=runtime, + tool_registry={"get_weather": get_weather}, + ), + path=f"/{runtime}/path_to_my_agent", + ) + app = FastAPI(title="Agent-Spec x AG-UI Examples - Backend Tool") + app.include_router(router) + + if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8000) + + ``` + + + + + +### Render the tool call in your frontend +At this point, your agent will be able to call the `get_weather` tool. Now +we just need to add a `useRenderToolCall` hook to render the tool call in +the UI. + + + In order to render a tool call in the UI, the name of the action must match the name of the tool. + + +```tsx title="app/page.tsx" +import { useRenderToolCall } from "@copilotkit/react-core"; // [!code highlight] +// ... + +const YourMainContent = () => { + // ... + // [!code highlight:12] + useRenderToolCall({ + name: "get_weather", + render: ({status, args}) => { + return ( +

+ {status !== "complete" && "Calling weather API..."} + {status === "complete" && `Called the weather API for ${args.location}.`} +

+ ); + }, + }); + // ... +} +``` + +
+ +### Give it a try! + +Try asking the agent to get the weather for a location. You should see the custom UI component that we added +render the tool call and display the arguments that were passed to the tool. + + +
+ +## Default Tool Rendering + + + diff --git a/docs/content/docs/agent-spec/generative-ui/frontend-tools.mdx b/docs/content/docs/agent-spec/generative-ui/frontend-tools.mdx new file mode 100644 index 0000000000..acfeaf7ab2 --- /dev/null +++ b/docs/content/docs/agent-spec/generative-ui/frontend-tools.mdx @@ -0,0 +1,163 @@ +--- +title: Frontend Tools +icon: "lucide/Wrench" +description: Create frontend tools and use them within your Agent Spec configured agent. +--- +import InstallSDKSnippet from "@/snippets/install-sdk.mdx" +import { IframeSwitcher } from "@/components/content" +import { Tabs, Tab } from "fumadocs-ui/components/tabs" +import RunAndConnect from "@/snippets/integrations/agent-spec/run-and-connect.mdx" + + + +## What is this? +Frontend tools enable you to define client-side functions that your agents defined with Agent Spec can invoke, with execution happening entirely in the user's browser. When your agent calls a frontend tool, +the logic runs on the client side, giving you direct access to the frontend environment. + +This can be utilized for to let [your agent control the UI](/frontend-actions), [generative UI](/agent-spec/generative-ui/frontend-tools), or for Human-in-the-loop interactions. + +In this guide, we cover the use of frontend tools for generative UI. + +## When should I use this? +Use frontend tools when you need your agent to interact with client-side primitives such as: +- Reading or modifying React component state +- Accessing browser APIs like localStorage, sessionStorage, or cookies +- Triggering UI updates or animations +- Interacting with third-party frontend libraries +- Performing actions that require the user's immediate browser context + +## Implementation + + + + ### Run and connect your agent + + + + + ### Create a frontend tool + + First, you'll need to create a frontend tool using the [useFrontendTool](/reference/hooks/useFrontendTool) hook. Here's a simple one to get you started + that says hello to the user. + + ```tsx title="page.tsx" + import { useFrontendTool } from "@copilotkit/react-core" // [!code highlight] + + export function Page() { + // ... + + // [!code highlight:25] + useFrontendTool({ + name: "sayHello", + description: "Say hello to the user", + parameters: [ + { + name: "name", + type: "string", + description: "The name of the user to say hello to", + required: true, + }, + ], + handler({ name }) { + // Handler returns the result of the tool call + return { currentURLPath: window.location.href, userName: name }; + }, + render: ({ args }) => { + // Renders UI based on the data of the tool call + return ( +
+

Hello, {args.name}!

+

You're currently on {window.location.href}

+
+ ); + }, + }); + + // ... + } + ``` + + Now you need to indicate to the Agent that they can use this tool similarly how you defined backend tools. + +
+ + + ### Give the tool to the Agent + ```python title="agent.py" + + # Create the agent + from pyagentspec.agent import Agent + from pyagentspec.llms import OpenAiCompatibleConfig + from pyagentspec.property import StringProperty + from pyagentspec.tools import ClientTool + from pyagentspec.serialization import AgentSpecSerializer + + llm = OpenAiCompatibleConfig( + name="my_llm", + model_id="gpt-4o-mini", + url="https://api.openai.com/v1", + ) + sayhello_tool = ClientTool( # ClientTool is used for frontend tools + name="sayHello", + description="Say hello to the user.", + inputs=[StringProperty(title="name", description="The name of the user to say hello to")], + ) + agent = Agent( + name="my_agent", + llm_config=llm, + system_prompt="An helpful assistant tasked with answering the user requests.", + tools=[sayhello_tool], + human_in_the_loop=True, + ) + agentspec_json_config = AgentSpecSerializer().to_json(agent) + + # Start the server + from fastapi import APIRouter, FastAPI + from ag_ui_agentspec.agent import AgentSpecAgent + from ag_ui_agentspec.endpoint import add_agentspec_fastapi_endpoint + + + runtime = "langgraph" # you can also choose "wayflow" + router = APIRouter() + add_agentspec_fastapi_endpoint( + app=router, + agentspec_agent=AgentSpecAgent( + agentspec_json_config, + runtime=runtime, + ), + path=f"/{runtime}/path_to_my_agent", + ) + app = FastAPI(title="Agent-Spec x AG-UI Examples - Frontend Tool") + app.include_router(router) + + if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8000) + + ``` + + + + + ### Give it a try! + You've now given your agent the ability to call the frontend tool you've just defined. + + +
+ diff --git a/docs/content/docs/agent-spec/generative-ui/index.mdx b/docs/content/docs/agent-spec/generative-ui/index.mdx new file mode 100644 index 0000000000..f31c549b83 --- /dev/null +++ b/docs/content/docs/agent-spec/generative-ui/index.mdx @@ -0,0 +1,42 @@ +--- +title: Generative UI +icon: "lucide/Paintbrush" +description: Render your agent's behavior with custom UI components. +--- +import { ServerIcon, SquareMousePointerIcon, BotIcon } from "lucide-react"; + + + Demo of Generative UI showing a meeting scheduling agent + + +## What is Generative UI? + +Generative UI lets you render your agent's state, progress, outputs, and tool calls with custom UI components in real-time. It bridges the gap between AI +agents and user interfaces. As your Agent Spec agent processes information and makes decisions, you can render custom UI components that: + +- Show loading states and progress indicators +- Display structured data in tables, cards, or charts +- Create interactive elements for user input + +## How can I use this? + +To get started, you first need to decide what is going to be backing your generative UI. Currently, there are two main variants of Generative UI with CopilotKit for Agent Spec. + + + } + title="Backend Tools" + description="Render your agent's tool calls with custom UI components" + href="/agent-spec/generative-ui/backend-tools" + /> + } + title="Frontend Tools" + description="Provide your agent with client-side tools to show custom components and drive the UI." + href="/agent-spec/generative-ui/frontend-tools" + /> + diff --git a/docs/content/docs/agent-spec/generative-ui/meta.json b/docs/content/docs/agent-spec/generative-ui/meta.json new file mode 100644 index 0000000000..1434b4f463 --- /dev/null +++ b/docs/content/docs/agent-spec/generative-ui/meta.json @@ -0,0 +1,6 @@ +{ + "title": "Generative UI", + "icon": "lucide/Paintbrush", + "defaultOpen": true, + "pages": ["backend-tools", "frontend-tools"] +} diff --git a/docs/content/docs/agent-spec/human-in-the-loop.mdx b/docs/content/docs/agent-spec/human-in-the-loop.mdx new file mode 100644 index 0000000000..ca4c837725 --- /dev/null +++ b/docs/content/docs/agent-spec/human-in-the-loop.mdx @@ -0,0 +1,117 @@ +--- +title: Human-in-the-Loop +icon: "lucide/Wrench" +description: Create frontend tools and use them within your Agent Spec AI agent for human-in-the-loop interactions. +--- +import InstallSDKSnippet from "@/snippets/install-sdk.mdx" +import RunAndConnect from "@/snippets/integrations/agent-spec/run-and-connect.mdx" +import { IframeSwitcherGroup } from "@/components/content" + + + +## What is this? + +Frontend tools enable you to define client-side functions that your Agent Spec agent can invoke, with execution happening entirely in the user's browser. When your agent calls a frontend tool, +the logic runs on the client side, giving you direct access to the frontend environment. + +This can be utilized to let [your agent control the UI](/frontend-actions), [generative UI](/agent-spec/generative-ui/frontend-tools), or for Human-in-the-loop interactions. + +In this guide, we cover the use of frontend tools for Human-in-the-loop. + +## When should I use this? + +Use frontend tools when you need your agent to interact with client-side primitives such as: +- Reading or modifying React component state +- Accessing browser APIs like localStorage, sessionStorage, or cookies +- Triggering UI updates or animations +- Interacting with third-party frontend libraries +- Performing actions that require the user's immediate browser context + +## Implementation + + + + ### Run and connect your agent + + + + + ### Create a frontend human-in-the-loop tool + + Frontend tools can be leveraged in a variety of ways. One of those ways is to have a human-in-the-loop flow where the response + of the tool is gated by a user's decision. + + In this example we will simulate an "approval" flow for executing a command. First, use the `useHumanInTheLoop` hook to create a tool that + prompts the user for approval. + + ```tsx title="page.tsx" + import { useHumanInTheLoop } from "@copilotkit/react-core" // [!code highlight] + + export function Page() { + // ... + + useHumanInTheLoop({ + name: "offerOptions", + description: "Give the user a choice between two options and have them select one.", + parameters: [ + { + name: "option_1", + type: "string", + description: "The first option", + required: true, + }, + { + name: "option_2", + type: "string", + description: "The second option", + required: true, + }, + ], + render: ({ args, respond }) => { + if (!respond) return <>; + return ( +
+ {/* [!code highlight:2] */} + + +
+ ); + }, + }); + + // ... + } + ``` +
+ + ### Set up your agent + + Human-in-the-loop tools are enabled through the concept of `ClientTool` in Agent Spec. Refer to the [Frontend Tools guide](/agent-spec/generative-ui/frontend-tools) for more details. In short, in your Agent Spec config, you need to provide the tool declarations, but the execution happens client-side (in your frontend). + + + ### Try it out! + + You've now given your agent the ability to show the user two options and have them select one. The agent will then be aware of the user's choice and can use it in subsequent steps. + + ``` + Can you show me two good options for a restaurant name? + ``` + +
diff --git a/docs/content/docs/agent-spec/index.mdx b/docs/content/docs/agent-spec/index.mdx new file mode 100644 index 0000000000..b189cebcb2 --- /dev/null +++ b/docs/content/docs/agent-spec/index.mdx @@ -0,0 +1,88 @@ +--- +title: Overview +icon: "custom/agentspecMark" +description: Bring your Agent‑Spec agents to your users with CopilotKit via AG‑UI. +hideHeader: true +--- +import { customIcons } from "@/lib/icons/custom-icons"; +import { FrameworkOverview } from "@/components/content/landing-pages/framework-overview"; + +} + header="Bring your Open Agent Spec agents to your users" + subheader="Give your Open Agent Spec agents real user-interactivity using CopilotKit and AG-UI. Build rich, interactive, agent-powered applications." + bannerVideo="https://cdn.copilotkit.ai/docs/copilotkit/videos/coagents/overview.mp4" + guideLink="/agent-spec/quickstart" + initCommand="npx copilotkit@latest init" + featuresLink="https://feature-viewer.copilotkit.ai/agent-spec-wayflow/feature/agentic_chat" + supportedFeatures={[ + { + title: "Generative UI", + description: "Render your agent's state, progress, outputs, and tool calls with custom UI components in real-time. Bridges the gap between AI agents and user interfaces.", + documentationLink: "/agent-spec/generative-ui", + demoLink: "https://feature-viewer.copilotkit.ai/agent-spec-wayflow/feature/tool_based_generative_ui", + videoUrl: "https://cdn.copilotkit.ai/docs/copilotkit/videos/coagents/haiku.mp4" + }, + { + title: "Human in the Loop", + description: "Empower users to guide agents at key checkpoints. Combine the best of AI and human judgment for more reliable and controllable agent behavior.", + documentationLink: "/agent-spec/human-in-the-loop", + demoLink: "https://examples-coagents-ai-travel-app.vercel.app/", + videoUrl: "https://cdn.copilotkit.ai/docs/copilotkit/images/coagents/human-in-the-loop-example.mp4" + } + ]} + architectureImage="https://cdn.copilotkit.ai/docs/copilotkit/images/generic-agui-architecture.png" + liveDemos={[ + { + type: "saas", + title: "SaaS Copilot", + description: "A traditional SaaS application enhanced with AI agents. These copilots integrate seamlessly into existing workflows, providing intelligent assistance for specific business processes. Perfect for customer service, sales automation, project management, and domain-specific tasks where users need guided, step-by-step AI assistance.", + iframeUrl: "https://examples-coagents-ai-travel-app.vercel.app?copilotOpen=true" + }, + { + type: "canvas", + title: "Canvas Copilot", + description: "An infinite canvas interface where users collaborate with AI agents in a spatial, visual environment. These copilots excel at research, brainstorming, content creation, and complex problem-solving where information needs to be organized, connected, and explored in a non-linear, creative way.", + iframeUrl: "https://examples-coagents-research-canvas-ui.vercel.app/" + } + ]} +/> + + +# Open Agent Spec + CopilotKit + +Bring your Agent Spec agents to an interactive UI using CopilotKit and AG‑UI. Use our Next.js starter to connect a CopilotKit UI to your Agent‑Spec FastAPI endpoint that streams AG‑UI events. + +## Quickstart + +```bash +npx copilotkit@latest create -f agent-spec +``` + +Then set an endpoint for your backend (defaults to `http://localhost:8000/copilotkit`): + +```dotenv title=".env.local" +COPILOTKIT_REMOTE_ENDPOINT=http://localhost:8000/copilotkit +``` + +Run your Agent Spec FastAPI server and start the Next.js app. You can use the example server at `ag-ui/integrations/agent-spec/python/examples/server.py`. + +## How it works + +- Backend: Your FastAPI endpoint (from the Agent Spec integration) emits AG‑UI SSE events. +- Frontend: The Next.js template proxies requests to your backend using CopilotKit Runtime. +- Protocol: AG‑UI spans/events power streaming text, tool calls and results, and run lifecycle. + +## Links + +- Next.js starter flag: `-f agent-spec` +- Example FastAPI server: `ag-ui/integrations/agent-spec/python/examples/server.py` +- Endpoint helper: `ag-ui/integrations/agent-spec/python/ag_ui_agentspec/endpoint.py` + +## Learn more + +- Agent Spec docs home: https://oracle.github.io/agent-spec/development/docs_home.html +- Specification overview: https://oracle.github.io/agent-spec/development/agentspec/index.html +- API reference: https://oracle.github.io/agent-spec/development/api/index.html +- Reference sheet: https://oracle.github.io/agent-spec/development/misc/reference_sheet.html diff --git a/docs/content/docs/agent-spec/langgraph.mdx b/docs/content/docs/agent-spec/langgraph.mdx new file mode 100644 index 0000000000..0d70b0b582 --- /dev/null +++ b/docs/content/docs/agent-spec/langgraph.mdx @@ -0,0 +1,91 @@ +--- +title: LangGraph Integration +description: Connect an Agent Spec (LangGraph) backend to CopilotKit via the AG‑UI endpoint and stream agent runs to the UI. +icon: "lucide/Workflow" +--- + +import RunAndConnect from "@/snippets/integrations/agent-spec/run-and-connect.mdx"; + +## What is this? + +Wire an Agent Spec agent backed by LangGraph to CopilotKit’s UI via the AG‑UI event protocol. You’ll run a FastAPI endpoint that emits AG‑UI events and point your Next.js app at it. + +Key pieces: +- Backend endpoint: `ag-ui/integrations/agent-spec/python/ag_ui_agentspec/endpoint.py` +- Example server: `ag-ui/integrations/agent-spec/python/examples/server.py` +- Template UI: `npx copilotkit@latest create -f agent-spec` + +## When should I use this? + +Use this integration when you already have a LangGraph-based agent described by an Agent Spec and want a turnkey UI that streams assistant text, tool calls/results, and run lifecycle with minimal wiring. + +## Prerequisites + +- Python 3.9+ +- Node.js 20+ +- An Agent Spec file that targets a LangGraph runtime (JSON/YAML) + +## Steps + +### 1. Start the Agent Spec FastAPI server + +Use the provided example server and select the LangGraph runtime. + +```bash title="Start FastAPI (LangGraph)" +export AGENT_SPEC_PATH=/absolute/path/to/your-agent-spec.json +export AGENT_SPEC_RUNTIME=langgraph +python ag-ui/integrations/agent-spec/python/examples/server.py +``` + +What it does: +- Loads your Agent Spec from `AGENT_SPEC_PATH`. +- Constructs an `AgentSpecAgent(runtime="langgraph")`. +- Mounts the AG‑UI endpoint at `/copilotkit` using `add_agentspec_fastapi_endpoint(...)`. + +Endpoint implementation reference: + +```python title="ag-ui/integrations/agent-spec/python/ag_ui_agentspec/endpoint.py" +def add_agentspec_fastapi_endpoint(app, agentspec_agent, path="/"): + @app.post(path) + async def agentic_chat_endpoint(input_data: RunAgentInput, request: Request): + # Bridges Agent‑Spec telemetry to AG‑UI events via EVENT_QUEUE and streams SSE + ... +``` + +### 2. Scaffold and connect the UI + + + +If you already have the starter, set your backend endpoint in `.env.local`: + +```dotenv title=".env.local" +COPILOTKIT_REMOTE_ENDPOINT=http://localhost:8000/copilotkit +``` + +Then run the app (for example with `pnpm dev`) and open `http://localhost:3000`. + +## How it works + +- The Agent Spec adapter converts LangGraph callbacks into Agent‑Spec telemetry. +- `AgUiSpanProcessor` translates spans/events into AG‑UI events and enqueues them onto a per‑request queue (`EVENT_QUEUE`). +- The FastAPI endpoint drains that queue and streams SSE to the browser. CopilotKit consumes these to render: + - assistant text: `TEXT_MESSAGE_START/CONTENT/END` + - tool call lifecycle: `TOOL_CALL_START/ARGS/END` + - tool results: `TOOL_CALL_RESULT` + - run lifecycle: `RUN_STARTED/RUN_FINISHED` + +## Troubleshooting + +- Ensure `AGENT_SPEC_RUNTIME=langgraph` is set when starting the server. +- The endpoint path must match your UI’s `COPILOTKIT_REMOTE_ENDPOINT` (default `/copilotkit`). +- The endpoint asserts a queue is bound (no print fallback). If you get queue errors, check that requests go through the provided FastAPI route. + +## Next steps + +- Build richer UIs with agentic chat and generative UI. +- Pass full chat history between turns. The adapter and processor handle `message_id` and tool‑call lifecycle for you. + +## Learn more + +- Agent Spec docs home: https://oracle.github.io/agent-spec/development/docs_home.html +- Specification overview: https://oracle.github.io/agent-spec/development/agentspec/index.html diff --git a/docs/content/docs/agent-spec/meta.json b/docs/content/docs/agent-spec/meta.json new file mode 100644 index 0000000000..3e69c981b3 --- /dev/null +++ b/docs/content/docs/agent-spec/meta.json @@ -0,0 +1,22 @@ +{ + "title": "Open Agent Spec", + "root": true, + "pages": [ + "---Getting Started---", + "index", + "quickstart", + "vibe-coding-mcp", + "---Guides---", + "agentic-chat-ui", + "langgraph", + "wayflow", + "custom-look-and-feel", + "frontend-actions", + "generative-ui", + "human-in-the-loop", + "---Premium Features---", + "...premium", + "---Troubleshooting---", + "...troubleshooting" + ] +} diff --git a/docs/content/docs/agent-spec/premium/headless-ui.mdx b/docs/content/docs/agent-spec/premium/headless-ui.mdx new file mode 100644 index 0000000000..de5fa9adcf --- /dev/null +++ b/docs/content/docs/agent-spec/premium/headless-ui.mdx @@ -0,0 +1,8 @@ +--- +title: "Fully Headless UI" +description: "Fully customize your Copilot's UI from the ground up using headless UI" +icon: "lucide/Settings" +--- +import HeadlessUI from "@/snippets/shared/guides/custom-look-and-feel/headless-ui.mdx"; + + \ No newline at end of file diff --git a/docs/content/docs/agent-spec/premium/inspector.mdx b/docs/content/docs/agent-spec/premium/inspector.mdx new file mode 100644 index 0000000000..c4cd53630b --- /dev/null +++ b/docs/content/docs/agent-spec/premium/inspector.mdx @@ -0,0 +1,9 @@ +--- +title: Inspector +icon: "lucide/SearchCheck" +description: Inspector for debugging actions, readables, agent status, messages, and context. +--- + +import Inspector from "@/snippets/shared/premium/inspector.mdx"; + + diff --git a/docs/content/docs/agent-spec/premium/meta.json b/docs/content/docs/agent-spec/premium/meta.json new file mode 100644 index 0000000000..076ce46d60 --- /dev/null +++ b/docs/content/docs/agent-spec/premium/meta.json @@ -0,0 +1,4 @@ +{ + "title": "Premium Features", + "pages": ["overview", "headless-ui", "observability", "inspector"] +} diff --git a/docs/content/docs/agent-spec/premium/observability.mdx b/docs/content/docs/agent-spec/premium/observability.mdx new file mode 100644 index 0000000000..6bf9a04026 --- /dev/null +++ b/docs/content/docs/agent-spec/premium/observability.mdx @@ -0,0 +1,9 @@ +--- +title: "Observability" +description: "Monitor your CopilotKit application with comprehensive observability hooks. Understand user interactions, chat events, and system errors." +icon: "lucide/Activity" +--- + +import Observability from "@/snippets/shared/premium/observability.mdx"; + + \ No newline at end of file diff --git a/docs/content/docs/agent-spec/premium/overview.mdx b/docs/content/docs/agent-spec/premium/overview.mdx new file mode 100644 index 0000000000..825d857944 --- /dev/null +++ b/docs/content/docs/agent-spec/premium/overview.mdx @@ -0,0 +1,9 @@ +--- +title: CopilotKit Premium +icon: "lucide/Star" +description: Premium features for CopilotKit. +--- + +import Overview from "@/snippets/shared/premium/overview.mdx"; + + \ No newline at end of file diff --git a/docs/content/docs/agent-spec/quickstart.mdx b/docs/content/docs/agent-spec/quickstart.mdx new file mode 100644 index 0000000000..de82557950 --- /dev/null +++ b/docs/content/docs/agent-spec/quickstart.mdx @@ -0,0 +1,55 @@ +--- +title: Quickstart +description: Turn your Agent Spec agent into an agent‑native application in minutes. +icon: "lucide/Play" +hideTOC: true +--- + +## Prerequisites + +- Node.js 20+ +- Python 3.9+ + +## 1) Scaffold the UI + +Start from our starter template: + +```bash +npx copilotkit@latest create -f agent-spec +``` + +## 2) Run your Agent‑Spec server + +Use the FastAPI example or your own server with the Agent‑Spec endpoint mounted at `/copilotkit`: + +```bash title="ag-ui/integrations/agent-spec/python/examples/server.py" +python server.py +# Requires AGENT_SPEC_PATH pointing to an Agent‑Spec JSON/YAML file +``` + +## 3) Connect the UI to your server + +Set the endpoint in your Next.js app: + +```dotenv title=".env.local" +COPILOTKIT_REMOTE_ENDPOINT=http://localhost:8000/copilotkit +``` + +## 4) Run Next.js + +```bash +pnpm dev +# or npm run dev / yarn dev / bun dev +``` + +Open http://localhost:3000 and start chatting with your agent. + +## Notes + +- The template uses CopilotKit Runtime to proxy to your FastAPI endpoint. +- AG‑UI streaming covers text, tool calls, tool results, and run lifecycle. + +## Learn more + +- Agent Spec docs home: https://oracle.github.io/agent-spec/development/docs_home.html +- Specification overview: https://oracle.github.io/agent-spec/development/agentspec/index.html diff --git a/docs/content/docs/agent-spec/shared-state/index.mdx b/docs/content/docs/agent-spec/shared-state/index.mdx new file mode 100644 index 0000000000..8a14231666 --- /dev/null +++ b/docs/content/docs/agent-spec/shared-state/index.mdx @@ -0,0 +1,12 @@ +--- +title: Shared State +icon: "lucide/Repeat" +description: Synchronize app and agent state in Agent Spec integrations. +--- + +Agent Spec agentic chat supports streaming text and tool events. For advanced shared state patterns, mirror what you surface as messages or tool calls until a dedicated state channel is available. + +## Learn more + +- Agent Spec docs home: https://oracle.github.io/agent-spec/development/docs_home.html +- Reference sheet (formats): https://oracle.github.io/agent-spec/development/misc/reference_sheet.html diff --git a/docs/content/docs/agent-spec/troubleshooting/common-issues.mdx b/docs/content/docs/agent-spec/troubleshooting/common-issues.mdx new file mode 100644 index 0000000000..8074c11fc9 --- /dev/null +++ b/docs/content/docs/agent-spec/troubleshooting/common-issues.mdx @@ -0,0 +1,9 @@ +--- +title: Common Copilot Issues +description: Common issues you may encounter when using Copilots. +icon: "lucide/LifeBuoy" +--- + +import CommonIssues from "@/snippets/shared/troubleshooting/common-issues.mdx"; + + \ No newline at end of file diff --git a/docs/content/docs/agent-spec/troubleshooting/error-debugging.mdx b/docs/content/docs/agent-spec/troubleshooting/error-debugging.mdx new file mode 100644 index 0000000000..7e9c4ee7de --- /dev/null +++ b/docs/content/docs/agent-spec/troubleshooting/error-debugging.mdx @@ -0,0 +1,9 @@ +--- +title: "Error Debugging & Observability" +description: "Learn how to debug errors in CopilotKit with dev console and set up error observability for monitoring services." +icon: "lucide/TriangleAlert" +--- + +import ErrorDebugging from "@/snippets/shared/troubleshooting/error-debugging.mdx"; + + \ No newline at end of file diff --git a/docs/content/docs/agent-spec/troubleshooting/meta.json b/docs/content/docs/agent-spec/troubleshooting/meta.json new file mode 100644 index 0000000000..15015c7b3a --- /dev/null +++ b/docs/content/docs/agent-spec/troubleshooting/meta.json @@ -0,0 +1,8 @@ +{ + "pages": [ + "error-debugging", + "common-issues", + "migrate-to-1.10.X", + "migrate-to-1.8.2" + ] +} diff --git a/docs/content/docs/agent-spec/troubleshooting/migrate-to-1.10.X.mdx b/docs/content/docs/agent-spec/troubleshooting/migrate-to-1.10.X.mdx new file mode 100644 index 0000000000..ea6825b308 --- /dev/null +++ b/docs/content/docs/agent-spec/troubleshooting/migrate-to-1.10.X.mdx @@ -0,0 +1,8 @@ +--- +title: Migrate to 1.10.X +description: Migration guide for CopilotKit 1.10.X +icon: "lucide/RefreshCw" +--- +import MigrateTo1100 from "@/snippets/shared/troubleshooting/migrate-to-1.10.X.mdx"; + + \ No newline at end of file diff --git a/docs/content/docs/agent-spec/troubleshooting/migrate-to-1.8.2.mdx b/docs/content/docs/agent-spec/troubleshooting/migrate-to-1.8.2.mdx new file mode 100644 index 0000000000..7979baf226 --- /dev/null +++ b/docs/content/docs/agent-spec/troubleshooting/migrate-to-1.8.2.mdx @@ -0,0 +1,8 @@ +--- +title: Migrate to 1.8.2 +description: Migration guide for CopilotKit 1.8.2 +icon: "lucide/RefreshCw" +--- +import MigrateTo182 from "@/snippets/shared/troubleshooting/migrate-to-1.8.2.mdx"; + + \ No newline at end of file diff --git a/docs/content/docs/agent-spec/vibe-coding-mcp.mdx b/docs/content/docs/agent-spec/vibe-coding-mcp.mdx new file mode 100644 index 0000000000..0578bb49a2 --- /dev/null +++ b/docs/content/docs/agent-spec/vibe-coding-mcp.mdx @@ -0,0 +1,8 @@ +--- +title: "Vibe Coding MCP" +icon: "lucide/Code" +description: "Use our MCP server to connect your Pydantic AI agents to CopilotKit." +--- +import MCPSetup from "@/snippets/shared/guides/mcp-server-setup.mdx"; + + \ No newline at end of file diff --git a/docs/content/docs/agent-spec/wayflow.mdx b/docs/content/docs/agent-spec/wayflow.mdx new file mode 100644 index 0000000000..ee3a4fbd4e --- /dev/null +++ b/docs/content/docs/agent-spec/wayflow.mdx @@ -0,0 +1,91 @@ +--- +title: WayFlow Integration +description: Connect an Agent Spec (WayFlow) backend to CopilotKit via the AG‑UI endpoint and stream agent runs to the UI. +icon: "lucide/Gauge" +--- + +import RunAndConnect from "@/snippets/integrations/agent-spec/run-and-connect.mdx"; + +## What is this? + +Wire an Agent Spec agent backed by WayFlow to CopilotKit’s UI via the AG‑UI event protocol. You’ll run a FastAPI endpoint that emits AG‑UI events and point your Next.js app at it. + +Key pieces: +- Backend endpoint: `ag-ui/integrations/agent-spec/python/ag_ui_agentspec/endpoint.py` +- Example server: `ag-ui/integrations/agent-spec/python/examples/server.py` +- Template UI: `npx copilotkit@latest create -f agent-spec` + +## When should I use this? + +Use this integration when you already have a WayFlow-based agent described by an Agent Spec and want a turnkey UI that streams assistant text, tool calls/results, and run lifecycle with minimal wiring. + +## Prerequisites + +- Python 3.9+ +- Node.js 20+ +- An Agent Spec file that targets a WayFlow runtime (JSON/YAML) + +## Steps + +### 1. Start the Agent Spec FastAPI server + +Use the provided example server and select the WayFlow runtime. + +```bash title="Start FastAPI (WayFlow)" +export AGENT_SPEC_PATH=/absolute/path/to/your-agent-spec.json +export AGENT_SPEC_RUNTIME=wayflow +python ag-ui/integrations/agent-spec/python/examples/server.py +``` + +What it does: +- Loads your Agent Spec from `AGENT_SPEC_PATH`. +- Constructs an `AgentSpecAgent(runtime="wayflow")`. +- Mounts the AG‑UI endpoint at `/copilotkit` using `add_agentspec_fastapi_endpoint(...)`. + +Endpoint implementation reference: + +```python title="ag-ui/integrations/agent-spec/python/ag_ui_agentspec/endpoint.py" +def add_agentspec_fastapi_endpoint(app, agentspec_agent, path="/"): + @app.post(path) + async def agentic_chat_endpoint(input_data: RunAgentInput, request: Request): + # Bridges Agent‑Spec telemetry to AG‑UI events via EVENT_QUEUE and streams SSE + ... +``` + +### 2. Scaffold and connect the UI + + + +If you already have the starter, set your backend endpoint in `.env.local`: + +```dotenv title=".env.local" +COPILOTKIT_REMOTE_ENDPOINT=http://localhost:8000/copilotkit +``` + +Then run the app (for example with `pnpm dev`) and open `http://localhost:3000`. + +## How it works + +- The Agent Spec adapter and processors translate WayFlow runs into AG‑UI events. +- `AgUiSpanProcessor` enqueues events onto a per‑request queue (`EVENT_QUEUE`). +- The FastAPI endpoint drains that queue and streams SSE to the browser. CopilotKit consumes these to render: + - assistant text: `TEXT_MESSAGE_START/CONTENT/END` + - tool call lifecycle: `TOOL_CALL_START/ARGS/END` + - tool results: `TOOL_CALL_RESULT` + - run lifecycle: `RUN_STARTED/RUN_FINISHED` + +## Troubleshooting + +- Ensure `AGENT_SPEC_RUNTIME=wayflow` is set when starting the server. +- The endpoint path must match your UI’s `COPILOTKIT_REMOTE_ENDPOINT` (default `/copilotkit`). +- The endpoint asserts a queue is bound. If you get queue errors, check that requests go through the provided FastAPI route. + +## Next steps + +- Build richer UIs with agentic chat and generative UI. +- For advanced usage, see the WayFlow docs and Agent Spec telemetry notes in the repo. + +## Learn more + +- Agent Spec docs home: https://oracle.github.io/agent-spec/development/docs_home.html +- Specification overview: https://oracle.github.io/agent-spec/development/agentspec/index.html diff --git a/docs/content/docs/agno/(other)/contributing/code-contributions/index.mdx b/docs/content/docs/agno/(other)/contributing/code-contributions/index.mdx index 16ef161e6c..f20ae7e49f 100644 --- a/docs/content/docs/agno/(other)/contributing/code-contributions/index.mdx +++ b/docs/content/docs/agno/(other)/contributing/code-contributions/index.mdx @@ -22,7 +22,7 @@ This guide will help you get started as smoothly as possible. Then, clone your fork to your local machine: ```bash git clone https://github.com//CopilotKit - cd CopilotKit/CopilotKit + cd CopilotKit/src/v1.x ```
diff --git a/docs/content/docs/agno/generative-ui/meta.json b/docs/content/docs/agno/generative-ui/meta.json index c7ec6a51f1..1434b4f463 100644 --- a/docs/content/docs/agno/generative-ui/meta.json +++ b/docs/content/docs/agno/generative-ui/meta.json @@ -1,5 +1,6 @@ { "title": "Generative UI", + "icon": "lucide/Paintbrush", "defaultOpen": true, "pages": ["backend-tools", "frontend-tools"] } diff --git a/docs/content/docs/aws-strands/(other)/contributing/code-contributions/index.mdx b/docs/content/docs/aws-strands/(other)/contributing/code-contributions/index.mdx index 16ef161e6c..f20ae7e49f 100644 --- a/docs/content/docs/aws-strands/(other)/contributing/code-contributions/index.mdx +++ b/docs/content/docs/aws-strands/(other)/contributing/code-contributions/index.mdx @@ -22,7 +22,7 @@ This guide will help you get started as smoothly as possible. Then, clone your fork to your local machine: ```bash git clone https://github.com//CopilotKit - cd CopilotKit/CopilotKit + cd CopilotKit/src/v1.x ``` diff --git a/docs/content/docs/aws-strands/generative-ui/meta.json b/docs/content/docs/aws-strands/generative-ui/meta.json index 33fb211de6..bb92117e49 100644 --- a/docs/content/docs/aws-strands/generative-ui/meta.json +++ b/docs/content/docs/aws-strands/generative-ui/meta.json @@ -1,6 +1,6 @@ { "title": "Generative UI", - "defaultOpen": true, "icon": "lucide/Paintbrush", + "defaultOpen": true, "pages": ["backend-tools", "frontend-tools", "agentic"] } diff --git a/docs/content/docs/crewai-crews/(other)/contributing/code-contributions/index.mdx b/docs/content/docs/crewai-crews/(other)/contributing/code-contributions/index.mdx index 16ef161e6c..f20ae7e49f 100644 --- a/docs/content/docs/crewai-crews/(other)/contributing/code-contributions/index.mdx +++ b/docs/content/docs/crewai-crews/(other)/contributing/code-contributions/index.mdx @@ -22,7 +22,7 @@ This guide will help you get started as smoothly as possible. Then, clone your fork to your local machine: ```bash git clone https://github.com//CopilotKit - cd CopilotKit/CopilotKit + cd CopilotKit/src/v1.x ``` diff --git a/docs/content/docs/crewai-flows/(other)/contributing/code-contributions/index.mdx b/docs/content/docs/crewai-flows/(other)/contributing/code-contributions/index.mdx index 16ef161e6c..f20ae7e49f 100644 --- a/docs/content/docs/crewai-flows/(other)/contributing/code-contributions/index.mdx +++ b/docs/content/docs/crewai-flows/(other)/contributing/code-contributions/index.mdx @@ -22,7 +22,7 @@ This guide will help you get started as smoothly as possible. Then, clone your fork to your local machine: ```bash git clone https://github.com//CopilotKit - cd CopilotKit/CopilotKit + cd CopilotKit/src/v1.x ``` diff --git a/docs/content/docs/direct-to-llm/guides/custom-ai-assistant-behavior.mdx b/docs/content/docs/direct-to-llm/guides/custom-ai-assistant-behavior.mdx index 879d0462da..9e8b51fca3 100644 --- a/docs/content/docs/direct-to-llm/guides/custom-ai-assistant-behavior.mdx +++ b/docs/content/docs/direct-to-llm/guides/custom-ai-assistant-behavior.mdx @@ -99,7 +99,7 @@ It can be customized for **Copilot UI** as well as **programmatically**: ### Overwriting the default system message -For cases requiring complete control over the system message, you can use the `makeSystemMessage` function. We highly recommend reading CopilotKit's default system message before deciding to overwrite it, which can be found [here](https://github.com/CopilotKit/CopilotKit/blob/e48a34a66bb4dfd210e93dc41eee7d0f22d1a0c4/CopilotKit/packages/react-core/src/hooks/use-copilot-chat.ts#L240-L258). +For cases requiring complete control over the system message, you can use the `makeSystemMessage` function. We highly recommend reading CopilotKit's default system message before deciding to overwrite it, which can be found [here](https://github.com/CopilotKit/CopilotKit/blob/e48a34a66bb4dfd210e93dc41eee7d0f22d1a0c4/src/v1.x/packages/react-core/src/hooks/use-copilot-chat.ts#L240-L258). This approach is **not recommended** as it may interfere with more advanced optimizations made by CopilotKit. **Only use this approach if the other options are not enough.** diff --git a/docs/content/docs/langgraph/(other)/contributing/code-contributions/index.mdx b/docs/content/docs/langgraph/(other)/contributing/code-contributions/index.mdx index 16ef161e6c..f20ae7e49f 100644 --- a/docs/content/docs/langgraph/(other)/contributing/code-contributions/index.mdx +++ b/docs/content/docs/langgraph/(other)/contributing/code-contributions/index.mdx @@ -22,7 +22,7 @@ This guide will help you get started as smoothly as possible. Then, clone your fork to your local machine: ```bash git clone https://github.com//CopilotKit - cd CopilotKit/CopilotKit + cd CopilotKit/src/v1.x ``` diff --git a/docs/content/docs/langgraph/generative-ui/meta.json b/docs/content/docs/langgraph/generative-ui/meta.json index 8f18138594..54cc3c53e0 100644 --- a/docs/content/docs/langgraph/generative-ui/meta.json +++ b/docs/content/docs/langgraph/generative-ui/meta.json @@ -1,4 +1,5 @@ { "title": "Generative UI", + "icon": "lucide/Paintbrush", "pages": ["backend-tools", "frontend-tools", "agentic"] } diff --git a/docs/content/docs/llamaindex/generative-ui/meta.json b/docs/content/docs/llamaindex/generative-ui/meta.json index f4d74dd7dd..bb92117e49 100644 --- a/docs/content/docs/llamaindex/generative-ui/meta.json +++ b/docs/content/docs/llamaindex/generative-ui/meta.json @@ -1,5 +1,6 @@ { "title": "Generative UI", + "icon": "lucide/Paintbrush", "defaultOpen": true, "pages": ["backend-tools", "frontend-tools", "agentic"] } diff --git a/docs/content/docs/mastra/(other)/contributing/code-contributions/index.mdx b/docs/content/docs/mastra/(other)/contributing/code-contributions/index.mdx index 16ef161e6c..f20ae7e49f 100644 --- a/docs/content/docs/mastra/(other)/contributing/code-contributions/index.mdx +++ b/docs/content/docs/mastra/(other)/contributing/code-contributions/index.mdx @@ -22,7 +22,7 @@ This guide will help you get started as smoothly as possible. Then, clone your fork to your local machine: ```bash git clone https://github.com//CopilotKit - cd CopilotKit/CopilotKit + cd CopilotKit/src/v1.x ``` diff --git a/docs/content/docs/mastra/generative-ui/meta.json b/docs/content/docs/mastra/generative-ui/meta.json index d47fc3c528..1434b4f463 100644 --- a/docs/content/docs/mastra/generative-ui/meta.json +++ b/docs/content/docs/mastra/generative-ui/meta.json @@ -1,6 +1,6 @@ { "title": "Generative UI", - "icon": "lucide/LayoutDashboard", + "icon": "lucide/Paintbrush", "defaultOpen": true, "pages": ["backend-tools", "frontend-tools"] } diff --git a/docs/content/docs/meta.json b/docs/content/docs/meta.json index acf5fc2f67..ef29f3db33 100644 --- a/docs/content/docs/meta.json +++ b/docs/content/docs/meta.json @@ -10,6 +10,7 @@ "microsoft-agent-framework", "aws-strands", "direct-to-llm", + "agent-spec", "langgraph", "ag2", "agno", diff --git a/docs/content/docs/microsoft-agent-framework/(other)/contributing/code-contributions/index.mdx b/docs/content/docs/microsoft-agent-framework/(other)/contributing/code-contributions/index.mdx index 1134fc0b63..3ea706fcb8 100644 --- a/docs/content/docs/microsoft-agent-framework/(other)/contributing/code-contributions/index.mdx +++ b/docs/content/docs/microsoft-agent-framework/(other)/contributing/code-contributions/index.mdx @@ -22,7 +22,7 @@ This guide will help you get started as smoothly as possible. Then, clone your fork to your local machine: ```bash git clone https://github.com//CopilotKit - cd CopilotKit/CopilotKit + cd CopilotKit/src/v1.x ``` diff --git a/docs/content/docs/microsoft-agent-framework/generative-ui/meta.json b/docs/content/docs/microsoft-agent-framework/generative-ui/meta.json index 8f18138594..54cc3c53e0 100644 --- a/docs/content/docs/microsoft-agent-framework/generative-ui/meta.json +++ b/docs/content/docs/microsoft-agent-framework/generative-ui/meta.json @@ -1,4 +1,5 @@ { "title": "Generative UI", + "icon": "lucide/Paintbrush", "pages": ["backend-tools", "frontend-tools", "agentic"] } diff --git a/docs/content/docs/pydantic-ai/generative-ui/meta.json b/docs/content/docs/pydantic-ai/generative-ui/meta.json index f4d74dd7dd..bb92117e49 100644 --- a/docs/content/docs/pydantic-ai/generative-ui/meta.json +++ b/docs/content/docs/pydantic-ai/generative-ui/meta.json @@ -1,5 +1,6 @@ { "title": "Generative UI", + "icon": "lucide/Paintbrush", "defaultOpen": true, "pages": ["backend-tools", "frontend-tools", "agentic"] } diff --git a/docs/content/docs/reference/classes/CopilotRuntime.mdx b/docs/content/docs/reference/classes/CopilotRuntime.mdx index e34e470115..4a0eb1b2c3 100644 --- a/docs/content/docs/reference/classes/CopilotRuntime.mdx +++ b/docs/content/docs/reference/classes/CopilotRuntime.mdx @@ -7,7 +7,7 @@ description: "Copilot Runtime is the back-end component of CopilotKit, enabling /* * ATTENTION! DO NOT MODIFY THIS FILE! * This page is auto-generated. If you want to make any changes to this page, changes must be made at: - * CopilotKit/packages/runtime/src/lib/runtime/copilot-runtime.ts + * src/v1.x/packages/runtime/src/lib/runtime/copilot-runtime.ts */ } diff --git a/docs/content/docs/reference/classes/CopilotTask.mdx b/docs/content/docs/reference/classes/CopilotTask.mdx index c5ef31e06e..fdedd24939 100644 --- a/docs/content/docs/reference/classes/CopilotTask.mdx +++ b/docs/content/docs/reference/classes/CopilotTask.mdx @@ -7,7 +7,7 @@ description: "CopilotTask is used to execute one-off tasks, for example on butto /* * ATTENTION! DO NOT MODIFY THIS FILE! * This page is auto-generated. If you want to make any changes to this page, changes must be made at: - * CopilotKit/packages/react-core/src/lib/copilot-task.ts + * src/v1.x/packages/react-core/src/lib/copilot-task.ts */ } This class is used to execute one-off tasks, for example on button press. It can use the context available via [useCopilotReadable](/reference/hooks/useCopilotReadable) and the actions provided by [useCopilotAction](/reference/hooks/useCopilotAction), or you can provide your own context and actions. @@ -54,7 +54,7 @@ export function MyComponent() { } ``` -Have a look at the [Presentation Example App](https://github.com/CopilotKit/CopilotKit/blob/main/CopilotKit/examples/next-openai/src/app/presentation/page.tsx) for a more complete example. +Have a look at the [Presentation Example App](https://github.com/CopilotKit/CopilotKit/blob/main/src/v1.x/examples/next-openai/src/app/presentation/page.tsx) for a more complete example. ## Constructor Parameters diff --git a/docs/content/docs/reference/classes/llm-adapters/AnthropicAdapter.mdx b/docs/content/docs/reference/classes/llm-adapters/AnthropicAdapter.mdx index 62a56395d9..cd7be24ced 100644 --- a/docs/content/docs/reference/classes/llm-adapters/AnthropicAdapter.mdx +++ b/docs/content/docs/reference/classes/llm-adapters/AnthropicAdapter.mdx @@ -7,7 +7,7 @@ description: "Copilot Runtime adapter for Anthropic." /* * ATTENTION! DO NOT MODIFY THIS FILE! * This page is auto-generated. If you want to make any changes to this page, changes must be made at: - * CopilotKit/packages/runtime/src/service-adapters/anthropic/anthropic-adapter.ts + * src/v1.x/packages/runtime/src/service-adapters/anthropic/anthropic-adapter.ts */ } Copilot Runtime adapter for Anthropic. diff --git a/docs/content/docs/reference/classes/llm-adapters/GoogleGenerativeAIAdapter.mdx b/docs/content/docs/reference/classes/llm-adapters/GoogleGenerativeAIAdapter.mdx index e33cd3afde..e4acd75212 100644 --- a/docs/content/docs/reference/classes/llm-adapters/GoogleGenerativeAIAdapter.mdx +++ b/docs/content/docs/reference/classes/llm-adapters/GoogleGenerativeAIAdapter.mdx @@ -7,7 +7,7 @@ description: "Copilot Runtime adapter for Google Generative AI (e.g. Gemini)." /* * ATTENTION! DO NOT MODIFY THIS FILE! * This page is auto-generated. If you want to make any changes to this page, changes must be made at: - * CopilotKit/packages/runtime/src/service-adapters/google/google-genai-adapter.ts + * src/v1.x/packages/runtime/src/service-adapters/google/google-genai-adapter.ts */ } Copilot Runtime adapter for Google Generative AI (e.g. Gemini). diff --git a/docs/content/docs/reference/classes/llm-adapters/GroqAdapter.mdx b/docs/content/docs/reference/classes/llm-adapters/GroqAdapter.mdx index 22c7e63fe7..6f303df5ce 100644 --- a/docs/content/docs/reference/classes/llm-adapters/GroqAdapter.mdx +++ b/docs/content/docs/reference/classes/llm-adapters/GroqAdapter.mdx @@ -7,7 +7,7 @@ description: "Copilot Runtime adapter for Groq." /* * ATTENTION! DO NOT MODIFY THIS FILE! * This page is auto-generated. If you want to make any changes to this page, changes must be made at: - * CopilotKit/packages/runtime/src/service-adapters/groq/groq-adapter.ts + * src/v1.x/packages/runtime/src/service-adapters/groq/groq-adapter.ts */ } Copilot Runtime adapter for Groq. diff --git a/docs/content/docs/reference/classes/llm-adapters/LangChainAdapter.mdx b/docs/content/docs/reference/classes/llm-adapters/LangChainAdapter.mdx index 90dd29a190..49538f5cd7 100644 --- a/docs/content/docs/reference/classes/llm-adapters/LangChainAdapter.mdx +++ b/docs/content/docs/reference/classes/llm-adapters/LangChainAdapter.mdx @@ -7,7 +7,7 @@ description: "Copilot Runtime adapter for LangChain." /* * ATTENTION! DO NOT MODIFY THIS FILE! * This page is auto-generated. If you want to make any changes to this page, changes must be made at: - * CopilotKit/packages/runtime/src/service-adapters/langchain/langchain-adapter.ts + * src/v1.x/packages/runtime/src/service-adapters/langchain/langchain-adapter.ts */ } Copilot Runtime adapter for LangChain. diff --git a/docs/content/docs/reference/classes/llm-adapters/OpenAIAdapter.mdx b/docs/content/docs/reference/classes/llm-adapters/OpenAIAdapter.mdx index d446dee28e..aec381962f 100644 --- a/docs/content/docs/reference/classes/llm-adapters/OpenAIAdapter.mdx +++ b/docs/content/docs/reference/classes/llm-adapters/OpenAIAdapter.mdx @@ -7,7 +7,7 @@ description: "Copilot Runtime adapter for OpenAI." /* * ATTENTION! DO NOT MODIFY THIS FILE! * This page is auto-generated. If you want to make any changes to this page, changes must be made at: - * CopilotKit/packages/runtime/src/service-adapters/openai/openai-adapter.ts + * src/v1.x/packages/runtime/src/service-adapters/openai/openai-adapter.ts */ } Copilot Runtime adapter for OpenAI. diff --git a/docs/content/docs/reference/classes/llm-adapters/OpenAIAssistantAdapter.mdx b/docs/content/docs/reference/classes/llm-adapters/OpenAIAssistantAdapter.mdx index 34931bae42..371003302a 100644 --- a/docs/content/docs/reference/classes/llm-adapters/OpenAIAssistantAdapter.mdx +++ b/docs/content/docs/reference/classes/llm-adapters/OpenAIAssistantAdapter.mdx @@ -7,7 +7,7 @@ description: "Copilot Runtime adapter for OpenAI Assistant API." /* * ATTENTION! DO NOT MODIFY THIS FILE! * This page is auto-generated. If you want to make any changes to this page, changes must be made at: - * CopilotKit/packages/runtime/src/service-adapters/openai/openai-assistant-adapter.ts + * src/v1.x/packages/runtime/src/service-adapters/openai/openai-assistant-adapter.ts */ } Copilot Runtime adapter for the OpenAI Assistant API. diff --git a/docs/content/docs/reference/components/CopilotKit.mdx b/docs/content/docs/reference/components/CopilotKit.mdx index 3f61af6e4f..54e15de876 100644 --- a/docs/content/docs/reference/components/CopilotKit.mdx +++ b/docs/content/docs/reference/components/CopilotKit.mdx @@ -7,7 +7,7 @@ description: "The CopilotKit provider component, wrapping your application." /* * ATTENTION! DO NOT MODIFY THIS FILE! * This page is auto-generated. If you want to make any changes to this page, changes must be made at: - * CopilotKit/packages/react-core/src/components/copilot-provider/copilotkit.tsx + * src/v1.x/packages/react-core/src/components/copilot-provider/copilotkit.tsx */ } This component will typically wrap your entire application (or a sub-tree of your application where you want to have a copilot). It provides the copilot context to all other components and hooks. diff --git a/docs/content/docs/reference/components/CopilotTextarea.mdx b/docs/content/docs/reference/components/CopilotTextarea.mdx index 26db53883f..2944bc76f8 100644 --- a/docs/content/docs/reference/components/CopilotTextarea.mdx +++ b/docs/content/docs/reference/components/CopilotTextarea.mdx @@ -7,7 +7,7 @@ description: "An AI-powered textarea component for your application, which serve /* * ATTENTION! DO NOT MODIFY THIS FILE! * This page is auto-generated. If you want to make any changes to this page, changes must be made at: - * CopilotKit/packages/react-textarea/src/components/copilot-textarea/copilot-textarea.tsx + * src/v1.x/packages/react-textarea/src/components/copilot-textarea/copilot-textarea.tsx */ }
@@ -133,7 +133,7 @@ The shortcut to use to open the editor popover window. Default is `"Cmd-k"`. Configuration settings for the autosuggestions feature. - For full reference, [check the interface on GitHub](https://github.com/CopilotKit/CopilotKit/blob/main/CopilotKit/packages/react-textarea/src/types/base/base-copilot-textarea-props.tsx#L8). + For full reference, [check the interface on GitHub](https://github.com/CopilotKit/CopilotKit/blob/main/src/v1.x/packages/react-textarea/src/types/base/base-copilot-textarea-props.tsx#L8). The purpose of the text area in plain text. @@ -147,10 +147,10 @@ Configuration settings for the autosuggestions feature. NOTE: You must provide specify at least one of `suggestionsApiConfig` or `insertionApiConfig`. - For full reference, please [click here](https://github.com/CopilotKit/CopilotKit/blob/main/CopilotKit/packages/react-textarea/src/types/autosuggestions-config/suggestions-api-config.tsx#L4). + For full reference, please [click here](https://github.com/CopilotKit/CopilotKit/blob/main/src/v1.x/packages/react-textarea/src/types/autosuggestions-config/suggestions-api-config.tsx#L4). - For full reference, please [click here](https://github.com/CopilotKit/CopilotKit/blob/main/CopilotKit/packages/react-textarea/src/types/autosuggestions-config/insertions-api-config.tsx#L4). + For full reference, please [click here](https://github.com/CopilotKit/CopilotKit/blob/main/src/v1.x/packages/react-textarea/src/types/autosuggestions-config/insertions-api-config.tsx#L4). diff --git a/docs/content/docs/reference/components/chat/CopilotChat.mdx b/docs/content/docs/reference/components/chat/CopilotChat.mdx index cb1d517a55..e63140f957 100644 --- a/docs/content/docs/reference/components/chat/CopilotChat.mdx +++ b/docs/content/docs/reference/components/chat/CopilotChat.mdx @@ -7,7 +7,7 @@ description: "The CopilotChat component, providing a chat interface for interact /* * ATTENTION! DO NOT MODIFY THIS FILE! * This page is auto-generated. If you want to make any changes to this page, changes must be made at: - * CopilotKit/packages/react-ui/src/components/chat/Chat.tsx + * src/v1.x/packages/react-ui/src/components/chat/Chat.tsx */ }
diff --git a/docs/content/docs/reference/components/chat/CopilotPopup.mdx b/docs/content/docs/reference/components/chat/CopilotPopup.mdx index eb93d7999b..a15500d960 100644 --- a/docs/content/docs/reference/components/chat/CopilotPopup.mdx +++ b/docs/content/docs/reference/components/chat/CopilotPopup.mdx @@ -7,7 +7,7 @@ description: "The CopilotPopup component, providing a popup interface for intera /* * ATTENTION! DO NOT MODIFY THIS FILE! * This page is auto-generated. If you want to make any changes to this page, changes must be made at: - * CopilotKit/packages/react-ui/src/components/chat/Popup.tsx + * src/v1.x/packages/react-ui/src/components/chat/Popup.tsx */ }
diff --git a/docs/content/docs/reference/components/chat/CopilotSidebar.mdx b/docs/content/docs/reference/components/chat/CopilotSidebar.mdx index f7938cb9fe..20074a4cfe 100644 --- a/docs/content/docs/reference/components/chat/CopilotSidebar.mdx +++ b/docs/content/docs/reference/components/chat/CopilotSidebar.mdx @@ -7,7 +7,7 @@ description: "The CopilotSidebar component, providing a sidebar interface for in /* * ATTENTION! DO NOT MODIFY THIS FILE! * This page is auto-generated. If you want to make any changes to this page, changes must be made at: - * CopilotKit/packages/react-ui/src/components/chat/Sidebar.tsx + * src/v1.x/packages/react-ui/src/components/chat/Sidebar.tsx */ }
diff --git a/docs/content/docs/reference/hooks/useCoAgent.mdx b/docs/content/docs/reference/hooks/useCoAgent.mdx index ef43685b06..b8c487f822 100644 --- a/docs/content/docs/reference/hooks/useCoAgent.mdx +++ b/docs/content/docs/reference/hooks/useCoAgent.mdx @@ -7,7 +7,7 @@ description: "The useCoAgent hook allows you to share state bidirectionally betw /* * ATTENTION! DO NOT MODIFY THIS FILE! * This page is auto-generated. If you want to make any changes to this page, changes must be made at: - * CopilotKit/packages/react-core/src/hooks/use-coagent.ts + * src/v1.x/packages/react-core/src/hooks/use-coagent.ts */ } diff --git a/docs/content/docs/reference/hooks/useCoAgentStateRender.mdx b/docs/content/docs/reference/hooks/useCoAgentStateRender.mdx index 1cc8be43d9..270d5a8deb 100644 --- a/docs/content/docs/reference/hooks/useCoAgentStateRender.mdx +++ b/docs/content/docs/reference/hooks/useCoAgentStateRender.mdx @@ -7,7 +7,7 @@ description: "The useCoAgentStateRender hook allows you to render the state of t /* * ATTENTION! DO NOT MODIFY THIS FILE! * This page is auto-generated. If you want to make any changes to this page, changes must be made at: - * CopilotKit/packages/react-core/src/hooks/use-coagent-state-render.ts + * src/v1.x/packages/react-core/src/hooks/use-coagent-state-render.ts */ } The useCoAgentStateRender hook allows you to render UI or text based components on a Agentic Copilot's state in the chat. diff --git a/docs/content/docs/reference/hooks/useCopilotAdditionalInstructions.mdx b/docs/content/docs/reference/hooks/useCopilotAdditionalInstructions.mdx index 899eb9be62..db3d332d2a 100644 --- a/docs/content/docs/reference/hooks/useCopilotAdditionalInstructions.mdx +++ b/docs/content/docs/reference/hooks/useCopilotAdditionalInstructions.mdx @@ -7,7 +7,7 @@ description: "The useCopilotAdditionalInstructions hook allows you to provide ad /* * ATTENTION! DO NOT MODIFY THIS FILE! * This page is auto-generated. If you want to make any changes to this page, changes must be made at: - * CopilotKit/packages/react-core/src/hooks/use-copilot-additional-instructions.ts + * src/v1.x/packages/react-core/src/hooks/use-copilot-additional-instructions.ts */ } `useCopilotAdditionalInstructions` is a React hook that provides additional instructions diff --git a/docs/content/docs/reference/hooks/useCopilotChat.mdx b/docs/content/docs/reference/hooks/useCopilotChat.mdx index 2bb25aac0b..44730f8ee1 100644 --- a/docs/content/docs/reference/hooks/useCopilotChat.mdx +++ b/docs/content/docs/reference/hooks/useCopilotChat.mdx @@ -6,7 +6,7 @@ title: "useCopilotChat" /* * ATTENTION! DO NOT MODIFY THIS FILE! * This page is auto-generated. If you want to make any changes to this page, changes must be made at: - * CopilotKit/packages/react-core/src/hooks/use-copilot-chat.ts + * src/v1.x/packages/react-core/src/hooks/use-copilot-chat.ts */ } `useCopilotChat` is a lightweight React hook for headless chat interactions. diff --git a/docs/content/docs/reference/hooks/useCopilotChatHeadless_c.mdx b/docs/content/docs/reference/hooks/useCopilotChatHeadless_c.mdx index cc0958917a..704d22502b 100644 --- a/docs/content/docs/reference/hooks/useCopilotChatHeadless_c.mdx +++ b/docs/content/docs/reference/hooks/useCopilotChatHeadless_c.mdx @@ -6,7 +6,7 @@ title: "useCopilotChatHeadless_c" /* * ATTENTION! DO NOT MODIFY THIS FILE! * This page is auto-generated. If you want to make any changes to this page, changes must be made at: - * CopilotKit/packages/react-core/src/hooks/use-copilot-chat-headless_c.ts + * src/v1.x/packages/react-core/src/hooks/use-copilot-chat-headless_c.ts */ } `useCopilotChatHeadless_c` is for building fully custom UI (headless UI) implementations. diff --git a/docs/content/docs/reference/hooks/useCopilotChatSuggestions.mdx b/docs/content/docs/reference/hooks/useCopilotChatSuggestions.mdx index b1403550f4..6164f9171d 100644 --- a/docs/content/docs/reference/hooks/useCopilotChatSuggestions.mdx +++ b/docs/content/docs/reference/hooks/useCopilotChatSuggestions.mdx @@ -7,7 +7,7 @@ description: "The useCopilotChatSuggestions hook generates suggestions in the ch /* * ATTENTION! DO NOT MODIFY THIS FILE! * This page is auto-generated. If you want to make any changes to this page, changes must be made at: - * CopilotKit/packages/react-ui/src/hooks/use-copilot-chat-suggestions.tsx + * src/v1.x/packages/react-ui/src/hooks/use-copilot-chat-suggestions.tsx */ } diff --git a/docs/content/docs/reference/hooks/useCopilotReadable.mdx b/docs/content/docs/reference/hooks/useCopilotReadable.mdx index 97a79d2f6b..69ae137a8e 100644 --- a/docs/content/docs/reference/hooks/useCopilotReadable.mdx +++ b/docs/content/docs/reference/hooks/useCopilotReadable.mdx @@ -7,7 +7,7 @@ description: "The useCopilotReadable hook allows you to provide knowledge to you /* * ATTENTION! DO NOT MODIFY THIS FILE! * This page is auto-generated. If you want to make any changes to this page, changes must be made at: - * CopilotKit/packages/react-core/src/hooks/use-copilot-readable.ts + * src/v1.x/packages/react-core/src/hooks/use-copilot-readable.ts */ } `useCopilotReadable` is a React hook that provides app-state and other information diff --git a/docs/content/docs/reference/sdk/js/LangGraph.mdx b/docs/content/docs/reference/sdk/js/LangGraph.mdx index aa3f0f6e9c..d46c7e866b 100644 --- a/docs/content/docs/reference/sdk/js/LangGraph.mdx +++ b/docs/content/docs/reference/sdk/js/LangGraph.mdx @@ -7,7 +7,7 @@ description: "The CopilotKit LangGraph SDK for JavaScript allows you to build an /* * ATTENTION! DO NOT MODIFY THIS FILE! * This page is auto-generated. If you want to make any changes to this page, changes must be made at: - * CopilotKit/packages/sdk-js/src/langgraph.ts + * src/v1.x/packages/sdk-js/src/langgraph.ts */ } ## copilotkitCustomizeConfig diff --git a/docs/content/docs/reference/sdk/python/CrewAI.mdx b/docs/content/docs/reference/sdk/python/CrewAI.mdx index d2bb46fede..374e9061cc 100644 --- a/docs/content/docs/reference/sdk/python/CrewAI.mdx +++ b/docs/content/docs/reference/sdk/python/CrewAI.mdx @@ -7,7 +7,7 @@ description: "The CopilotKit CrewAI SDK for Python allows you to build and run C /* * ATTENTION! DO NOT MODIFY THIS FILE! * This page is auto-generated. If you want to make any changes to this page, changes must be made at: - * CopilotKit/../sdk-python/copilotkit/crewai/crewai_sdk.py + * src/v1.x/../../sdk-python/copilotkit/crewai/crewai_sdk.py */ } ## copilotkit_predict_state diff --git a/docs/content/docs/reference/sdk/python/CrewAIAgent.mdx b/docs/content/docs/reference/sdk/python/CrewAIAgent.mdx index 5f37c5ed20..9ff7b37eaa 100644 --- a/docs/content/docs/reference/sdk/python/CrewAIAgent.mdx +++ b/docs/content/docs/reference/sdk/python/CrewAIAgent.mdx @@ -7,7 +7,7 @@ description: "CrewAIAgent lets you define your agent for use with CopilotKit." /* * ATTENTION! DO NOT MODIFY THIS FILE! * This page is auto-generated. If you want to make any changes to this page, changes must be made at: - * CopilotKit/../sdk-python/copilotkit/crewai/crewai_agent.py + * src/v1.x/../../sdk-python/copilotkit/crewai/crewai_agent.py */ } ## CrewAIAgent diff --git a/docs/content/docs/reference/sdk/python/LangGraph.mdx b/docs/content/docs/reference/sdk/python/LangGraph.mdx index ed624e3559..075a015b6f 100644 --- a/docs/content/docs/reference/sdk/python/LangGraph.mdx +++ b/docs/content/docs/reference/sdk/python/LangGraph.mdx @@ -7,7 +7,7 @@ description: "The CopilotKit LangGraph SDK for Python allows you to build and ru /* * ATTENTION! DO NOT MODIFY THIS FILE! * This page is auto-generated. If you want to make any changes to this page, changes must be made at: - * CopilotKit/../sdk-python/copilotkit/langgraph.py + * src/v1.x/../../sdk-python/copilotkit/langgraph.py */ } ## copilotkit_customize_config diff --git a/docs/content/docs/reference/sdk/python/LangGraphAgent.mdx b/docs/content/docs/reference/sdk/python/LangGraphAgent.mdx index 673950acfc..357d5d24c6 100644 --- a/docs/content/docs/reference/sdk/python/LangGraphAgent.mdx +++ b/docs/content/docs/reference/sdk/python/LangGraphAgent.mdx @@ -7,7 +7,7 @@ description: "LangGraphAgent lets you define your agent for use with CopilotKit. /* * ATTENTION! DO NOT MODIFY THIS FILE! * This page is auto-generated. If you want to make any changes to this page, changes must be made at: - * CopilotKit/../sdk-python/copilotkit/langgraph_agent.py + * src/v1.x/../../sdk-python/copilotkit/langgraph_agent.py */ } ## LangGraphAgent diff --git a/docs/content/docs/reference/sdk/python/RemoteEndpoints.mdx b/docs/content/docs/reference/sdk/python/RemoteEndpoints.mdx index 8328ecbc63..ab04e13513 100644 --- a/docs/content/docs/reference/sdk/python/RemoteEndpoints.mdx +++ b/docs/content/docs/reference/sdk/python/RemoteEndpoints.mdx @@ -7,7 +7,7 @@ description: "CopilotKit Remote Endpoints allow you to connect actions and agent /* * ATTENTION! DO NOT MODIFY THIS FILE! * This page is auto-generated. If you want to make any changes to this page, changes must be made at: - * CopilotKit/../sdk-python/copilotkit/sdk.py + * src/v1.x/../../sdk-python/copilotkit/sdk.py */ } ## CopilotKitRemoteEndpoint diff --git a/docs/lib/icons/custom-icons.tsx b/docs/lib/icons/custom-icons.tsx index b4b8ac1286..2d8b477268 100644 --- a/docs/lib/icons/custom-icons.tsx +++ b/docs/lib/icons/custom-icons.tsx @@ -347,6 +347,38 @@ export const A2AIcon = (props: IconBaseProps) => ( ); + +export const AgentSpecMarkIcon = (props: IconBaseProps) => ( + +); + + export const customIcons = { a2a: A2AIcon, adk: ADKIcon, @@ -368,6 +400,7 @@ export const customIcons = { agnoBlack: AgnoIconBlack, llamaindex: LlamaIndexIcon, pydantic: PydanticAIIcon, + agentspecMark: AgentSpecMarkIcon, llm: RocketIcon, "direct-to-llm": RocketIcon, }; diff --git a/docs/snippets/integrations/agent-spec/run-and-connect.mdx b/docs/snippets/integrations/agent-spec/run-and-connect.mdx new file mode 100644 index 0000000000..2eaf6e5166 --- /dev/null +++ b/docs/snippets/integrations/agent-spec/run-and-connect.mdx @@ -0,0 +1,18 @@ +import { Accordions, Accordion } from "fumadocs-ui/components/accordion"; + +You'll need to run your agent and connect it to CopilotKit before proceeding. + +If you don't already have CopilotKit and your agent connected, choose one of the following options: + + + + You can follow the instructions in the [quickstart](/agent-spec/quickstart) guide. + + + Run the following command to create a brand new project with a pre-configured agent: + + ```bash + npx copilotkit@latest create -f agent-spec + ``` + + \ No newline at end of file diff --git a/docs/snippets/shared/contributing/code-contributions/development.mdx b/docs/snippets/shared/contributing/code-contributions/development.mdx index 84cfe55991..97beba357f 100644 --- a/docs/snippets/shared/contributing/code-contributions/development.mdx +++ b/docs/snippets/shared/contributing/code-contributions/development.mdx @@ -18,7 +18,7 @@ This guide will help you get started as smoothly as possible. Then, clone your fork to your local machine: ```bash git clone https://github.com//CopilotKit - cd CopilotKit/CopilotKit + cd CopilotKit/src/v1.x ```
diff --git a/docs/snippets/shared/guides/custom-look-and-feel/customize-built-in-ui-components.mdx b/docs/snippets/shared/guides/custom-look-and-feel/customize-built-in-ui-components.mdx index 59c22d5ae5..0d82f79757 100644 --- a/docs/snippets/shared/guides/custom-look-and-feel/customize-built-in-ui-components.mdx +++ b/docs/snippets/shared/guides/custom-look-and-feel/customize-built-in-ui-components.mdx @@ -70,7 +70,7 @@ In addition to customizing the colors, the CopilotKit CSS is structured to easil ### Reference -For a full list of styles and classes used in CopilotKit, click [here](https://github.com/CopilotKit/CopilotKit/blob/main/CopilotKit/packages/react-ui/src/css/). +For a full list of styles and classes used in CopilotKit, click [here](https://github.com/CopilotKit/CopilotKit/blob/main/src/v1.x/packages/react-ui/src/css/). | CSS Class | Description | diff --git a/registry/app/globals.css b/registry/app/globals.css deleted file mode 100644 index 404bb606b6..0000000000 --- a/registry/app/globals.css +++ /dev/null @@ -1,136 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -@layer base { - :root { - --background: 0 0% 100%; - --foreground: 240 10% 3.9%; - --card: 0 0% 100%; - --card-foreground: 240 10% 3.9%; - --popover: 0 0% 100%; - --popover-foreground: 240 10% 3.9%; - --primary: 240 5.9% 10%; - --primary-foreground: 0 0% 98%; - --secondary: 240 4.8% 95.9%; - --secondary-foreground: 240 5.9% 10%; - --muted: 240 4.8% 95.9%; - --muted-foreground: 240 3.8% 46.1%; - --accent: 240 4.8% 95.9%; - --accent-foreground: 240 5.9% 10%; - --destructive: 0 84.2% 60.2%; - --destructive-foreground: 0 0% 98%; - --border: 240 5.9% 90%; - --input: 240 5.9% 90%; - --ring: 240 10% 3.9%; - --chart-1: 12 76% 61%; - --chart-2: 173 58% 39%; - --chart-3: 197 37% 24%; - --chart-4: 43 74% 66%; - --chart-5: 27 87% 67%; - --radius: 0.5rem; - --sidebar-background: 0 0% 98%; - --sidebar-foreground: 240 5.3% 26.1%; - --sidebar-primary: 240 5.9% 10%; - --sidebar-primary-foreground: 0 0% 98%; - --sidebar-accent: 240 4.8% 95.9%; - --sidebar-accent-foreground: 240 5.9% 10%; - --sidebar-border: 220 13% 91%; - --sidebar-ring: 217.2 91.2% 59.8%; - } - .dark { - --background: 240 10% 3.9%; - --foreground: 0 0% 98%; - --card: 240 10% 3.9%; - --card-foreground: 0 0% 98%; - --popover: 240 10% 3.9%; - --popover-foreground: 0 0% 98%; - --primary: 0 0% 98%; - --primary-foreground: 240 5.9% 10%; - --secondary: 240 3.7% 15.9%; - --secondary-foreground: 0 0% 98%; - --muted: 240 3.7% 15.9%; - --muted-foreground: 240 5% 64.9%; - --accent: 240 3.7% 15.9%; - --accent-foreground: 0 0% 98%; - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 0 0% 98%; - --border: 240 3.7% 15.9%; - --input: 240 3.7% 15.9%; - --ring: 240 4.9% 83.9%; - --chart-1: 220 70% 50%; - --chart-2: 160 60% 45%; - --chart-3: 30 80% 55%; - --chart-4: 280 65% 60%; - --chart-5: 340 75% 55%; - --sidebar-background: 240 5.9% 10%; - --sidebar-foreground: 240 4.8% 95.9%; - --sidebar-primary: 224.3 76.3% 48%; - --sidebar-primary-foreground: 0 0% 100%; - --sidebar-accent: 240 3.7% 15.9%; - --sidebar-accent-foreground: 240 4.8% 95.9%; - --sidebar-border: 240 3.7% 15.9%; - --sidebar-ring: 217.2 91.2% 59.8%; - } -} - -@layer base { - * { - @apply border-border; - } - body { - @apply bg-background text-foreground; - } -} - -[data-rehype-pretty-code-fragment] { - @apply relative text-white; -} - -[data-rehype-pretty-code-fragment] code { - @apply grid min-w-full break-words rounded-none border-0 bg-transparent p-0; - counter-reset: line; - box-decoration-break: clone; -} - -[data-rehype-pretty-code-fragment] .line { - @apply px-4 min-h-[1rem] py-0.5 w-full inline-block; -} - -[data-rehype-pretty-code-fragment] [data-line-numbers] .line { - @apply px-2; -} - -[data-rehype-pretty-code-fragment] [data-line-numbers] > .line::before { - @apply text-zinc-50/40 text-xs; - counter-increment: line; - content: counter(line); - display: inline-block; - width: 1.8rem; - margin-right: 1.4rem; - text-align: right; -} - -[data-rehype-pretty-code-fragment] .line--highlighted { - @apply bg-zinc-700/50; -} - -[data-rehype-pretty-code-fragment] .line-highlighted span { - @apply relative; -} - -[data-rehype-pretty-code-fragment] .word--highlighted { - @apply rounded-md bg-zinc-700/50 border-zinc-700/70 p-1; -} - -.dark [data-rehype-pretty-code-fragment] .word--highlighted { - @apply bg-zinc-900; -} - -[data-rehype-pretty-code-title] { - @apply mt-2 pt-6 px-4 text-sm font-medium text-foreground; -} - -[data-rehype-pretty-code-title] + pre { - @apply mt-2; -} diff --git a/registry/app/page.tsx b/registry/app/page.tsx deleted file mode 100644 index 0d7cd1fcc6..0000000000 --- a/registry/app/page.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import * as React from "react" - -// This page displays items from the custom registry. -// You are free to implement this with your own design as needed. - -export default function Home() { - return ( -
-
-

Custom Registry

-

- A custom registry for distribution code using shadcn. -

-
-
- ) -} diff --git a/registry/components/ui/tooltip.tsx b/registry/components/ui/tooltip.tsx deleted file mode 100644 index 28e19183f4..0000000000 --- a/registry/components/ui/tooltip.tsx +++ /dev/null @@ -1,32 +0,0 @@ -"use client" - -import * as React from "react" -import * as TooltipPrimitive from "@radix-ui/react-tooltip" - -import { cn } from "@/lib/utils" - -const TooltipProvider = TooltipPrimitive.Provider - -const Tooltip = TooltipPrimitive.Root - -const TooltipTrigger = TooltipPrimitive.Trigger - -const TooltipContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, sideOffset = 4, ...props }, ref) => ( - - - -)) -TooltipContent.displayName = TooltipPrimitive.Content.displayName - -export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } diff --git a/registry/hooks/use-coagents-crew-starter.tsx b/registry/hooks/use-coagents-crew-starter.tsx deleted file mode 100644 index a4c57b96f7..0000000000 --- a/registry/hooks/use-coagents-crew-starter.tsx +++ /dev/null @@ -1,163 +0,0 @@ -"use client"; -import { - CrewsAgentState, - CrewsResponseStatus, - useCoAgent, - useCoAgentStateRender, - useCopilotAction, - useCopilotAdditionalInstructions, - useCopilotChat, -} from "@copilotkit/react-core"; -import { useEffect, useState } from "react"; -import CrewHumanFeedbackRenderer, { - CrewsFeedback, -} from "@/registry/crews/crew-human-feedback-renderer"; -import CrewStateRenderer from "@/registry/crews/crew-state-renderer"; -import { MessageRole, TextMessage } from "@copilotkit/runtime-client-gql"; -import { CrewInChatInput } from "@/registry/crews/crew-in-chat-input"; - -/** - * Hook: useCoagentsCrewStarter - * - * This hook provides a simplified interface for initializing and managing - * a copilot crew in your application. It handles: - * - * 1. Initialization with configured agent name from environment variables - * 2. Collection of user inputs through a form interface - * 3. Real-time state visualization during execution - * 4. Feedback collection when the crew needs user input - * 5. Result aggregation and presentation - * - * @param {Object} params - Parameters for initializing the crew - * @param {Array} params.inputs - Input field names to collect from the user - * @returns {Object} - An object containing the crew's output - * - * @example - * ```tsx - * const { output } = useCoagentsCrewStarter({ - * inputs: ["query", "location"] - * }); - * ``` - */ -export const useCoagentsCrewStarter = ({ - inputs, -}: { - inputs: Array; -}): { - output: string; -} => { - const [initialMessageSent, setInitialMessageSent] = useState(false); - - // Use the agent name from environment variables - const agentName = process.env.NEXT_PUBLIC_COPILOTKIT_AGENT_NAME || "DefaultAgent"; - - // Initialize the crew agent with a default state - const { state, setState, run } = useCoAgent< - CrewsAgentState & { - result: string; - inputs: Record; - } - >({ - name: agentName, - initialState: { - inputs: {}, - result: "Crew result will appear here...", - }, - }); - - const { appendMessage, isLoading } = useCopilotChat(); - - // Instructions for the copilot to ensure inputs are gathered - const instructions = - "INPUTS ARE ABSOLUTELY REQUIRED. Please call getInputs before proceeding with anything else."; - - // Send initial greeting when chat is loaded - useEffect(() => { - if (initialMessageSent || isLoading) return; - - setTimeout(async () => { - await appendMessage( - new TextMessage({ - content: "Hi! Please provide your inputs to get started.", - role: MessageRole.Developer, - }) - ); - setInitialMessageSent(true); - }, 0); - }, [initialMessageSent, isLoading, appendMessage]); - - // Send a message with the inputs once they are provided - useEffect(() => { - if (!initialMessageSent && Object.values(state?.inputs || {}).length > 0) { - appendMessage( - new TextMessage({ - role: MessageRole.Developer, - content: "My inputs are: " + JSON.stringify(state?.inputs), - }) - ).then(() => { - setInitialMessageSent(true); - }); - } - }, [initialMessageSent, state?.inputs, appendMessage]); - - // Provide additional instructions to the copilot - useCopilotAdditionalInstructions({ - instructions, - available: - Object.values(state?.inputs || {}).length > 0 ? "enabled" : "disabled", - }); - - // Action to get inputs from the user - useCopilotAction({ - name: "getInputs", - description: - "Collect required inputs from the user before starting the crew execution.", - renderAndWaitForResponse({ status, respond }) { - if (status === "inProgress" || status === "executing") { - return ( - { - setState({ - ...state, - inputs: inputValues, - }); - respond?.("Inputs submitted"); - }} - /> - ); - } - return
Inputs submitted
; - }, - }); - - // Render the crew's state in real-time - useCoAgentStateRender({ - name: agentName, - render: ({ state, status }) => ( - - ), - }); - - // Action to handle feedback requests from the crew - useCopilotAction({ - name: "crew_requesting_feedback", - description: "Request feedback from the user on the crew's output", - renderAndWaitForResponse(props) { - const { status, args, respond } = props; - return ( - - ); - }, - }); - - // Return the output result of the crew - return { - output: state?.result || "", - }; -}; diff --git a/registry/lib/utils.ts b/registry/lib/utils.ts deleted file mode 100644 index bd0c391ddd..0000000000 --- a/registry/lib/utils.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { clsx, type ClassValue } from "clsx" -import { twMerge } from "tailwind-merge" - -export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)) -} diff --git a/registry/package.json b/registry/package.json deleted file mode 100644 index 057e9111db..0000000000 --- a/registry/package.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "name": "registry-template", - "version": "0.1.0", - "private": true, - "scripts": { - "dev": "next dev --turbopack", - "build": "next build", - "start": "next start", - "lint": "next lint", - "registry:build": "shadcn build" - }, - "dependencies": { - "@copilotkit/react-core": "^1.8.8", - "@copilotkit/react-ui": "^1.8.8", - "@copilotkit/runtime": "^1.8.8", - "@copilotkit/runtime-client-gql": "^1.8.8", - "@modelcontextprotocol/sdk": "^1.24.0", - "@radix-ui/react-label": "^2.1.1", - "@radix-ui/react-slot": "^1.1.1", - "@radix-ui/react-tooltip": "^1.2.0", - "@tanstack/react-query": "^5.64.1", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "lucide-react": "^0.471.1", - "next": "15.4.10", - "next-themes": "^0.4.4", - "openai": "^4.87.4", - "react": "^19.0.0", - "react-dom": "^19.0.0", - "shadcn": "2.4.0", - "tailwind-merge": "^2.6.0", - "tailwindcss-animate": "^1.0.7", - "tooltip": "^1.6.1", - "zod": "^3.24.1" - }, - "devDependencies": { - "@eslint/eslintrc": "^3", - "@types/node": "^20", - "@types/react": "^19", - "@types/react-dom": "^19", - "eslint": "^9", - "eslint-config-next": "15.1.4", - "postcss": "^8", - "tailwindcss": "^3.4.1", - "typescript": "^5" - } -} diff --git a/registry/pnpm-lock.yaml b/registry/pnpm-lock.yaml deleted file mode 100644 index ece367da8a..0000000000 --- a/registry/pnpm-lock.yaml +++ /dev/null @@ -1,9838 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - - .: - dependencies: - '@copilotkit/react-core': - specifier: ^1.8.8 - version: 1.8.8(@types/react@19.0.6)(graphql@16.10.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@copilotkit/react-ui': - specifier: ^1.8.8 - version: 1.8.8(@types/react@19.0.6)(graphql@16.10.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@copilotkit/runtime': - specifier: ^1.8.8 - version: 1.8.8(@browserbasehq/sdk@2.6.0)(@browserbasehq/stagehand@1.14.0(@playwright/test@1.51.1)(deepmerge@4.3.1)(dotenv@16.6.1)(openai@4.87.4(ws@8.18.3)(zod@3.24.1))(zod@3.24.1))(@ibm-cloud/watsonx-ai@1.6.0(@langchain/core@0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1))))(axios@1.13.2)(google-auth-library@8.9.0)(ibm-cloud-sdk-core@5.3.2)(ignore@5.3.2)(jsonwebtoken@9.0.3)(playwright@1.51.1)(ws@8.18.3) - '@copilotkit/runtime-client-gql': - specifier: ^1.8.8 - version: 1.8.8(graphql@16.10.0)(react@19.0.0) - '@modelcontextprotocol/sdk': - specifier: ^1.24.0 - version: 1.25.1(@cfworker/json-schema@4.1.1)(hono@4.11.1)(zod@3.24.1) - '@radix-ui/react-label': - specifier: ^2.1.1 - version: 2.1.1(@types/react-dom@19.0.3(@types/react@19.0.6))(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-slot': - specifier: ^1.1.1 - version: 1.1.1(@types/react@19.0.6)(react@19.0.0) - '@radix-ui/react-tooltip': - specifier: ^1.2.0 - version: 1.2.0(@types/react-dom@19.0.3(@types/react@19.0.6))(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tanstack/react-query': - specifier: ^5.64.1 - version: 5.64.1(react@19.0.0) - class-variance-authority: - specifier: ^0.7.1 - version: 0.7.1 - clsx: - specifier: ^2.1.1 - version: 2.1.1 - lucide-react: - specifier: ^0.471.1 - version: 0.471.1(react@19.0.0) - next: - specifier: 15.4.10 - version: 15.4.10(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - next-themes: - specifier: ^0.4.4 - version: 0.4.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - openai: - specifier: ^4.87.4 - version: 4.87.4(ws@8.18.3)(zod@3.24.1) - react: - specifier: ^19.0.0 - version: 19.0.0 - react-dom: - specifier: ^19.0.0 - version: 19.0.0(react@19.0.0) - shadcn: - specifier: 2.4.0 - version: 2.4.0(@types/node@20.17.12)(typescript@5.7.3) - tailwind-merge: - specifier: ^2.6.0 - version: 2.6.0 - tailwindcss-animate: - specifier: ^1.0.7 - version: 1.0.7(tailwindcss@3.4.17) - tooltip: - specifier: ^1.6.1 - version: 1.6.1 - zod: - specifier: ^3.24.1 - version: 3.24.1 - devDependencies: - '@eslint/eslintrc': - specifier: ^3 - version: 3.2.0 - '@types/node': - specifier: ^20 - version: 20.17.12 - '@types/react': - specifier: ^19 - version: 19.0.6 - '@types/react-dom': - specifier: ^19 - version: 19.0.3(@types/react@19.0.6) - eslint: - specifier: ^9 - version: 9.18.0(jiti@1.21.7) - eslint-config-next: - specifier: 15.1.4 - version: 15.1.4(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3) - postcss: - specifier: ^8 - version: 8.4.49 - tailwindcss: - specifier: ^3.4.1 - version: 3.4.17 - typescript: - specifier: ^5 - version: 5.7.3 - -packages: - - '@0no-co/graphql.web@1.1.2': - resolution: {integrity: sha512-N2NGsU5FLBhT8NZ+3l2YrzZSHITjNXNuDhC4iDiikv0IujaJ0Xc6xIxQZ/Ek3Cb+rgPjnLHYyJm11tInuJn+cw==} - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 - peerDependenciesMeta: - graphql: - optional: true - - '@agentwire/client@0.0.26': - resolution: {integrity: sha512-EoZBoKzRit+o2vQ4Nu0lgnM3ONXeKiOdxHLhNX94egAJbXsC1LPvnsTFYJKONYLYqAM4Bqpn8Yrz9e38RFj9pQ==} - - '@agentwire/core@0.0.26': - resolution: {integrity: sha512-sUJwPt9AUAOq00/ZWyK2XtXLlD92eD3QTOTr8v0lcryst+f8FffoC9MSKx7aTFtmTL8g2Ku/0LnyLvsnOTweqg==} - - '@agentwire/encoder@0.0.26': - resolution: {integrity: sha512-Amtv4ogNoj63FHkABLnWxaVvoSl+5r1V+HkjuZGvcdgkT5PJLFq2VRYQMNoLg4zW3uK7SOjqngRlQvqukTpb3g==} - - '@agentwire/proto@0.0.26': - resolution: {integrity: sha512-SGBFWf4cNrapEgcSVMt9wMVi8XTIuah5tMVPeY7fyWA/569Plp3fgBNg9rQceGYRGdrZmIhGeYvWjS2+Th9Wyg==} - - '@alloc/quick-lru@5.2.0': - resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} - engines: {node: '>=10'} - - '@ampproject/remapping@2.3.0': - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} - - '@antfu/ni@23.3.1': - resolution: {integrity: sha512-C90iyzm/jLV7Lomv2UzwWUzRv9WZr1oRsFRKsX5HjQL4EXrbi9H/RtBkjCP+NF+ABZXUKpAa4F1dkoTaea4zHg==} - hasBin: true - - '@anthropic-ai/sdk@0.27.3': - resolution: {integrity: sha512-IjLt0gd3L4jlOfilxVXTifn42FnVffMgDC04RJK1KDZpmkBWLv0XC92MVVmkxrFZNS/7l3xWgP/I3nqtX1sQHw==} - - '@babel/code-frame@7.26.2': - resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} - engines: {node: '>=6.9.0'} - - '@babel/compat-data@7.26.5': - resolution: {integrity: sha512-XvcZi1KWf88RVbF9wn8MN6tYFloU5qX8KjuF3E1PVBmJ9eypXfs4GRiJwLuTZL0iSnJUKn1BFPa5BPZZJyFzPg==} - engines: {node: '>=6.9.0'} - - '@babel/core@7.26.0': - resolution: {integrity: sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==} - engines: {node: '>=6.9.0'} - - '@babel/generator@7.26.5': - resolution: {integrity: sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-annotate-as-pure@7.25.9': - resolution: {integrity: sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==} - engines: {node: '>=6.9.0'} - - '@babel/helper-compilation-targets@7.26.5': - resolution: {integrity: sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-create-class-features-plugin@7.25.9': - resolution: {integrity: sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-member-expression-to-functions@7.25.9': - resolution: {integrity: sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-imports@7.25.9': - resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-module-transforms@7.26.0': - resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-optimise-call-expression@7.25.9': - resolution: {integrity: sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-plugin-utils@7.26.5': - resolution: {integrity: sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==} - engines: {node: '>=6.9.0'} - - '@babel/helper-replace-supers@7.26.5': - resolution: {integrity: sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 - - '@babel/helper-skip-transparent-expression-wrappers@7.25.9': - resolution: {integrity: sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-string-parser@7.25.9': - resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-identifier@7.25.9': - resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-option@7.25.9': - resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} - engines: {node: '>=6.9.0'} - - '@babel/helpers@7.26.0': - resolution: {integrity: sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==} - engines: {node: '>=6.9.0'} - - '@babel/parser@7.26.5': - resolution: {integrity: sha512-SRJ4jYmXRqV1/Xc+TIVG84WjHBXKlxO9sHQnA2Pf12QQEAp1LOh6kDzNHXcUnbH1QI0FDoPPVOt+vyUDucxpaw==} - engines: {node: '>=6.0.0'} - hasBin: true - - '@babel/plugin-syntax-typescript@7.25.9': - resolution: {integrity: sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-typescript@7.26.5': - resolution: {integrity: sha512-GJhPO0y8SD5EYVCy2Zr+9dSZcEgaSmq5BLR0Oc25TOEhC+ba49vUAGZFjy8v79z9E1mdldq4x9d1xgh4L1d5dQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/runtime@7.26.10': - resolution: {integrity: sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==} - engines: {node: '>=6.9.0'} - - '@babel/template@7.25.9': - resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==} - engines: {node: '>=6.9.0'} - - '@babel/traverse@7.26.5': - resolution: {integrity: sha512-rkOSPOw+AXbgtwUga3U4u8RpoK9FEFWBNAlTpcnkLFjL5CT+oyHNuUUC/xx6XefEJ16r38r8Bc/lfp6rYuHeJQ==} - engines: {node: '>=6.9.0'} - - '@babel/types@7.26.5': - resolution: {integrity: sha512-L6mZmwFDK6Cjh1nRCLXpa6no13ZIioJDz7mdkzHv399pThrTa/k0nUlNaenOeh2kWu/iaOQYElEpKPUswUa9Vg==} - engines: {node: '>=6.9.0'} - - '@browserbasehq/sdk@2.6.0': - resolution: {integrity: sha512-83iXP5D7xMm8Wyn66TUaUrgoByCmAJuoMoZQI3sGg3JAiMlTfnCIMqyVBoNSaItaPIkaCnrsj6LiusmXV2X9YA==} - - '@browserbasehq/stagehand@1.14.0': - resolution: {integrity: sha512-Hi/EzgMFWz+FKyepxHTrqfTPjpsuBS4zRy3e9sbMpBgLPv+9c0R+YZEvS7Bw4mTS66QtvvURRT6zgDGFotthVQ==} - peerDependencies: - '@playwright/test': ^1.42.1 - deepmerge: ^4.3.1 - dotenv: ^16.4.5 - openai: ^4.62.1 - zod: ^3.23.8 - - '@bufbuild/protobuf@2.2.5': - resolution: {integrity: sha512-/g5EzJifw5GF8aren8wZ/G5oMuPoGeS6MQD3ca8ddcvdXR5UELUfdTZITCGNhNXynY/AYl3Z4plmxdj/tRl/hQ==} - - '@bundled-es-modules/cookie@2.0.1': - resolution: {integrity: sha512-8o+5fRPLNbjbdGRRmJj3h6Hh1AQJf2dk3qQ/5ZFb+PXkRNiSoMGGUKlsgLfrxneb72axVJyIYji64E2+nNfYyw==} - - '@bundled-es-modules/statuses@1.0.1': - resolution: {integrity: sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==} - - '@bundled-es-modules/tough-cookie@0.1.6': - resolution: {integrity: sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==} - - '@cfworker/json-schema@4.1.1': - resolution: {integrity: sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==} - - '@copilotkit/react-core@1.8.8': - resolution: {integrity: sha512-y2Hbg+LMYq2vEvl31WpAOOvv/Ud+8rxfbBMzDSYonTfu3DyBhKm6Y0XH0a/ZXoyVQX3/4MqIymnuf5q5Y1G53w==} - peerDependencies: - react: ^18 || ^19 || ^19.0.0-rc - react-dom: ^18 || ^19 || ^19.0.0-rc - - '@copilotkit/react-ui@1.8.8': - resolution: {integrity: sha512-neFnX8hyIY5Wv1ut2j5bfd7qd5N5GTx/3aJmXJJUtpB6wDSbvl9uBCDhtbwOFyA+m7i0ebIVIcd9M0rylefbXg==} - peerDependencies: - react: ^18 || ^19 || ^19.0.0-rc - - '@copilotkit/runtime-client-gql@1.8.8': - resolution: {integrity: sha512-NO63z7o778wYPT3SbNh6WH0Mg6mWgwXB4YWA5B0EAD7LoyIPAJYt8sqLNjhDmiEdC67fzRZn2lxmJ/jqNDwyJQ==} - peerDependencies: - react: ^18 || ^19 || ^19.0.0-rc - - '@copilotkit/runtime@1.8.8': - resolution: {integrity: sha512-vlZKrmgjdHQPVgrUIyUA97LXM/SouqDVI3CDWfEOmYrpZFCQil9IeaIZ/4bX8CGgktiKaWbrz9iNEvaucLt5UA==} - - '@copilotkit/shared@1.8.8': - resolution: {integrity: sha512-0Ojd/RG6DEHogVTnqV1TT5dzo7dAxREAS5O0TndYNULppQJlr+CeCBtjKDu0Kov65TVWhRYcNfH4DqTg4xz8Nw==} - - '@emnapi/runtime@1.7.1': - resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} - - '@envelop/core@5.2.3': - resolution: {integrity: sha512-KfoGlYD/XXQSc3BkM1/k15+JQbkQ4ateHazeZoWl9P71FsLTDXSjGy6j7QqfhpIDSbxNISqhPMfZHYSbDFOofQ==} - engines: {node: '>=18.0.0'} - - '@envelop/instrumentation@1.0.0': - resolution: {integrity: sha512-cxgkB66RQB95H3X27jlnxCRNTmPuSTgmBAq6/4n2Dtv4hsk4yz8FadA1ggmd0uZzvKqWD6CR+WFgTjhDqg7eyw==} - engines: {node: '>=18.0.0'} - - '@envelop/types@5.2.1': - resolution: {integrity: sha512-CsFmA3u3c2QoLDTfEpGr4t25fjMU31nyvse7IzWTvb0ZycuPjMjb0fjlheh+PbhBYb9YLugnT2uY6Mwcg1o+Zg==} - engines: {node: '>=18.0.0'} - - '@eslint-community/eslint-utils@4.4.1': - resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - - '@eslint-community/regexpp@4.12.1': - resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - - '@eslint/config-array@0.19.1': - resolution: {integrity: sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/core@0.10.0': - resolution: {integrity: sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/eslintrc@3.2.0': - resolution: {integrity: sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/js@9.18.0': - resolution: {integrity: sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/object-schema@2.1.5': - resolution: {integrity: sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/plugin-kit@0.2.5': - resolution: {integrity: sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@floating-ui/core@1.6.9': - resolution: {integrity: sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==} - - '@floating-ui/dom@1.6.13': - resolution: {integrity: sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==} - - '@floating-ui/react-dom@2.1.2': - resolution: {integrity: sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==} - peerDependencies: - react: '>=16.8.0' - react-dom: '>=16.8.0' - - '@floating-ui/react@0.26.28': - resolution: {integrity: sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==} - peerDependencies: - react: '>=16.8.0' - react-dom: '>=16.8.0' - - '@floating-ui/utils@0.2.9': - resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==} - - '@graphql-tools/executor@1.4.6': - resolution: {integrity: sha512-vtwuotFe9DR1gZ2VXYRxcL6GVP6dYUHWibA9JNOkdRiwCW/icTY7oU9xUVITnOAfjNh9k8Z43kZmiyr2aMopVA==} - engines: {node: '>=16.0.0'} - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - - '@graphql-tools/merge@9.0.24': - resolution: {integrity: sha512-NzWx/Afl/1qHT3Nm1bghGG2l4jub28AdvtG11PoUlmjcIjnFBJMv4vqL0qnxWe8A82peWo4/TkVdjJRLXwgGEw==} - engines: {node: '>=16.0.0'} - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - - '@graphql-tools/schema@10.0.23': - resolution: {integrity: sha512-aEGVpd1PCuGEwqTXCStpEkmheTHNdMayiIKH1xDWqYp9i8yKv9FRDgkGrY4RD8TNxnf7iII+6KOBGaJ3ygH95A==} - engines: {node: '>=16.0.0'} - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - - '@graphql-tools/utils@10.8.6': - resolution: {integrity: sha512-Alc9Vyg0oOsGhRapfL3xvqh1zV8nKoFUdtLhXX7Ki4nClaIJXckrA86j+uxEuG3ic6j4jlM1nvcWXRn/71AVLQ==} - engines: {node: '>=16.0.0'} - peerDependencies: - graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - - '@graphql-typed-document-node/core@3.2.0': - resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==} - peerDependencies: - graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 - - '@graphql-yoga/logger@2.0.1': - resolution: {integrity: sha512-Nv0BoDGLMg9QBKy9cIswQ3/6aKaKjlTh87x3GiBg2Z4RrjyrM48DvOOK0pJh1C1At+b0mUIM67cwZcFTDLN4sA==} - engines: {node: '>=18.0.0'} - - '@graphql-yoga/plugin-defer-stream@3.13.2': - resolution: {integrity: sha512-2RoaNwRtJcBYFfBV1qHgxMGPFVA8MAKqGJ2YM1psPuEF1OLlcrtCUc+e/IdugrF0Th/S2DeUvbA1+13FxyzIlw==} - engines: {node: '>=18.0.0'} - peerDependencies: - graphql: ^15.2.0 || ^16.0.0 - graphql-yoga: ^5.13.2 - - '@graphql-yoga/subscription@5.0.3': - resolution: {integrity: sha512-xLGEataxCULjL9rlTCVFL1IW2E90TnDIL+mE4lZBi9X92jc6s+Q+jk6Ax3I98kryCJh0UMiwjit7CaIIczTiJg==} - engines: {node: '>=18.0.0'} - - '@graphql-yoga/typed-event-target@3.0.2': - resolution: {integrity: sha512-ZpJxMqB+Qfe3rp6uszCQoag4nSw42icURnBRfFYSOmTgEeOe4rD0vYlbA8spvCu2TlCesNTlEN9BLWtQqLxabA==} - engines: {node: '>=18.0.0'} - - '@headlessui/react@2.2.0': - resolution: {integrity: sha512-RzCEg+LXsuI7mHiSomsu/gBJSjpupm6A1qIZ5sWjd7JhARNlMiSA4kKfJpCKwU9tE+zMRterhhrP74PvfJrpXQ==} - engines: {node: '>=10'} - peerDependencies: - react: ^18 || ^19 || ^19.0.0-rc - react-dom: ^18 || ^19 || ^19.0.0-rc - - '@hono/node-server@1.19.7': - resolution: {integrity: sha512-vUcD0uauS7EU2caukW8z5lJKtoGMokxNbJtBiwHgpqxEXokaHCBkQUmCHhjFB1VUTWdqj25QoMkMKzgjq+uhrw==} - engines: {node: '>=18.14.1'} - peerDependencies: - hono: ^4 - - '@humanfs/core@0.19.1': - resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} - engines: {node: '>=18.18.0'} - - '@humanfs/node@0.16.6': - resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} - engines: {node: '>=18.18.0'} - - '@humanwhocodes/module-importer@1.0.1': - resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} - engines: {node: '>=12.22'} - - '@humanwhocodes/retry@0.3.1': - resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} - engines: {node: '>=18.18'} - - '@humanwhocodes/retry@0.4.1': - resolution: {integrity: sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==} - engines: {node: '>=18.18'} - - '@ibm-cloud/watsonx-ai@1.6.0': - resolution: {integrity: sha512-Cm2UmNX8ynZgvTvTSS3w7Ix13nAO7E5KCvrDinrEBenJhZgG+WrzhToPbQXNJSg7A9NXwSlf31Fk57Rw7Jil2Q==} - engines: {node: '>=18.0.0'} - - '@img/colour@1.0.0': - resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} - engines: {node: '>=18'} - - '@img/sharp-darwin-arm64@0.34.5': - resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [darwin] - - '@img/sharp-darwin-x64@0.34.5': - resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [darwin] - - '@img/sharp-libvips-darwin-arm64@1.2.4': - resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} - cpu: [arm64] - os: [darwin] - - '@img/sharp-libvips-darwin-x64@1.2.4': - resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} - cpu: [x64] - os: [darwin] - - '@img/sharp-libvips-linux-arm64@1.2.4': - resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} - cpu: [arm64] - os: [linux] - - '@img/sharp-libvips-linux-arm@1.2.4': - resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} - cpu: [arm] - os: [linux] - - '@img/sharp-libvips-linux-ppc64@1.2.4': - resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} - cpu: [ppc64] - os: [linux] - - '@img/sharp-libvips-linux-riscv64@1.2.4': - resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} - cpu: [riscv64] - os: [linux] - - '@img/sharp-libvips-linux-s390x@1.2.4': - resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} - cpu: [s390x] - os: [linux] - - '@img/sharp-libvips-linux-x64@1.2.4': - resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} - cpu: [x64] - os: [linux] - - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': - resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} - cpu: [arm64] - os: [linux] - - '@img/sharp-libvips-linuxmusl-x64@1.2.4': - resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} - cpu: [x64] - os: [linux] - - '@img/sharp-linux-arm64@0.34.5': - resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - - '@img/sharp-linux-arm@0.34.5': - resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm] - os: [linux] - - '@img/sharp-linux-ppc64@0.34.5': - resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [ppc64] - os: [linux] - - '@img/sharp-linux-riscv64@0.34.5': - resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [riscv64] - os: [linux] - - '@img/sharp-linux-s390x@0.34.5': - resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [s390x] - os: [linux] - - '@img/sharp-linux-x64@0.34.5': - resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - - '@img/sharp-linuxmusl-arm64@0.34.5': - resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [linux] - - '@img/sharp-linuxmusl-x64@0.34.5': - resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [linux] - - '@img/sharp-wasm32@0.34.5': - resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [wasm32] - - '@img/sharp-win32-arm64@0.34.5': - resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [arm64] - os: [win32] - - '@img/sharp-win32-ia32@0.34.5': - resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [ia32] - os: [win32] - - '@img/sharp-win32-x64@0.34.5': - resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [x64] - os: [win32] - - '@inquirer/confirm@5.1.6': - resolution: {integrity: sha512-6ZXYK3M1XmaVBZX6FCfChgtponnL0R6I7k8Nu+kaoNkT828FVZTcca1MqmWQipaW2oNREQl5AaPCUOOCVNdRMw==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/core@10.1.7': - resolution: {integrity: sha512-AA9CQhlrt6ZgiSy6qoAigiA1izOa751ugX6ioSjqgJ+/Gd+tEN/TORk5sUYNjXuHWfW0r1n/a6ak4u/NqHHrtA==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@inquirer/figures@1.0.10': - resolution: {integrity: sha512-Ey6176gZmeqZuY/W/nZiUyvmb1/qInjcpiZjXWi6nON+nxJpD1bxtSoBxNliGISae32n6OwbY+TSXPZ1CfS4bw==} - engines: {node: '>=18'} - - '@inquirer/type@3.0.4': - resolution: {integrity: sha512-2MNFrDY8jkFYc9Il9DgLsHhMzuHnOYM1+CUYVWbzu9oT0hC7V7EcYvdCKeoll/Fcci04A+ERZ9wcc7cQ8lTkIA==} - engines: {node: '>=18'} - peerDependencies: - '@types/node': '>=18' - peerDependenciesMeta: - '@types/node': - optional: true - - '@isaacs/cliui@8.0.2': - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} - - '@jridgewell/gen-mapping@0.3.8': - resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} - engines: {node: '>=6.0.0'} - - '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - - '@jridgewell/set-array@1.2.1': - resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} - engines: {node: '>=6.0.0'} - - '@jridgewell/sourcemap-codec@1.5.0': - resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} - - '@jridgewell/trace-mapping@0.3.25': - resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - - '@langchain/community@0.3.36': - resolution: {integrity: sha512-4jBB4yqux8CGfCwlBbtXck5qP0yJPwDvtwI4KUN2j/At+zSZn1FyTL11G75ctG2b5GO7u+cR6QatDXIPooJphA==} - engines: {node: '>=18'} - peerDependencies: - '@arcjet/redact': ^v1.0.0-alpha.23 - '@aws-crypto/sha256-js': ^5.0.0 - '@aws-sdk/client-bedrock-agent-runtime': ^3.749.0 - '@aws-sdk/client-bedrock-runtime': ^3.749.0 - '@aws-sdk/client-dynamodb': ^3.749.0 - '@aws-sdk/client-kendra': ^3.749.0 - '@aws-sdk/client-lambda': ^3.749.0 - '@aws-sdk/client-s3': ^3.749.0 - '@aws-sdk/client-sagemaker-runtime': ^3.749.0 - '@aws-sdk/client-sfn': ^3.749.0 - '@aws-sdk/credential-provider-node': ^3.388.0 - '@aws-sdk/dsql-signer': '*' - '@azure/search-documents': ^12.0.0 - '@azure/storage-blob': ^12.15.0 - '@browserbasehq/sdk': '*' - '@browserbasehq/stagehand': ^1.0.0 - '@clickhouse/client': ^0.2.5 - '@cloudflare/ai': '*' - '@datastax/astra-db-ts': ^1.0.0 - '@elastic/elasticsearch': ^8.4.0 - '@getmetal/metal-sdk': '*' - '@getzep/zep-cloud': ^1.0.6 - '@getzep/zep-js': ^0.9.0 - '@gomomento/sdk': ^1.51.1 - '@gomomento/sdk-core': ^1.51.1 - '@google-ai/generativelanguage': '*' - '@google-cloud/storage': ^6.10.1 || ^7.7.0 - '@gradientai/nodejs-sdk': ^1.2.0 - '@huggingface/inference': ^2.6.4 - '@huggingface/transformers': ^3.2.3 - '@ibm-cloud/watsonx-ai': '*' - '@lancedb/lancedb': ^0.12.0 - '@langchain/core': '>=0.2.21 <0.4.0' - '@layerup/layerup-security': ^1.5.12 - '@libsql/client': ^0.14.0 - '@mendable/firecrawl-js': ^1.4.3 - '@mlc-ai/web-llm': '*' - '@mozilla/readability': '*' - '@neondatabase/serverless': '*' - '@notionhq/client': ^2.2.10 - '@opensearch-project/opensearch': '*' - '@pinecone-database/pinecone': '*' - '@planetscale/database': ^1.8.0 - '@premai/prem-sdk': ^0.3.25 - '@qdrant/js-client-rest': ^1.8.2 - '@raycast/api': ^1.55.2 - '@rockset/client': ^0.9.1 - '@smithy/eventstream-codec': ^2.0.5 - '@smithy/protocol-http': ^3.0.6 - '@smithy/signature-v4': ^2.0.10 - '@smithy/util-utf8': ^2.0.0 - '@spider-cloud/spider-client': ^0.0.21 - '@supabase/supabase-js': ^2.45.0 - '@tensorflow-models/universal-sentence-encoder': '*' - '@tensorflow/tfjs-converter': '*' - '@tensorflow/tfjs-core': '*' - '@upstash/ratelimit': ^1.1.3 || ^2.0.3 - '@upstash/redis': ^1.20.6 - '@upstash/vector': ^1.1.1 - '@vercel/kv': '*' - '@vercel/postgres': '*' - '@writerai/writer-sdk': ^0.40.2 - '@xata.io/client': ^0.28.0 - '@zilliz/milvus2-sdk-node': '>=2.3.5' - apify-client: ^2.7.1 - assemblyai: ^4.6.0 - better-sqlite3: '>=9.4.0 <12.0.0' - cassandra-driver: ^4.7.2 - cborg: ^4.1.1 - cheerio: ^1.0.0-rc.12 - chromadb: '*' - closevector-common: 0.1.3 - closevector-node: 0.1.6 - closevector-web: 0.1.6 - cohere-ai: '*' - convex: ^1.3.1 - crypto-js: ^4.2.0 - d3-dsv: ^2.0.0 - discord.js: ^14.14.1 - dria: ^0.0.3 - duck-duck-scrape: ^2.2.5 - epub2: ^3.0.1 - fast-xml-parser: '*' - firebase-admin: ^11.9.0 || ^12.0.0 - google-auth-library: '*' - googleapis: '*' - hnswlib-node: ^3.0.0 - html-to-text: ^9.0.5 - ibm-cloud-sdk-core: '*' - ignore: ^5.2.0 - interface-datastore: ^8.2.11 - ioredis: ^5.3.2 - it-all: ^3.0.4 - jsdom: '*' - jsonwebtoken: ^9.0.2 - llmonitor: ^0.5.9 - lodash: ^4.17.21 - lunary: ^0.7.10 - mammoth: ^1.6.0 - mariadb: ^3.4.0 - mongodb: '>=5.2.0' - mysql2: ^3.9.8 - neo4j-driver: '*' - notion-to-md: ^3.1.0 - officeparser: ^4.0.4 - openai: '*' - pdf-parse: 1.1.1 - pg: ^8.11.0 - pg-copy-streams: ^6.0.5 - pickleparser: ^0.2.1 - playwright: ^1.32.1 - portkey-ai: ^0.1.11 - puppeteer: '*' - pyodide: '>=0.24.1 <0.27.0' - redis: '*' - replicate: '*' - sonix-speech-recognition: ^2.1.1 - srt-parser-2: ^1.2.3 - typeorm: ^0.3.20 - typesense: ^1.5.3 - usearch: ^1.1.1 - voy-search: 0.6.2 - weaviate-ts-client: '*' - web-auth-library: ^1.0.3 - word-extractor: '*' - ws: ^8.14.2 - youtubei.js: '*' - peerDependenciesMeta: - '@arcjet/redact': - optional: true - '@aws-crypto/sha256-js': - optional: true - '@aws-sdk/client-bedrock-agent-runtime': - optional: true - '@aws-sdk/client-bedrock-runtime': - optional: true - '@aws-sdk/client-dynamodb': - optional: true - '@aws-sdk/client-kendra': - optional: true - '@aws-sdk/client-lambda': - optional: true - '@aws-sdk/client-s3': - optional: true - '@aws-sdk/client-sagemaker-runtime': - optional: true - '@aws-sdk/client-sfn': - optional: true - '@aws-sdk/credential-provider-node': - optional: true - '@aws-sdk/dsql-signer': - optional: true - '@azure/search-documents': - optional: true - '@azure/storage-blob': - optional: true - '@browserbasehq/sdk': - optional: true - '@clickhouse/client': - optional: true - '@cloudflare/ai': - optional: true - '@datastax/astra-db-ts': - optional: true - '@elastic/elasticsearch': - optional: true - '@getmetal/metal-sdk': - optional: true - '@getzep/zep-cloud': - optional: true - '@getzep/zep-js': - optional: true - '@gomomento/sdk': - optional: true - '@gomomento/sdk-core': - optional: true - '@google-ai/generativelanguage': - optional: true - '@google-cloud/storage': - optional: true - '@gradientai/nodejs-sdk': - optional: true - '@huggingface/inference': - optional: true - '@huggingface/transformers': - optional: true - '@lancedb/lancedb': - optional: true - '@layerup/layerup-security': - optional: true - '@libsql/client': - optional: true - '@mendable/firecrawl-js': - optional: true - '@mlc-ai/web-llm': - optional: true - '@mozilla/readability': - optional: true - '@neondatabase/serverless': - optional: true - '@notionhq/client': - optional: true - '@opensearch-project/opensearch': - optional: true - '@pinecone-database/pinecone': - optional: true - '@planetscale/database': - optional: true - '@premai/prem-sdk': - optional: true - '@qdrant/js-client-rest': - optional: true - '@raycast/api': - optional: true - '@rockset/client': - optional: true - '@smithy/eventstream-codec': - optional: true - '@smithy/protocol-http': - optional: true - '@smithy/signature-v4': - optional: true - '@smithy/util-utf8': - optional: true - '@spider-cloud/spider-client': - optional: true - '@supabase/supabase-js': - optional: true - '@tensorflow-models/universal-sentence-encoder': - optional: true - '@tensorflow/tfjs-converter': - optional: true - '@tensorflow/tfjs-core': - optional: true - '@upstash/ratelimit': - optional: true - '@upstash/redis': - optional: true - '@upstash/vector': - optional: true - '@vercel/kv': - optional: true - '@vercel/postgres': - optional: true - '@writerai/writer-sdk': - optional: true - '@xata.io/client': - optional: true - '@zilliz/milvus2-sdk-node': - optional: true - apify-client: - optional: true - assemblyai: - optional: true - better-sqlite3: - optional: true - cassandra-driver: - optional: true - cborg: - optional: true - cheerio: - optional: true - chromadb: - optional: true - closevector-common: - optional: true - closevector-node: - optional: true - closevector-web: - optional: true - cohere-ai: - optional: true - convex: - optional: true - crypto-js: - optional: true - d3-dsv: - optional: true - discord.js: - optional: true - dria: - optional: true - duck-duck-scrape: - optional: true - epub2: - optional: true - fast-xml-parser: - optional: true - firebase-admin: - optional: true - google-auth-library: - optional: true - googleapis: - optional: true - hnswlib-node: - optional: true - html-to-text: - optional: true - ignore: - optional: true - interface-datastore: - optional: true - ioredis: - optional: true - it-all: - optional: true - jsdom: - optional: true - jsonwebtoken: - optional: true - llmonitor: - optional: true - lodash: - optional: true - lunary: - optional: true - mammoth: - optional: true - mariadb: - optional: true - mongodb: - optional: true - mysql2: - optional: true - neo4j-driver: - optional: true - notion-to-md: - optional: true - officeparser: - optional: true - pdf-parse: - optional: true - pg: - optional: true - pg-copy-streams: - optional: true - pickleparser: - optional: true - playwright: - optional: true - portkey-ai: - optional: true - puppeteer: - optional: true - pyodide: - optional: true - redis: - optional: true - replicate: - optional: true - sonix-speech-recognition: - optional: true - srt-parser-2: - optional: true - typeorm: - optional: true - typesense: - optional: true - usearch: - optional: true - voy-search: - optional: true - weaviate-ts-client: - optional: true - web-auth-library: - optional: true - word-extractor: - optional: true - ws: - optional: true - youtubei.js: - optional: true - - '@langchain/core@0.3.42': - resolution: {integrity: sha512-pT/jC5lqWK3YGDq8dQwgKoa6anqAhMtG1x5JbnrOj9NdaLeBbCKBDQ+/Ykzk3nZ8o+0UMsaXNZo7IVL83VVjHg==} - engines: {node: '>=18'} - - '@langchain/google-common@0.1.8': - resolution: {integrity: sha512-8auqWw2PMPhcHQHS+nMN3tVZrUPgSLckUaFeOHDOeSBiDvBd4KCybPwyl2oCwMDGvmyIxvOOckkMdeGaJ92vpQ==} - engines: {node: '>=18'} - peerDependencies: - '@langchain/core': '>=0.2.21 <0.4.0' - - '@langchain/google-gauth@0.1.8': - resolution: {integrity: sha512-2QK7d5SQMrnSv7X4j05BGfO74hiA8FJuNwSsQKZvzlGoVnNXil3x2aqD5V+zsYOPpxhkDCpNlmh2Pue2Wzy1rQ==} - engines: {node: '>=18'} - peerDependencies: - '@langchain/core': '>=0.2.21 <0.4.0' - - '@langchain/langgraph-sdk@0.0.36': - resolution: {integrity: sha512-KkAZM0uXBaMcD/dpGTBppOhbvNX6gz+Y1zFAC898OblegFkSvICrkd0oRQ5Ro/GWK/NAoDymnMUDXeZDdUkSuw==} - - '@langchain/openai@0.4.5': - resolution: {integrity: sha512-S/sqC71GVsCDiFGU0A0VQDFGNrjcuz72FxlfuSxwOuo955qad/0Yp0hRhWJilPOjgDByGwaeZkOaxC/oE9ABdQ==} - engines: {node: '>=18'} - peerDependencies: - '@langchain/core': '>=0.3.39 <0.4.0' - - '@langchain/textsplitters@0.1.0': - resolution: {integrity: sha512-djI4uw9rlkAb5iMhtLED+xJebDdAG935AdP4eRTB02R7OB/act55Bj9wsskhZsvuyQRpO4O1wQOp85s6T6GWmw==} - engines: {node: '>=18'} - peerDependencies: - '@langchain/core': '>=0.2.21 <0.4.0' - - '@lukeed/csprng@1.1.0': - resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} - engines: {node: '>=8'} - - '@lukeed/uuid@2.0.1': - resolution: {integrity: sha512-qC72D4+CDdjGqJvkFMMEAtancHUQ7/d/tAiHf64z8MopFDmcrtbcJuerDtFceuAfQJ2pDSfCKCtbqoGBNnwg0w==} - engines: {node: '>=8'} - - '@modelcontextprotocol/sdk@1.25.1': - resolution: {integrity: sha512-yO28oVFFC7EBoiKdAn+VqRm+plcfv4v0xp6osG/VsCB0NlPZWi87ajbCZZ8f/RvOFLEu7//rSRmuZZ7lMoe3gQ==} - engines: {node: '>=18'} - peerDependencies: - '@cfworker/json-schema': ^4.1.1 - zod: ^3.25 || ^4.0 - peerDependenciesMeta: - '@cfworker/json-schema': - optional: true - - '@mswjs/interceptors@0.37.6': - resolution: {integrity: sha512-wK+5pLK5XFmgtH3aQ2YVvA3HohS3xqV/OxuVOdNx9Wpnz7VE/fnC+e1A7ln6LFYeck7gOJ/dsZV6OLplOtAJ2w==} - engines: {node: '>=18'} - - '@next/env@15.4.10': - resolution: {integrity: sha512-knhmoJ0Vv7VRf6pZEPSnciUG1S4bIhWx+qTYBW/AjxEtlzsiNORPk8sFDCEvqLfmKuey56UB9FL1UdHEV3uBrg==} - - '@next/eslint-plugin-next@15.1.4': - resolution: {integrity: sha512-HwlEXwCK3sr6zmVGEvWBjW9tBFs1Oe6hTmTLoFQtpm4As5HCdu8jfSE0XJOp7uhfEGLniIx8yrGxEWwNnY0fmQ==} - - '@next/swc-darwin-arm64@15.4.8': - resolution: {integrity: sha512-Pf6zXp7yyQEn7sqMxur6+kYcywx5up1J849psyET7/8pG2gQTVMjU3NzgIt8SeEP5to3If/SaWmaA6H6ysBr1A==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] - - '@next/swc-darwin-x64@15.4.8': - resolution: {integrity: sha512-xla6AOfz68a6kq3gRQccWEvFC/VRGJmA/QuSLENSO7CZX5WIEkSz7r1FdXUjtGCQ1c2M+ndUAH7opdfLK1PQbw==} - engines: {node: '>= 10'} - cpu: [x64] - os: [darwin] - - '@next/swc-linux-arm64-gnu@15.4.8': - resolution: {integrity: sha512-y3fmp+1Px/SJD+5ntve5QLZnGLycsxsVPkTzAc3zUiXYSOlTPqT8ynfmt6tt4fSo1tAhDPmryXpYKEAcoAPDJw==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - - '@next/swc-linux-arm64-musl@15.4.8': - resolution: {integrity: sha512-DX/L8VHzrr1CfwaVjBQr3GWCqNNFgyWJbeQ10Lx/phzbQo3JNAxUok1DZ8JHRGcL6PgMRgj6HylnLNndxn4Z6A==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - - '@next/swc-linux-x64-gnu@15.4.8': - resolution: {integrity: sha512-9fLAAXKAL3xEIFdKdzG5rUSvSiZTLLTCc6JKq1z04DR4zY7DbAPcRvNm3K1inVhTiQCs19ZRAgUerHiVKMZZIA==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - - '@next/swc-linux-x64-musl@15.4.8': - resolution: {integrity: sha512-s45V7nfb5g7dbS7JK6XZDcapicVrMMvX2uYgOHP16QuKH/JA285oy6HcxlKqwUNaFY/UC6EvQ8QZUOo19cBKSA==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - - '@next/swc-win32-arm64-msvc@15.4.8': - resolution: {integrity: sha512-KjgeQyOAq7t/HzAJcWPGA8X+4WY03uSCZ2Ekk98S9OgCFsb6lfBE3dbUzUuEQAN2THbwYgFfxX2yFTCMm8Kehw==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [win32] - - '@next/swc-win32-x64-msvc@15.4.8': - resolution: {integrity: sha512-Exsmf/+42fWVnLMaZHzshukTBxZrSwuuLKFvqhGHJ+mC1AokqieLY/XzAl3jc/CqhXLqLY3RRjkKJ9YnLPcRWg==} - engines: {node: '>= 10'} - cpu: [x64] - os: [win32] - - '@nodelib/fs.scandir@2.1.5': - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} - - '@nodelib/fs.stat@2.0.5': - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} - - '@nodelib/fs.walk@1.2.8': - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} - - '@nolyfill/is-core-module@1.0.39': - resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} - engines: {node: '>=12.4.0'} - - '@open-draft/deferred-promise@2.2.0': - resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} - - '@open-draft/logger@0.3.0': - resolution: {integrity: sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==} - - '@open-draft/until@2.1.0': - resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} - - '@opentelemetry/api@1.9.0': - resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} - engines: {node: '>=8.0.0'} - - '@pkgjs/parseargs@0.11.0': - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - - '@playwright/test@1.51.1': - resolution: {integrity: sha512-nM+kEaTSAoVlXmMPH10017vn3FSiFqr/bh4fKg9vmAdMfd9SDqRZNvPSiAHADc/itWak+qPvMPZQOPwCBW7k7Q==} - engines: {node: '>=18'} - hasBin: true - - '@radix-ui/primitive@1.1.2': - resolution: {integrity: sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==} - - '@radix-ui/react-arrow@1.1.3': - resolution: {integrity: sha512-2dvVU4jva0qkNZH6HHWuSz5FN5GeU5tymvCgutF8WaXz9WnD1NgUhy73cqzkjkN4Zkn8lfTPv5JIfrC221W+Nw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-compose-refs@1.1.1': - resolution: {integrity: sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-compose-refs@1.1.2': - resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-context@1.1.2': - resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-dismissable-layer@1.1.6': - resolution: {integrity: sha512-7gpgMT2gyKym9Jz2ZhlRXSg2y6cNQIK8d/cqBZ0RBCaps8pFryCWXiUKI+uHGFrhMrbGUP7U6PWgiXzIxoyF3Q==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-id@1.1.1': - resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-label@2.1.1': - resolution: {integrity: sha512-UUw5E4e/2+4kFMH7+YxORXGWggtY6sM8WIwh5RZchhLuUg2H1hc98Py+pr8HMz6rdaYrK2t296ZEjYLOCO5uUw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-popper@1.2.3': - resolution: {integrity: sha512-iNb9LYUMkne9zIahukgQmHlSBp9XWGeQQ7FvUGNk45ywzOb6kQa+Ca38OphXlWDiKvyneo9S+KSJsLfLt8812A==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-portal@1.1.5': - resolution: {integrity: sha512-ps/67ZqsFm+Mb6lSPJpfhRLrVL2i2fntgCmGMqqth4eaGUf+knAuuRtWVJrNjUhExgmdRqftSgzpf0DF0n6yXA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-presence@1.1.3': - resolution: {integrity: sha512-IrVLIhskYhH3nLvtcBLQFZr61tBG7wx7O3kEmdzcYwRGAEBmBicGGL7ATzNgruYJ3xBTbuzEEq9OXJM3PAX3tA==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-primitive@2.0.1': - resolution: {integrity: sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-primitive@2.0.3': - resolution: {integrity: sha512-Pf/t/GkndH7CQ8wE2hbkXA+WyZ83fhQQn5DDmwDiDo6AwN/fhaH8oqZ0jRjMrO2iaMhDi6P1HRx6AZwyMinY1g==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-slot@1.1.1': - resolution: {integrity: sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-slot@1.2.0': - resolution: {integrity: sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-tooltip@1.2.0': - resolution: {integrity: sha512-b1Sdc75s7zN9B8ONQTGBSHL3XS8+IcjcOIY51fhM4R1Hx8s0YbgqgyNZiri4qcYMVZK8hfCZVBiyCm7N9rs0rw==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/react-use-callback-ref@1.1.1': - resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-controllable-state@1.1.1': - resolution: {integrity: sha512-YnEXIy8/ga01Y1PN0VfaNH//MhA91JlEGVBDxDzROqwrAtG5Yr2QGEPz8A/rJA3C7ZAHryOYGaUv8fLSW2H/mg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-escape-keydown@1.1.1': - resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-layout-effect@1.1.1': - resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-rect@1.1.1': - resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-size@1.1.1': - resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-visually-hidden@1.1.3': - resolution: {integrity: sha512-oXSF3ZQRd5fvomd9hmUCb2EHSZbPp3ZSHAHJJU/DlF9XoFkJBBW8RHU/E8WEH+RbSfJd/QFA0sl8ClJXknBwHQ==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/rect@1.1.1': - resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} - - '@react-aria/focus@3.20.1': - resolution: {integrity: sha512-lgYs+sQ1TtBrAXnAdRBQrBo0/7o5H6IrfDxec1j+VRpcXL0xyk0xPq+m3lZp8typzIghqDgpnKkJ5Jf4OrzPIw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/interactions@3.24.1': - resolution: {integrity: sha512-OWEcIC6UQfWq4Td5Ptuh4PZQ4LHLJr/JL2jGYvuNL6EgL3bWvzPrRYIF/R64YbfVxIC7FeZpPSkS07sZ93/NoA==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/ssr@3.9.7': - resolution: {integrity: sha512-GQygZaGlmYjmYM+tiNBA5C6acmiDWF52Nqd40bBp0Znk4M4hP+LTmI0lpI1BuKMw45T8RIhrAsICIfKwZvi2Gg==} - engines: {node: '>= 12'} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-aria/utils@3.28.1': - resolution: {integrity: sha512-mnHFF4YOVu9BRFQ1SZSKfPhg3z+lBRYoW5mLcYTQihbKhz48+I1sqRkP7ahMITr8ANH3nb34YaMME4XWmK2Mgg==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-stately/flags@3.1.0': - resolution: {integrity: sha512-KSHOCxTFpBtxhIRcKwsD1YDTaNxFtCYuAUb0KEihc16QwqZViq4hasgPBs2gYm7fHRbw7WYzWKf6ZSo/+YsFlg==} - - '@react-stately/utils@3.10.5': - resolution: {integrity: sha512-iMQSGcpaecghDIh3mZEpZfoFH3ExBwTtuBEcvZ2XnGzCgQjeYXcMdIUwAfVQLXFTdHUHGF6Gu6/dFrYsCzySBQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@react-types/shared@3.28.0': - resolution: {integrity: sha512-9oMEYIDc3sk0G5rysnYvdNrkSg7B04yTKl50HHSZVbokeHpnU0yRmsDaWb9B/5RprcKj8XszEk5guBO8Sa/Q+Q==} - peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - - '@repeaterjs/repeater@3.0.6': - resolution: {integrity: sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA==} - - '@rtsao/scc@1.1.0': - resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} - - '@rushstack/eslint-patch@1.10.5': - resolution: {integrity: sha512-kkKUDVlII2DQiKy7UstOR1ErJP8kUKAQ4oa+SQtM0K+lPdmmjj0YnnxBgtTVYH7mUKtbsxeFC9y0AmK7Yb78/A==} - - '@scarf/scarf@1.4.0': - resolution: {integrity: sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==} - - '@segment/analytics-core@1.8.1': - resolution: {integrity: sha512-EYcdBdhfi1pOYRX+Sf5orpzzYYFmDHTEu6+w0hjXpW5bWkWct+Nv6UJg1hF4sGDKEQjpZIinLTpQ4eioFM4KeQ==} - - '@segment/analytics-generic-utils@1.2.0': - resolution: {integrity: sha512-DfnW6mW3YQOLlDQQdR89k4EqfHb0g/3XvBXkovH1FstUN93eL1kfW9CsDcVQyH3bAC5ZsFyjA/o/1Q2j0QeoWw==} - - '@segment/analytics-node@2.2.1': - resolution: {integrity: sha512-J+p5r2BewzowI6YsnSH6U+W9IAvRbEyheqDOSUEwh6QDbxjwcBXwzuPwtz5DtEjbEGMu2QwrwoJvZlZ/n5fugw==} - engines: {node: '>=18'} - - '@swc/helpers@0.5.15': - resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} - - '@tanstack/query-core@5.64.1': - resolution: {integrity: sha512-978Wx4Wl4UJZbmvU/rkaM9cQtXXrbhK0lsz/UZhYIbyKYA8E4LdomTwyh2GHZ4oU0BKKoDH4YlKk2VscCUgNmg==} - - '@tanstack/react-query@5.64.1': - resolution: {integrity: sha512-vW5ggHpIO2Yjj44b4sB+Fd3cdnlMJppXRBJkEHvld6FXh3j5dwWJoQo7mGtKI2RbSFyiyu/PhGAy0+Vv5ev9Eg==} - peerDependencies: - react: ^18 || ^19 - - '@tanstack/react-virtual@3.13.4': - resolution: {integrity: sha512-jPWC3BXvVLHsMX67NEHpJaZ+/FySoNxFfBEiF4GBc1+/nVwdRm+UcSCYnKP3pXQr0eEsDpXi/PQZhNfJNopH0g==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - '@tanstack/virtual-core@3.13.4': - resolution: {integrity: sha512-fNGO9fjjSLns87tlcto106enQQLycCKR4DPNpgq3djP5IdcPFdPAmaKjsgzIeRhH7hWrELgW12hYnRthS5kLUw==} - - '@tokenizer/token@0.3.0': - resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} - - '@ts-morph/common@0.19.0': - resolution: {integrity: sha512-Unz/WHmd4pGax91rdIKWi51wnVUW11QttMEPpBiBgIewnc9UQIX7UDLxr5vRlqeByXCwhkF6VabSsI0raWcyAQ==} - - '@types/cookie@0.6.0': - resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} - - '@types/debug@4.1.12': - resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} - - '@types/estree@1.0.6': - resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} - - '@types/hast@2.3.10': - resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==} - - '@types/json-schema@7.0.15': - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - - '@types/json5@0.0.29': - resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - - '@types/katex@0.16.7': - resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==} - - '@types/mdast@3.0.15': - resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==} - - '@types/ms@2.1.0': - resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - - '@types/node-fetch@2.6.12': - resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==} - - '@types/node-fetch@2.6.13': - resolution: {integrity: sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==} - - '@types/node@18.19.130': - resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} - - '@types/node@18.19.80': - resolution: {integrity: sha512-kEWeMwMeIvxYkeg1gTc01awpwLbfMRZXdIhwRcakd/KlK53jmRC26LqcbIt7fnAQTu5GzlnWmzA3H6+l1u6xxQ==} - - '@types/node@20.17.12': - resolution: {integrity: sha512-vo/wmBgMIiEA23A/knMfn/cf37VnuF52nZh5ZoW0GWt4e4sxNquibrMRJ7UQsA06+MBx9r/H1jsI9grYjQCQlw==} - - '@types/node@20.19.27': - resolution: {integrity: sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug==} - - '@types/prop-types@15.7.14': - resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==} - - '@types/react-dom@19.0.3': - resolution: {integrity: sha512-0Knk+HJiMP/qOZgMyNFamlIjw9OFCsyC2ZbigmEEyXXixgre6IQpm/4V+r3qH4GC1JPvRJKInw+on2rV6YZLeA==} - peerDependencies: - '@types/react': ^19.0.0 - - '@types/react@19.0.6': - resolution: {integrity: sha512-gIlMztcTeDgXCUj0vCBOqEuSEhX//63fW9SZtCJ+agxoQTOklwDfiEMlTWn4mR/C/UK5VHlpwsCsOyf7/hc4lw==} - - '@types/retry@0.12.0': - resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} - - '@types/semver@7.5.8': - resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} - - '@types/statuses@2.0.5': - resolution: {integrity: sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==} - - '@types/tough-cookie@4.0.5': - resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} - - '@types/unist@2.0.11': - resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} - - '@types/uuid@10.0.0': - resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==} - - '@types/validator@13.12.2': - resolution: {integrity: sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA==} - - '@typescript-eslint/eslint-plugin@8.19.1': - resolution: {integrity: sha512-tJzcVyvvb9h/PB96g30MpxACd9IrunT7GF9wfA9/0TJ1LxGOJx1TdPzSbBBnNED7K9Ka8ybJsnEpiXPktolTLg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' - - '@typescript-eslint/parser@8.19.1': - resolution: {integrity: sha512-67gbfv8rAwawjYx3fYArwldTQKoYfezNUT4D5ioWetr/xCrxXxvleo3uuiFuKfejipvq+og7mjz3b0G2bVyUCw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' - - '@typescript-eslint/scope-manager@8.19.1': - resolution: {integrity: sha512-60L9KIuN/xgmsINzonOcMDSB8p82h95hoBfSBtXuO4jlR1R9L1xSkmVZKgCPVfavDlXihh4ARNjXhh1gGnLC7Q==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/type-utils@8.19.1': - resolution: {integrity: sha512-Rp7k9lhDKBMRJB/nM9Ksp1zs4796wVNyihG9/TU9R6KCJDNkQbc2EOKjrBtLYh3396ZdpXLtr/MkaSEmNMtykw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' - - '@typescript-eslint/types@8.19.1': - resolution: {integrity: sha512-JBVHMLj7B1K1v1051ZaMMgLW4Q/jre5qGK0Ew6UgXz1Rqh+/xPzV1aW581OM00X6iOfyr1be+QyW8LOUf19BbA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@typescript-eslint/typescript-estree@8.19.1': - resolution: {integrity: sha512-jk/TZwSMJlxlNnqhy0Eod1PNEvCkpY6MXOXE/WLlblZ6ibb32i2We4uByoKPv1d0OD2xebDv4hbs3fm11SMw8Q==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '>=4.8.4 <5.8.0' - - '@typescript-eslint/utils@8.19.1': - resolution: {integrity: sha512-IxG5gLO0Ne+KaUc8iW1A+XuKLd63o4wlbI1Zp692n1xojCl/THvgIKXJXBZixTh5dd5+yTJ/VXH7GJaaw21qXA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.8.0' - - '@typescript-eslint/visitor-keys@8.19.1': - resolution: {integrity: sha512-fzmjU8CHK853V/avYZAvuVut3ZTfwN5YtMaoi+X9Y9MA9keaWNHC3zEQ9zvyX/7Hj+5JkNyK1l7TOR2hevHB6Q==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@urql/core@5.1.1': - resolution: {integrity: sha512-aGh024z5v2oINGD/In6rAtVKTm4VmQ2TxKQBAtk2ZSME5dunZFcjltw4p5ENQg+5CBhZ3FHMzl0Oa+rwqiWqlg==} - - '@whatwg-node/disposablestack@0.0.6': - resolution: {integrity: sha512-LOtTn+JgJvX8WfBVJtF08TGrdjuFzGJc4mkP8EdDI8ADbvO7kiexYep1o8dwnt0okb0jYclCDXF13xU7Ge4zSw==} - engines: {node: '>=18.0.0'} - - '@whatwg-node/events@0.1.2': - resolution: {integrity: sha512-ApcWxkrs1WmEMS2CaLLFUEem/49erT3sxIVjpzU5f6zmVcnijtDSrhoK2zVobOIikZJdH63jdAXOrvjf6eOUNQ==} - engines: {node: '>=18.0.0'} - - '@whatwg-node/fetch@0.10.5': - resolution: {integrity: sha512-+yFJU3hmXPAHJULwx0VzCIsvr/H0lvbPvbOH3areOH3NAuCxCwaJsQ8w6/MwwMcvEWIynSsmAxoyaH04KeosPg==} - engines: {node: '>=18.0.0'} - - '@whatwg-node/node-fetch@0.7.14': - resolution: {integrity: sha512-GMCUrFq3gXQSgWMnEBMaQUxh1rd1vi3Kp4MRQT6UKbnRycm4QmUSxp8ZIySxLQ96cpyBvonEH0BYmdQe/pWy8A==} - engines: {node: '>=18.0.0'} - - '@whatwg-node/promise-helpers@1.3.0': - resolution: {integrity: sha512-486CouizxHXucj8Ky153DDragfkMcHtVEToF5Pn/fInhUUSiCmt9Q4JVBa6UK5q4RammFBtGQ4C9qhGlXU9YbA==} - engines: {node: '>=16.0.0'} - - '@whatwg-node/server@0.10.1': - resolution: {integrity: sha512-SKZjZAhQe8o34pDHUDFn5PV5Otq/gaj/A6AlQ6Sa4+A12YBO0znKKwfoCwGxv/BSRRx0zUqLdI8fZ0ui+EyUlw==} - engines: {node: '>=18.0.0'} - - abort-controller@3.0.0: - resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} - engines: {node: '>=6.5'} - - accepts@1.3.8: - resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} - engines: {node: '>= 0.6'} - - accepts@2.0.0: - resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} - engines: {node: '>= 0.6'} - - acorn-jsx@5.3.2: - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - - acorn@8.14.0: - resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} - engines: {node: '>=0.4.0'} - hasBin: true - - agent-base@6.0.2: - resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} - engines: {node: '>= 6.0.0'} - - agent-base@7.1.3: - resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} - engines: {node: '>= 14'} - - agentkeepalive@4.6.0: - resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} - engines: {node: '>= 8.0.0'} - - ajv-formats@3.0.1: - resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true - - ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - - ajv@8.17.1: - resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} - - ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} - - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - - ansi-regex@6.1.0: - resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} - engines: {node: '>=12'} - - ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - - ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - - ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} - engines: {node: '>=12'} - - any-promise@1.3.0: - resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} - - anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - - arg@5.0.2: - resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} - - argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - - aria-query@5.3.2: - resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} - engines: {node: '>= 0.4'} - - array-buffer-byte-length@1.0.2: - resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} - engines: {node: '>= 0.4'} - - array-flatten@1.1.1: - resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} - - array-includes@3.1.8: - resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} - engines: {node: '>= 0.4'} - - array.prototype.findlast@1.2.5: - resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} - engines: {node: '>= 0.4'} - - array.prototype.findlastindex@1.2.5: - resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==} - engines: {node: '>= 0.4'} - - array.prototype.flat@1.3.3: - resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} - engines: {node: '>= 0.4'} - - array.prototype.flatmap@1.3.3: - resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} - engines: {node: '>= 0.4'} - - array.prototype.tosorted@1.1.4: - resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} - engines: {node: '>= 0.4'} - - arraybuffer.prototype.slice@1.0.4: - resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} - engines: {node: '>= 0.4'} - - arrify@2.0.1: - resolution: {integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==} - engines: {node: '>=8'} - - ast-types-flow@0.0.8: - resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} - - ast-types@0.16.1: - resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} - engines: {node: '>=4'} - - asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - - atomic-sleep@1.0.0: - resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} - engines: {node: '>=8.0.0'} - - available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} - - axe-core@4.10.2: - resolution: {integrity: sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==} - engines: {node: '>=4'} - - axios@1.13.2: - resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==} - - axobject-query@4.1.0: - resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} - engines: {node: '>= 0.4'} - - bail@2.0.2: - resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} - - balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - - bignumber.js@9.1.2: - resolution: {integrity: sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==} - - binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} - - bl@5.1.0: - resolution: {integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==} - - body-parser@1.20.3: - resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - - body-parser@2.2.0: - resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} - engines: {node: '>=18'} - - brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} - - brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} - - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - - browserslist@4.24.4: - resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - - buffer-equal-constant-time@1.0.1: - resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} - - buffer@6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - - busboy@1.6.0: - resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} - engines: {node: '>=10.16.0'} - - bytes@3.1.2: - resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} - engines: {node: '>= 0.8'} - - call-bind-apply-helpers@1.0.1: - resolution: {integrity: sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==} - engines: {node: '>= 0.4'} - - call-bind@1.0.8: - resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} - engines: {node: '>= 0.4'} - - call-bound@1.0.3: - resolution: {integrity: sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==} - engines: {node: '>= 0.4'} - - callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - - camelcase-css@2.0.1: - resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} - engines: {node: '>= 6'} - - camelcase@6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} - - caniuse-lite@1.0.30001692: - resolution: {integrity: sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==} - - caniuse-lite@1.0.30001760: - resolution: {integrity: sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==} - - ccount@2.0.1: - resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - - chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - - chalk@5.4.1: - resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - - character-entities-legacy@1.1.4: - resolution: {integrity: sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==} - - character-entities@1.2.4: - resolution: {integrity: sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==} - - character-entities@2.0.2: - resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} - - character-reference-invalid@1.1.4: - resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==} - - chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} - - class-transformer@0.5.1: - resolution: {integrity: sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==} - - class-validator@0.14.1: - resolution: {integrity: sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==} - - class-variance-authority@0.7.1: - resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} - - cli-cursor@4.0.0: - resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - cli-spinners@2.9.2: - resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} - engines: {node: '>=6'} - - cli-width@4.1.0: - resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} - engines: {node: '>= 12'} - - client-only@0.0.1: - resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} - - cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} - - clone@1.0.4: - resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} - engines: {node: '>=0.8'} - - clsx@2.1.1: - resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} - engines: {node: '>=6'} - - code-block-writer@12.0.0: - resolution: {integrity: sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==} - - color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - - color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - - colorette@2.0.20: - resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} - - combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} - - comma-separated-tokens@1.0.8: - resolution: {integrity: sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==} - - comma-separated-tokens@2.0.3: - resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} - - commander@10.0.1: - resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} - engines: {node: '>=14'} - - commander@4.1.1: - resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} - engines: {node: '>= 6'} - - commander@8.3.0: - resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} - engines: {node: '>= 12'} - - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - - console-table-printer@2.12.1: - resolution: {integrity: sha512-wKGOQRRvdnd89pCeH96e2Fn4wkbenSP6LMHfjfyNLMbGuHEFbMqQNuxXqd0oXG9caIOQ1FTvc5Uijp9/4jujnQ==} - - contains@0.1.1: - resolution: {integrity: sha512-gYfqkG4HpF+/uC1oBvm56WPjG6oRyViz06kQyWlprPp0dYQbMhuijwFffsm1WQW8H2aS9rM6WvK1sPvGPAyTYg==} - - content-disposition@0.5.4: - resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} - engines: {node: '>= 0.6'} - - content-disposition@1.0.0: - resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} - engines: {node: '>= 0.6'} - - content-type@1.0.5: - resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} - engines: {node: '>= 0.6'} - - convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - - cookie-signature@1.0.6: - resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} - - cookie-signature@1.2.2: - resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} - engines: {node: '>=6.6.0'} - - cookie@0.7.1: - resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} - engines: {node: '>= 0.6'} - - cookie@0.7.2: - resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} - engines: {node: '>= 0.6'} - - cors@2.8.5: - resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} - engines: {node: '>= 0.10'} - - cosmiconfig@8.3.6: - resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} - engines: {node: '>=14'} - peerDependencies: - typescript: '>=4.9.5' - peerDependenciesMeta: - typescript: - optional: true - - cross-inspect@1.0.1: - resolution: {integrity: sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==} - engines: {node: '>=16.0.0'} - - cross-spawn@7.0.6: - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} - engines: {node: '>= 8'} - - cssesc@3.0.0: - resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} - engines: {node: '>=4'} - hasBin: true - - csstype@3.1.3: - resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - - damerau-levenshtein@1.0.8: - resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} - - data-uri-to-buffer@4.0.1: - resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} - engines: {node: '>= 12'} - - data-view-buffer@1.0.2: - resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} - engines: {node: '>= 0.4'} - - data-view-byte-length@1.0.2: - resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} - engines: {node: '>= 0.4'} - - data-view-byte-offset@1.0.1: - resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} - engines: {node: '>= 0.4'} - - dateformat@4.6.3: - resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==} - - debug@2.6.9: - resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - debug@3.2.7: - resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - debug@4.4.0: - resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - debug@4.4.3: - resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - decamelize@1.2.0: - resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} - engines: {node: '>=0.10.0'} - - decode-named-character-reference@1.1.0: - resolution: {integrity: sha512-Wy+JTSbFThEOXQIR2L6mxJvEs+veIzpmqD7ynWxMXGpnk3smkHQOp6forLdHsKpAMW9iJpaBBIxz285t1n1C3w==} - - deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} - - deepmerge@4.3.1: - resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} - engines: {node: '>=0.10.0'} - - defaults@1.0.4: - resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} - - define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} - - define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} - - delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} - - depd@2.0.0: - resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} - engines: {node: '>= 0.8'} - - dequal@2.0.3: - resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} - engines: {node: '>=6'} - - destroy@1.2.0: - resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - - detect-libc@2.1.2: - resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} - engines: {node: '>=8'} - - didyoumean@1.2.2: - resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} - - diff@5.2.0: - resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} - engines: {node: '>=0.3.1'} - - dlv@1.1.3: - resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} - - doctrine@2.1.0: - resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} - engines: {node: '>=0.10.0'} - - dotenv@16.6.1: - resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} - engines: {node: '>=12'} - - dset@3.1.4: - resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==} - engines: {node: '>=4'} - - dunder-proto@1.0.1: - resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} - engines: {node: '>= 0.4'} - - eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - - ecdsa-sig-formatter@1.0.11: - resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} - - ee-first@1.1.1: - resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - - electron-to-chromium@1.5.80: - resolution: {integrity: sha512-LTrKpW0AqIuHwmlVNV+cjFYTnXtM9K37OGhpe0ZI10ScPSxqVSryZHIY3WnCS5NSYbBODRTZyhRMS2h5FAEqAw==} - - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - - encodeurl@1.0.2: - resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} - engines: {node: '>= 0.8'} - - encodeurl@2.0.0: - resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} - engines: {node: '>= 0.8'} - - end-of-stream@1.4.4: - resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} - - enhanced-resolve@5.18.0: - resolution: {integrity: sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==} - engines: {node: '>=10.13.0'} - - error-ex@1.3.2: - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - - es-abstract@1.23.9: - resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==} - engines: {node: '>= 0.4'} - - es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - - es-iterator-helpers@1.2.1: - resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} - engines: {node: '>= 0.4'} - - es-object-atoms@1.0.0: - resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} - engines: {node: '>= 0.4'} - - es-set-tostringtag@2.1.0: - resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} - engines: {node: '>= 0.4'} - - es-shim-unscopables@1.0.2: - resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} - - es-to-primitive@1.3.0: - resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} - engines: {node: '>= 0.4'} - - escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} - - escape-html@1.0.3: - resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - - escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - - escape-string-regexp@5.0.0: - resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} - engines: {node: '>=12'} - - eslint-config-next@15.1.4: - resolution: {integrity: sha512-u9+7lFmfhKNgGjhQ9tBeyCFsPJyq0SvGioMJBngPC7HXUpR0U+ckEwQR48s7TrRNHra1REm6evGL2ie38agALg==} - peerDependencies: - eslint: ^7.23.0 || ^8.0.0 || ^9.0.0 - typescript: '>=3.3.1' - peerDependenciesMeta: - typescript: - optional: true - - eslint-import-resolver-node@0.3.9: - resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} - - eslint-import-resolver-typescript@3.7.0: - resolution: {integrity: sha512-Vrwyi8HHxY97K5ebydMtffsWAn1SCR9eol49eCd5fJS4O1WV7PaAjbcjmbfJJSMz/t4Mal212Uz/fQZrOB8mow==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - eslint: '*' - eslint-plugin-import: '*' - eslint-plugin-import-x: '*' - peerDependenciesMeta: - eslint-plugin-import: - optional: true - eslint-plugin-import-x: - optional: true - - eslint-module-utils@2.12.0: - resolution: {integrity: sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: '*' - eslint-import-resolver-node: '*' - eslint-import-resolver-typescript: '*' - eslint-import-resolver-webpack: '*' - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - eslint: - optional: true - eslint-import-resolver-node: - optional: true - eslint-import-resolver-typescript: - optional: true - eslint-import-resolver-webpack: - optional: true - - eslint-plugin-import@2.31.0: - resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - - eslint-plugin-jsx-a11y@6.10.2: - resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==} - engines: {node: '>=4.0'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 - - eslint-plugin-react-hooks@5.1.0: - resolution: {integrity: sha512-mpJRtPgHN2tNAvZ35AMfqeB3Xqeo273QxrHJsbBEPWODRM4r0yB6jfoROqKEYrOn27UtRPpcpHc2UqyBSuUNTw==} - engines: {node: '>=10'} - peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 - - eslint-plugin-react@7.37.4: - resolution: {integrity: sha512-BGP0jRmfYyvOyvMoRX/uoUeW+GqNj9y16bPQzqAHf3AYII/tDs+jMN0dBVkl88/OZwNGwrVFxE7riHsXVfy/LQ==} - engines: {node: '>=4'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 - - eslint-scope@8.2.0: - resolution: {integrity: sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - eslint-visitor-keys@3.4.3: - resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - eslint-visitor-keys@4.2.0: - resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - eslint@9.18.0: - resolution: {integrity: sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - hasBin: true - peerDependencies: - jiti: '*' - peerDependenciesMeta: - jiti: - optional: true - - espree@10.3.0: - resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - - esquery@1.6.0: - resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} - engines: {node: '>=0.10'} - - esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} - - estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} - - esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} - - etag@1.8.1: - resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} - engines: {node: '>= 0.6'} - - event-target-shim@5.0.1: - resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} - engines: {node: '>=6'} - - eventemitter3@4.0.7: - resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} - - events@3.3.0: - resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} - engines: {node: '>=0.8.x'} - - eventsource-parser@3.0.1: - resolution: {integrity: sha512-VARTJ9CYeuQYb0pZEPbzi740OWFgpHe7AYJ2WFZVnUDUQp5Dk2yJUgF36YsZ81cOyxT0QxmXD2EQpapAouzWVA==} - engines: {node: '>=18.0.0'} - - eventsource@3.0.6: - resolution: {integrity: sha512-l19WpE2m9hSuyP06+FbuUUf1G+R0SFLrtQfbRb9PRr+oimOfxQhgGCbVaXg5IvZyyTThJsxh6L/srkMiCeBPDA==} - engines: {node: '>=18.0.0'} - - execa@7.2.0: - resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} - engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} - - expr-eval@2.0.2: - resolution: {integrity: sha512-4EMSHGOPSwAfBiibw3ndnP0AvjDWLsMvGOvWEZ2F96IGk0bIVdjQisOHxReSkE13mHcfbuCiXw+G4y0zv6N8Eg==} - - express-rate-limit@7.5.0: - resolution: {integrity: sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==} - engines: {node: '>= 16'} - peerDependencies: - express: ^4.11 || 5 || ^5.0.0-beta.1 - - express@4.21.2: - resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} - engines: {node: '>= 0.10.0'} - - express@5.1.0: - resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} - engines: {node: '>= 18'} - - extend@3.0.2: - resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} - - fast-copy@3.0.2: - resolution: {integrity: sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==} - - fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - - fast-glob@3.3.1: - resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} - engines: {node: '>=8.6.0'} - - fast-glob@3.3.3: - resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} - engines: {node: '>=8.6.0'} - - fast-json-patch@3.1.1: - resolution: {integrity: sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==} - - fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - - fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - - fast-redact@3.5.0: - resolution: {integrity: sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==} - engines: {node: '>=6'} - - fast-safe-stringify@2.1.1: - resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} - - fast-text-encoding@1.0.6: - resolution: {integrity: sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w==} - - fast-uri@3.1.0: - resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} - - fastq@1.18.0: - resolution: {integrity: sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==} - - fault@1.0.4: - resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==} - - fetch-blob@3.2.0: - resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} - engines: {node: ^12.20 || >= 14.13} - - file-entry-cache@8.0.0: - resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} - engines: {node: '>=16.0.0'} - - file-type@16.5.4: - resolution: {integrity: sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==} - engines: {node: '>=10'} - - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} - - finalhandler@1.3.1: - resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} - engines: {node: '>= 0.8'} - - finalhandler@2.1.0: - resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} - engines: {node: '>= 0.8'} - - find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} - - flat-cache@4.0.1: - resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} - engines: {node: '>=16'} - - flat@5.0.2: - resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} - hasBin: true - - flatted@3.3.2: - resolution: {integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==} - - follow-redirects@1.15.11: - resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - - for-each@0.3.3: - resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} - - foreground-child@3.3.0: - resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} - engines: {node: '>=14'} - - form-data-encoder@1.7.2: - resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==} - - form-data@4.0.0: - resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} - engines: {node: '>= 6'} - - form-data@4.0.2: - resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==} - engines: {node: '>= 6'} - - form-data@4.0.5: - resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} - engines: {node: '>= 6'} - - format@0.2.2: - resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==} - engines: {node: '>=0.4.x'} - - formdata-node@4.4.1: - resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} - engines: {node: '>= 12.20'} - - formdata-polyfill@4.0.10: - resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} - engines: {node: '>=12.20.0'} - - forwarded@0.2.0: - resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} - engines: {node: '>= 0.6'} - - fresh@0.5.2: - resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} - engines: {node: '>= 0.6'} - - fresh@2.0.0: - resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} - engines: {node: '>= 0.8'} - - fs-extra@11.2.0: - resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} - engines: {node: '>=14.14'} - - fsevents@2.3.2: - resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - - fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - function.prototype.name@1.1.8: - resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} - engines: {node: '>= 0.4'} - - functions-have-names@1.2.3: - resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - - gaxios@5.1.3: - resolution: {integrity: sha512-95hVgBRgEIRQQQHIbnxBXeHbW4TqFk4ZDJW7wmVtvYar72FdhRIo1UGOLS2eRAKCPEdPBWu+M7+A33D9CdX9rA==} - engines: {node: '>=12'} - - gcp-metadata@5.3.0: - resolution: {integrity: sha512-FNTkdNEnBdlqF2oatizolQqNANMrcqJt6AAYt99B3y1aLLC8Hc5IOBb+ZnnzllodEEf6xMBp6wRcBbc16fa65w==} - engines: {node: '>=12'} - - gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} - - get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - - get-intrinsic@1.2.7: - resolution: {integrity: sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==} - engines: {node: '>= 0.4'} - - get-own-enumerable-keys@1.0.0: - resolution: {integrity: sha512-PKsK2FSrQCyxcGHsGrLDcK0lx+0Ke+6e8KFFozA9/fIQLhQzPaRvJFdcz7+Axg3jUH/Mq+NI4xa5u/UT2tQskA==} - engines: {node: '>=14.16'} - - get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} - engines: {node: '>= 0.4'} - - get-stream@6.0.1: - resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} - engines: {node: '>=10'} - - get-symbol-description@1.1.0: - resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} - engines: {node: '>= 0.4'} - - get-tsconfig@4.8.1: - resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} - - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - - glob-parent@6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} - engines: {node: '>=10.13.0'} - - glob@10.4.5: - resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} - hasBin: true - - globals@11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} - - globals@14.0.0: - resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} - engines: {node: '>=18'} - - globalthis@1.0.4: - resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} - engines: {node: '>= 0.4'} - - google-auth-library@8.9.0: - resolution: {integrity: sha512-f7aQCJODJFmYWN6PeNKzgvy9LI2tYmXnzpNDHEjG5sDNPgGb2FXQyTBnXeSH+PAtpKESFD+LmHw3Ox3mN7e1Fg==} - engines: {node: '>=12'} - - google-p12-pem@4.0.1: - resolution: {integrity: sha512-WPkN4yGtz05WZ5EhtlxNDWPhC4JIic6G8ePitwUWy4l+XPVYec+a0j0Ts47PDtW59y3RwAhUd9/h9ZZ63px6RQ==} - engines: {node: '>=12.0.0'} - deprecated: Package is no longer maintained - hasBin: true - - gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} - engines: {node: '>= 0.4'} - - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - - graphql-query-complexity@0.12.0: - resolution: {integrity: sha512-fWEyuSL6g/+nSiIRgIipfI6UXTI7bAxrpPlCY1c0+V3pAEUo1ybaKmSBgNr1ed2r+agm1plJww8Loig9y6s2dw==} - peerDependencies: - graphql: ^14.6.0 || ^15.0.0 || ^16.0.0 - - graphql-scalars@1.24.2: - resolution: {integrity: sha512-FoZ11yxIauEnH0E5rCUkhDXHVn/A6BBfovJdimRZCQlFCl+h7aVvarKmI15zG4VtQunmCDdqdtNs6ixThy3uAg==} - engines: {node: '>=10'} - peerDependencies: - graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - - graphql-yoga@5.13.2: - resolution: {integrity: sha512-ZXhIoAPCV2K2ozwpxDL1ZXhhI2SvIp3hJMaSRaHcojLGE9w9iV8oYGPnZKcV5eisF3VE13RcIF4Ys6TTkU338Q==} - engines: {node: '>=18.0.0'} - peerDependencies: - graphql: ^15.2.0 || ^16.0.0 - - graphql@16.10.0: - resolution: {integrity: sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==} - engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} - - groq-sdk@0.5.0: - resolution: {integrity: sha512-RVmhW7qZ+XZoy5fIuSdx/LGQJONpL8MHgZEW7dFwTdgkzStub2XQx6OKv28CHogijdwH41J+Npj/z2jBPu3vmw==} - - gtoken@6.1.2: - resolution: {integrity: sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ==} - engines: {node: '>=12.0.0'} - - has-bigints@1.1.0: - resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} - engines: {node: '>= 0.4'} - - has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - - has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - - has-proto@1.2.0: - resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} - engines: {node: '>= 0.4'} - - has-symbols@1.1.0: - resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} - engines: {node: '>= 0.4'} - - has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} - - hasown@1.0.1: - resolution: {integrity: sha512-My8IVgPaNw1TPrcOtLxG5N2BQJUr2YYI8a3ei3Njx4QIZ+WzEkvLQ4jySrcy6YNfq1JwHpyimb4p2Rw5IuE/SA==} - - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} - - hast-util-parse-selector@2.2.5: - resolution: {integrity: sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==} - - hast-util-whitespace@2.0.1: - resolution: {integrity: sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==} - - hastscript@6.0.0: - resolution: {integrity: sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==} - - headers-polyfill@4.0.3: - resolution: {integrity: sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==} - - help-me@5.0.0: - resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} - - highlight.js@10.7.3: - resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} - - highlightjs-vue@1.0.0: - resolution: {integrity: sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==} - - hono@4.11.1: - resolution: {integrity: sha512-KsFcH0xxHes0J4zaQgWbYwmz3UPOOskdqZmItstUG93+Wk1ePBLkLGwbP9zlmh1BFUiL8Qp+Xfu9P7feJWpGNg==} - engines: {node: '>=16.9.0'} - - http-errors@2.0.0: - resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} - engines: {node: '>= 0.8'} - - https-proxy-agent@5.0.1: - resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} - engines: {node: '>= 6'} - - https-proxy-agent@6.2.1: - resolution: {integrity: sha512-ONsE3+yfZF2caH5+bJlcddtWqNI3Gvs5A38+ngvljxaBiRXRswym2c7yf8UAeFpRFKjFNHIFEHqR/OLAWJzyiA==} - engines: {node: '>= 14'} - - human-signals@4.3.1: - resolution: {integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==} - engines: {node: '>=14.18.0'} - - humanize-ms@1.2.1: - resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} - - ibm-cloud-sdk-core@5.3.2: - resolution: {integrity: sha512-YhtS+7hGNO61h/4jNShHxbbuJ1TnDqiFKQzfEaqePnonOvv8NnxWxOk92FlKKCCzZNOT34Gnd7WCLVJTntwEFQ==} - engines: {node: '>=18'} - - iconv-lite@0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} - engines: {node: '>=0.10.0'} - - iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} - - ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - - ignore@5.3.2: - resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} - engines: {node: '>= 4'} - - import-fresh@3.3.0: - resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} - engines: {node: '>=6'} - - imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - inline-style-parser@0.1.1: - resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==} - - internal-slot@1.1.0: - resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} - engines: {node: '>= 0.4'} - - ipaddr.js@1.9.1: - resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} - engines: {node: '>= 0.10'} - - is-alphabetical@1.0.4: - resolution: {integrity: sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==} - - is-alphanumerical@1.0.4: - resolution: {integrity: sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==} - - is-array-buffer@3.0.5: - resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} - engines: {node: '>= 0.4'} - - is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - - is-async-function@2.1.0: - resolution: {integrity: sha512-GExz9MtyhlZyXYLxzlJRj5WUCE661zhDa1Yna52CN57AJsymh+DvXXjyveSioqSRdxvUrdKdvqB1b5cVKsNpWQ==} - engines: {node: '>= 0.4'} - - is-bigint@1.1.0: - resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} - engines: {node: '>= 0.4'} - - is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - - is-boolean-object@1.2.1: - resolution: {integrity: sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==} - engines: {node: '>= 0.4'} - - is-buffer@2.0.5: - resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==} - engines: {node: '>=4'} - - is-bun-module@1.3.0: - resolution: {integrity: sha512-DgXeu5UWI0IsMQundYb5UAOzm6G2eVnarJ0byP6Tm55iZNKceD59LNPA2L4VvsScTtHcw0yEkVwSf7PC+QoLSA==} - - is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} - - is-core-module@2.16.1: - resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} - engines: {node: '>= 0.4'} - - is-data-view@1.0.2: - resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} - engines: {node: '>= 0.4'} - - is-date-object@1.1.0: - resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} - engines: {node: '>= 0.4'} - - is-decimal@1.0.4: - resolution: {integrity: sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==} - - is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} - - is-finalizationregistry@1.1.1: - resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} - engines: {node: '>= 0.4'} - - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - - is-generator-function@1.1.0: - resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} - engines: {node: '>= 0.4'} - - is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} - - is-hexadecimal@1.0.4: - resolution: {integrity: sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==} - - is-interactive@2.0.0: - resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} - engines: {node: '>=12'} - - is-map@2.0.3: - resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} - engines: {node: '>= 0.4'} - - is-node-process@1.2.0: - resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} - - is-number-object@1.1.1: - resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} - engines: {node: '>= 0.4'} - - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - - is-obj@3.0.0: - resolution: {integrity: sha512-IlsXEHOjtKhpN8r/tRFj2nDyTmHvcfNeu/nrRIcXE17ROeatXchkojffa1SpdqW4cr/Fj6QkEf/Gn4zf6KKvEQ==} - engines: {node: '>=12'} - - is-plain-obj@4.1.0: - resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} - engines: {node: '>=12'} - - is-promise@4.0.0: - resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} - - is-regex@1.2.1: - resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} - engines: {node: '>= 0.4'} - - is-regexp@3.1.0: - resolution: {integrity: sha512-rbku49cWloU5bSMI+zaRaXdQHXnthP6DZ/vLnfdSKyL4zUzuWnomtOEiZZOd+ioQ+avFo/qau3KPTc7Fjy1uPA==} - engines: {node: '>=12'} - - is-set@2.0.3: - resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} - engines: {node: '>= 0.4'} - - is-shared-array-buffer@1.0.4: - resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} - engines: {node: '>= 0.4'} - - is-stream@2.0.1: - resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} - engines: {node: '>=8'} - - is-stream@3.0.0: - resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - is-string@1.1.1: - resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} - engines: {node: '>= 0.4'} - - is-symbol@1.1.1: - resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} - engines: {node: '>= 0.4'} - - is-typed-array@1.1.15: - resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} - engines: {node: '>= 0.4'} - - is-unicode-supported@1.3.0: - resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} - engines: {node: '>=12'} - - is-weakmap@2.0.2: - resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} - engines: {node: '>= 0.4'} - - is-weakref@1.1.0: - resolution: {integrity: sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==} - engines: {node: '>= 0.4'} - - is-weakset@2.0.4: - resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} - engines: {node: '>= 0.4'} - - isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - - isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - - isstream@0.1.2: - resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} - - iterator.prototype@1.1.5: - resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} - engines: {node: '>= 0.4'} - - jackspeak@3.4.3: - resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - - jiti@1.21.7: - resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} - hasBin: true - - jose@5.10.0: - resolution: {integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==} - - jose@6.1.3: - resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} - - joycon@3.1.1: - resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} - engines: {node: '>=10'} - - js-tiktoken@1.0.19: - resolution: {integrity: sha512-XC63YQeEcS47Y53gg950xiZ4IWmkfMe4p2V9OSaBt26q+p47WHn18izuXzSclCI73B7yGqtfRsT6jcZQI0y08g==} - - js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - - js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true - - jsesc@3.1.0: - resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} - engines: {node: '>=6'} - hasBin: true - - json-bigint@1.0.0: - resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} - - json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - - json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - - json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - - json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - - json-schema-typed@8.0.2: - resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} - - json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} - - json5@1.0.2: - resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} - hasBin: true - - json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - - jsonfile@6.1.0: - resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} - - jsonpointer@5.0.1: - resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} - engines: {node: '>=0.10.0'} - - jsonwebtoken@9.0.3: - resolution: {integrity: sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==} - engines: {node: '>=12', npm: '>=6'} - - jsx-ast-utils@3.3.5: - resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} - engines: {node: '>=4.0'} - - jwa@2.0.0: - resolution: {integrity: sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==} - - jwa@2.0.1: - resolution: {integrity: sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==} - - jws@4.0.0: - resolution: {integrity: sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==} - - jws@4.0.1: - resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} - - katex@0.16.21: - resolution: {integrity: sha512-XvqR7FgOHtWupfMiigNzmh+MgUVmDGU2kXZm899ZkPfcuoPuFxyHmXsgATDpFZDAXCI8tvinaVcDo8PIIJSo4A==} - hasBin: true - - keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - - kleur@3.0.3: - resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} - engines: {node: '>=6'} - - kleur@4.1.5: - resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} - engines: {node: '>=6'} - - langchain@0.3.19: - resolution: {integrity: sha512-aGhoTvTBS5ulatA67RHbJ4bcV5zcYRYdm5IH+hpX99RYSFXG24XF3ghSjhYi6sxW+SUnEQ99fJhA5kroVpKNhw==} - engines: {node: '>=18'} - peerDependencies: - '@langchain/anthropic': '*' - '@langchain/aws': '*' - '@langchain/cerebras': '*' - '@langchain/cohere': '*' - '@langchain/core': '>=0.2.21 <0.4.0' - '@langchain/deepseek': '*' - '@langchain/google-genai': '*' - '@langchain/google-vertexai': '*' - '@langchain/google-vertexai-web': '*' - '@langchain/groq': '*' - '@langchain/mistralai': '*' - '@langchain/ollama': '*' - '@langchain/xai': '*' - axios: '*' - cheerio: '*' - handlebars: ^4.7.8 - peggy: ^3.0.2 - typeorm: '*' - peerDependenciesMeta: - '@langchain/anthropic': - optional: true - '@langchain/aws': - optional: true - '@langchain/cerebras': - optional: true - '@langchain/cohere': - optional: true - '@langchain/deepseek': - optional: true - '@langchain/google-genai': - optional: true - '@langchain/google-vertexai': - optional: true - '@langchain/google-vertexai-web': - optional: true - '@langchain/groq': - optional: true - '@langchain/mistralai': - optional: true - '@langchain/ollama': - optional: true - '@langchain/xai': - optional: true - axios: - optional: true - cheerio: - optional: true - handlebars: - optional: true - peggy: - optional: true - typeorm: - optional: true - - langsmith@0.3.14: - resolution: {integrity: sha512-MzoxdRkFFV/6140vpP5V2e2fkTG6x/0zIjw77bsRwAXEMjPRTUyDazfXeSyrS5uJvbLgxAXc+MF1h6vPWe6SXQ==} - peerDependencies: - openai: '*' - peerDependenciesMeta: - openai: - optional: true - - language-subtag-registry@0.3.23: - resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} - - language-tags@1.0.9: - resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} - engines: {node: '>=0.10'} - - levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} - - libphonenumber-js@1.12.6: - resolution: {integrity: sha512-PJiS4ETaUfCOFLpmtKzAbqZQjCCKVu2OhTV4SVNNE7c2nu/dACvtCqj4L0i/KWNnIgRv7yrILvBj5Lonv5Ncxw==} - - lilconfig@3.1.3: - resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} - engines: {node: '>=14'} - - lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - - locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} - - lodash.get@4.4.2: - resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} - deprecated: This package is deprecated. Use the optional chaining (?.) operator instead. - - lodash.includes@4.3.0: - resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} - - lodash.isboolean@3.0.3: - resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} - - lodash.isinteger@4.0.4: - resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} - - lodash.isnumber@3.0.3: - resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} - - lodash.isplainobject@4.0.6: - resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} - - lodash.isstring@4.0.1: - resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} - - lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} - - lodash.once@4.1.1: - resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} - - log-symbols@5.1.0: - resolution: {integrity: sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==} - engines: {node: '>=12'} - - longest-streak@3.1.0: - resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} - - loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true - - lowlight@1.20.0: - resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==} - - lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - - lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - - lru-cache@6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} - - lucide-react@0.471.1: - resolution: {integrity: sha512-syOxwPhf62gg2YOsz72HRn+CIpeudFy67AeKnSR8Hn/fIIF4ubhNbRF+pQ2CaJrl+X9Os4PL87z2DXQi3DVeDA==} - peerDependencies: - react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 - - markdown-table@3.0.4: - resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} - - matches-selector@1.2.0: - resolution: {integrity: sha512-c4vLwYWyl+Ji+U43eU/G5FwxWd4ZH0ePUsFs5y0uwD9HUEFBXUQ1zUUan+78IpRD+y4pUfG0nAzNM292K7ItvA==} - - math-intrinsics@1.1.0: - resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} - engines: {node: '>= 0.4'} - - mdast-util-definitions@5.1.2: - resolution: {integrity: sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA==} - - mdast-util-find-and-replace@2.2.2: - resolution: {integrity: sha512-MTtdFRz/eMDHXzeK6W3dO7mXUlF82Gom4y0oOgvHhh/HXZAGvIQDUvQ0SuUx+j2tv44b8xTHOm8K/9OoRFnXKw==} - - mdast-util-from-markdown@1.3.1: - resolution: {integrity: sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==} - - mdast-util-gfm-autolink-literal@1.0.3: - resolution: {integrity: sha512-My8KJ57FYEy2W2LyNom4n3E7hKTuQk/0SES0u16tjA9Z3oFkF4RrC/hPAPgjlSpezsOvI8ObcXcElo92wn5IGA==} - - mdast-util-gfm-footnote@1.0.2: - resolution: {integrity: sha512-56D19KOGbE00uKVj3sgIykpwKL179QsVFwx/DCW0u/0+URsryacI4MAdNJl0dh+u2PSsD9FtxPFbHCzJ78qJFQ==} - - mdast-util-gfm-strikethrough@1.0.3: - resolution: {integrity: sha512-DAPhYzTYrRcXdMjUtUjKvW9z/FNAMTdU0ORyMcbmkwYNbKocDpdk+PX1L1dQgOID/+vVs1uBQ7ElrBQfZ0cuiQ==} - - mdast-util-gfm-table@1.0.7: - resolution: {integrity: sha512-jjcpmNnQvrmN5Vx7y7lEc2iIOEytYv7rTvu+MeyAsSHTASGCCRA79Igg2uKssgOs1i1po8s3plW0sTu1wkkLGg==} - - mdast-util-gfm-task-list-item@1.0.2: - resolution: {integrity: sha512-PFTA1gzfp1B1UaiJVyhJZA1rm0+Tzn690frc/L8vNX1Jop4STZgOE6bxUhnzdVSB+vm2GU1tIsuQcA9bxTQpMQ==} - - mdast-util-gfm@2.0.2: - resolution: {integrity: sha512-qvZ608nBppZ4icQlhQQIAdc6S3Ffj9RGmzwUKUWuEICFnd1LVkN3EktF7ZHAgfcEdvZB5owU9tQgt99e2TlLjg==} - - mdast-util-math@2.0.2: - resolution: {integrity: sha512-8gmkKVp9v6+Tgjtq6SYx9kGPpTf6FVYRa53/DLh479aldR9AyP48qeVOgNZ5X7QUK7nOy4yw7vg6mbiGcs9jWQ==} - - mdast-util-phrasing@3.0.1: - resolution: {integrity: sha512-WmI1gTXUBJo4/ZmSk79Wcb2HcjPJBzM1nlI/OUWA8yk2X9ik3ffNbBGsU+09BFmXaL1IBb9fiuvq6/KMiNycSg==} - - mdast-util-to-hast@12.3.0: - resolution: {integrity: sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==} - - mdast-util-to-markdown@1.5.0: - resolution: {integrity: sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A==} - - mdast-util-to-string@3.2.0: - resolution: {integrity: sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==} - - media-typer@0.3.0: - resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} - engines: {node: '>= 0.6'} - - media-typer@1.1.0: - resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} - engines: {node: '>= 0.8'} - - merge-descriptors@1.0.3: - resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} - - merge-descriptors@2.0.0: - resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} - engines: {node: '>=18'} - - merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - - merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} - - methods@1.1.2: - resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} - engines: {node: '>= 0.6'} - - micromark-core-commonmark@1.1.0: - resolution: {integrity: sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==} - - micromark-extension-gfm-autolink-literal@1.0.5: - resolution: {integrity: sha512-z3wJSLrDf8kRDOh2qBtoTRD53vJ+CWIyo7uyZuxf/JAbNJjiHsOpG1y5wxk8drtv3ETAHutCu6N3thkOOgueWg==} - - micromark-extension-gfm-footnote@1.1.2: - resolution: {integrity: sha512-Yxn7z7SxgyGWRNa4wzf8AhYYWNrwl5q1Z8ii+CSTTIqVkmGZF1CElX2JI8g5yGoM3GAman9/PVCUFUSJ0kB/8Q==} - - micromark-extension-gfm-strikethrough@1.0.7: - resolution: {integrity: sha512-sX0FawVE1o3abGk3vRjOH50L5TTLr3b5XMqnP9YDRb34M0v5OoZhG+OHFz1OffZ9dlwgpTBKaT4XW/AsUVnSDw==} - - micromark-extension-gfm-table@1.0.7: - resolution: {integrity: sha512-3ZORTHtcSnMQEKtAOsBQ9/oHp9096pI/UvdPtN7ehKvrmZZ2+bbWhi0ln+I9drmwXMt5boocn6OlwQzNXeVeqw==} - - micromark-extension-gfm-tagfilter@1.0.2: - resolution: {integrity: sha512-5XWB9GbAUSHTn8VPU8/1DBXMuKYT5uOgEjJb8gN3mW0PNW5OPHpSdojoqf+iq1xo7vWzw/P8bAHY0n6ijpXF7g==} - - micromark-extension-gfm-task-list-item@1.0.5: - resolution: {integrity: sha512-RMFXl2uQ0pNQy6Lun2YBYT9g9INXtWJULgbt01D/x8/6yJ2qpKyzdZD3pi6UIkzF++Da49xAelVKUeUMqd5eIQ==} - - micromark-extension-gfm@2.0.3: - resolution: {integrity: sha512-vb9OoHqrhCmbRidQv/2+Bc6pkP0FrtlhurxZofvOEy5o8RtuuvTq+RQ1Vw5ZDNrVraQZu3HixESqbG+0iKk/MQ==} - - micromark-extension-math@2.1.2: - resolution: {integrity: sha512-es0CcOV89VNS9wFmyn+wyFTKweXGW4CEvdaAca6SWRWPyYCbBisnjaHLjWO4Nszuiud84jCpkHsqAJoa768Pvg==} - - micromark-factory-destination@1.1.0: - resolution: {integrity: sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==} - - micromark-factory-label@1.1.0: - resolution: {integrity: sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==} - - micromark-factory-space@1.1.0: - resolution: {integrity: sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==} - - micromark-factory-title@1.1.0: - resolution: {integrity: sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==} - - micromark-factory-whitespace@1.1.0: - resolution: {integrity: sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==} - - micromark-util-character@1.2.0: - resolution: {integrity: sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==} - - micromark-util-chunked@1.1.0: - resolution: {integrity: sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==} - - micromark-util-classify-character@1.1.0: - resolution: {integrity: sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==} - - micromark-util-combine-extensions@1.1.0: - resolution: {integrity: sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==} - - micromark-util-decode-numeric-character-reference@1.1.0: - resolution: {integrity: sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==} - - micromark-util-decode-string@1.1.0: - resolution: {integrity: sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==} - - micromark-util-encode@1.1.0: - resolution: {integrity: sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==} - - micromark-util-html-tag-name@1.2.0: - resolution: {integrity: sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==} - - micromark-util-normalize-identifier@1.1.0: - resolution: {integrity: sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==} - - micromark-util-resolve-all@1.1.0: - resolution: {integrity: sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==} - - micromark-util-sanitize-uri@1.2.0: - resolution: {integrity: sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==} - - micromark-util-subtokenize@1.1.0: - resolution: {integrity: sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==} - - micromark-util-symbol@1.1.0: - resolution: {integrity: sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==} - - micromark-util-types@1.1.0: - resolution: {integrity: sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==} - - micromark@3.2.0: - resolution: {integrity: sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==} - - micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} - - mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - - mime-db@1.54.0: - resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} - engines: {node: '>= 0.6'} - - mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} - - mime-types@3.0.1: - resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} - engines: {node: '>= 0.6'} - - mime@1.6.0: - resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} - engines: {node: '>=4'} - hasBin: true - - mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - - mimic-fn@4.0.0: - resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} - engines: {node: '>=12'} - - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - - minimatch@7.4.6: - resolution: {integrity: sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==} - engines: {node: '>=10'} - - minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} - engines: {node: '>=16 || 14 >=14.17'} - - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - - minipass@7.1.2: - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} - engines: {node: '>=16 || 14 >=14.17'} - - mkdirp@2.1.6: - resolution: {integrity: sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==} - engines: {node: '>=10'} - hasBin: true - - mri@1.2.0: - resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} - engines: {node: '>=4'} - - ms@2.0.0: - resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} - - ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - - msw@2.7.3: - resolution: {integrity: sha512-+mycXv8l2fEAjFZ5sjrtjJDmm2ceKGjrNbBr1durRg6VkU9fNUE/gsmQ51hWbHqs+l35W1iM+ZsmOD9Fd6lspw==} - engines: {node: '>=18'} - hasBin: true - peerDependencies: - typescript: '>= 4.8.x' - peerDependenciesMeta: - typescript: - optional: true - - mustache@4.2.0: - resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} - hasBin: true - - mute-stream@2.0.0: - resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} - engines: {node: ^18.17.0 || >=20.5.0} - - mz@2.7.0: - resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - - nanoid@3.3.8: - resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - - natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - - negotiator@0.6.3: - resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} - engines: {node: '>= 0.6'} - - negotiator@1.0.0: - resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} - engines: {node: '>= 0.6'} - - newify@1.1.9: - resolution: {integrity: sha512-kSq656w2zWHOsUtqYOMyMLUYOFAFWj5kPIQfjOedppsteTF/NsZ76SmIrBqMl8J7KVu5DuXNYknAj2LOeR6nOw==} - - next-themes@0.4.4: - resolution: {integrity: sha512-LDQ2qIOJF0VnuVrrMSMLrWGjRMkq+0mpgl6e0juCLqdJ+oo8Q84JRWT6Wh11VDQKkMMe+dVzDKLWs5n87T+PkQ==} - peerDependencies: - react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc - react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc - - next@15.4.10: - resolution: {integrity: sha512-itVlc79QjpKMFMRhP+kbGKaSG/gZM6RCvwhEbwmCNF06CdDiNaoHcbeg0PqkEa2GOcn8KJ0nnc7+yL7EjoYLHQ==} - engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} - hasBin: true - peerDependencies: - '@opentelemetry/api': ^1.1.0 - '@playwright/test': ^1.51.1 - babel-plugin-react-compiler: '*' - react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 - react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 - sass: ^1.3.0 - peerDependenciesMeta: - '@opentelemetry/api': - optional: true - '@playwright/test': - optional: true - babel-plugin-react-compiler: - optional: true - sass: - optional: true - - node-domexception@1.0.0: - resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} - engines: {node: '>=10.5.0'} - - node-fetch@2.7.0: - resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} - engines: {node: 4.x || >=6.0.0} - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - - node-fetch@3.3.2: - resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - node-forge@1.3.1: - resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} - engines: {node: '>= 6.13.0'} - - node-releases@2.0.19: - resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} - - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - - npm-run-path@5.3.0: - resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - object-assign@2.1.1: - resolution: {integrity: sha512-CdsOUYIh5wIiozhJ3rLQgmUTgcyzFwZZrqhkKhODMoGtPKM+wt0h0CNIoauJWMsS9822EdzPsF/6mb4nLvPN5g==} - engines: {node: '>=0.10.0'} - - object-assign@3.0.0: - resolution: {integrity: sha512-jHP15vXVGeVh1HuaA2wY6lxk+whK/x4KBG88VXeRma7CCun7iGD5qPc4eYykQ9sdQvg8jkwFKsSxHln2ybW3xQ==} - engines: {node: '>=0.10.0'} - - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - - object-hash@3.0.0: - resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} - engines: {node: '>= 6'} - - object-inspect@1.13.3: - resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==} - engines: {node: '>= 0.4'} - - object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - - object.assign@4.1.7: - resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} - engines: {node: '>= 0.4'} - - object.entries@1.1.8: - resolution: {integrity: sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==} - engines: {node: '>= 0.4'} - - object.fromentries@2.0.8: - resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} - engines: {node: '>= 0.4'} - - object.groupby@1.0.3: - resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} - engines: {node: '>= 0.4'} - - object.values@1.2.1: - resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} - engines: {node: '>= 0.4'} - - on-exit-leak-free@2.1.2: - resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} - engines: {node: '>=14.0.0'} - - on-finished@2.4.1: - resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} - engines: {node: '>= 0.8'} - - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - - onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} - - onetime@6.0.0: - resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} - engines: {node: '>=12'} - - openai@4.87.4: - resolution: {integrity: sha512-lsfM20jZY4A0lNexfoUAkfmrEXxaTXvv8OKYicpeAJUNHObpRgkvC7pxPgMnB6gc9ID8OCwzzhEhBpNy69UR7w==} - hasBin: true - peerDependencies: - ws: ^8.18.0 - zod: ^3.23.8 - peerDependenciesMeta: - ws: - optional: true - zod: - optional: true - - openapi-types@12.1.3: - resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} - - optionator@0.9.4: - resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} - engines: {node: '>= 0.8.0'} - - ora@6.3.1: - resolution: {integrity: sha512-ERAyNnZOfqM+Ao3RAvIXkYh5joP220yf59gVe2X/cI6SiCxIdi4c9HZKZD8R6q/RDXEje1THBju6iExiSsgJaQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - outvariant@1.4.3: - resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} - - own-keys@1.0.1: - resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} - engines: {node: '>= 0.4'} - - p-finally@1.0.0: - resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} - engines: {node: '>=4'} - - p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} - - p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} - - p-queue@6.6.2: - resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==} - engines: {node: '>=8'} - - p-retry@4.6.2: - resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} - engines: {node: '>=8'} - - p-timeout@3.2.0: - resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} - engines: {node: '>=8'} - - package-json-from-dist@1.0.1: - resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - - parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} - - parse-entities@2.0.0: - resolution: {integrity: sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==} - - parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} - - parseurl@1.3.3: - resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} - engines: {node: '>= 0.8'} - - partial-json@0.1.7: - resolution: {integrity: sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==} - - path-browserify@1.0.1: - resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} - - path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} - - path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - - path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} - engines: {node: '>=12'} - - path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - - path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} - - path-to-regexp@0.1.12: - resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} - - path-to-regexp@6.3.0: - resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} - - path-to-regexp@8.2.0: - resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==} - engines: {node: '>=16'} - - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - - peek-readable@4.1.0: - resolution: {integrity: sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==} - engines: {node: '>=8'} - - picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} - - pify@2.3.0: - resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} - engines: {node: '>=0.10.0'} - - pino-abstract-transport@2.0.0: - resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==} - - pino-pretty@11.3.0: - resolution: {integrity: sha512-oXwn7ICywaZPHmu3epHGU2oJX4nPmKvHvB/bwrJHlGcbEWaVcotkpyVHMKLKmiVryWYByNp0jpgAcXpFJDXJzA==} - hasBin: true - - pino-std-serializers@7.0.0: - resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==} - - pino@9.6.0: - resolution: {integrity: sha512-i85pKRCt4qMjZ1+L7sy2Ag4t1atFcdbEt76+7iRJn1g2BvsnRMGu9p8pivl9fs63M2kF/A0OacFZhTub+m/qMg==} - hasBin: true - - pirates@4.0.6: - resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} - engines: {node: '>= 6'} - - pkce-challenge@5.0.0: - resolution: {integrity: sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==} - engines: {node: '>=16.20.0'} - - playwright-core@1.51.1: - resolution: {integrity: sha512-/crRMj8+j/Nq5s8QcvegseuyeZPxpQCZb6HNk3Sos3BlZyAknRjoyJPFWkpNn8v0+P3WiwqFF8P+zQo4eqiNuw==} - engines: {node: '>=18'} - hasBin: true - - playwright@1.51.1: - resolution: {integrity: sha512-kkx+MB2KQRkyxjYPc3a0wLZZoDczmppyGJIvQ43l+aZihkaVvmu/21kiyaHeHjiFxjxNNFnUncKmcGIyOojsaw==} - engines: {node: '>=18'} - hasBin: true - - possible-typed-array-names@1.0.0: - resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} - engines: {node: '>= 0.4'} - - postcss-import@15.1.0: - resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} - engines: {node: '>=14.0.0'} - peerDependencies: - postcss: ^8.0.0 - - postcss-js@4.0.1: - resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} - engines: {node: ^12 || ^14 || >= 16} - peerDependencies: - postcss: ^8.4.21 - - postcss-load-config@4.0.2: - resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} - engines: {node: '>= 14'} - peerDependencies: - postcss: '>=8.0.9' - ts-node: '>=9.0.0' - peerDependenciesMeta: - postcss: - optional: true - ts-node: - optional: true - - postcss-nested@6.2.0: - resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} - engines: {node: '>=12.0'} - peerDependencies: - postcss: ^8.2.14 - - postcss-selector-parser@6.1.2: - resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} - engines: {node: '>=4'} - - postcss-value-parser@4.2.0: - resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - - postcss@8.4.31: - resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} - engines: {node: ^10 || ^12 || >=14} - - postcss@8.4.49: - resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} - engines: {node: ^10 || ^12 || >=14} - - prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} - - prismjs@1.27.0: - resolution: {integrity: sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==} - engines: {node: '>=6'} - - prismjs@1.30.0: - resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} - engines: {node: '>=6'} - - process-warning@4.0.1: - resolution: {integrity: sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==} - - process@0.11.10: - resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} - engines: {node: '>= 0.6.0'} - - prompts@2.4.2: - resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} - engines: {node: '>= 6'} - - prop-types@15.8.1: - resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} - - property-information@5.6.0: - resolution: {integrity: sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==} - - property-information@6.5.0: - resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==} - - proxy-addr@2.0.7: - resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} - engines: {node: '>= 0.10'} - - proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - - psl@1.15.0: - resolution: {integrity: sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==} - - pump@3.0.2: - resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==} - - punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} - - qs@6.13.0: - resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} - engines: {node: '>=0.6'} - - qs@6.14.0: - resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} - engines: {node: '>=0.6'} - - querystringify@2.2.0: - resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} - - queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - - quick-format-unescaped@4.0.4: - resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} - - range-parser@1.2.1: - resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} - engines: {node: '>= 0.6'} - - raw-body@2.5.2: - resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} - engines: {node: '>= 0.8'} - - raw-body@3.0.0: - resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} - engines: {node: '>= 0.8'} - - react-dom@19.0.0: - resolution: {integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==} - peerDependencies: - react: ^19.0.0 - - react-is@16.13.1: - resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - - react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - - react-markdown@8.0.7: - resolution: {integrity: sha512-bvWbzG4MtOU62XqBx3Xx+zB2raaFFsq4mYiAzfjXJMEz2sixgeAfraA3tvzULF02ZdOMUOKTBFFaZJDDrq+BJQ==} - peerDependencies: - '@types/react': '>=16' - react: '>=16' - - react-style-normalizer@1.2.8: - resolution: {integrity: sha512-smaq27S/Gfj08wsNvLuuA03SKr6K44BVZFyqQEBFyqlQ9MSUi6ydRoBfNcZQeIUNkjRxi1W97sNNs54zFqGC2Q==} - - react-syntax-highlighter@15.6.1: - resolution: {integrity: sha512-OqJ2/vL7lEeV5zTJyG7kmARppUjiB9h9udl4qHQjjgEos66z00Ia0OckwYfRxCSFrW8RJIBnsBwQsHZbVPspqg==} - peerDependencies: - react: '>= 0.14.0' - - react@19.0.0: - resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} - engines: {node: '>=0.10.0'} - - read-cache@1.0.0: - resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} - - readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} - - readable-stream@4.7.0: - resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - readable-web-to-node-stream@3.0.4: - resolution: {integrity: sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw==} - engines: {node: '>=8'} - - readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} - - real-require@0.2.0: - resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} - engines: {node: '>= 12.13.0'} - - recast@0.23.9: - resolution: {integrity: sha512-Hx/BGIbwj+Des3+xy5uAtAbdCyqK9y9wbBcDFDYanLS9JnMqf7OeF87HQwUimE87OEc72mr6tkKUKMBBL+hF9Q==} - engines: {node: '>= 4'} - - reflect-metadata@0.2.2: - resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} - - reflect.getprototypeof@1.0.10: - resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} - engines: {node: '>= 0.4'} - - refractor@3.6.0: - resolution: {integrity: sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==} - - regenerator-runtime@0.14.1: - resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} - - regexp.prototype.flags@1.5.4: - resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} - engines: {node: '>= 0.4'} - - region-align@2.1.3: - resolution: {integrity: sha512-99+X/UE6brBMJ4mUUviuBlrdMDyCdvyOpkEJsFcRUDAKWyClBHjZV/ivLUsLevgfpSapo7ZYeYrimuyP26wO0w==} - - region@2.1.2: - resolution: {integrity: sha512-e34CShd2J76g0YZVZiCBqyJox8wcM3dMRpeuBEN4bJAL4kev6cWAhlJPnb3HwozS90goRd1nuwY5P3Qy+6aAaA==} - - remark-gfm@3.0.1: - resolution: {integrity: sha512-lEFDoi2PICJyNrACFOfDD3JlLkuSbOa5Wd8EPt06HUdptv8Gn0bxYTdbU/XXQ3swAPkEaGxxPN9cbnMHvVu1Ig==} - - remark-math@5.1.1: - resolution: {integrity: sha512-cE5T2R/xLVtfFI4cCePtiRn+e6jKMtFDR3P8V3qpv8wpKjwvHoBA4eJzvX+nVrnlNy0911bdGmuspCSwetfYHw==} - - remark-parse@10.0.2: - resolution: {integrity: sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw==} - - remark-rehype@10.1.0: - resolution: {integrity: sha512-EFmR5zppdBp0WQeDVZ/b66CWJipB2q2VLNFMabzDSGR66Z2fQii83G5gTBbgGEnEEA0QRussvrFHxk1HWGJskw==} - - require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - - require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} - - requires-port@1.0.0: - resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} - - resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} - - resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - - resolve@1.22.10: - resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} - engines: {node: '>= 0.4'} - hasBin: true - - resolve@2.0.0-next.5: - resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} - hasBin: true - - restore-cursor@4.0.0: - resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - retry-axios@2.6.0: - resolution: {integrity: sha512-pOLi+Gdll3JekwuFjXO3fTq+L9lzMQGcSq7M5gIjExcl3Gu1hd4XXuf5o3+LuSBsaULQH7DiNbsqPd1chVpQGQ==} - engines: {node: '>=10.7.0'} - peerDependencies: - axios: '*' - - retry@0.13.1: - resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} - engines: {node: '>= 4'} - - reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - - router@2.2.0: - resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} - engines: {node: '>= 18'} - - run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - - rxjs@7.8.1: - resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} - - rxjs@7.8.2: - resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} - - sade@1.8.1: - resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} - engines: {node: '>=6'} - - safe-array-concat@1.1.3: - resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} - engines: {node: '>=0.4'} - - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - - safe-push-apply@1.0.0: - resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} - engines: {node: '>= 0.4'} - - safe-regex-test@1.1.0: - resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} - engines: {node: '>= 0.4'} - - safe-stable-stringify@2.5.0: - resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} - engines: {node: '>=10'} - - safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - - scheduler@0.25.0: - resolution: {integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==} - - secure-json-parse@2.7.0: - resolution: {integrity: sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==} - - semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - - semver@7.6.3: - resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} - engines: {node: '>=10'} - hasBin: true - - semver@7.7.3: - resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} - engines: {node: '>=10'} - hasBin: true - - send@0.19.0: - resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} - engines: {node: '>= 0.8.0'} - - send@1.2.0: - resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} - engines: {node: '>= 18'} - - serve-static@1.16.2: - resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} - engines: {node: '>= 0.8.0'} - - serve-static@2.2.0: - resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} - engines: {node: '>= 18'} - - set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} - - set-function-name@2.0.2: - resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} - engines: {node: '>= 0.4'} - - set-proto@1.0.0: - resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} - engines: {node: '>= 0.4'} - - setprototypeof@1.2.0: - resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - - shadcn@2.4.0: - resolution: {integrity: sha512-SN8PqL+VnJccP2IHkft9kNA96UBo0vTUKgDFww/K0i8D+sDmvXwXKStNdqlDcLcF6sYllwZCzV0axxWROAdFCw==} - hasBin: true - - sharp@0.34.5: - resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - - shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - - shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - - side-channel-list@1.0.0: - resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} - engines: {node: '>= 0.4'} - - side-channel-map@1.0.1: - resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} - engines: {node: '>= 0.4'} - - side-channel-weakmap@1.0.2: - resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} - engines: {node: '>= 0.4'} - - side-channel@1.1.0: - resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} - engines: {node: '>= 0.4'} - - signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - - signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - - simple-wcswidth@1.0.1: - resolution: {integrity: sha512-xMO/8eNREtaROt7tJvWJqHBDTMFN4eiQ5I4JRMuilwfnFcV5W9u7RUkueNkdw0jPqGMX36iCywelS5yilTuOxg==} - - sisteransi@1.0.5: - resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - - sonic-boom@4.2.0: - resolution: {integrity: sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==} - - source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} - - source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - - space-separated-tokens@1.1.5: - resolution: {integrity: sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==} - - space-separated-tokens@2.0.2: - resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} - - split2@4.2.0: - resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} - engines: {node: '>= 10.x'} - - stable-hash@0.0.4: - resolution: {integrity: sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==} - - statuses@2.0.1: - resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} - engines: {node: '>= 0.8'} - - stdin-discarder@0.1.0: - resolution: {integrity: sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - - streamsearch@1.1.0: - resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} - engines: {node: '>=10.0.0'} - - strict-event-emitter@0.5.1: - resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} - - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} - - string.prototype.includes@2.0.1: - resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} - engines: {node: '>= 0.4'} - - string.prototype.matchall@4.0.12: - resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} - engines: {node: '>= 0.4'} - - string.prototype.repeat@1.0.0: - resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} - - string.prototype.trim@1.2.10: - resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} - engines: {node: '>= 0.4'} - - string.prototype.trimend@1.0.9: - resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} - engines: {node: '>= 0.4'} - - string.prototype.trimstart@1.0.8: - resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} - engines: {node: '>= 0.4'} - - string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - - stringify-object@5.0.0: - resolution: {integrity: sha512-zaJYxz2FtcMb4f+g60KsRNFOpVMUyuJgA51Zi5Z1DOTC3S59+OQiVOzE9GZt0x72uBGWKsQIuBKeF9iusmKFsg==} - engines: {node: '>=14.16'} - - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} - engines: {node: '>=12'} - - strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} - - strip-final-newline@3.0.0: - resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} - engines: {node: '>=12'} - - strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} - - strtok3@6.3.0: - resolution: {integrity: sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==} - engines: {node: '>=10'} - - style-to-object@0.4.4: - resolution: {integrity: sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==} - - styled-jsx@5.1.6: - resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} - engines: {node: '>= 12.0.0'} - peerDependencies: - '@babel/core': '*' - babel-plugin-macros: '*' - react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' - peerDependenciesMeta: - '@babel/core': - optional: true - babel-plugin-macros: - optional: true - - sucrase@3.35.0: - resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} - engines: {node: '>=16 || 14 >=14.17'} - hasBin: true - - supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - - supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} - - tabbable@6.2.0: - resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} - - tailwind-merge@2.6.0: - resolution: {integrity: sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==} - - tailwindcss-animate@1.0.7: - resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==} - peerDependencies: - tailwindcss: '>=3.0.0 || insiders' - - tailwindcss@3.4.17: - resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==} - engines: {node: '>=14.0.0'} - hasBin: true - - tapable@2.2.1: - resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} - engines: {node: '>=6'} - - thenify-all@1.6.0: - resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} - engines: {node: '>=0.8'} - - thenify@3.3.1: - resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - - thread-stream@3.1.0: - resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} - - tiny-invariant@1.3.3: - resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} - - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - - to-style@1.3.3: - resolution: {integrity: sha512-9K8KYegr9hrdm8yPpu5iZjJp5t6RPAp4gFDU5hD9zR8hwqgF4fsoSitMtkRKQG2qkP5j/uG3wajbgV09rjmIqg==} - - toidentifier@1.0.1: - resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} - engines: {node: '>=0.6'} - - token-types@4.2.1: - resolution: {integrity: sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==} - engines: {node: '>=10'} - - tooltip@1.6.1: - resolution: {integrity: sha512-9E1WHNSLhH2fmrc2/+4Hmp7yWYqKx9sm1C5V7vC7udHqDjSAjVgUcLQsCQ1WXuSFWgjnRuDKEmxAqNF5/nxJjg==} - - tough-cookie@4.1.4: - resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} - engines: {node: '>=6'} - - tr46@0.0.3: - resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - - trim-lines@3.0.1: - resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} - - trough@2.2.0: - resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} - - ts-api-utils@2.0.0: - resolution: {integrity: sha512-xCt/TOAc+EOHS1XPnijD3/yzpH6qg2xppZO1YDqGoVsNXfQfzHpOdNuXwrwOU8u4ITXJyDCTyt8w5g1sZv9ynQ==} - engines: {node: '>=18.12'} - peerDependencies: - typescript: '>=4.8.4' - - ts-interface-checker@0.1.13: - resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - - ts-morph@18.0.0: - resolution: {integrity: sha512-Kg5u0mk19PIIe4islUI/HWRvm9bC1lHejK4S0oh1zaZ77TMZAEmQC0sHQYiu2RgCQFZKXz1fMVi/7nOOeirznA==} - - tsconfig-paths@3.15.0: - resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} - - tsconfig-paths@4.2.0: - resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} - engines: {node: '>=6'} - - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - - type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} - - type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - - type-fest@4.37.0: - resolution: {integrity: sha512-S/5/0kFftkq27FPNye0XM1e2NsnoD/3FS+pBmbjmmtLT6I+i344KoOf7pvXreaFsDamWeaJX55nczA1m5PsBDg==} - engines: {node: '>=16'} - - type-graphql@2.0.0-rc.1: - resolution: {integrity: sha512-HCu4j3jR0tZvAAoO7DMBT3MRmah0DFRe5APymm9lXUghXA0sbhiMf6SLRafRYfk0R0KiUQYRduuGP3ap1RnF1Q==} - engines: {node: '>= 18.12.0'} - peerDependencies: - class-validator: '>=0.14.0' - graphql: ^16.8.1 - graphql-scalars: ^1.22.4 - peerDependenciesMeta: - class-validator: - optional: true - - type-is@1.6.18: - resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} - engines: {node: '>= 0.6'} - - type-is@2.0.1: - resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} - engines: {node: '>= 0.6'} - - typed-array-buffer@1.0.3: - resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} - engines: {node: '>= 0.4'} - - typed-array-byte-length@1.0.3: - resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} - engines: {node: '>= 0.4'} - - typed-array-byte-offset@1.0.4: - resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} - engines: {node: '>= 0.4'} - - typed-array-length@1.0.7: - resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} - engines: {node: '>= 0.4'} - - typescript@5.7.3: - resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} - engines: {node: '>=14.17'} - hasBin: true - - unbox-primitive@1.1.0: - resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} - engines: {node: '>= 0.4'} - - undici-types@5.26.5: - resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - - undici-types@6.19.8: - resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} - - undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - - unified@10.1.2: - resolution: {integrity: sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==} - - unist-util-generated@2.0.1: - resolution: {integrity: sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==} - - unist-util-is@5.2.1: - resolution: {integrity: sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==} - - unist-util-position@4.0.4: - resolution: {integrity: sha512-kUBE91efOWfIVBo8xzh/uZQ7p9ffYRtUbMRZBNFYwf0RK8koUMx6dGUfwylLOKmaT2cs4wSW96QoYUSXAyEtpg==} - - unist-util-stringify-position@3.0.3: - resolution: {integrity: sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==} - - unist-util-visit-parents@5.1.3: - resolution: {integrity: sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==} - - unist-util-visit@4.1.2: - resolution: {integrity: sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==} - - universalify@0.2.0: - resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} - engines: {node: '>= 4.0.0'} - - universalify@2.0.1: - resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} - engines: {node: '>= 10.0.0'} - - unpipe@1.0.0: - resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} - engines: {node: '>= 0.8'} - - untruncate-json@0.0.1: - resolution: {integrity: sha512-4W9enDK4X1y1s2S/Rz7ysw6kDuMS3VmRjMFg7GZrNO+98OSe+x5Lh7PKYoVjy3lW/1wmhs6HW0lusnQRHgMarA==} - - update-browserslist-db@1.1.2: - resolution: {integrity: sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - - uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - - url-parse@1.5.10: - resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} - - urlpattern-polyfill@10.0.0: - resolution: {integrity: sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==} - - urql@4.2.2: - resolution: {integrity: sha512-3GgqNa6iF7bC4hY/ImJKN4REQILcSU9VKcKL8gfELZM8mM5BnLH1BsCc8kBdnVGD1LIFOs4W3O2idNHhON1r0w==} - peerDependencies: - '@urql/core': ^5.0.0 - react: '>= 16.8.0' - - util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - - utils-merge@1.0.1: - resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} - engines: {node: '>= 0.4.0'} - - uuid@10.0.0: - resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} - hasBin: true - - uuid@11.1.0: - resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} - hasBin: true - - uuid@9.0.1: - resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} - hasBin: true - - uvu@0.5.6: - resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==} - engines: {node: '>=8'} - hasBin: true - - validator@13.12.0: - resolution: {integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==} - engines: {node: '>= 0.10'} - - vary@1.1.2: - resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} - engines: {node: '>= 0.8'} - - vfile-message@3.1.4: - resolution: {integrity: sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==} - - vfile@5.3.7: - resolution: {integrity: sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==} - - wcwidth@1.0.1: - resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} - - web-streams-polyfill@3.3.3: - resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} - engines: {node: '>= 8'} - - web-streams-polyfill@4.0.0-beta.3: - resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} - engines: {node: '>= 14'} - - webidl-conversions@3.0.1: - resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - - whatwg-url@5.0.0: - resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - - which-boxed-primitive@1.1.1: - resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} - engines: {node: '>= 0.4'} - - which-builtin-type@1.2.1: - resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} - engines: {node: '>= 0.4'} - - which-collection@1.0.2: - resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} - engines: {node: '>= 0.4'} - - which-typed-array@1.1.18: - resolution: {integrity: sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==} - engines: {node: '>= 0.4'} - - which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - - wonka@6.3.5: - resolution: {integrity: sha512-SSil+ecw6B4/Dm7Pf2sAshKQ5hWFvfyGlfPbEd6A14dOH6VDjrmbY86u6nZvy9omGwwIPFR8V41+of1EezgoUw==} - - word-wrap@1.2.5: - resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} - engines: {node: '>=0.10.0'} - - wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} - - wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - - wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} - - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - ws@8.18.3: - resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - xtend@4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} - - y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - - yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - - yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - - yaml@2.7.0: - resolution: {integrity: sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==} - engines: {node: '>= 14'} - hasBin: true - - yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - - yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} - - yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} - - yoctocolors-cjs@2.1.2: - resolution: {integrity: sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==} - engines: {node: '>=18'} - - zod-to-json-schema@3.24.4: - resolution: {integrity: sha512-0uNlcvgabyrni9Ag8Vghj21drk7+7tp7VTwwR7KxxXXc/3pbXz2PHlDgj3cICahgF1kHm4dExBFj7BXrZJXzig==} - peerDependencies: - zod: ^3.24.1 - - zod-to-json-schema@3.25.0: - resolution: {integrity: sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==} - peerDependencies: - zod: ^3.25 || ^4 - - zod@3.24.1: - resolution: {integrity: sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==} - - zwitch@2.0.4: - resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} - -snapshots: - - '@0no-co/graphql.web@1.1.2(graphql@16.10.0)': - optionalDependencies: - graphql: 16.10.0 - - '@agentwire/client@0.0.26': - dependencies: - '@agentwire/core': 0.0.26 - '@agentwire/encoder': 0.0.26 - '@agentwire/proto': 0.0.26 - '@types/uuid': 10.0.0 - fast-json-patch: 3.1.1 - rxjs: 7.8.1 - untruncate-json: 0.0.1 - uuid: 11.1.0 - zod: 3.24.1 - - '@agentwire/core@0.0.26': - dependencies: - rxjs: 7.8.1 - zod: 3.24.1 - - '@agentwire/encoder@0.0.26': - dependencies: - '@agentwire/core': 0.0.26 - '@agentwire/proto': 0.0.26 - - '@agentwire/proto@0.0.26': - dependencies: - '@agentwire/core': 0.0.26 - '@bufbuild/protobuf': 2.2.5 - - '@alloc/quick-lru@5.2.0': {} - - '@ampproject/remapping@2.3.0': - dependencies: - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 - - '@antfu/ni@23.3.1': {} - - '@anthropic-ai/sdk@0.27.3': - dependencies: - '@types/node': 18.19.80 - '@types/node-fetch': 2.6.12 - abort-controller: 3.0.0 - agentkeepalive: 4.6.0 - form-data-encoder: 1.7.2 - formdata-node: 4.4.1 - node-fetch: 2.7.0 - transitivePeerDependencies: - - encoding - - '@babel/code-frame@7.26.2': - dependencies: - '@babel/helper-validator-identifier': 7.25.9 - js-tokens: 4.0.0 - picocolors: 1.1.1 - - '@babel/compat-data@7.26.5': {} - - '@babel/core@7.26.0': - dependencies: - '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.26.2 - '@babel/generator': 7.26.5 - '@babel/helper-compilation-targets': 7.26.5 - '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0) - '@babel/helpers': 7.26.0 - '@babel/parser': 7.26.5 - '@babel/template': 7.25.9 - '@babel/traverse': 7.26.5 - '@babel/types': 7.26.5 - convert-source-map: 2.0.0 - debug: 4.4.0 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - '@babel/generator@7.26.5': - dependencies: - '@babel/parser': 7.26.5 - '@babel/types': 7.26.5 - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 - jsesc: 3.1.0 - - '@babel/helper-annotate-as-pure@7.25.9': - dependencies: - '@babel/types': 7.26.5 - - '@babel/helper-compilation-targets@7.26.5': - dependencies: - '@babel/compat-data': 7.26.5 - '@babel/helper-validator-option': 7.25.9 - browserslist: 4.24.4 - lru-cache: 5.1.1 - semver: 6.3.1 - - '@babel/helper-create-class-features-plugin@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-annotate-as-pure': 7.25.9 - '@babel/helper-member-expression-to-functions': 7.25.9 - '@babel/helper-optimise-call-expression': 7.25.9 - '@babel/helper-replace-supers': 7.26.5(@babel/core@7.26.0) - '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 - '@babel/traverse': 7.26.5 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - - '@babel/helper-member-expression-to-functions@7.25.9': - dependencies: - '@babel/traverse': 7.26.5 - '@babel/types': 7.26.5 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-imports@7.25.9': - dependencies: - '@babel/traverse': 7.26.5 - '@babel/types': 7.26.5 - transitivePeerDependencies: - - supports-color - - '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-module-imports': 7.25.9 - '@babel/helper-validator-identifier': 7.25.9 - '@babel/traverse': 7.26.5 - transitivePeerDependencies: - - supports-color - - '@babel/helper-optimise-call-expression@7.25.9': - dependencies: - '@babel/types': 7.26.5 - - '@babel/helper-plugin-utils@7.26.5': {} - - '@babel/helper-replace-supers@7.26.5(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-member-expression-to-functions': 7.25.9 - '@babel/helper-optimise-call-expression': 7.25.9 - '@babel/traverse': 7.26.5 - transitivePeerDependencies: - - supports-color - - '@babel/helper-skip-transparent-expression-wrappers@7.25.9': - dependencies: - '@babel/traverse': 7.26.5 - '@babel/types': 7.26.5 - transitivePeerDependencies: - - supports-color - - '@babel/helper-string-parser@7.25.9': {} - - '@babel/helper-validator-identifier@7.25.9': {} - - '@babel/helper-validator-option@7.25.9': {} - - '@babel/helpers@7.26.0': - dependencies: - '@babel/template': 7.25.9 - '@babel/types': 7.26.5 - - '@babel/parser@7.26.5': - dependencies: - '@babel/types': 7.26.5 - - '@babel/plugin-syntax-typescript@7.25.9(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-plugin-utils': 7.26.5 - - '@babel/plugin-transform-typescript@7.26.5(@babel/core@7.26.0)': - dependencies: - '@babel/core': 7.26.0 - '@babel/helper-annotate-as-pure': 7.25.9 - '@babel/helper-create-class-features-plugin': 7.25.9(@babel/core@7.26.0) - '@babel/helper-plugin-utils': 7.26.5 - '@babel/helper-skip-transparent-expression-wrappers': 7.25.9 - '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.0) - transitivePeerDependencies: - - supports-color - - '@babel/runtime@7.26.10': - dependencies: - regenerator-runtime: 0.14.1 - - '@babel/template@7.25.9': - dependencies: - '@babel/code-frame': 7.26.2 - '@babel/parser': 7.26.5 - '@babel/types': 7.26.5 - - '@babel/traverse@7.26.5': - dependencies: - '@babel/code-frame': 7.26.2 - '@babel/generator': 7.26.5 - '@babel/parser': 7.26.5 - '@babel/template': 7.25.9 - '@babel/types': 7.26.5 - debug: 4.4.0 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - - '@babel/types@7.26.5': - dependencies: - '@babel/helper-string-parser': 7.25.9 - '@babel/helper-validator-identifier': 7.25.9 - - '@browserbasehq/sdk@2.6.0': - dependencies: - '@types/node': 18.19.130 - '@types/node-fetch': 2.6.13 - abort-controller: 3.0.0 - agentkeepalive: 4.6.0 - form-data-encoder: 1.7.2 - formdata-node: 4.4.1 - node-fetch: 2.7.0 - transitivePeerDependencies: - - encoding - - '@browserbasehq/stagehand@1.14.0(@playwright/test@1.51.1)(deepmerge@4.3.1)(dotenv@16.6.1)(openai@4.87.4(ws@8.18.3)(zod@3.24.1))(zod@3.24.1)': - dependencies: - '@anthropic-ai/sdk': 0.27.3 - '@browserbasehq/sdk': 2.6.0 - '@playwright/test': 1.51.1 - deepmerge: 4.3.1 - dotenv: 16.6.1 - openai: 4.87.4(ws@8.18.3)(zod@3.24.1) - ws: 8.18.3 - zod: 3.24.1 - zod-to-json-schema: 3.25.0(zod@3.24.1) - transitivePeerDependencies: - - bufferutil - - encoding - - utf-8-validate - - '@bufbuild/protobuf@2.2.5': {} - - '@bundled-es-modules/cookie@2.0.1': - dependencies: - cookie: 0.7.2 - - '@bundled-es-modules/statuses@1.0.1': - dependencies: - statuses: 2.0.1 - - '@bundled-es-modules/tough-cookie@0.1.6': - dependencies: - '@types/tough-cookie': 4.0.5 - tough-cookie: 4.1.4 - - '@cfworker/json-schema@4.1.1': {} - - '@copilotkit/react-core@1.8.8(@types/react@19.0.6)(graphql@16.10.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@copilotkit/runtime-client-gql': 1.8.8(graphql@16.10.0)(react@19.0.0) - '@copilotkit/shared': 1.8.8 - '@scarf/scarf': 1.4.0 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - react-markdown: 8.0.7(@types/react@19.0.6)(react@19.0.0) - untruncate-json: 0.0.1 - transitivePeerDependencies: - - '@types/react' - - encoding - - graphql - - supports-color - - '@copilotkit/react-ui@1.8.8(@types/react@19.0.6)(graphql@16.10.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@copilotkit/react-core': 1.8.8(@types/react@19.0.6)(graphql@16.10.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@copilotkit/runtime-client-gql': 1.8.8(graphql@16.10.0)(react@19.0.0) - '@copilotkit/shared': 1.8.8 - '@headlessui/react': 2.2.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-markdown: 8.0.7(@types/react@19.0.6)(react@19.0.0) - react-syntax-highlighter: 15.6.1(react@19.0.0) - remark-gfm: 3.0.1 - remark-math: 5.1.1 - transitivePeerDependencies: - - '@types/react' - - encoding - - graphql - - react-dom - - supports-color - - '@copilotkit/runtime-client-gql@1.8.8(graphql@16.10.0)(react@19.0.0)': - dependencies: - '@copilotkit/shared': 1.8.8 - '@urql/core': 5.1.1(graphql@16.10.0) - react: 19.0.0 - untruncate-json: 0.0.1 - urql: 4.2.2(@urql/core@5.1.1(graphql@16.10.0))(react@19.0.0) - transitivePeerDependencies: - - encoding - - graphql - - '@copilotkit/runtime@1.8.8(@browserbasehq/sdk@2.6.0)(@browserbasehq/stagehand@1.14.0(@playwright/test@1.51.1)(deepmerge@4.3.1)(dotenv@16.6.1)(openai@4.87.4(ws@8.18.3)(zod@3.24.1))(zod@3.24.1))(@ibm-cloud/watsonx-ai@1.6.0(@langchain/core@0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1))))(axios@1.13.2)(google-auth-library@8.9.0)(ibm-cloud-sdk-core@5.3.2)(ignore@5.3.2)(jsonwebtoken@9.0.3)(playwright@1.51.1)(ws@8.18.3)': - dependencies: - '@agentwire/client': 0.0.26 - '@agentwire/core': 0.0.26 - '@agentwire/encoder': 0.0.26 - '@agentwire/proto': 0.0.26 - '@anthropic-ai/sdk': 0.27.3 - '@copilotkit/shared': 1.8.8 - '@graphql-yoga/plugin-defer-stream': 3.13.2(graphql-yoga@5.13.2(graphql@16.10.0))(graphql@16.10.0) - '@langchain/community': 0.3.36(@browserbasehq/sdk@2.6.0)(@browserbasehq/stagehand@1.14.0(@playwright/test@1.51.1)(deepmerge@4.3.1)(dotenv@16.6.1)(openai@4.87.4(ws@8.18.3)(zod@3.24.1))(zod@3.24.1))(@ibm-cloud/watsonx-ai@1.6.0(@langchain/core@0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1))))(@langchain/core@0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1)))(axios@1.13.2)(google-auth-library@8.9.0)(ibm-cloud-sdk-core@5.3.2)(ignore@5.3.2)(jsonwebtoken@9.0.3)(openai@4.87.4(ws@8.18.3)(zod@3.24.1))(playwright@1.51.1)(ws@8.18.3) - '@langchain/core': 0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1)) - '@langchain/google-gauth': 0.1.8(@langchain/core@0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1)))(zod@3.24.1) - '@langchain/langgraph-sdk': 0.0.36 - '@langchain/openai': 0.4.5(@langchain/core@0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1)))(ws@8.18.3) - class-transformer: 0.5.1 - class-validator: 0.14.1 - express: 4.21.2 - graphql: 16.10.0 - graphql-scalars: 1.24.2(graphql@16.10.0) - graphql-yoga: 5.13.2(graphql@16.10.0) - groq-sdk: 0.5.0 - langchain: 0.3.19(@langchain/core@0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1)))(axios@1.13.2)(openai@4.87.4(ws@8.18.3)(zod@3.24.1))(ws@8.18.3) - openai: 4.87.4(ws@8.18.3)(zod@3.24.1) - partial-json: 0.1.7 - pino: 9.6.0 - pino-pretty: 11.3.0 - reflect-metadata: 0.2.2 - rxjs: 7.8.2 - type-graphql: 2.0.0-rc.1(class-validator@0.14.1)(graphql-scalars@1.24.2(graphql@16.10.0))(graphql@16.10.0) - zod: 3.24.1 - transitivePeerDependencies: - - '@arcjet/redact' - - '@aws-crypto/sha256-js' - - '@aws-sdk/client-bedrock-agent-runtime' - - '@aws-sdk/client-bedrock-runtime' - - '@aws-sdk/client-dynamodb' - - '@aws-sdk/client-kendra' - - '@aws-sdk/client-lambda' - - '@aws-sdk/client-s3' - - '@aws-sdk/client-sagemaker-runtime' - - '@aws-sdk/client-sfn' - - '@aws-sdk/credential-provider-node' - - '@aws-sdk/dsql-signer' - - '@azure/search-documents' - - '@azure/storage-blob' - - '@browserbasehq/sdk' - - '@browserbasehq/stagehand' - - '@clickhouse/client' - - '@cloudflare/ai' - - '@datastax/astra-db-ts' - - '@elastic/elasticsearch' - - '@getmetal/metal-sdk' - - '@getzep/zep-cloud' - - '@getzep/zep-js' - - '@gomomento/sdk' - - '@gomomento/sdk-core' - - '@google-ai/generativelanguage' - - '@google-cloud/storage' - - '@gradientai/nodejs-sdk' - - '@huggingface/inference' - - '@huggingface/transformers' - - '@ibm-cloud/watsonx-ai' - - '@lancedb/lancedb' - - '@langchain/anthropic' - - '@langchain/aws' - - '@langchain/cerebras' - - '@langchain/cohere' - - '@langchain/deepseek' - - '@langchain/google-genai' - - '@langchain/google-vertexai' - - '@langchain/google-vertexai-web' - - '@langchain/groq' - - '@langchain/mistralai' - - '@langchain/ollama' - - '@langchain/xai' - - '@layerup/layerup-security' - - '@libsql/client' - - '@mendable/firecrawl-js' - - '@mlc-ai/web-llm' - - '@mozilla/readability' - - '@neondatabase/serverless' - - '@notionhq/client' - - '@opensearch-project/opensearch' - - '@pinecone-database/pinecone' - - '@planetscale/database' - - '@premai/prem-sdk' - - '@qdrant/js-client-rest' - - '@raycast/api' - - '@rockset/client' - - '@smithy/eventstream-codec' - - '@smithy/protocol-http' - - '@smithy/signature-v4' - - '@smithy/util-utf8' - - '@spider-cloud/spider-client' - - '@supabase/supabase-js' - - '@tensorflow-models/universal-sentence-encoder' - - '@tensorflow/tfjs-converter' - - '@tensorflow/tfjs-core' - - '@upstash/ratelimit' - - '@upstash/redis' - - '@upstash/vector' - - '@vercel/kv' - - '@vercel/postgres' - - '@writerai/writer-sdk' - - '@xata.io/client' - - '@zilliz/milvus2-sdk-node' - - apify-client - - assemblyai - - axios - - better-sqlite3 - - cassandra-driver - - cborg - - cheerio - - chromadb - - closevector-common - - closevector-node - - closevector-web - - cohere-ai - - convex - - crypto-js - - d3-dsv - - discord.js - - dria - - duck-duck-scrape - - encoding - - epub2 - - fast-xml-parser - - firebase-admin - - google-auth-library - - googleapis - - handlebars - - hnswlib-node - - html-to-text - - ibm-cloud-sdk-core - - ignore - - interface-datastore - - ioredis - - it-all - - jsdom - - jsonwebtoken - - llmonitor - - lodash - - lunary - - mammoth - - mariadb - - mongodb - - mysql2 - - neo4j-driver - - notion-to-md - - officeparser - - pdf-parse - - peggy - - pg - - pg-copy-streams - - pickleparser - - playwright - - portkey-ai - - puppeteer - - pyodide - - redis - - replicate - - sonix-speech-recognition - - srt-parser-2 - - supports-color - - typeorm - - typesense - - usearch - - voy-search - - weaviate-ts-client - - web-auth-library - - word-extractor - - ws - - youtubei.js - - '@copilotkit/shared@1.8.8': - dependencies: - '@segment/analytics-node': 2.2.1 - chalk: 4.1.2 - graphql: 16.10.0 - uuid: 10.0.0 - zod: 3.24.1 - zod-to-json-schema: 3.24.4(zod@3.24.1) - transitivePeerDependencies: - - encoding - - '@emnapi/runtime@1.7.1': - dependencies: - tslib: 2.8.1 - optional: true - - '@envelop/core@5.2.3': - dependencies: - '@envelop/instrumentation': 1.0.0 - '@envelop/types': 5.2.1 - '@whatwg-node/promise-helpers': 1.3.0 - tslib: 2.8.1 - - '@envelop/instrumentation@1.0.0': - dependencies: - '@whatwg-node/promise-helpers': 1.3.0 - tslib: 2.8.1 - - '@envelop/types@5.2.1': - dependencies: - '@whatwg-node/promise-helpers': 1.3.0 - tslib: 2.8.1 - - '@eslint-community/eslint-utils@4.4.1(eslint@9.18.0(jiti@1.21.7))': - dependencies: - eslint: 9.18.0(jiti@1.21.7) - eslint-visitor-keys: 3.4.3 - - '@eslint-community/regexpp@4.12.1': {} - - '@eslint/config-array@0.19.1': - dependencies: - '@eslint/object-schema': 2.1.5 - debug: 4.4.0 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - - '@eslint/core@0.10.0': - dependencies: - '@types/json-schema': 7.0.15 - - '@eslint/eslintrc@3.2.0': - dependencies: - ajv: 6.12.6 - debug: 4.4.0 - espree: 10.3.0 - globals: 14.0.0 - ignore: 5.3.2 - import-fresh: 3.3.0 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - - '@eslint/js@9.18.0': {} - - '@eslint/object-schema@2.1.5': {} - - '@eslint/plugin-kit@0.2.5': - dependencies: - '@eslint/core': 0.10.0 - levn: 0.4.1 - - '@floating-ui/core@1.6.9': - dependencies: - '@floating-ui/utils': 0.2.9 - - '@floating-ui/dom@1.6.13': - dependencies: - '@floating-ui/core': 1.6.9 - '@floating-ui/utils': 0.2.9 - - '@floating-ui/react-dom@2.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@floating-ui/dom': 1.6.13 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - - '@floating-ui/react@0.26.28(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@floating-ui/react-dom': 2.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@floating-ui/utils': 0.2.9 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - tabbable: 6.2.0 - - '@floating-ui/utils@0.2.9': {} - - '@graphql-tools/executor@1.4.6(graphql@16.10.0)': - dependencies: - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - '@graphql-typed-document-node/core': 3.2.0(graphql@16.10.0) - '@repeaterjs/repeater': 3.0.6 - '@whatwg-node/disposablestack': 0.0.6 - '@whatwg-node/promise-helpers': 1.3.0 - graphql: 16.10.0 - tslib: 2.8.1 - - '@graphql-tools/merge@9.0.24(graphql@16.10.0)': - dependencies: - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - graphql: 16.10.0 - tslib: 2.8.1 - - '@graphql-tools/schema@10.0.23(graphql@16.10.0)': - dependencies: - '@graphql-tools/merge': 9.0.24(graphql@16.10.0) - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - graphql: 16.10.0 - tslib: 2.8.1 - - '@graphql-tools/utils@10.8.6(graphql@16.10.0)': - dependencies: - '@graphql-typed-document-node/core': 3.2.0(graphql@16.10.0) - '@whatwg-node/promise-helpers': 1.3.0 - cross-inspect: 1.0.1 - dset: 3.1.4 - graphql: 16.10.0 - tslib: 2.8.1 - - '@graphql-typed-document-node/core@3.2.0(graphql@16.10.0)': - dependencies: - graphql: 16.10.0 - - '@graphql-yoga/logger@2.0.1': - dependencies: - tslib: 2.8.1 - - '@graphql-yoga/plugin-defer-stream@3.13.2(graphql-yoga@5.13.2(graphql@16.10.0))(graphql@16.10.0)': - dependencies: - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - graphql: 16.10.0 - graphql-yoga: 5.13.2(graphql@16.10.0) - - '@graphql-yoga/subscription@5.0.3': - dependencies: - '@graphql-yoga/typed-event-target': 3.0.2 - '@repeaterjs/repeater': 3.0.6 - '@whatwg-node/events': 0.1.2 - tslib: 2.8.1 - - '@graphql-yoga/typed-event-target@3.0.2': - dependencies: - '@repeaterjs/repeater': 3.0.6 - tslib: 2.8.1 - - '@headlessui/react@2.2.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@floating-ui/react': 0.26.28(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@react-aria/focus': 3.20.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@react-aria/interactions': 3.24.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@tanstack/react-virtual': 3.13.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - - '@hono/node-server@1.19.7(hono@4.11.1)': - dependencies: - hono: 4.11.1 - - '@humanfs/core@0.19.1': {} - - '@humanfs/node@0.16.6': - dependencies: - '@humanfs/core': 0.19.1 - '@humanwhocodes/retry': 0.3.1 - - '@humanwhocodes/module-importer@1.0.1': {} - - '@humanwhocodes/retry@0.3.1': {} - - '@humanwhocodes/retry@0.4.1': {} - - '@ibm-cloud/watsonx-ai@1.6.0(@langchain/core@0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1)))': - dependencies: - '@langchain/textsplitters': 0.1.0(@langchain/core@0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1))) - '@types/node': 18.19.130 - extend: 3.0.2 - ibm-cloud-sdk-core: 5.3.2 - transitivePeerDependencies: - - '@langchain/core' - - supports-color - - '@img/colour@1.0.0': - optional: true - - '@img/sharp-darwin-arm64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.2.4 - optional: true - - '@img/sharp-darwin-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.2.4 - optional: true - - '@img/sharp-libvips-darwin-arm64@1.2.4': - optional: true - - '@img/sharp-libvips-darwin-x64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-arm64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-arm@1.2.4': - optional: true - - '@img/sharp-libvips-linux-ppc64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-riscv64@1.2.4': - optional: true - - '@img/sharp-libvips-linux-s390x@1.2.4': - optional: true - - '@img/sharp-libvips-linux-x64@1.2.4': - optional: true - - '@img/sharp-libvips-linuxmusl-arm64@1.2.4': - optional: true - - '@img/sharp-libvips-linuxmusl-x64@1.2.4': - optional: true - - '@img/sharp-linux-arm64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.2.4 - optional: true - - '@img/sharp-linux-arm@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.2.4 - optional: true - - '@img/sharp-linux-ppc64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.2.4 - optional: true - - '@img/sharp-linux-riscv64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-riscv64': 1.2.4 - optional: true - - '@img/sharp-linux-s390x@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.2.4 - optional: true - - '@img/sharp-linux-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.2.4 - optional: true - - '@img/sharp-linuxmusl-arm64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 - optional: true - - '@img/sharp-linuxmusl-x64@0.34.5': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 - optional: true - - '@img/sharp-wasm32@0.34.5': - dependencies: - '@emnapi/runtime': 1.7.1 - optional: true - - '@img/sharp-win32-arm64@0.34.5': - optional: true - - '@img/sharp-win32-ia32@0.34.5': - optional: true - - '@img/sharp-win32-x64@0.34.5': - optional: true - - '@inquirer/confirm@5.1.6(@types/node@20.17.12)': - dependencies: - '@inquirer/core': 10.1.7(@types/node@20.17.12) - '@inquirer/type': 3.0.4(@types/node@20.17.12) - optionalDependencies: - '@types/node': 20.17.12 - - '@inquirer/core@10.1.7(@types/node@20.17.12)': - dependencies: - '@inquirer/figures': 1.0.10 - '@inquirer/type': 3.0.4(@types/node@20.17.12) - ansi-escapes: 4.3.2 - cli-width: 4.1.0 - mute-stream: 2.0.0 - signal-exit: 4.1.0 - wrap-ansi: 6.2.0 - yoctocolors-cjs: 2.1.2 - optionalDependencies: - '@types/node': 20.17.12 - - '@inquirer/figures@1.0.10': {} - - '@inquirer/type@3.0.4(@types/node@20.17.12)': - optionalDependencies: - '@types/node': 20.17.12 - - '@isaacs/cliui@8.0.2': - dependencies: - string-width: 5.1.2 - string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.0 - strip-ansi-cjs: strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: wrap-ansi@7.0.0 - - '@jridgewell/gen-mapping@0.3.8': - dependencies: - '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping': 0.3.25 - - '@jridgewell/resolve-uri@3.1.2': {} - - '@jridgewell/set-array@1.2.1': {} - - '@jridgewell/sourcemap-codec@1.5.0': {} - - '@jridgewell/trace-mapping@0.3.25': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 - - '@langchain/community@0.3.36(@browserbasehq/sdk@2.6.0)(@browserbasehq/stagehand@1.14.0(@playwright/test@1.51.1)(deepmerge@4.3.1)(dotenv@16.6.1)(openai@4.87.4(ws@8.18.3)(zod@3.24.1))(zod@3.24.1))(@ibm-cloud/watsonx-ai@1.6.0(@langchain/core@0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1))))(@langchain/core@0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1)))(axios@1.13.2)(google-auth-library@8.9.0)(ibm-cloud-sdk-core@5.3.2)(ignore@5.3.2)(jsonwebtoken@9.0.3)(openai@4.87.4(ws@8.18.3)(zod@3.24.1))(playwright@1.51.1)(ws@8.18.3)': - dependencies: - '@browserbasehq/stagehand': 1.14.0(@playwright/test@1.51.1)(deepmerge@4.3.1)(dotenv@16.6.1)(openai@4.87.4(ws@8.18.3)(zod@3.24.1))(zod@3.24.1) - '@ibm-cloud/watsonx-ai': 1.6.0(@langchain/core@0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1))) - '@langchain/core': 0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1)) - '@langchain/openai': 0.4.5(@langchain/core@0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1)))(ws@8.18.3) - binary-extensions: 2.3.0 - expr-eval: 2.0.2 - flat: 5.0.2 - ibm-cloud-sdk-core: 5.3.2 - js-yaml: 4.1.0 - langchain: 0.3.19(@langchain/core@0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1)))(axios@1.13.2)(openai@4.87.4(ws@8.18.3)(zod@3.24.1))(ws@8.18.3) - langsmith: 0.3.14(openai@4.87.4(ws@8.18.3)(zod@3.24.1)) - openai: 4.87.4(ws@8.18.3)(zod@3.24.1) - uuid: 10.0.0 - zod: 3.24.1 - zod-to-json-schema: 3.24.4(zod@3.24.1) - optionalDependencies: - '@browserbasehq/sdk': 2.6.0 - google-auth-library: 8.9.0 - ignore: 5.3.2 - jsonwebtoken: 9.0.3 - playwright: 1.51.1 - ws: 8.18.3 - transitivePeerDependencies: - - '@langchain/anthropic' - - '@langchain/aws' - - '@langchain/cerebras' - - '@langchain/cohere' - - '@langchain/deepseek' - - '@langchain/google-genai' - - '@langchain/google-vertexai' - - '@langchain/google-vertexai-web' - - '@langchain/groq' - - '@langchain/mistralai' - - '@langchain/ollama' - - '@langchain/xai' - - axios - - encoding - - handlebars - - peggy - - '@langchain/core@0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1))': - dependencies: - '@cfworker/json-schema': 4.1.1 - ansi-styles: 5.2.0 - camelcase: 6.3.0 - decamelize: 1.2.0 - js-tiktoken: 1.0.19 - langsmith: 0.3.14(openai@4.87.4(ws@8.18.3)(zod@3.24.1)) - mustache: 4.2.0 - p-queue: 6.6.2 - p-retry: 4.6.2 - uuid: 10.0.0 - zod: 3.24.1 - zod-to-json-schema: 3.24.4(zod@3.24.1) - transitivePeerDependencies: - - openai - - '@langchain/google-common@0.1.8(@langchain/core@0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1)))(zod@3.24.1)': - dependencies: - '@langchain/core': 0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1)) - uuid: 10.0.0 - zod-to-json-schema: 3.24.4(zod@3.24.1) - transitivePeerDependencies: - - zod - - '@langchain/google-gauth@0.1.8(@langchain/core@0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1)))(zod@3.24.1)': - dependencies: - '@langchain/core': 0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1)) - '@langchain/google-common': 0.1.8(@langchain/core@0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1)))(zod@3.24.1) - google-auth-library: 8.9.0 - transitivePeerDependencies: - - encoding - - supports-color - - zod - - '@langchain/langgraph-sdk@0.0.36': - dependencies: - '@types/json-schema': 7.0.15 - p-queue: 6.6.2 - p-retry: 4.6.2 - uuid: 9.0.1 - - '@langchain/openai@0.4.5(@langchain/core@0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1)))(ws@8.18.3)': - dependencies: - '@langchain/core': 0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1)) - js-tiktoken: 1.0.19 - openai: 4.87.4(ws@8.18.3)(zod@3.24.1) - zod: 3.24.1 - zod-to-json-schema: 3.24.4(zod@3.24.1) - transitivePeerDependencies: - - encoding - - ws - - '@langchain/textsplitters@0.1.0(@langchain/core@0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1)))': - dependencies: - '@langchain/core': 0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1)) - js-tiktoken: 1.0.19 - - '@lukeed/csprng@1.1.0': {} - - '@lukeed/uuid@2.0.1': - dependencies: - '@lukeed/csprng': 1.1.0 - - '@modelcontextprotocol/sdk@1.25.1(@cfworker/json-schema@4.1.1)(hono@4.11.1)(zod@3.24.1)': - dependencies: - '@hono/node-server': 1.19.7(hono@4.11.1) - ajv: 8.17.1 - ajv-formats: 3.0.1(ajv@8.17.1) - content-type: 1.0.5 - cors: 2.8.5 - cross-spawn: 7.0.6 - eventsource: 3.0.6 - eventsource-parser: 3.0.1 - express: 5.1.0 - express-rate-limit: 7.5.0(express@5.1.0) - jose: 6.1.3 - json-schema-typed: 8.0.2 - pkce-challenge: 5.0.0 - raw-body: 3.0.0 - zod: 3.24.1 - zod-to-json-schema: 3.25.0(zod@3.24.1) - optionalDependencies: - '@cfworker/json-schema': 4.1.1 - transitivePeerDependencies: - - hono - - supports-color - - '@mswjs/interceptors@0.37.6': - dependencies: - '@open-draft/deferred-promise': 2.2.0 - '@open-draft/logger': 0.3.0 - '@open-draft/until': 2.1.0 - is-node-process: 1.2.0 - outvariant: 1.4.3 - strict-event-emitter: 0.5.1 - - '@next/env@15.4.10': {} - - '@next/eslint-plugin-next@15.1.4': - dependencies: - fast-glob: 3.3.1 - - '@next/swc-darwin-arm64@15.4.8': - optional: true - - '@next/swc-darwin-x64@15.4.8': - optional: true - - '@next/swc-linux-arm64-gnu@15.4.8': - optional: true - - '@next/swc-linux-arm64-musl@15.4.8': - optional: true - - '@next/swc-linux-x64-gnu@15.4.8': - optional: true - - '@next/swc-linux-x64-musl@15.4.8': - optional: true - - '@next/swc-win32-arm64-msvc@15.4.8': - optional: true - - '@next/swc-win32-x64-msvc@15.4.8': - optional: true - - '@nodelib/fs.scandir@2.1.5': - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - - '@nodelib/fs.stat@2.0.5': {} - - '@nodelib/fs.walk@1.2.8': - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.18.0 - - '@nolyfill/is-core-module@1.0.39': {} - - '@open-draft/deferred-promise@2.2.0': {} - - '@open-draft/logger@0.3.0': - dependencies: - is-node-process: 1.2.0 - outvariant: 1.4.3 - - '@open-draft/until@2.1.0': {} - - '@opentelemetry/api@1.9.0': - optional: true - - '@pkgjs/parseargs@0.11.0': - optional: true - - '@playwright/test@1.51.1': - dependencies: - playwright: 1.51.1 - - '@radix-ui/primitive@1.1.2': {} - - '@radix-ui/react-arrow@1.1.3(@types/react-dom@19.0.3(@types/react@19.0.6))(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@radix-ui/react-primitive': 2.0.3(@types/react-dom@19.0.3(@types/react@19.0.6))(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - optionalDependencies: - '@types/react': 19.0.6 - '@types/react-dom': 19.0.3(@types/react@19.0.6) - - '@radix-ui/react-compose-refs@1.1.1(@types/react@19.0.6)(react@19.0.0)': - dependencies: - react: 19.0.0 - optionalDependencies: - '@types/react': 19.0.6 - - '@radix-ui/react-compose-refs@1.1.2(@types/react@19.0.6)(react@19.0.0)': - dependencies: - react: 19.0.0 - optionalDependencies: - '@types/react': 19.0.6 - - '@radix-ui/react-context@1.1.2(@types/react@19.0.6)(react@19.0.0)': - dependencies: - react: 19.0.0 - optionalDependencies: - '@types/react': 19.0.6 - - '@radix-ui/react-dismissable-layer@1.1.6(@types/react-dom@19.0.3(@types/react@19.0.6))(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.6)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.3(@types/react-dom@19.0.3(@types/react@19.0.6))(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.6)(react@19.0.0) - '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.0.6)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - optionalDependencies: - '@types/react': 19.0.6 - '@types/react-dom': 19.0.3(@types/react@19.0.6) - - '@radix-ui/react-id@1.1.1(@types/react@19.0.6)(react@19.0.0)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.6)(react@19.0.0) - react: 19.0.0 - optionalDependencies: - '@types/react': 19.0.6 - - '@radix-ui/react-label@2.1.1(@types/react-dom@19.0.3(@types/react@19.0.6))(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.3(@types/react@19.0.6))(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - optionalDependencies: - '@types/react': 19.0.6 - '@types/react-dom': 19.0.3(@types/react@19.0.6) - - '@radix-ui/react-popper@1.2.3(@types/react-dom@19.0.3(@types/react@19.0.6))(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@floating-ui/react-dom': 2.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-arrow': 1.1.3(@types/react-dom@19.0.3(@types/react@19.0.6))(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.6)(react@19.0.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.0.6)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.3(@types/react-dom@19.0.3(@types/react@19.0.6))(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.6)(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.6)(react@19.0.0) - '@radix-ui/react-use-rect': 1.1.1(@types/react@19.0.6)(react@19.0.0) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.0.6)(react@19.0.0) - '@radix-ui/rect': 1.1.1 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - optionalDependencies: - '@types/react': 19.0.6 - '@types/react-dom': 19.0.3(@types/react@19.0.6) - - '@radix-ui/react-portal@1.1.5(@types/react-dom@19.0.3(@types/react@19.0.6))(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@radix-ui/react-primitive': 2.0.3(@types/react-dom@19.0.3(@types/react@19.0.6))(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.6)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - optionalDependencies: - '@types/react': 19.0.6 - '@types/react-dom': 19.0.3(@types/react@19.0.6) - - '@radix-ui/react-presence@1.1.3(@types/react-dom@19.0.3(@types/react@19.0.6))(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.6)(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.6)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - optionalDependencies: - '@types/react': 19.0.6 - '@types/react-dom': 19.0.3(@types/react@19.0.6) - - '@radix-ui/react-primitive@2.0.1(@types/react-dom@19.0.3(@types/react@19.0.6))(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@radix-ui/react-slot': 1.1.1(@types/react@19.0.6)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - optionalDependencies: - '@types/react': 19.0.6 - '@types/react-dom': 19.0.3(@types/react@19.0.6) - - '@radix-ui/react-primitive@2.0.3(@types/react-dom@19.0.3(@types/react@19.0.6))(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@radix-ui/react-slot': 1.2.0(@types/react@19.0.6)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - optionalDependencies: - '@types/react': 19.0.6 - '@types/react-dom': 19.0.3(@types/react@19.0.6) - - '@radix-ui/react-slot@1.1.1(@types/react@19.0.6)(react@19.0.0)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.6)(react@19.0.0) - react: 19.0.0 - optionalDependencies: - '@types/react': 19.0.6 - - '@radix-ui/react-slot@1.2.0(@types/react@19.0.6)(react@19.0.0)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.6)(react@19.0.0) - react: 19.0.0 - optionalDependencies: - '@types/react': 19.0.6 - - '@radix-ui/react-tooltip@1.2.0(@types/react-dom@19.0.3(@types/react@19.0.6))(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@radix-ui/primitive': 1.1.2 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.6)(react@19.0.0) - '@radix-ui/react-context': 1.1.2(@types/react@19.0.6)(react@19.0.0) - '@radix-ui/react-dismissable-layer': 1.1.6(@types/react-dom@19.0.3(@types/react@19.0.6))(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.0.6)(react@19.0.0) - '@radix-ui/react-popper': 1.2.3(@types/react-dom@19.0.3(@types/react@19.0.6))(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-portal': 1.1.5(@types/react-dom@19.0.3(@types/react@19.0.6))(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-presence': 1.1.3(@types/react-dom@19.0.3(@types/react@19.0.6))(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.3(@types/react-dom@19.0.3(@types/react@19.0.6))(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-slot': 1.2.0(@types/react@19.0.6)(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.1(@types/react@19.0.6)(react@19.0.0) - '@radix-ui/react-visually-hidden': 1.1.3(@types/react-dom@19.0.3(@types/react@19.0.6))(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - optionalDependencies: - '@types/react': 19.0.6 - '@types/react-dom': 19.0.3(@types/react@19.0.6) - - '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.0.6)(react@19.0.0)': - dependencies: - react: 19.0.0 - optionalDependencies: - '@types/react': 19.0.6 - - '@radix-ui/react-use-controllable-state@1.1.1(@types/react@19.0.6)(react@19.0.0)': - dependencies: - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.6)(react@19.0.0) - react: 19.0.0 - optionalDependencies: - '@types/react': 19.0.6 - - '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.0.6)(react@19.0.0)': - dependencies: - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.0.6)(react@19.0.0) - react: 19.0.0 - optionalDependencies: - '@types/react': 19.0.6 - - '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.0.6)(react@19.0.0)': - dependencies: - react: 19.0.0 - optionalDependencies: - '@types/react': 19.0.6 - - '@radix-ui/react-use-rect@1.1.1(@types/react@19.0.6)(react@19.0.0)': - dependencies: - '@radix-ui/rect': 1.1.1 - react: 19.0.0 - optionalDependencies: - '@types/react': 19.0.6 - - '@radix-ui/react-use-size@1.1.1(@types/react@19.0.6)(react@19.0.0)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.6)(react@19.0.0) - react: 19.0.0 - optionalDependencies: - '@types/react': 19.0.6 - - '@radix-ui/react-visually-hidden@1.1.3(@types/react-dom@19.0.3(@types/react@19.0.6))(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@radix-ui/react-primitive': 2.0.3(@types/react-dom@19.0.3(@types/react@19.0.6))(@types/react@19.0.6)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - optionalDependencies: - '@types/react': 19.0.6 - '@types/react-dom': 19.0.3(@types/react@19.0.6) - - '@radix-ui/rect@1.1.1': {} - - '@react-aria/focus@3.20.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@react-aria/interactions': 3.24.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@react-aria/utils': 3.28.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@react-types/shared': 3.28.0(react@19.0.0) - '@swc/helpers': 0.5.15 - clsx: 2.1.1 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - - '@react-aria/interactions@3.24.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@react-aria/ssr': 3.9.7(react@19.0.0) - '@react-aria/utils': 3.28.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@react-stately/flags': 3.1.0 - '@react-types/shared': 3.28.0(react@19.0.0) - '@swc/helpers': 0.5.15 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - - '@react-aria/ssr@3.9.7(react@19.0.0)': - dependencies: - '@swc/helpers': 0.5.15 - react: 19.0.0 - - '@react-aria/utils@3.28.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@react-aria/ssr': 3.9.7(react@19.0.0) - '@react-stately/flags': 3.1.0 - '@react-stately/utils': 3.10.5(react@19.0.0) - '@react-types/shared': 3.28.0(react@19.0.0) - '@swc/helpers': 0.5.15 - clsx: 2.1.1 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - - '@react-stately/flags@3.1.0': - dependencies: - '@swc/helpers': 0.5.15 - - '@react-stately/utils@3.10.5(react@19.0.0)': - dependencies: - '@swc/helpers': 0.5.15 - react: 19.0.0 - - '@react-types/shared@3.28.0(react@19.0.0)': - dependencies: - react: 19.0.0 - - '@repeaterjs/repeater@3.0.6': {} - - '@rtsao/scc@1.1.0': {} - - '@rushstack/eslint-patch@1.10.5': {} - - '@scarf/scarf@1.4.0': {} - - '@segment/analytics-core@1.8.1': - dependencies: - '@lukeed/uuid': 2.0.1 - '@segment/analytics-generic-utils': 1.2.0 - dset: 3.1.4 - tslib: 2.8.1 - - '@segment/analytics-generic-utils@1.2.0': - dependencies: - tslib: 2.8.1 - - '@segment/analytics-node@2.2.1': - dependencies: - '@lukeed/uuid': 2.0.1 - '@segment/analytics-core': 1.8.1 - '@segment/analytics-generic-utils': 1.2.0 - buffer: 6.0.3 - jose: 5.10.0 - node-fetch: 2.7.0 - tslib: 2.8.1 - transitivePeerDependencies: - - encoding - - '@swc/helpers@0.5.15': - dependencies: - tslib: 2.8.1 - - '@tanstack/query-core@5.64.1': {} - - '@tanstack/react-query@5.64.1(react@19.0.0)': - dependencies: - '@tanstack/query-core': 5.64.1 - react: 19.0.0 - - '@tanstack/react-virtual@3.13.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@tanstack/virtual-core': 3.13.4 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - - '@tanstack/virtual-core@3.13.4': {} - - '@tokenizer/token@0.3.0': {} - - '@ts-morph/common@0.19.0': - dependencies: - fast-glob: 3.3.3 - minimatch: 7.4.6 - mkdirp: 2.1.6 - path-browserify: 1.0.1 - - '@types/cookie@0.6.0': {} - - '@types/debug@4.1.12': - dependencies: - '@types/ms': 2.1.0 - - '@types/estree@1.0.6': {} - - '@types/hast@2.3.10': - dependencies: - '@types/unist': 2.0.11 - - '@types/json-schema@7.0.15': {} - - '@types/json5@0.0.29': {} - - '@types/katex@0.16.7': {} - - '@types/mdast@3.0.15': - dependencies: - '@types/unist': 2.0.11 - - '@types/ms@2.1.0': {} - - '@types/node-fetch@2.6.12': - dependencies: - '@types/node': 20.17.12 - form-data: 4.0.2 - - '@types/node-fetch@2.6.13': - dependencies: - '@types/node': 20.19.27 - form-data: 4.0.5 - - '@types/node@18.19.130': - dependencies: - undici-types: 5.26.5 - - '@types/node@18.19.80': - dependencies: - undici-types: 5.26.5 - - '@types/node@20.17.12': - dependencies: - undici-types: 6.19.8 - - '@types/node@20.19.27': - dependencies: - undici-types: 6.21.0 - - '@types/prop-types@15.7.14': {} - - '@types/react-dom@19.0.3(@types/react@19.0.6)': - dependencies: - '@types/react': 19.0.6 - - '@types/react@19.0.6': - dependencies: - csstype: 3.1.3 - - '@types/retry@0.12.0': {} - - '@types/semver@7.5.8': {} - - '@types/statuses@2.0.5': {} - - '@types/tough-cookie@4.0.5': {} - - '@types/unist@2.0.11': {} - - '@types/uuid@10.0.0': {} - - '@types/validator@13.12.2': {} - - '@typescript-eslint/eslint-plugin@8.19.1(@typescript-eslint/parser@8.19.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3))(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3)': - dependencies: - '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.19.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3) - '@typescript-eslint/scope-manager': 8.19.1 - '@typescript-eslint/type-utils': 8.19.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3) - '@typescript-eslint/utils': 8.19.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3) - '@typescript-eslint/visitor-keys': 8.19.1 - eslint: 9.18.0(jiti@1.21.7) - graphemer: 1.4.0 - ignore: 5.3.2 - natural-compare: 1.4.0 - ts-api-utils: 2.0.0(typescript@5.7.3) - typescript: 5.7.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/parser@8.19.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3)': - dependencies: - '@typescript-eslint/scope-manager': 8.19.1 - '@typescript-eslint/types': 8.19.1 - '@typescript-eslint/typescript-estree': 8.19.1(typescript@5.7.3) - '@typescript-eslint/visitor-keys': 8.19.1 - debug: 4.4.0 - eslint: 9.18.0(jiti@1.21.7) - typescript: 5.7.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/scope-manager@8.19.1': - dependencies: - '@typescript-eslint/types': 8.19.1 - '@typescript-eslint/visitor-keys': 8.19.1 - - '@typescript-eslint/type-utils@8.19.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3)': - dependencies: - '@typescript-eslint/typescript-estree': 8.19.1(typescript@5.7.3) - '@typescript-eslint/utils': 8.19.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3) - debug: 4.4.0 - eslint: 9.18.0(jiti@1.21.7) - ts-api-utils: 2.0.0(typescript@5.7.3) - typescript: 5.7.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/types@8.19.1': {} - - '@typescript-eslint/typescript-estree@8.19.1(typescript@5.7.3)': - dependencies: - '@typescript-eslint/types': 8.19.1 - '@typescript-eslint/visitor-keys': 8.19.1 - debug: 4.4.0 - fast-glob: 3.3.3 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.6.3 - ts-api-utils: 2.0.0(typescript@5.7.3) - typescript: 5.7.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/utils@8.19.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3)': - dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.18.0(jiti@1.21.7)) - '@typescript-eslint/scope-manager': 8.19.1 - '@typescript-eslint/types': 8.19.1 - '@typescript-eslint/typescript-estree': 8.19.1(typescript@5.7.3) - eslint: 9.18.0(jiti@1.21.7) - typescript: 5.7.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/visitor-keys@8.19.1': - dependencies: - '@typescript-eslint/types': 8.19.1 - eslint-visitor-keys: 4.2.0 - - '@urql/core@5.1.1(graphql@16.10.0)': - dependencies: - '@0no-co/graphql.web': 1.1.2(graphql@16.10.0) - wonka: 6.3.5 - transitivePeerDependencies: - - graphql - - '@whatwg-node/disposablestack@0.0.6': - dependencies: - '@whatwg-node/promise-helpers': 1.3.0 - tslib: 2.8.1 - - '@whatwg-node/events@0.1.2': - dependencies: - tslib: 2.8.1 - - '@whatwg-node/fetch@0.10.5': - dependencies: - '@whatwg-node/node-fetch': 0.7.14 - urlpattern-polyfill: 10.0.0 - - '@whatwg-node/node-fetch@0.7.14': - dependencies: - '@whatwg-node/disposablestack': 0.0.6 - '@whatwg-node/promise-helpers': 1.3.0 - busboy: 1.6.0 - tslib: 2.8.1 - - '@whatwg-node/promise-helpers@1.3.0': - dependencies: - tslib: 2.8.1 - - '@whatwg-node/server@0.10.1': - dependencies: - '@envelop/instrumentation': 1.0.0 - '@whatwg-node/disposablestack': 0.0.6 - '@whatwg-node/fetch': 0.10.5 - '@whatwg-node/promise-helpers': 1.3.0 - tslib: 2.8.1 - - abort-controller@3.0.0: - dependencies: - event-target-shim: 5.0.1 - - accepts@1.3.8: - dependencies: - mime-types: 2.1.35 - negotiator: 0.6.3 - - accepts@2.0.0: - dependencies: - mime-types: 3.0.1 - negotiator: 1.0.0 - - acorn-jsx@5.3.2(acorn@8.14.0): - dependencies: - acorn: 8.14.0 - - acorn@8.14.0: {} - - agent-base@6.0.2: - dependencies: - debug: 4.4.0 - transitivePeerDependencies: - - supports-color - - agent-base@7.1.3: {} - - agentkeepalive@4.6.0: - dependencies: - humanize-ms: 1.2.1 - - ajv-formats@3.0.1(ajv@8.17.1): - optionalDependencies: - ajv: 8.17.1 - - ajv@6.12.6: - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - - ajv@8.17.1: - dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.1.0 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - - ansi-escapes@4.3.2: - dependencies: - type-fest: 0.21.3 - - ansi-regex@5.0.1: {} - - ansi-regex@6.1.0: {} - - ansi-styles@4.3.0: - dependencies: - color-convert: 2.0.1 - - ansi-styles@5.2.0: {} - - ansi-styles@6.2.1: {} - - any-promise@1.3.0: {} - - anymatch@3.1.3: - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 - - arg@5.0.2: {} - - argparse@2.0.1: {} - - aria-query@5.3.2: {} - - array-buffer-byte-length@1.0.2: - dependencies: - call-bound: 1.0.3 - is-array-buffer: 3.0.5 - - array-flatten@1.1.1: {} - - array-includes@3.1.8: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-object-atoms: 1.0.0 - get-intrinsic: 1.2.7 - is-string: 1.1.1 - - array.prototype.findlast@1.2.5: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-errors: 1.3.0 - es-object-atoms: 1.0.0 - es-shim-unscopables: 1.0.2 - - array.prototype.findlastindex@1.2.5: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-errors: 1.3.0 - es-object-atoms: 1.0.0 - es-shim-unscopables: 1.0.2 - - array.prototype.flat@1.3.3: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-shim-unscopables: 1.0.2 - - array.prototype.flatmap@1.3.3: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-shim-unscopables: 1.0.2 - - array.prototype.tosorted@1.1.4: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-errors: 1.3.0 - es-shim-unscopables: 1.0.2 - - arraybuffer.prototype.slice@1.0.4: - dependencies: - array-buffer-byte-length: 1.0.2 - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-errors: 1.3.0 - get-intrinsic: 1.2.7 - is-array-buffer: 3.0.5 - - arrify@2.0.1: {} - - ast-types-flow@0.0.8: {} - - ast-types@0.16.1: - dependencies: - tslib: 2.8.1 - - asynckit@0.4.0: {} - - atomic-sleep@1.0.0: {} - - available-typed-arrays@1.0.7: - dependencies: - possible-typed-array-names: 1.0.0 - - axe-core@4.10.2: {} - - axios@1.13.2(debug@4.4.3): - dependencies: - follow-redirects: 1.15.11(debug@4.4.3) - form-data: 4.0.5 - proxy-from-env: 1.1.0 - transitivePeerDependencies: - - debug - - axobject-query@4.1.0: {} - - bail@2.0.2: {} - - balanced-match@1.0.2: {} - - base64-js@1.5.1: {} - - bignumber.js@9.1.2: {} - - binary-extensions@2.3.0: {} - - bl@5.1.0: - dependencies: - buffer: 6.0.3 - inherits: 2.0.4 - readable-stream: 3.6.2 - - body-parser@1.20.3: - dependencies: - bytes: 3.1.2 - content-type: 1.0.5 - debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - on-finished: 2.4.1 - qs: 6.13.0 - raw-body: 2.5.2 - type-is: 1.6.18 - unpipe: 1.0.0 - transitivePeerDependencies: - - supports-color - - body-parser@2.2.0: - dependencies: - bytes: 3.1.2 - content-type: 1.0.5 - debug: 4.4.0 - http-errors: 2.0.0 - iconv-lite: 0.6.3 - on-finished: 2.4.1 - qs: 6.14.0 - raw-body: 3.0.0 - type-is: 2.0.1 - transitivePeerDependencies: - - supports-color - - brace-expansion@1.1.11: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - - brace-expansion@2.0.1: - dependencies: - balanced-match: 1.0.2 - - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - - browserslist@4.24.4: - dependencies: - caniuse-lite: 1.0.30001692 - electron-to-chromium: 1.5.80 - node-releases: 2.0.19 - update-browserslist-db: 1.1.2(browserslist@4.24.4) - - buffer-equal-constant-time@1.0.1: {} - - buffer@6.0.3: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - - busboy@1.6.0: - dependencies: - streamsearch: 1.1.0 - - bytes@3.1.2: {} - - call-bind-apply-helpers@1.0.1: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - - call-bind@1.0.8: - dependencies: - call-bind-apply-helpers: 1.0.1 - es-define-property: 1.0.1 - get-intrinsic: 1.2.7 - set-function-length: 1.2.2 - - call-bound@1.0.3: - dependencies: - call-bind-apply-helpers: 1.0.1 - get-intrinsic: 1.2.7 - - callsites@3.1.0: {} - - camelcase-css@2.0.1: {} - - camelcase@6.3.0: {} - - caniuse-lite@1.0.30001692: {} - - caniuse-lite@1.0.30001760: {} - - ccount@2.0.1: {} - - chalk@4.1.2: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - - chalk@5.4.1: {} - - character-entities-legacy@1.1.4: {} - - character-entities@1.2.4: {} - - character-entities@2.0.2: {} - - character-reference-invalid@1.1.4: {} - - chokidar@3.6.0: - dependencies: - anymatch: 3.1.3 - braces: 3.0.3 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 - - class-transformer@0.5.1: {} - - class-validator@0.14.1: - dependencies: - '@types/validator': 13.12.2 - libphonenumber-js: 1.12.6 - validator: 13.12.0 - - class-variance-authority@0.7.1: - dependencies: - clsx: 2.1.1 - - cli-cursor@4.0.0: - dependencies: - restore-cursor: 4.0.0 - - cli-spinners@2.9.2: {} - - cli-width@4.1.0: {} - - client-only@0.0.1: {} - - cliui@8.0.1: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - - clone@1.0.4: {} - - clsx@2.1.1: {} - - code-block-writer@12.0.0: {} - - color-convert@2.0.1: - dependencies: - color-name: 1.1.4 - - color-name@1.1.4: {} - - colorette@2.0.20: {} - - combined-stream@1.0.8: - dependencies: - delayed-stream: 1.0.0 - - comma-separated-tokens@1.0.8: {} - - comma-separated-tokens@2.0.3: {} - - commander@10.0.1: {} - - commander@4.1.1: {} - - commander@8.3.0: {} - - concat-map@0.0.1: {} - - console-table-printer@2.12.1: - dependencies: - simple-wcswidth: 1.0.1 - - contains@0.1.1: {} - - content-disposition@0.5.4: - dependencies: - safe-buffer: 5.2.1 - - content-disposition@1.0.0: - dependencies: - safe-buffer: 5.2.1 - - content-type@1.0.5: {} - - convert-source-map@2.0.0: {} - - cookie-signature@1.0.6: {} - - cookie-signature@1.2.2: {} - - cookie@0.7.1: {} - - cookie@0.7.2: {} - - cors@2.8.5: - dependencies: - object-assign: 4.1.1 - vary: 1.1.2 - - cosmiconfig@8.3.6(typescript@5.7.3): - dependencies: - import-fresh: 3.3.0 - js-yaml: 4.1.0 - parse-json: 5.2.0 - path-type: 4.0.0 - optionalDependencies: - typescript: 5.7.3 - - cross-inspect@1.0.1: - dependencies: - tslib: 2.8.1 - - cross-spawn@7.0.6: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - cssesc@3.0.0: {} - - csstype@3.1.3: {} - - damerau-levenshtein@1.0.8: {} - - data-uri-to-buffer@4.0.1: {} - - data-view-buffer@1.0.2: - dependencies: - call-bound: 1.0.3 - es-errors: 1.3.0 - is-data-view: 1.0.2 - - data-view-byte-length@1.0.2: - dependencies: - call-bound: 1.0.3 - es-errors: 1.3.0 - is-data-view: 1.0.2 - - data-view-byte-offset@1.0.1: - dependencies: - call-bound: 1.0.3 - es-errors: 1.3.0 - is-data-view: 1.0.2 - - dateformat@4.6.3: {} - - debug@2.6.9: - dependencies: - ms: 2.0.0 - - debug@3.2.7: - dependencies: - ms: 2.1.3 - - debug@4.4.0: - dependencies: - ms: 2.1.3 - - debug@4.4.3: - dependencies: - ms: 2.1.3 - - decamelize@1.2.0: {} - - decode-named-character-reference@1.1.0: - dependencies: - character-entities: 2.0.2 - - deep-is@0.1.4: {} - - deepmerge@4.3.1: {} - - defaults@1.0.4: - dependencies: - clone: 1.0.4 - - define-data-property@1.1.4: - dependencies: - es-define-property: 1.0.1 - es-errors: 1.3.0 - gopd: 1.2.0 - - define-properties@1.2.1: - dependencies: - define-data-property: 1.1.4 - has-property-descriptors: 1.0.2 - object-keys: 1.1.1 - - delayed-stream@1.0.0: {} - - depd@2.0.0: {} - - dequal@2.0.3: {} - - destroy@1.2.0: {} - - detect-libc@2.1.2: - optional: true - - didyoumean@1.2.2: {} - - diff@5.2.0: {} - - dlv@1.1.3: {} - - doctrine@2.1.0: - dependencies: - esutils: 2.0.3 - - dotenv@16.6.1: {} - - dset@3.1.4: {} - - dunder-proto@1.0.1: - dependencies: - call-bind-apply-helpers: 1.0.1 - es-errors: 1.3.0 - gopd: 1.2.0 - - eastasianwidth@0.2.0: {} - - ecdsa-sig-formatter@1.0.11: - dependencies: - safe-buffer: 5.2.1 - - ee-first@1.1.1: {} - - electron-to-chromium@1.5.80: {} - - emoji-regex@8.0.0: {} - - emoji-regex@9.2.2: {} - - encodeurl@1.0.2: {} - - encodeurl@2.0.0: {} - - end-of-stream@1.4.4: - dependencies: - once: 1.4.0 - - enhanced-resolve@5.18.0: - dependencies: - graceful-fs: 4.2.11 - tapable: 2.2.1 - - error-ex@1.3.2: - dependencies: - is-arrayish: 0.2.1 - - es-abstract@1.23.9: - dependencies: - array-buffer-byte-length: 1.0.2 - arraybuffer.prototype.slice: 1.0.4 - available-typed-arrays: 1.0.7 - call-bind: 1.0.8 - call-bound: 1.0.3 - data-view-buffer: 1.0.2 - data-view-byte-length: 1.0.2 - data-view-byte-offset: 1.0.1 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.0.0 - es-set-tostringtag: 2.1.0 - es-to-primitive: 1.3.0 - function.prototype.name: 1.1.8 - get-intrinsic: 1.2.7 - get-proto: 1.0.1 - get-symbol-description: 1.1.0 - globalthis: 1.0.4 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - has-proto: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.2 - internal-slot: 1.1.0 - is-array-buffer: 3.0.5 - is-callable: 1.2.7 - is-data-view: 1.0.2 - is-regex: 1.2.1 - is-shared-array-buffer: 1.0.4 - is-string: 1.1.1 - is-typed-array: 1.1.15 - is-weakref: 1.1.0 - math-intrinsics: 1.1.0 - object-inspect: 1.13.3 - object-keys: 1.1.1 - object.assign: 4.1.7 - own-keys: 1.0.1 - regexp.prototype.flags: 1.5.4 - safe-array-concat: 1.1.3 - safe-push-apply: 1.0.0 - safe-regex-test: 1.1.0 - set-proto: 1.0.0 - string.prototype.trim: 1.2.10 - string.prototype.trimend: 1.0.9 - string.prototype.trimstart: 1.0.8 - typed-array-buffer: 1.0.3 - typed-array-byte-length: 1.0.3 - typed-array-byte-offset: 1.0.4 - typed-array-length: 1.0.7 - unbox-primitive: 1.1.0 - which-typed-array: 1.1.18 - - es-define-property@1.0.1: {} - - es-errors@1.3.0: {} - - es-iterator-helpers@1.2.1: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.3 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-errors: 1.3.0 - es-set-tostringtag: 2.1.0 - function-bind: 1.1.2 - get-intrinsic: 1.2.7 - globalthis: 1.0.4 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - has-proto: 1.2.0 - has-symbols: 1.1.0 - internal-slot: 1.1.0 - iterator.prototype: 1.1.5 - safe-array-concat: 1.1.3 - - es-object-atoms@1.0.0: - dependencies: - es-errors: 1.3.0 - - es-set-tostringtag@2.1.0: - dependencies: - es-errors: 1.3.0 - get-intrinsic: 1.2.7 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - es-shim-unscopables@1.0.2: - dependencies: - hasown: 2.0.2 - - es-to-primitive@1.3.0: - dependencies: - is-callable: 1.2.7 - is-date-object: 1.1.0 - is-symbol: 1.1.1 - - escalade@3.2.0: {} - - escape-html@1.0.3: {} - - escape-string-regexp@4.0.0: {} - - escape-string-regexp@5.0.0: {} - - eslint-config-next@15.1.4(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3): - dependencies: - '@next/eslint-plugin-next': 15.1.4 - '@rushstack/eslint-patch': 1.10.5 - '@typescript-eslint/eslint-plugin': 8.19.1(@typescript-eslint/parser@8.19.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3))(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3) - '@typescript-eslint/parser': 8.19.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3) - eslint: 9.18.0(jiti@1.21.7) - eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@9.18.0(jiti@1.21.7)) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.19.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@9.18.0(jiti@1.21.7)) - eslint-plugin-jsx-a11y: 6.10.2(eslint@9.18.0(jiti@1.21.7)) - eslint-plugin-react: 7.37.4(eslint@9.18.0(jiti@1.21.7)) - eslint-plugin-react-hooks: 5.1.0(eslint@9.18.0(jiti@1.21.7)) - optionalDependencies: - typescript: 5.7.3 - transitivePeerDependencies: - - eslint-import-resolver-webpack - - eslint-plugin-import-x - - supports-color - - eslint-import-resolver-node@0.3.9: - dependencies: - debug: 3.2.7 - is-core-module: 2.16.1 - resolve: 1.22.10 - transitivePeerDependencies: - - supports-color - - eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0)(eslint@9.18.0(jiti@1.21.7)): - dependencies: - '@nolyfill/is-core-module': 1.0.39 - debug: 4.4.0 - enhanced-resolve: 5.18.0 - eslint: 9.18.0(jiti@1.21.7) - fast-glob: 3.3.3 - get-tsconfig: 4.8.1 - is-bun-module: 1.3.0 - is-glob: 4.0.3 - stable-hash: 0.0.4 - optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.19.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@9.18.0(jiti@1.21.7)) - transitivePeerDependencies: - - supports-color - - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.19.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.18.0(jiti@1.21.7)): - dependencies: - debug: 3.2.7 - optionalDependencies: - '@typescript-eslint/parser': 8.19.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3) - eslint: 9.18.0(jiti@1.21.7) - eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@9.18.0(jiti@1.21.7)) - transitivePeerDependencies: - - supports-color - - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.19.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3))(eslint-import-resolver-typescript@3.7.0)(eslint@9.18.0(jiti@1.21.7)): - dependencies: - '@rtsao/scc': 1.1.0 - array-includes: 3.1.8 - array.prototype.findlastindex: 1.2.5 - array.prototype.flat: 1.3.3 - array.prototype.flatmap: 1.3.3 - debug: 3.2.7 - doctrine: 2.1.0 - eslint: 9.18.0(jiti@1.21.7) - eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.19.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.18.0(jiti@1.21.7)) - hasown: 2.0.2 - is-core-module: 2.16.1 - is-glob: 4.0.3 - minimatch: 3.1.2 - object.fromentries: 2.0.8 - object.groupby: 1.0.3 - object.values: 1.2.1 - semver: 6.3.1 - string.prototype.trimend: 1.0.9 - tsconfig-paths: 3.15.0 - optionalDependencies: - '@typescript-eslint/parser': 8.19.1(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3) - transitivePeerDependencies: - - eslint-import-resolver-typescript - - eslint-import-resolver-webpack - - supports-color - - eslint-plugin-jsx-a11y@6.10.2(eslint@9.18.0(jiti@1.21.7)): - dependencies: - aria-query: 5.3.2 - array-includes: 3.1.8 - array.prototype.flatmap: 1.3.3 - ast-types-flow: 0.0.8 - axe-core: 4.10.2 - axobject-query: 4.1.0 - damerau-levenshtein: 1.0.8 - emoji-regex: 9.2.2 - eslint: 9.18.0(jiti@1.21.7) - hasown: 2.0.2 - jsx-ast-utils: 3.3.5 - language-tags: 1.0.9 - minimatch: 3.1.2 - object.fromentries: 2.0.8 - safe-regex-test: 1.1.0 - string.prototype.includes: 2.0.1 - - eslint-plugin-react-hooks@5.1.0(eslint@9.18.0(jiti@1.21.7)): - dependencies: - eslint: 9.18.0(jiti@1.21.7) - - eslint-plugin-react@7.37.4(eslint@9.18.0(jiti@1.21.7)): - dependencies: - array-includes: 3.1.8 - array.prototype.findlast: 1.2.5 - array.prototype.flatmap: 1.3.3 - array.prototype.tosorted: 1.1.4 - doctrine: 2.1.0 - es-iterator-helpers: 1.2.1 - eslint: 9.18.0(jiti@1.21.7) - estraverse: 5.3.0 - hasown: 2.0.2 - jsx-ast-utils: 3.3.5 - minimatch: 3.1.2 - object.entries: 1.1.8 - object.fromentries: 2.0.8 - object.values: 1.2.1 - prop-types: 15.8.1 - resolve: 2.0.0-next.5 - semver: 6.3.1 - string.prototype.matchall: 4.0.12 - string.prototype.repeat: 1.0.0 - - eslint-scope@8.2.0: - dependencies: - esrecurse: 4.3.0 - estraverse: 5.3.0 - - eslint-visitor-keys@3.4.3: {} - - eslint-visitor-keys@4.2.0: {} - - eslint@9.18.0(jiti@1.21.7): - dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@9.18.0(jiti@1.21.7)) - '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.19.1 - '@eslint/core': 0.10.0 - '@eslint/eslintrc': 3.2.0 - '@eslint/js': 9.18.0 - '@eslint/plugin-kit': 0.2.5 - '@humanfs/node': 0.16.6 - '@humanwhocodes/module-importer': 1.0.1 - '@humanwhocodes/retry': 0.4.1 - '@types/estree': 1.0.6 - '@types/json-schema': 7.0.15 - ajv: 6.12.6 - chalk: 4.1.2 - cross-spawn: 7.0.6 - debug: 4.4.0 - escape-string-regexp: 4.0.0 - eslint-scope: 8.2.0 - eslint-visitor-keys: 4.2.0 - espree: 10.3.0 - esquery: 1.6.0 - esutils: 2.0.3 - fast-deep-equal: 3.1.3 - file-entry-cache: 8.0.0 - find-up: 5.0.0 - glob-parent: 6.0.2 - ignore: 5.3.2 - imurmurhash: 0.1.4 - is-glob: 4.0.3 - json-stable-stringify-without-jsonify: 1.0.1 - lodash.merge: 4.6.2 - minimatch: 3.1.2 - natural-compare: 1.4.0 - optionator: 0.9.4 - optionalDependencies: - jiti: 1.21.7 - transitivePeerDependencies: - - supports-color - - espree@10.3.0: - dependencies: - acorn: 8.14.0 - acorn-jsx: 5.3.2(acorn@8.14.0) - eslint-visitor-keys: 4.2.0 - - esprima@4.0.1: {} - - esquery@1.6.0: - dependencies: - estraverse: 5.3.0 - - esrecurse@4.3.0: - dependencies: - estraverse: 5.3.0 - - estraverse@5.3.0: {} - - esutils@2.0.3: {} - - etag@1.8.1: {} - - event-target-shim@5.0.1: {} - - eventemitter3@4.0.7: {} - - events@3.3.0: {} - - eventsource-parser@3.0.1: {} - - eventsource@3.0.6: - dependencies: - eventsource-parser: 3.0.1 - - execa@7.2.0: - dependencies: - cross-spawn: 7.0.6 - get-stream: 6.0.1 - human-signals: 4.3.1 - is-stream: 3.0.0 - merge-stream: 2.0.0 - npm-run-path: 5.3.0 - onetime: 6.0.0 - signal-exit: 3.0.7 - strip-final-newline: 3.0.0 - - expr-eval@2.0.2: {} - - express-rate-limit@7.5.0(express@5.1.0): - dependencies: - express: 5.1.0 - - express@4.21.2: - dependencies: - accepts: 1.3.8 - array-flatten: 1.1.1 - body-parser: 1.20.3 - content-disposition: 0.5.4 - content-type: 1.0.5 - cookie: 0.7.1 - cookie-signature: 1.0.6 - debug: 2.6.9 - depd: 2.0.0 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - finalhandler: 1.3.1 - fresh: 0.5.2 - http-errors: 2.0.0 - merge-descriptors: 1.0.3 - methods: 1.1.2 - on-finished: 2.4.1 - parseurl: 1.3.3 - path-to-regexp: 0.1.12 - proxy-addr: 2.0.7 - qs: 6.13.0 - range-parser: 1.2.1 - safe-buffer: 5.2.1 - send: 0.19.0 - serve-static: 1.16.2 - setprototypeof: 1.2.0 - statuses: 2.0.1 - type-is: 1.6.18 - utils-merge: 1.0.1 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color - - express@5.1.0: - dependencies: - accepts: 2.0.0 - body-parser: 2.2.0 - content-disposition: 1.0.0 - content-type: 1.0.5 - cookie: 0.7.2 - cookie-signature: 1.2.2 - debug: 4.4.0 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - finalhandler: 2.1.0 - fresh: 2.0.0 - http-errors: 2.0.0 - merge-descriptors: 2.0.0 - mime-types: 3.0.1 - on-finished: 2.4.1 - once: 1.4.0 - parseurl: 1.3.3 - proxy-addr: 2.0.7 - qs: 6.14.0 - range-parser: 1.2.1 - router: 2.2.0 - send: 1.2.0 - serve-static: 2.2.0 - statuses: 2.0.1 - type-is: 2.0.1 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color - - extend@3.0.2: {} - - fast-copy@3.0.2: {} - - fast-deep-equal@3.1.3: {} - - fast-glob@3.3.1: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - - fast-glob@3.3.3: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - - fast-json-patch@3.1.1: {} - - fast-json-stable-stringify@2.1.0: {} - - fast-levenshtein@2.0.6: {} - - fast-redact@3.5.0: {} - - fast-safe-stringify@2.1.1: {} - - fast-text-encoding@1.0.6: {} - - fast-uri@3.1.0: {} - - fastq@1.18.0: - dependencies: - reusify: 1.0.4 - - fault@1.0.4: - dependencies: - format: 0.2.2 - - fetch-blob@3.2.0: - dependencies: - node-domexception: 1.0.0 - web-streams-polyfill: 3.3.3 - - file-entry-cache@8.0.0: - dependencies: - flat-cache: 4.0.1 - - file-type@16.5.4: - dependencies: - readable-web-to-node-stream: 3.0.4 - strtok3: 6.3.0 - token-types: 4.2.1 - - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 - - finalhandler@1.3.1: - dependencies: - debug: 2.6.9 - encodeurl: 2.0.0 - escape-html: 1.0.3 - on-finished: 2.4.1 - parseurl: 1.3.3 - statuses: 2.0.1 - unpipe: 1.0.0 - transitivePeerDependencies: - - supports-color - - finalhandler@2.1.0: - dependencies: - debug: 4.4.0 - encodeurl: 2.0.0 - escape-html: 1.0.3 - on-finished: 2.4.1 - parseurl: 1.3.3 - statuses: 2.0.1 - transitivePeerDependencies: - - supports-color - - find-up@5.0.0: - dependencies: - locate-path: 6.0.0 - path-exists: 4.0.0 - - flat-cache@4.0.1: - dependencies: - flatted: 3.3.2 - keyv: 4.5.4 - - flat@5.0.2: {} - - flatted@3.3.2: {} - - follow-redirects@1.15.11(debug@4.4.3): - optionalDependencies: - debug: 4.4.3 - - for-each@0.3.3: - dependencies: - is-callable: 1.2.7 - - foreground-child@3.3.0: - dependencies: - cross-spawn: 7.0.6 - signal-exit: 4.1.0 - - form-data-encoder@1.7.2: {} - - form-data@4.0.0: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.35 - - form-data@4.0.2: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - es-set-tostringtag: 2.1.0 - mime-types: 2.1.35 - - form-data@4.0.5: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - es-set-tostringtag: 2.1.0 - hasown: 2.0.2 - mime-types: 2.1.35 - - format@0.2.2: {} - - formdata-node@4.4.1: - dependencies: - node-domexception: 1.0.0 - web-streams-polyfill: 4.0.0-beta.3 - - formdata-polyfill@4.0.10: - dependencies: - fetch-blob: 3.2.0 - - forwarded@0.2.0: {} - - fresh@0.5.2: {} - - fresh@2.0.0: {} - - fs-extra@11.2.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 2.0.1 - - fsevents@2.3.2: - optional: true - - fsevents@2.3.3: - optional: true - - function-bind@1.1.2: {} - - function.prototype.name@1.1.8: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.3 - define-properties: 1.2.1 - functions-have-names: 1.2.3 - hasown: 2.0.2 - is-callable: 1.2.7 - - functions-have-names@1.2.3: {} - - gaxios@5.1.3: - dependencies: - extend: 3.0.2 - https-proxy-agent: 5.0.1 - is-stream: 2.0.1 - node-fetch: 2.7.0 - transitivePeerDependencies: - - encoding - - supports-color - - gcp-metadata@5.3.0: - dependencies: - gaxios: 5.1.3 - json-bigint: 1.0.0 - transitivePeerDependencies: - - encoding - - supports-color - - gensync@1.0.0-beta.2: {} - - get-caller-file@2.0.5: {} - - get-intrinsic@1.2.7: - dependencies: - call-bind-apply-helpers: 1.0.1 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.0.0 - 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 - - get-own-enumerable-keys@1.0.0: {} - - get-proto@1.0.1: - dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.0.0 - - get-stream@6.0.1: {} - - get-symbol-description@1.1.0: - dependencies: - call-bound: 1.0.3 - es-errors: 1.3.0 - get-intrinsic: 1.2.7 - - get-tsconfig@4.8.1: - dependencies: - resolve-pkg-maps: 1.0.0 - - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - - glob-parent@6.0.2: - dependencies: - is-glob: 4.0.3 - - glob@10.4.5: - dependencies: - foreground-child: 3.3.0 - jackspeak: 3.4.3 - minimatch: 9.0.5 - minipass: 7.1.2 - package-json-from-dist: 1.0.1 - path-scurry: 1.11.1 - - globals@11.12.0: {} - - globals@14.0.0: {} - - globalthis@1.0.4: - dependencies: - define-properties: 1.2.1 - gopd: 1.2.0 - - google-auth-library@8.9.0: - dependencies: - arrify: 2.0.1 - base64-js: 1.5.1 - ecdsa-sig-formatter: 1.0.11 - fast-text-encoding: 1.0.6 - gaxios: 5.1.3 - gcp-metadata: 5.3.0 - gtoken: 6.1.2 - jws: 4.0.0 - lru-cache: 6.0.0 - transitivePeerDependencies: - - encoding - - supports-color - - google-p12-pem@4.0.1: - dependencies: - node-forge: 1.3.1 - - gopd@1.2.0: {} - - graceful-fs@4.2.11: {} - - graphemer@1.4.0: {} - - graphql-query-complexity@0.12.0(graphql@16.10.0): - dependencies: - graphql: 16.10.0 - lodash.get: 4.4.2 - - graphql-scalars@1.24.2(graphql@16.10.0): - dependencies: - graphql: 16.10.0 - tslib: 2.8.1 - - graphql-yoga@5.13.2(graphql@16.10.0): - dependencies: - '@envelop/core': 5.2.3 - '@envelop/instrumentation': 1.0.0 - '@graphql-tools/executor': 1.4.6(graphql@16.10.0) - '@graphql-tools/schema': 10.0.23(graphql@16.10.0) - '@graphql-tools/utils': 10.8.6(graphql@16.10.0) - '@graphql-yoga/logger': 2.0.1 - '@graphql-yoga/subscription': 5.0.3 - '@whatwg-node/fetch': 0.10.5 - '@whatwg-node/promise-helpers': 1.3.0 - '@whatwg-node/server': 0.10.1 - dset: 3.1.4 - graphql: 16.10.0 - lru-cache: 10.4.3 - tslib: 2.8.1 - - graphql@16.10.0: {} - - groq-sdk@0.5.0: - dependencies: - '@types/node': 18.19.80 - '@types/node-fetch': 2.6.12 - abort-controller: 3.0.0 - agentkeepalive: 4.6.0 - form-data-encoder: 1.7.2 - formdata-node: 4.4.1 - node-fetch: 2.7.0 - web-streams-polyfill: 3.3.3 - transitivePeerDependencies: - - encoding - - gtoken@6.1.2: - dependencies: - gaxios: 5.1.3 - google-p12-pem: 4.0.1 - jws: 4.0.0 - transitivePeerDependencies: - - encoding - - supports-color - - has-bigints@1.1.0: {} - - has-flag@4.0.0: {} - - has-property-descriptors@1.0.2: - dependencies: - es-define-property: 1.0.1 - - has-proto@1.2.0: - dependencies: - dunder-proto: 1.0.1 - - has-symbols@1.1.0: {} - - has-tostringtag@1.0.2: - dependencies: - has-symbols: 1.1.0 - - hasown@1.0.1: {} - - hasown@2.0.2: - dependencies: - function-bind: 1.1.2 - - hast-util-parse-selector@2.2.5: {} - - hast-util-whitespace@2.0.1: {} - - hastscript@6.0.0: - dependencies: - '@types/hast': 2.3.10 - comma-separated-tokens: 1.0.8 - hast-util-parse-selector: 2.2.5 - property-information: 5.6.0 - space-separated-tokens: 1.1.5 - - headers-polyfill@4.0.3: {} - - help-me@5.0.0: {} - - highlight.js@10.7.3: {} - - highlightjs-vue@1.0.0: {} - - hono@4.11.1: {} - - http-errors@2.0.0: - dependencies: - depd: 2.0.0 - inherits: 2.0.4 - setprototypeof: 1.2.0 - statuses: 2.0.1 - toidentifier: 1.0.1 - - https-proxy-agent@5.0.1: - dependencies: - agent-base: 6.0.2 - debug: 4.4.0 - transitivePeerDependencies: - - supports-color - - https-proxy-agent@6.2.1: - dependencies: - agent-base: 7.1.3 - debug: 4.4.0 - transitivePeerDependencies: - - supports-color - - human-signals@4.3.1: {} - - humanize-ms@1.2.1: - dependencies: - ms: 2.1.3 - - ibm-cloud-sdk-core@5.3.2: - dependencies: - '@types/debug': 4.1.12 - '@types/node': 18.19.130 - '@types/tough-cookie': 4.0.5 - axios: 1.13.2(debug@4.4.3) - camelcase: 6.3.0 - debug: 4.4.3 - dotenv: 16.6.1 - extend: 3.0.2 - file-type: 16.5.4 - form-data: 4.0.0 - isstream: 0.1.2 - jsonwebtoken: 9.0.3 - mime-types: 2.1.35 - retry-axios: 2.6.0(axios@1.13.2(debug@4.4.3)) - tough-cookie: 4.1.4 - transitivePeerDependencies: - - supports-color - - iconv-lite@0.4.24: - dependencies: - safer-buffer: 2.1.2 - - iconv-lite@0.6.3: - dependencies: - safer-buffer: 2.1.2 - - ieee754@1.2.1: {} - - ignore@5.3.2: {} - - import-fresh@3.3.0: - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - - imurmurhash@0.1.4: {} - - inherits@2.0.4: {} - - inline-style-parser@0.1.1: {} - - internal-slot@1.1.0: - dependencies: - es-errors: 1.3.0 - hasown: 2.0.2 - side-channel: 1.1.0 - - ipaddr.js@1.9.1: {} - - is-alphabetical@1.0.4: {} - - is-alphanumerical@1.0.4: - dependencies: - is-alphabetical: 1.0.4 - is-decimal: 1.0.4 - - is-array-buffer@3.0.5: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.3 - get-intrinsic: 1.2.7 - - is-arrayish@0.2.1: {} - - is-async-function@2.1.0: - dependencies: - call-bound: 1.0.3 - get-proto: 1.0.1 - has-tostringtag: 1.0.2 - safe-regex-test: 1.1.0 - - is-bigint@1.1.0: - dependencies: - has-bigints: 1.1.0 - - is-binary-path@2.1.0: - dependencies: - binary-extensions: 2.3.0 - - is-boolean-object@1.2.1: - dependencies: - call-bound: 1.0.3 - has-tostringtag: 1.0.2 - - is-buffer@2.0.5: {} - - is-bun-module@1.3.0: - dependencies: - semver: 7.6.3 - - is-callable@1.2.7: {} - - is-core-module@2.16.1: - dependencies: - hasown: 2.0.2 - - is-data-view@1.0.2: - dependencies: - call-bound: 1.0.3 - get-intrinsic: 1.2.7 - is-typed-array: 1.1.15 - - is-date-object@1.1.0: - dependencies: - call-bound: 1.0.3 - has-tostringtag: 1.0.2 - - is-decimal@1.0.4: {} - - is-extglob@2.1.1: {} - - is-finalizationregistry@1.1.1: - dependencies: - call-bound: 1.0.3 - - is-fullwidth-code-point@3.0.0: {} - - is-generator-function@1.1.0: - dependencies: - call-bound: 1.0.3 - get-proto: 1.0.1 - has-tostringtag: 1.0.2 - safe-regex-test: 1.1.0 - - is-glob@4.0.3: - dependencies: - is-extglob: 2.1.1 - - is-hexadecimal@1.0.4: {} - - is-interactive@2.0.0: {} - - is-map@2.0.3: {} - - is-node-process@1.2.0: {} - - is-number-object@1.1.1: - dependencies: - call-bound: 1.0.3 - has-tostringtag: 1.0.2 - - is-number@7.0.0: {} - - is-obj@3.0.0: {} - - is-plain-obj@4.1.0: {} - - is-promise@4.0.0: {} - - is-regex@1.2.1: - dependencies: - call-bound: 1.0.3 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - is-regexp@3.1.0: {} - - is-set@2.0.3: {} - - is-shared-array-buffer@1.0.4: - dependencies: - call-bound: 1.0.3 - - is-stream@2.0.1: {} - - is-stream@3.0.0: {} - - is-string@1.1.1: - dependencies: - call-bound: 1.0.3 - has-tostringtag: 1.0.2 - - is-symbol@1.1.1: - dependencies: - call-bound: 1.0.3 - has-symbols: 1.1.0 - safe-regex-test: 1.1.0 - - is-typed-array@1.1.15: - dependencies: - which-typed-array: 1.1.18 - - is-unicode-supported@1.3.0: {} - - is-weakmap@2.0.2: {} - - is-weakref@1.1.0: - dependencies: - call-bound: 1.0.3 - - is-weakset@2.0.4: - dependencies: - call-bound: 1.0.3 - get-intrinsic: 1.2.7 - - isarray@2.0.5: {} - - isexe@2.0.0: {} - - isstream@0.1.2: {} - - iterator.prototype@1.1.5: - dependencies: - define-data-property: 1.1.4 - es-object-atoms: 1.0.0 - get-intrinsic: 1.2.7 - get-proto: 1.0.1 - has-symbols: 1.1.0 - set-function-name: 2.0.2 - - jackspeak@3.4.3: - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - - jiti@1.21.7: {} - - jose@5.10.0: {} - - jose@6.1.3: {} - - joycon@3.1.1: {} - - js-tiktoken@1.0.19: - dependencies: - base64-js: 1.5.1 - - js-tokens@4.0.0: {} - - js-yaml@4.1.0: - dependencies: - argparse: 2.0.1 - - jsesc@3.1.0: {} - - json-bigint@1.0.0: - dependencies: - bignumber.js: 9.1.2 - - json-buffer@3.0.1: {} - - json-parse-even-better-errors@2.3.1: {} - - json-schema-traverse@0.4.1: {} - - json-schema-traverse@1.0.0: {} - - json-schema-typed@8.0.2: {} - - json-stable-stringify-without-jsonify@1.0.1: {} - - json5@1.0.2: - dependencies: - minimist: 1.2.8 - - json5@2.2.3: {} - - jsonfile@6.1.0: - dependencies: - universalify: 2.0.1 - optionalDependencies: - graceful-fs: 4.2.11 - - jsonpointer@5.0.1: {} - - jsonwebtoken@9.0.3: - dependencies: - jws: 4.0.1 - lodash.includes: 4.3.0 - lodash.isboolean: 3.0.3 - lodash.isinteger: 4.0.4 - lodash.isnumber: 3.0.3 - lodash.isplainobject: 4.0.6 - lodash.isstring: 4.0.1 - lodash.once: 4.1.1 - ms: 2.1.3 - semver: 7.7.3 - - jsx-ast-utils@3.3.5: - dependencies: - array-includes: 3.1.8 - array.prototype.flat: 1.3.3 - object.assign: 4.1.7 - object.values: 1.2.1 - - jwa@2.0.0: - dependencies: - buffer-equal-constant-time: 1.0.1 - ecdsa-sig-formatter: 1.0.11 - safe-buffer: 5.2.1 - - jwa@2.0.1: - dependencies: - buffer-equal-constant-time: 1.0.1 - ecdsa-sig-formatter: 1.0.11 - safe-buffer: 5.2.1 - - jws@4.0.0: - dependencies: - jwa: 2.0.0 - safe-buffer: 5.2.1 - - jws@4.0.1: - dependencies: - jwa: 2.0.1 - safe-buffer: 5.2.1 - - katex@0.16.21: - dependencies: - commander: 8.3.0 - - keyv@4.5.4: - dependencies: - json-buffer: 3.0.1 - - kleur@3.0.3: {} - - kleur@4.1.5: {} - - langchain@0.3.19(@langchain/core@0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1)))(axios@1.13.2)(openai@4.87.4(ws@8.18.3)(zod@3.24.1))(ws@8.18.3): - dependencies: - '@langchain/core': 0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1)) - '@langchain/openai': 0.4.5(@langchain/core@0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1)))(ws@8.18.3) - '@langchain/textsplitters': 0.1.0(@langchain/core@0.3.42(openai@4.87.4(ws@8.18.3)(zod@3.24.1))) - js-tiktoken: 1.0.19 - js-yaml: 4.1.0 - jsonpointer: 5.0.1 - langsmith: 0.3.14(openai@4.87.4(ws@8.18.3)(zod@3.24.1)) - openapi-types: 12.1.3 - p-retry: 4.6.2 - uuid: 10.0.0 - yaml: 2.7.0 - zod: 3.24.1 - zod-to-json-schema: 3.24.4(zod@3.24.1) - optionalDependencies: - axios: 1.13.2(debug@4.4.3) - transitivePeerDependencies: - - encoding - - openai - - ws - - langsmith@0.3.14(openai@4.87.4(ws@8.18.3)(zod@3.24.1)): - dependencies: - '@types/uuid': 10.0.0 - chalk: 4.1.2 - console-table-printer: 2.12.1 - p-queue: 6.6.2 - p-retry: 4.6.2 - semver: 7.6.3 - uuid: 10.0.0 - optionalDependencies: - openai: 4.87.4(ws@8.18.3)(zod@3.24.1) - - language-subtag-registry@0.3.23: {} - - language-tags@1.0.9: - dependencies: - language-subtag-registry: 0.3.23 - - levn@0.4.1: - dependencies: - prelude-ls: 1.2.1 - type-check: 0.4.0 - - libphonenumber-js@1.12.6: {} - - lilconfig@3.1.3: {} - - lines-and-columns@1.2.4: {} - - locate-path@6.0.0: - dependencies: - p-locate: 5.0.0 - - lodash.get@4.4.2: {} - - lodash.includes@4.3.0: {} - - lodash.isboolean@3.0.3: {} - - lodash.isinteger@4.0.4: {} - - lodash.isnumber@3.0.3: {} - - lodash.isplainobject@4.0.6: {} - - lodash.isstring@4.0.1: {} - - lodash.merge@4.6.2: {} - - lodash.once@4.1.1: {} - - log-symbols@5.1.0: - dependencies: - chalk: 5.4.1 - is-unicode-supported: 1.3.0 - - longest-streak@3.1.0: {} - - loose-envify@1.4.0: - dependencies: - js-tokens: 4.0.0 - - lowlight@1.20.0: - dependencies: - fault: 1.0.4 - highlight.js: 10.7.3 - - lru-cache@10.4.3: {} - - lru-cache@5.1.1: - dependencies: - yallist: 3.1.1 - - lru-cache@6.0.0: - dependencies: - yallist: 4.0.0 - - lucide-react@0.471.1(react@19.0.0): - dependencies: - react: 19.0.0 - - markdown-table@3.0.4: {} - - matches-selector@1.2.0: {} - - math-intrinsics@1.1.0: {} - - mdast-util-definitions@5.1.2: - dependencies: - '@types/mdast': 3.0.15 - '@types/unist': 2.0.11 - unist-util-visit: 4.1.2 - - mdast-util-find-and-replace@2.2.2: - dependencies: - '@types/mdast': 3.0.15 - escape-string-regexp: 5.0.0 - unist-util-is: 5.2.1 - unist-util-visit-parents: 5.1.3 - - mdast-util-from-markdown@1.3.1: - dependencies: - '@types/mdast': 3.0.15 - '@types/unist': 2.0.11 - decode-named-character-reference: 1.1.0 - mdast-util-to-string: 3.2.0 - micromark: 3.2.0 - micromark-util-decode-numeric-character-reference: 1.1.0 - micromark-util-decode-string: 1.1.0 - micromark-util-normalize-identifier: 1.1.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - unist-util-stringify-position: 3.0.3 - uvu: 0.5.6 - transitivePeerDependencies: - - supports-color - - mdast-util-gfm-autolink-literal@1.0.3: - dependencies: - '@types/mdast': 3.0.15 - ccount: 2.0.1 - mdast-util-find-and-replace: 2.2.2 - micromark-util-character: 1.2.0 - - mdast-util-gfm-footnote@1.0.2: - dependencies: - '@types/mdast': 3.0.15 - mdast-util-to-markdown: 1.5.0 - micromark-util-normalize-identifier: 1.1.0 - - mdast-util-gfm-strikethrough@1.0.3: - dependencies: - '@types/mdast': 3.0.15 - mdast-util-to-markdown: 1.5.0 - - mdast-util-gfm-table@1.0.7: - dependencies: - '@types/mdast': 3.0.15 - markdown-table: 3.0.4 - mdast-util-from-markdown: 1.3.1 - mdast-util-to-markdown: 1.5.0 - transitivePeerDependencies: - - supports-color - - mdast-util-gfm-task-list-item@1.0.2: - dependencies: - '@types/mdast': 3.0.15 - mdast-util-to-markdown: 1.5.0 - - mdast-util-gfm@2.0.2: - dependencies: - mdast-util-from-markdown: 1.3.1 - mdast-util-gfm-autolink-literal: 1.0.3 - mdast-util-gfm-footnote: 1.0.2 - mdast-util-gfm-strikethrough: 1.0.3 - mdast-util-gfm-table: 1.0.7 - mdast-util-gfm-task-list-item: 1.0.2 - mdast-util-to-markdown: 1.5.0 - transitivePeerDependencies: - - supports-color - - mdast-util-math@2.0.2: - dependencies: - '@types/mdast': 3.0.15 - longest-streak: 3.1.0 - mdast-util-to-markdown: 1.5.0 - - mdast-util-phrasing@3.0.1: - dependencies: - '@types/mdast': 3.0.15 - unist-util-is: 5.2.1 - - mdast-util-to-hast@12.3.0: - dependencies: - '@types/hast': 2.3.10 - '@types/mdast': 3.0.15 - mdast-util-definitions: 5.1.2 - micromark-util-sanitize-uri: 1.2.0 - trim-lines: 3.0.1 - unist-util-generated: 2.0.1 - unist-util-position: 4.0.4 - unist-util-visit: 4.1.2 - - mdast-util-to-markdown@1.5.0: - dependencies: - '@types/mdast': 3.0.15 - '@types/unist': 2.0.11 - longest-streak: 3.1.0 - mdast-util-phrasing: 3.0.1 - mdast-util-to-string: 3.2.0 - micromark-util-decode-string: 1.1.0 - unist-util-visit: 4.1.2 - zwitch: 2.0.4 - - mdast-util-to-string@3.2.0: - dependencies: - '@types/mdast': 3.0.15 - - media-typer@0.3.0: {} - - media-typer@1.1.0: {} - - merge-descriptors@1.0.3: {} - - merge-descriptors@2.0.0: {} - - merge-stream@2.0.0: {} - - merge2@1.4.1: {} - - methods@1.1.2: {} - - micromark-core-commonmark@1.1.0: - dependencies: - decode-named-character-reference: 1.1.0 - micromark-factory-destination: 1.1.0 - micromark-factory-label: 1.1.0 - micromark-factory-space: 1.1.0 - micromark-factory-title: 1.1.0 - micromark-factory-whitespace: 1.1.0 - micromark-util-character: 1.2.0 - micromark-util-chunked: 1.1.0 - micromark-util-classify-character: 1.1.0 - micromark-util-html-tag-name: 1.2.0 - micromark-util-normalize-identifier: 1.1.0 - micromark-util-resolve-all: 1.1.0 - micromark-util-subtokenize: 1.1.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - uvu: 0.5.6 - - micromark-extension-gfm-autolink-literal@1.0.5: - dependencies: - micromark-util-character: 1.2.0 - micromark-util-sanitize-uri: 1.2.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - - micromark-extension-gfm-footnote@1.1.2: - dependencies: - micromark-core-commonmark: 1.1.0 - micromark-factory-space: 1.1.0 - micromark-util-character: 1.2.0 - micromark-util-normalize-identifier: 1.1.0 - micromark-util-sanitize-uri: 1.2.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - uvu: 0.5.6 - - micromark-extension-gfm-strikethrough@1.0.7: - dependencies: - micromark-util-chunked: 1.1.0 - micromark-util-classify-character: 1.1.0 - micromark-util-resolve-all: 1.1.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - uvu: 0.5.6 - - micromark-extension-gfm-table@1.0.7: - dependencies: - micromark-factory-space: 1.1.0 - micromark-util-character: 1.2.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - uvu: 0.5.6 - - micromark-extension-gfm-tagfilter@1.0.2: - dependencies: - micromark-util-types: 1.1.0 - - micromark-extension-gfm-task-list-item@1.0.5: - dependencies: - micromark-factory-space: 1.1.0 - micromark-util-character: 1.2.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - uvu: 0.5.6 - - micromark-extension-gfm@2.0.3: - dependencies: - micromark-extension-gfm-autolink-literal: 1.0.5 - micromark-extension-gfm-footnote: 1.1.2 - micromark-extension-gfm-strikethrough: 1.0.7 - micromark-extension-gfm-table: 1.0.7 - micromark-extension-gfm-tagfilter: 1.0.2 - micromark-extension-gfm-task-list-item: 1.0.5 - micromark-util-combine-extensions: 1.1.0 - micromark-util-types: 1.1.0 - - micromark-extension-math@2.1.2: - dependencies: - '@types/katex': 0.16.7 - katex: 0.16.21 - micromark-factory-space: 1.1.0 - micromark-util-character: 1.2.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - uvu: 0.5.6 - - micromark-factory-destination@1.1.0: - dependencies: - micromark-util-character: 1.2.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - - micromark-factory-label@1.1.0: - dependencies: - micromark-util-character: 1.2.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - uvu: 0.5.6 - - micromark-factory-space@1.1.0: - dependencies: - micromark-util-character: 1.2.0 - micromark-util-types: 1.1.0 - - micromark-factory-title@1.1.0: - dependencies: - micromark-factory-space: 1.1.0 - micromark-util-character: 1.2.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - - micromark-factory-whitespace@1.1.0: - dependencies: - micromark-factory-space: 1.1.0 - micromark-util-character: 1.2.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - - micromark-util-character@1.2.0: - dependencies: - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - - micromark-util-chunked@1.1.0: - dependencies: - micromark-util-symbol: 1.1.0 - - micromark-util-classify-character@1.1.0: - dependencies: - micromark-util-character: 1.2.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - - micromark-util-combine-extensions@1.1.0: - dependencies: - micromark-util-chunked: 1.1.0 - micromark-util-types: 1.1.0 - - micromark-util-decode-numeric-character-reference@1.1.0: - dependencies: - micromark-util-symbol: 1.1.0 - - micromark-util-decode-string@1.1.0: - dependencies: - decode-named-character-reference: 1.1.0 - micromark-util-character: 1.2.0 - micromark-util-decode-numeric-character-reference: 1.1.0 - micromark-util-symbol: 1.1.0 - - micromark-util-encode@1.1.0: {} - - micromark-util-html-tag-name@1.2.0: {} - - micromark-util-normalize-identifier@1.1.0: - dependencies: - micromark-util-symbol: 1.1.0 - - micromark-util-resolve-all@1.1.0: - dependencies: - micromark-util-types: 1.1.0 - - micromark-util-sanitize-uri@1.2.0: - dependencies: - micromark-util-character: 1.2.0 - micromark-util-encode: 1.1.0 - micromark-util-symbol: 1.1.0 - - micromark-util-subtokenize@1.1.0: - dependencies: - micromark-util-chunked: 1.1.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - uvu: 0.5.6 - - micromark-util-symbol@1.1.0: {} - - micromark-util-types@1.1.0: {} - - micromark@3.2.0: - dependencies: - '@types/debug': 4.1.12 - debug: 4.4.0 - decode-named-character-reference: 1.1.0 - micromark-core-commonmark: 1.1.0 - micromark-factory-space: 1.1.0 - micromark-util-character: 1.2.0 - micromark-util-chunked: 1.1.0 - micromark-util-combine-extensions: 1.1.0 - micromark-util-decode-numeric-character-reference: 1.1.0 - micromark-util-encode: 1.1.0 - micromark-util-normalize-identifier: 1.1.0 - micromark-util-resolve-all: 1.1.0 - micromark-util-sanitize-uri: 1.2.0 - micromark-util-subtokenize: 1.1.0 - micromark-util-symbol: 1.1.0 - micromark-util-types: 1.1.0 - uvu: 0.5.6 - transitivePeerDependencies: - - supports-color - - micromatch@4.0.8: - dependencies: - braces: 3.0.3 - picomatch: 2.3.1 - - mime-db@1.52.0: {} - - mime-db@1.54.0: {} - - mime-types@2.1.35: - dependencies: - mime-db: 1.52.0 - - mime-types@3.0.1: - dependencies: - mime-db: 1.54.0 - - mime@1.6.0: {} - - mimic-fn@2.1.0: {} - - mimic-fn@4.0.0: {} - - minimatch@3.1.2: - dependencies: - brace-expansion: 1.1.11 - - minimatch@7.4.6: - dependencies: - brace-expansion: 2.0.1 - - minimatch@9.0.5: - dependencies: - brace-expansion: 2.0.1 - - minimist@1.2.8: {} - - minipass@7.1.2: {} - - mkdirp@2.1.6: {} - - mri@1.2.0: {} - - ms@2.0.0: {} - - ms@2.1.3: {} - - msw@2.7.3(@types/node@20.17.12)(typescript@5.7.3): - dependencies: - '@bundled-es-modules/cookie': 2.0.1 - '@bundled-es-modules/statuses': 1.0.1 - '@bundled-es-modules/tough-cookie': 0.1.6 - '@inquirer/confirm': 5.1.6(@types/node@20.17.12) - '@mswjs/interceptors': 0.37.6 - '@open-draft/deferred-promise': 2.2.0 - '@open-draft/until': 2.1.0 - '@types/cookie': 0.6.0 - '@types/statuses': 2.0.5 - graphql: 16.10.0 - headers-polyfill: 4.0.3 - is-node-process: 1.2.0 - outvariant: 1.4.3 - path-to-regexp: 6.3.0 - picocolors: 1.1.1 - strict-event-emitter: 0.5.1 - type-fest: 4.37.0 - yargs: 17.7.2 - optionalDependencies: - typescript: 5.7.3 - transitivePeerDependencies: - - '@types/node' - - mustache@4.2.0: {} - - mute-stream@2.0.0: {} - - mz@2.7.0: - dependencies: - any-promise: 1.3.0 - object-assign: 4.1.1 - thenify-all: 1.6.0 - - nanoid@3.3.11: {} - - nanoid@3.3.8: {} - - natural-compare@1.4.0: {} - - negotiator@0.6.3: {} - - negotiator@1.0.0: {} - - newify@1.1.9: {} - - next-themes@0.4.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0): - dependencies: - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - - next@15.4.10(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(@playwright/test@1.51.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): - dependencies: - '@next/env': 15.4.10 - '@swc/helpers': 0.5.15 - caniuse-lite: 1.0.30001760 - postcss: 8.4.31 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - styled-jsx: 5.1.6(@babel/core@7.26.0)(react@19.0.0) - optionalDependencies: - '@next/swc-darwin-arm64': 15.4.8 - '@next/swc-darwin-x64': 15.4.8 - '@next/swc-linux-arm64-gnu': 15.4.8 - '@next/swc-linux-arm64-musl': 15.4.8 - '@next/swc-linux-x64-gnu': 15.4.8 - '@next/swc-linux-x64-musl': 15.4.8 - '@next/swc-win32-arm64-msvc': 15.4.8 - '@next/swc-win32-x64-msvc': 15.4.8 - '@opentelemetry/api': 1.9.0 - '@playwright/test': 1.51.1 - sharp: 0.34.5 - transitivePeerDependencies: - - '@babel/core' - - babel-plugin-macros - - node-domexception@1.0.0: {} - - node-fetch@2.7.0: - dependencies: - whatwg-url: 5.0.0 - - node-fetch@3.3.2: - dependencies: - data-uri-to-buffer: 4.0.1 - fetch-blob: 3.2.0 - formdata-polyfill: 4.0.10 - - node-forge@1.3.1: {} - - node-releases@2.0.19: {} - - normalize-path@3.0.0: {} - - npm-run-path@5.3.0: - dependencies: - path-key: 4.0.0 - - object-assign@2.1.1: {} - - object-assign@3.0.0: {} - - object-assign@4.1.1: {} - - object-hash@3.0.0: {} - - object-inspect@1.13.3: {} - - object-keys@1.1.1: {} - - object.assign@4.1.7: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.3 - define-properties: 1.2.1 - es-object-atoms: 1.0.0 - has-symbols: 1.1.0 - object-keys: 1.1.1 - - object.entries@1.1.8: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-object-atoms: 1.0.0 - - object.fromentries@2.0.8: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-object-atoms: 1.0.0 - - object.groupby@1.0.3: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.23.9 - - object.values@1.2.1: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.3 - define-properties: 1.2.1 - es-object-atoms: 1.0.0 - - on-exit-leak-free@2.1.2: {} - - on-finished@2.4.1: - dependencies: - ee-first: 1.1.1 - - once@1.4.0: - dependencies: - wrappy: 1.0.2 - - onetime@5.1.2: - dependencies: - mimic-fn: 2.1.0 - - onetime@6.0.0: - dependencies: - mimic-fn: 4.0.0 - - openai@4.87.4(ws@8.18.3)(zod@3.24.1): - dependencies: - '@types/node': 18.19.80 - '@types/node-fetch': 2.6.12 - abort-controller: 3.0.0 - agentkeepalive: 4.6.0 - form-data-encoder: 1.7.2 - formdata-node: 4.4.1 - node-fetch: 2.7.0 - optionalDependencies: - ws: 8.18.3 - zod: 3.24.1 - transitivePeerDependencies: - - encoding - - openapi-types@12.1.3: {} - - optionator@0.9.4: - dependencies: - deep-is: 0.1.4 - fast-levenshtein: 2.0.6 - levn: 0.4.1 - prelude-ls: 1.2.1 - type-check: 0.4.0 - word-wrap: 1.2.5 - - ora@6.3.1: - dependencies: - chalk: 5.4.1 - cli-cursor: 4.0.0 - cli-spinners: 2.9.2 - is-interactive: 2.0.0 - is-unicode-supported: 1.3.0 - log-symbols: 5.1.0 - stdin-discarder: 0.1.0 - strip-ansi: 7.1.0 - wcwidth: 1.0.1 - - outvariant@1.4.3: {} - - own-keys@1.0.1: - dependencies: - get-intrinsic: 1.2.7 - object-keys: 1.1.1 - safe-push-apply: 1.0.0 - - p-finally@1.0.0: {} - - p-limit@3.1.0: - dependencies: - yocto-queue: 0.1.0 - - p-locate@5.0.0: - dependencies: - p-limit: 3.1.0 - - p-queue@6.6.2: - dependencies: - eventemitter3: 4.0.7 - p-timeout: 3.2.0 - - p-retry@4.6.2: - dependencies: - '@types/retry': 0.12.0 - retry: 0.13.1 - - p-timeout@3.2.0: - dependencies: - p-finally: 1.0.0 - - package-json-from-dist@1.0.1: {} - - parent-module@1.0.1: - dependencies: - callsites: 3.1.0 - - parse-entities@2.0.0: - dependencies: - character-entities: 1.2.4 - character-entities-legacy: 1.1.4 - character-reference-invalid: 1.1.4 - is-alphanumerical: 1.0.4 - is-decimal: 1.0.4 - is-hexadecimal: 1.0.4 - - parse-json@5.2.0: - dependencies: - '@babel/code-frame': 7.26.2 - error-ex: 1.3.2 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 - - parseurl@1.3.3: {} - - partial-json@0.1.7: {} - - path-browserify@1.0.1: {} - - path-exists@4.0.0: {} - - path-key@3.1.1: {} - - path-key@4.0.0: {} - - path-parse@1.0.7: {} - - path-scurry@1.11.1: - dependencies: - lru-cache: 10.4.3 - minipass: 7.1.2 - - path-to-regexp@0.1.12: {} - - path-to-regexp@6.3.0: {} - - path-to-regexp@8.2.0: {} - - path-type@4.0.0: {} - - peek-readable@4.1.0: {} - - picocolors@1.1.1: {} - - picomatch@2.3.1: {} - - pify@2.3.0: {} - - pino-abstract-transport@2.0.0: - dependencies: - split2: 4.2.0 - - pino-pretty@11.3.0: - dependencies: - colorette: 2.0.20 - dateformat: 4.6.3 - fast-copy: 3.0.2 - fast-safe-stringify: 2.1.1 - help-me: 5.0.0 - joycon: 3.1.1 - minimist: 1.2.8 - on-exit-leak-free: 2.1.2 - pino-abstract-transport: 2.0.0 - pump: 3.0.2 - readable-stream: 4.7.0 - secure-json-parse: 2.7.0 - sonic-boom: 4.2.0 - strip-json-comments: 3.1.1 - - pino-std-serializers@7.0.0: {} - - pino@9.6.0: - dependencies: - atomic-sleep: 1.0.0 - fast-redact: 3.5.0 - on-exit-leak-free: 2.1.2 - pino-abstract-transport: 2.0.0 - pino-std-serializers: 7.0.0 - process-warning: 4.0.1 - quick-format-unescaped: 4.0.4 - real-require: 0.2.0 - safe-stable-stringify: 2.5.0 - sonic-boom: 4.2.0 - thread-stream: 3.1.0 - - pirates@4.0.6: {} - - pkce-challenge@5.0.0: {} - - playwright-core@1.51.1: {} - - playwright@1.51.1: - dependencies: - playwright-core: 1.51.1 - optionalDependencies: - fsevents: 2.3.2 - - possible-typed-array-names@1.0.0: {} - - postcss-import@15.1.0(postcss@8.4.49): - dependencies: - postcss: 8.4.49 - postcss-value-parser: 4.2.0 - read-cache: 1.0.0 - resolve: 1.22.10 - - postcss-js@4.0.1(postcss@8.4.49): - dependencies: - camelcase-css: 2.0.1 - postcss: 8.4.49 - - postcss-load-config@4.0.2(postcss@8.4.49): - dependencies: - lilconfig: 3.1.3 - yaml: 2.7.0 - optionalDependencies: - postcss: 8.4.49 - - postcss-nested@6.2.0(postcss@8.4.49): - dependencies: - postcss: 8.4.49 - postcss-selector-parser: 6.1.2 - - postcss-selector-parser@6.1.2: - dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 - - postcss-value-parser@4.2.0: {} - - postcss@8.4.31: - dependencies: - nanoid: 3.3.11 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - postcss@8.4.49: - dependencies: - nanoid: 3.3.8 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - prelude-ls@1.2.1: {} - - prismjs@1.27.0: {} - - prismjs@1.30.0: {} - - process-warning@4.0.1: {} - - process@0.11.10: {} - - prompts@2.4.2: - dependencies: - kleur: 3.0.3 - sisteransi: 1.0.5 - - prop-types@15.8.1: - dependencies: - loose-envify: 1.4.0 - object-assign: 4.1.1 - react-is: 16.13.1 - - property-information@5.6.0: - dependencies: - xtend: 4.0.2 - - property-information@6.5.0: {} - - proxy-addr@2.0.7: - dependencies: - forwarded: 0.2.0 - ipaddr.js: 1.9.1 - - proxy-from-env@1.1.0: {} - - psl@1.15.0: - dependencies: - punycode: 2.3.1 - - pump@3.0.2: - dependencies: - end-of-stream: 1.4.4 - once: 1.4.0 - - punycode@2.3.1: {} - - qs@6.13.0: - dependencies: - side-channel: 1.1.0 - - qs@6.14.0: - dependencies: - side-channel: 1.1.0 - - querystringify@2.2.0: {} - - queue-microtask@1.2.3: {} - - quick-format-unescaped@4.0.4: {} - - range-parser@1.2.1: {} - - raw-body@2.5.2: - dependencies: - bytes: 3.1.2 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - unpipe: 1.0.0 - - raw-body@3.0.0: - dependencies: - bytes: 3.1.2 - http-errors: 2.0.0 - iconv-lite: 0.6.3 - unpipe: 1.0.0 - - react-dom@19.0.0(react@19.0.0): - dependencies: - react: 19.0.0 - scheduler: 0.25.0 - - react-is@16.13.1: {} - - react-is@18.3.1: {} - - react-markdown@8.0.7(@types/react@19.0.6)(react@19.0.0): - dependencies: - '@types/hast': 2.3.10 - '@types/prop-types': 15.7.14 - '@types/react': 19.0.6 - '@types/unist': 2.0.11 - comma-separated-tokens: 2.0.3 - hast-util-whitespace: 2.0.1 - prop-types: 15.8.1 - property-information: 6.5.0 - react: 19.0.0 - react-is: 18.3.1 - remark-parse: 10.0.2 - remark-rehype: 10.1.0 - space-separated-tokens: 2.0.2 - style-to-object: 0.4.4 - unified: 10.1.2 - unist-util-visit: 4.1.2 - vfile: 5.3.7 - transitivePeerDependencies: - - supports-color - - react-style-normalizer@1.2.8: {} - - react-syntax-highlighter@15.6.1(react@19.0.0): - dependencies: - '@babel/runtime': 7.26.10 - highlight.js: 10.7.3 - highlightjs-vue: 1.0.0 - lowlight: 1.20.0 - prismjs: 1.30.0 - react: 19.0.0 - refractor: 3.6.0 - - react@19.0.0: {} - - read-cache@1.0.0: - dependencies: - pify: 2.3.0 - - readable-stream@3.6.2: - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - - readable-stream@4.7.0: - dependencies: - abort-controller: 3.0.0 - buffer: 6.0.3 - events: 3.3.0 - process: 0.11.10 - string_decoder: 1.3.0 - - readable-web-to-node-stream@3.0.4: - dependencies: - readable-stream: 4.7.0 - - readdirp@3.6.0: - dependencies: - picomatch: 2.3.1 - - real-require@0.2.0: {} - - recast@0.23.9: - dependencies: - ast-types: 0.16.1 - esprima: 4.0.1 - source-map: 0.6.1 - tiny-invariant: 1.3.3 - tslib: 2.8.1 - - reflect-metadata@0.2.2: {} - - reflect.getprototypeof@1.0.10: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-errors: 1.3.0 - es-object-atoms: 1.0.0 - get-intrinsic: 1.2.7 - get-proto: 1.0.1 - which-builtin-type: 1.2.1 - - refractor@3.6.0: - dependencies: - hastscript: 6.0.0 - parse-entities: 2.0.0 - prismjs: 1.27.0 - - regenerator-runtime@0.14.1: {} - - regexp.prototype.flags@1.5.4: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-errors: 1.3.0 - get-proto: 1.0.1 - gopd: 1.2.0 - set-function-name: 2.0.2 - - region-align@2.1.3: - dependencies: - object-assign: 4.1.1 - region: 2.1.2 - - region@2.1.2: - dependencies: - hasown: 1.0.1 - newify: 1.1.9 - object-assign: 2.1.1 - - remark-gfm@3.0.1: - dependencies: - '@types/mdast': 3.0.15 - mdast-util-gfm: 2.0.2 - micromark-extension-gfm: 2.0.3 - unified: 10.1.2 - transitivePeerDependencies: - - supports-color - - remark-math@5.1.1: - dependencies: - '@types/mdast': 3.0.15 - mdast-util-math: 2.0.2 - micromark-extension-math: 2.1.2 - unified: 10.1.2 - - remark-parse@10.0.2: - dependencies: - '@types/mdast': 3.0.15 - mdast-util-from-markdown: 1.3.1 - unified: 10.1.2 - transitivePeerDependencies: - - supports-color - - remark-rehype@10.1.0: - dependencies: - '@types/hast': 2.3.10 - '@types/mdast': 3.0.15 - mdast-util-to-hast: 12.3.0 - unified: 10.1.2 - - require-directory@2.1.1: {} - - require-from-string@2.0.2: {} - - requires-port@1.0.0: {} - - resolve-from@4.0.0: {} - - resolve-pkg-maps@1.0.0: {} - - resolve@1.22.10: - dependencies: - is-core-module: 2.16.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - - resolve@2.0.0-next.5: - dependencies: - is-core-module: 2.16.1 - path-parse: 1.0.7 - supports-preserve-symlinks-flag: 1.0.0 - - restore-cursor@4.0.0: - dependencies: - onetime: 5.1.2 - signal-exit: 3.0.7 - - retry-axios@2.6.0(axios@1.13.2(debug@4.4.3)): - dependencies: - axios: 1.13.2(debug@4.4.3) - - retry@0.13.1: {} - - reusify@1.0.4: {} - - router@2.2.0: - dependencies: - debug: 4.4.0 - depd: 2.0.0 - is-promise: 4.0.0 - parseurl: 1.3.3 - path-to-regexp: 8.2.0 - transitivePeerDependencies: - - supports-color - - run-parallel@1.2.0: - dependencies: - queue-microtask: 1.2.3 - - rxjs@7.8.1: - dependencies: - tslib: 2.8.1 - - rxjs@7.8.2: - dependencies: - tslib: 2.8.1 - - sade@1.8.1: - dependencies: - mri: 1.2.0 - - safe-array-concat@1.1.3: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.3 - get-intrinsic: 1.2.7 - has-symbols: 1.1.0 - isarray: 2.0.5 - - safe-buffer@5.2.1: {} - - safe-push-apply@1.0.0: - dependencies: - es-errors: 1.3.0 - isarray: 2.0.5 - - safe-regex-test@1.1.0: - dependencies: - call-bound: 1.0.3 - es-errors: 1.3.0 - is-regex: 1.2.1 - - safe-stable-stringify@2.5.0: {} - - safer-buffer@2.1.2: {} - - scheduler@0.25.0: {} - - secure-json-parse@2.7.0: {} - - semver@6.3.1: {} - - semver@7.6.3: {} - - semver@7.7.3: {} - - send@0.19.0: - dependencies: - debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 - encodeurl: 1.0.2 - escape-html: 1.0.3 - etag: 1.8.1 - fresh: 0.5.2 - http-errors: 2.0.0 - mime: 1.6.0 - ms: 2.1.3 - on-finished: 2.4.1 - range-parser: 1.2.1 - statuses: 2.0.1 - transitivePeerDependencies: - - supports-color - - send@1.2.0: - dependencies: - debug: 4.4.0 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - fresh: 2.0.0 - http-errors: 2.0.0 - mime-types: 3.0.1 - ms: 2.1.3 - on-finished: 2.4.1 - range-parser: 1.2.1 - statuses: 2.0.1 - transitivePeerDependencies: - - supports-color - - serve-static@1.16.2: - dependencies: - encodeurl: 2.0.0 - escape-html: 1.0.3 - parseurl: 1.3.3 - send: 0.19.0 - transitivePeerDependencies: - - supports-color - - serve-static@2.2.0: - dependencies: - encodeurl: 2.0.0 - escape-html: 1.0.3 - parseurl: 1.3.3 - send: 1.2.0 - transitivePeerDependencies: - - supports-color - - set-function-length@1.2.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.2.7 - gopd: 1.2.0 - has-property-descriptors: 1.0.2 - - set-function-name@2.0.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - functions-have-names: 1.2.3 - has-property-descriptors: 1.0.2 - - set-proto@1.0.0: - dependencies: - dunder-proto: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.0.0 - - setprototypeof@1.2.0: {} - - shadcn@2.4.0(@types/node@20.17.12)(typescript@5.7.3): - dependencies: - '@antfu/ni': 23.3.1 - '@babel/core': 7.26.0 - '@babel/parser': 7.26.5 - '@babel/plugin-transform-typescript': 7.26.5(@babel/core@7.26.0) - commander: 10.0.1 - cosmiconfig: 8.3.6(typescript@5.7.3) - deepmerge: 4.3.1 - diff: 5.2.0 - execa: 7.2.0 - fast-glob: 3.3.3 - fs-extra: 11.2.0 - https-proxy-agent: 6.2.1 - kleur: 4.1.5 - msw: 2.7.3(@types/node@20.17.12)(typescript@5.7.3) - node-fetch: 3.3.2 - ora: 6.3.1 - postcss: 8.4.49 - prompts: 2.4.2 - recast: 0.23.9 - stringify-object: 5.0.0 - ts-morph: 18.0.0 - tsconfig-paths: 4.2.0 - zod: 3.24.1 - transitivePeerDependencies: - - '@types/node' - - supports-color - - typescript - - sharp@0.34.5: - dependencies: - '@img/colour': 1.0.0 - detect-libc: 2.1.2 - semver: 7.7.3 - optionalDependencies: - '@img/sharp-darwin-arm64': 0.34.5 - '@img/sharp-darwin-x64': 0.34.5 - '@img/sharp-libvips-darwin-arm64': 1.2.4 - '@img/sharp-libvips-darwin-x64': 1.2.4 - '@img/sharp-libvips-linux-arm': 1.2.4 - '@img/sharp-libvips-linux-arm64': 1.2.4 - '@img/sharp-libvips-linux-ppc64': 1.2.4 - '@img/sharp-libvips-linux-riscv64': 1.2.4 - '@img/sharp-libvips-linux-s390x': 1.2.4 - '@img/sharp-libvips-linux-x64': 1.2.4 - '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 - '@img/sharp-libvips-linuxmusl-x64': 1.2.4 - '@img/sharp-linux-arm': 0.34.5 - '@img/sharp-linux-arm64': 0.34.5 - '@img/sharp-linux-ppc64': 0.34.5 - '@img/sharp-linux-riscv64': 0.34.5 - '@img/sharp-linux-s390x': 0.34.5 - '@img/sharp-linux-x64': 0.34.5 - '@img/sharp-linuxmusl-arm64': 0.34.5 - '@img/sharp-linuxmusl-x64': 0.34.5 - '@img/sharp-wasm32': 0.34.5 - '@img/sharp-win32-arm64': 0.34.5 - '@img/sharp-win32-ia32': 0.34.5 - '@img/sharp-win32-x64': 0.34.5 - optional: true - - shebang-command@2.0.0: - dependencies: - shebang-regex: 3.0.0 - - shebang-regex@3.0.0: {} - - side-channel-list@1.0.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.3 - - side-channel-map@1.0.1: - dependencies: - call-bound: 1.0.3 - es-errors: 1.3.0 - get-intrinsic: 1.2.7 - object-inspect: 1.13.3 - - side-channel-weakmap@1.0.2: - dependencies: - call-bound: 1.0.3 - es-errors: 1.3.0 - get-intrinsic: 1.2.7 - object-inspect: 1.13.3 - side-channel-map: 1.0.1 - - side-channel@1.1.0: - dependencies: - es-errors: 1.3.0 - object-inspect: 1.13.3 - side-channel-list: 1.0.0 - side-channel-map: 1.0.1 - side-channel-weakmap: 1.0.2 - - signal-exit@3.0.7: {} - - signal-exit@4.1.0: {} - - simple-wcswidth@1.0.1: {} - - sisteransi@1.0.5: {} - - sonic-boom@4.2.0: - dependencies: - atomic-sleep: 1.0.0 - - source-map-js@1.2.1: {} - - source-map@0.6.1: {} - - space-separated-tokens@1.1.5: {} - - space-separated-tokens@2.0.2: {} - - split2@4.2.0: {} - - stable-hash@0.0.4: {} - - statuses@2.0.1: {} - - stdin-discarder@0.1.0: - dependencies: - bl: 5.1.0 - - streamsearch@1.1.0: {} - - strict-event-emitter@0.5.1: {} - - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - string-width@5.1.2: - dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 9.2.2 - strip-ansi: 7.1.0 - - string.prototype.includes@2.0.1: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-abstract: 1.23.9 - - string.prototype.matchall@4.0.12: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.3 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-errors: 1.3.0 - es-object-atoms: 1.0.0 - get-intrinsic: 1.2.7 - gopd: 1.2.0 - has-symbols: 1.1.0 - internal-slot: 1.1.0 - regexp.prototype.flags: 1.5.4 - set-function-name: 2.0.2 - side-channel: 1.1.0 - - string.prototype.repeat@1.0.0: - dependencies: - define-properties: 1.2.1 - es-abstract: 1.23.9 - - string.prototype.trim@1.2.10: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.3 - define-data-property: 1.1.4 - define-properties: 1.2.1 - es-abstract: 1.23.9 - es-object-atoms: 1.0.0 - has-property-descriptors: 1.0.2 - - string.prototype.trimend@1.0.9: - dependencies: - call-bind: 1.0.8 - call-bound: 1.0.3 - define-properties: 1.2.1 - es-object-atoms: 1.0.0 - - string.prototype.trimstart@1.0.8: - dependencies: - call-bind: 1.0.8 - define-properties: 1.2.1 - es-object-atoms: 1.0.0 - - string_decoder@1.3.0: - dependencies: - safe-buffer: 5.2.1 - - stringify-object@5.0.0: - dependencies: - get-own-enumerable-keys: 1.0.0 - is-obj: 3.0.0 - is-regexp: 3.1.0 - - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - - strip-ansi@7.1.0: - dependencies: - ansi-regex: 6.1.0 - - strip-bom@3.0.0: {} - - strip-final-newline@3.0.0: {} - - strip-json-comments@3.1.1: {} - - strtok3@6.3.0: - dependencies: - '@tokenizer/token': 0.3.0 - peek-readable: 4.1.0 - - style-to-object@0.4.4: - dependencies: - inline-style-parser: 0.1.1 - - styled-jsx@5.1.6(@babel/core@7.26.0)(react@19.0.0): - dependencies: - client-only: 0.0.1 - react: 19.0.0 - optionalDependencies: - '@babel/core': 7.26.0 - - sucrase@3.35.0: - dependencies: - '@jridgewell/gen-mapping': 0.3.8 - commander: 4.1.1 - glob: 10.4.5 - lines-and-columns: 1.2.4 - mz: 2.7.0 - pirates: 4.0.6 - ts-interface-checker: 0.1.13 - - supports-color@7.2.0: - dependencies: - has-flag: 4.0.0 - - supports-preserve-symlinks-flag@1.0.0: {} - - tabbable@6.2.0: {} - - tailwind-merge@2.6.0: {} - - tailwindcss-animate@1.0.7(tailwindcss@3.4.17): - dependencies: - tailwindcss: 3.4.17 - - tailwindcss@3.4.17: - dependencies: - '@alloc/quick-lru': 5.2.0 - arg: 5.0.2 - chokidar: 3.6.0 - didyoumean: 1.2.2 - dlv: 1.1.3 - fast-glob: 3.3.3 - glob-parent: 6.0.2 - is-glob: 4.0.3 - jiti: 1.21.7 - lilconfig: 3.1.3 - micromatch: 4.0.8 - normalize-path: 3.0.0 - object-hash: 3.0.0 - picocolors: 1.1.1 - postcss: 8.4.49 - postcss-import: 15.1.0(postcss@8.4.49) - postcss-js: 4.0.1(postcss@8.4.49) - postcss-load-config: 4.0.2(postcss@8.4.49) - postcss-nested: 6.2.0(postcss@8.4.49) - postcss-selector-parser: 6.1.2 - resolve: 1.22.10 - sucrase: 3.35.0 - transitivePeerDependencies: - - ts-node - - tapable@2.2.1: {} - - thenify-all@1.6.0: - dependencies: - thenify: 3.3.1 - - thenify@3.3.1: - dependencies: - any-promise: 1.3.0 - - thread-stream@3.1.0: - dependencies: - real-require: 0.2.0 - - tiny-invariant@1.3.3: {} - - to-regex-range@5.0.1: - dependencies: - is-number: 7.0.0 - - to-style@1.3.3: {} - - toidentifier@1.0.1: {} - - token-types@4.2.1: - dependencies: - '@tokenizer/token': 0.3.0 - ieee754: 1.2.1 - - tooltip@1.6.1: - dependencies: - clone: 1.0.4 - contains: 0.1.1 - escape-html: 1.0.3 - matches-selector: 1.2.0 - object-assign: 3.0.0 - react-style-normalizer: 1.2.8 - region-align: 2.1.3 - to-style: 1.3.3 - - tough-cookie@4.1.4: - dependencies: - psl: 1.15.0 - punycode: 2.3.1 - universalify: 0.2.0 - url-parse: 1.5.10 - - tr46@0.0.3: {} - - trim-lines@3.0.1: {} - - trough@2.2.0: {} - - ts-api-utils@2.0.0(typescript@5.7.3): - dependencies: - typescript: 5.7.3 - - ts-interface-checker@0.1.13: {} - - ts-morph@18.0.0: - dependencies: - '@ts-morph/common': 0.19.0 - code-block-writer: 12.0.0 - - tsconfig-paths@3.15.0: - dependencies: - '@types/json5': 0.0.29 - json5: 1.0.2 - minimist: 1.2.8 - strip-bom: 3.0.0 - - tsconfig-paths@4.2.0: - dependencies: - json5: 2.2.3 - minimist: 1.2.8 - strip-bom: 3.0.0 - - tslib@2.8.1: {} - - type-check@0.4.0: - dependencies: - prelude-ls: 1.2.1 - - type-fest@0.21.3: {} - - type-fest@4.37.0: {} - - type-graphql@2.0.0-rc.1(class-validator@0.14.1)(graphql-scalars@1.24.2(graphql@16.10.0))(graphql@16.10.0): - dependencies: - '@graphql-yoga/subscription': 5.0.3 - '@types/node': 20.17.12 - '@types/semver': 7.5.8 - graphql: 16.10.0 - graphql-query-complexity: 0.12.0(graphql@16.10.0) - graphql-scalars: 1.24.2(graphql@16.10.0) - semver: 7.6.3 - tslib: 2.8.1 - optionalDependencies: - class-validator: 0.14.1 - - type-is@1.6.18: - dependencies: - media-typer: 0.3.0 - mime-types: 2.1.35 - - type-is@2.0.1: - dependencies: - content-type: 1.0.5 - media-typer: 1.1.0 - mime-types: 3.0.1 - - typed-array-buffer@1.0.3: - dependencies: - call-bound: 1.0.3 - es-errors: 1.3.0 - is-typed-array: 1.1.15 - - typed-array-byte-length@1.0.3: - dependencies: - call-bind: 1.0.8 - for-each: 0.3.3 - gopd: 1.2.0 - has-proto: 1.2.0 - is-typed-array: 1.1.15 - - typed-array-byte-offset@1.0.4: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.8 - for-each: 0.3.3 - gopd: 1.2.0 - has-proto: 1.2.0 - is-typed-array: 1.1.15 - reflect.getprototypeof: 1.0.10 - - typed-array-length@1.0.7: - dependencies: - call-bind: 1.0.8 - for-each: 0.3.3 - gopd: 1.2.0 - is-typed-array: 1.1.15 - possible-typed-array-names: 1.0.0 - reflect.getprototypeof: 1.0.10 - - typescript@5.7.3: {} - - unbox-primitive@1.1.0: - dependencies: - call-bound: 1.0.3 - has-bigints: 1.1.0 - has-symbols: 1.1.0 - which-boxed-primitive: 1.1.1 - - undici-types@5.26.5: {} - - undici-types@6.19.8: {} - - undici-types@6.21.0: {} - - unified@10.1.2: - dependencies: - '@types/unist': 2.0.11 - bail: 2.0.2 - extend: 3.0.2 - is-buffer: 2.0.5 - is-plain-obj: 4.1.0 - trough: 2.2.0 - vfile: 5.3.7 - - unist-util-generated@2.0.1: {} - - unist-util-is@5.2.1: - dependencies: - '@types/unist': 2.0.11 - - unist-util-position@4.0.4: - dependencies: - '@types/unist': 2.0.11 - - unist-util-stringify-position@3.0.3: - dependencies: - '@types/unist': 2.0.11 - - unist-util-visit-parents@5.1.3: - dependencies: - '@types/unist': 2.0.11 - unist-util-is: 5.2.1 - - unist-util-visit@4.1.2: - dependencies: - '@types/unist': 2.0.11 - unist-util-is: 5.2.1 - unist-util-visit-parents: 5.1.3 - - universalify@0.2.0: {} - - universalify@2.0.1: {} - - unpipe@1.0.0: {} - - untruncate-json@0.0.1: {} - - update-browserslist-db@1.1.2(browserslist@4.24.4): - dependencies: - browserslist: 4.24.4 - escalade: 3.2.0 - picocolors: 1.1.1 - - uri-js@4.4.1: - dependencies: - punycode: 2.3.1 - - url-parse@1.5.10: - dependencies: - querystringify: 2.2.0 - requires-port: 1.0.0 - - urlpattern-polyfill@10.0.0: {} - - urql@4.2.2(@urql/core@5.1.1(graphql@16.10.0))(react@19.0.0): - dependencies: - '@urql/core': 5.1.1(graphql@16.10.0) - react: 19.0.0 - wonka: 6.3.5 - - util-deprecate@1.0.2: {} - - utils-merge@1.0.1: {} - - uuid@10.0.0: {} - - uuid@11.1.0: {} - - uuid@9.0.1: {} - - uvu@0.5.6: - dependencies: - dequal: 2.0.3 - diff: 5.2.0 - kleur: 4.1.5 - sade: 1.8.1 - - validator@13.12.0: {} - - vary@1.1.2: {} - - vfile-message@3.1.4: - dependencies: - '@types/unist': 2.0.11 - unist-util-stringify-position: 3.0.3 - - vfile@5.3.7: - dependencies: - '@types/unist': 2.0.11 - is-buffer: 2.0.5 - unist-util-stringify-position: 3.0.3 - vfile-message: 3.1.4 - - wcwidth@1.0.1: - dependencies: - defaults: 1.0.4 - - web-streams-polyfill@3.3.3: {} - - web-streams-polyfill@4.0.0-beta.3: {} - - webidl-conversions@3.0.1: {} - - whatwg-url@5.0.0: - dependencies: - tr46: 0.0.3 - webidl-conversions: 3.0.1 - - which-boxed-primitive@1.1.1: - dependencies: - is-bigint: 1.1.0 - is-boolean-object: 1.2.1 - is-number-object: 1.1.1 - is-string: 1.1.1 - is-symbol: 1.1.1 - - which-builtin-type@1.2.1: - dependencies: - call-bound: 1.0.3 - function.prototype.name: 1.1.8 - has-tostringtag: 1.0.2 - is-async-function: 2.1.0 - is-date-object: 1.1.0 - is-finalizationregistry: 1.1.1 - is-generator-function: 1.1.0 - is-regex: 1.2.1 - is-weakref: 1.1.0 - isarray: 2.0.5 - which-boxed-primitive: 1.1.1 - which-collection: 1.0.2 - which-typed-array: 1.1.18 - - which-collection@1.0.2: - dependencies: - is-map: 2.0.3 - is-set: 2.0.3 - is-weakmap: 2.0.2 - is-weakset: 2.0.4 - - which-typed-array@1.1.18: - dependencies: - available-typed-arrays: 1.0.7 - call-bind: 1.0.8 - call-bound: 1.0.3 - for-each: 0.3.3 - gopd: 1.2.0 - has-tostringtag: 1.0.2 - - which@2.0.2: - dependencies: - isexe: 2.0.0 - - wonka@6.3.5: {} - - word-wrap@1.2.5: {} - - wrap-ansi@6.2.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrap-ansi@7.0.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrap-ansi@8.1.0: - dependencies: - ansi-styles: 6.2.1 - string-width: 5.1.2 - strip-ansi: 7.1.0 - - wrappy@1.0.2: {} - - ws@8.18.3: {} - - xtend@4.0.2: {} - - y18n@5.0.8: {} - - yallist@3.1.1: {} - - yallist@4.0.0: {} - - yaml@2.7.0: {} - - yargs-parser@21.1.1: {} - - yargs@17.7.2: - dependencies: - cliui: 8.0.1 - escalade: 3.2.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 21.1.1 - - yocto-queue@0.1.0: {} - - yoctocolors-cjs@2.1.2: {} - - zod-to-json-schema@3.24.4(zod@3.24.1): - dependencies: - zod: 3.24.1 - - zod-to-json-schema@3.25.0(zod@3.24.1): - dependencies: - zod: 3.24.1 - - zod@3.24.1: {} - - zwitch@2.0.4: {} diff --git a/registry/postcss.config.mjs b/registry/postcss.config.mjs deleted file mode 100644 index 1a69fd2a45..0000000000 --- a/registry/postcss.config.mjs +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import('postcss-load-config').Config} */ -const config = { - plugins: { - tailwindcss: {}, - }, -}; - -export default config; diff --git a/registry/public/r/agent-layout.json b/registry/public/r/agent-layout.json deleted file mode 100644 index eb57a9a61d..0000000000 --- a/registry/public/r/agent-layout.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "$schema": "https://ui.shadcn.com/schema/registry-item.json", - "name": "agent-layout", - "type": "registry:block", - "title": "Agent Layout", - "description": "A layout for CopilotKit with an agent.", - "dependencies": [ - "@copilotkit/react-core" - ], - "files": [ - { - "path": "registry/layout/agent-layout.tsx", - "content": "import \"@copilotkit/react-ui/styles.css\";\nimport React, { ReactNode } from \"react\";\nimport { CopilotKit } from \"@copilotkit/react-core\";\n\n// Where CopilotKit will proxy requests to. If you're using Copilot Cloud, this environment variable will be empty.\nconst runtimeUrl = process.env.NEXT_PUBLIC_COPILOTKIT_RUNTIME_URL\n// When using Copilot Cloud, all we need is the publicApiKey.\nconst publicApiKey = process.env.NEXT_PUBLIC_COPILOT_API_KEY;\n// The name of the agent that we'll be using.\nconst agentName = process.env.NEXT_PUBLIC_COPILOTKIT_AGENT_NAME\n\nexport default function Layout({ children }: { children: ReactNode }) {\n return (\n \n {children}\n \n );\n}\n", - "type": "registry:page", - "target": "app/copilotkit/layout.tsx" - } - ] -} \ No newline at end of file diff --git a/registry/public/r/agent-starter.json b/registry/public/r/agent-spec.json similarity index 78% rename from registry/public/r/agent-starter.json rename to registry/public/r/agent-spec.json index c786c30ed4..cbe6f3bed3 100644 --- a/registry/public/r/agent-starter.json +++ b/registry/public/r/agent-spec.json @@ -1,12 +1,13 @@ { "$schema": "https://ui.shadcn.com/schema/registry-item.json", - "name": "agent-starter", + "name": "agent-spec", "type": "registry:block", - "title": "Agent Starter", - "description": "A starter for CopilotKit with an agent.", + "title": "Open Agent Spec Starter", + "description": "A starter for CopilotKit with an Agent Spec backend.", "dependencies": [ "@copilotkit/runtime", - "@copilotkit/react-core" + "@copilotkit/react-core", + "@copilotkit/react-ui" ], "files": [ { @@ -17,9 +18,9 @@ }, { "path": "registry/runtime/remote-endpoint.ts", - "content": "import { NextRequest } from \"next/server\";\nimport {\n CopilotRuntime,\n copilotRuntimeNextJSAppRouterEndpoint,\n ExperimentalEmptyAdapter,\n} from \"@copilotkit/runtime\";\n\nconst serviceAdapter = new ExperimentalEmptyAdapter();\n\nconst runtime = new CopilotRuntime({\n remoteEndpoints: [\n { url: process.env.COPILOTKIT_REMOTE_ENDPOINT || \"http://localhost:8000/copilotkit\" },\n ],\n});\n\nexport const POST = async (req: NextRequest) => {\n const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({\n runtime,\n serviceAdapter,\n endpoint: \"/api/copilotkit\",\n });\n\n return handleRequest(req);\n};\n", + "content": "import { NextRequest } from \"next/server\";\nimport {\n CopilotRuntime,\n copilotRuntimeNextJSAppRouterEndpoint,\n ExperimentalEmptyAdapter,\n} from \"@copilotkit/runtime\";\n\nconst serviceAdapter = new ExperimentalEmptyAdapter();\n\nconst runtime = new CopilotRuntime({\n remoteEndpoints: [\n { url: process.env.COPILOTKIT_REMOTE_ENDPOINT || \"http://localhost:8000/chat\" },\n ],\n});\n\nexport const POST = async (req: NextRequest) => {\n const { handleRequest } = copilotRuntimeNextJSAppRouterEndpoint({\n runtime,\n serviceAdapter,\n endpoint: \"/api/copilotkit\",\n });\n\n return handleRequest(req);\n};\n", "type": "registry:lib", "target": "app/api/copilotkit/route.ts" } ] -} \ No newline at end of file +} diff --git a/registry/public/r/chat.json b/registry/public/r/chat.json deleted file mode 100644 index cecf526c06..0000000000 --- a/registry/public/r/chat.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "$schema": "https://ui.shadcn.com/schema/registry-item.json", - "name": "chat", - "type": "registry:page", - "title": "Chat", - "description": "A chat component for CopilotKit.", - "dependencies": [ - "@copilotkit/react-ui" - ], - "files": [ - { - "path": "registry/chat/chat.tsx", - "content": "import { CopilotChat } from \"@copilotkit/react-ui\";\n\nexport default function Chat() {\n return ;\n}\n", - "type": "registry:page", - "target": "app/copilotkit/page.tsx" - } - ] -} \ No newline at end of file diff --git a/registry/public/r/coagents-crew-starter.json b/registry/public/r/coagents-crew-starter.json deleted file mode 100644 index 40fdd3c351..0000000000 --- a/registry/public/r/coagents-crew-starter.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "$schema": "https://ui.shadcn.com/schema/registry-item.json", - "name": "coagents-crew-starter", - "type": "registry:page", - "title": "Coagents Crew Starter", - "description": "Quickstart for CopilotKit and CrewAI Crews on CrewAI Enterprise.", - "dependencies": [ - "@copilotkit/react-core", - "@copilotkit/react-ui", - "@copilotkit/runtime-client-gql" - ], - "files": [ - { - "path": "hooks/use-coagents-crew-starter.tsx", - "content": "\"use client\";\nimport {\n CrewsAgentState,\n CrewsResponseStatus,\n useCoAgent,\n useCoAgentStateRender,\n useCopilotAction,\n useCopilotAdditionalInstructions,\n useCopilotChat,\n} from \"@copilotkit/react-core\";\nimport { useEffect, useState } from \"react\";\nimport CrewHumanFeedbackRenderer, {\n CrewsFeedback,\n} from \"@/registry/crews/crew-human-feedback-renderer\";\nimport CrewStateRenderer from \"@/registry/crews/crew-state-renderer\";\nimport { MessageRole, TextMessage } from \"@copilotkit/runtime-client-gql\";\nimport { CrewInChatInput } from \"@/registry/crews/crew-in-chat-input\";\n\n/**\n * Hook: useCoagentsCrewStarter\n *\n * This hook provides a simplified interface for initializing and managing \n * a copilot crew in your application. It handles:\n * \n * 1. Initialization with configured agent name from environment variables\n * 2. Collection of user inputs through a form interface\n * 3. Real-time state visualization during execution\n * 4. Feedback collection when the crew needs user input\n * 5. Result aggregation and presentation\n *\n * @param {Object} params - Parameters for initializing the crew\n * @param {Array} params.inputs - Input field names to collect from the user\n * @returns {Object} - An object containing the crew's output\n * \n * @example\n * ```tsx\n * const { output } = useCoagentsCrewStarter({\n * inputs: [\"query\", \"location\"]\n * });\n * ```\n */\nexport const useCoagentsCrewStarter = ({\n inputs,\n}: {\n inputs: Array;\n}): {\n output: string;\n} => {\n const [initialMessageSent, setInitialMessageSent] = useState(false);\n \n // Use the agent name from environment variables\n const agentName = process.env.NEXT_PUBLIC_COPILOTKIT_AGENT_NAME || \"DefaultAgent\";\n\n // Initialize the crew agent with a default state\n const { state, setState, run } = useCoAgent<\n CrewsAgentState & {\n result: string;\n inputs: Record;\n }\n >({\n name: agentName,\n initialState: {\n inputs: {},\n result: \"Crew result will appear here...\",\n },\n });\n\n const { appendMessage, isLoading } = useCopilotChat();\n\n // Instructions for the copilot to ensure inputs are gathered\n const instructions =\n \"INPUTS ARE ABSOLUTELY REQUIRED. Please call getInputs before proceeding with anything else.\";\n\n // Send initial greeting when chat is loaded\n useEffect(() => {\n if (initialMessageSent || isLoading) return;\n\n setTimeout(async () => {\n await appendMessage(\n new TextMessage({\n content: \"Hi! Please provide your inputs to get started.\",\n role: MessageRole.Developer,\n })\n );\n setInitialMessageSent(true);\n }, 0);\n }, [initialMessageSent, isLoading, appendMessage]);\n\n // Send a message with the inputs once they are provided\n useEffect(() => {\n if (!initialMessageSent && Object.values(state?.inputs || {}).length > 0) {\n appendMessage(\n new TextMessage({\n role: MessageRole.Developer,\n content: \"My inputs are: \" + JSON.stringify(state?.inputs),\n })\n ).then(() => {\n setInitialMessageSent(true);\n });\n }\n }, [initialMessageSent, state?.inputs, appendMessage]);\n\n // Provide additional instructions to the copilot\n useCopilotAdditionalInstructions({\n instructions,\n available:\n Object.values(state?.inputs || {}).length > 0 ? \"enabled\" : \"disabled\",\n });\n\n // Action to get inputs from the user\n useCopilotAction({\n name: \"getInputs\",\n description:\n \"Collect required inputs from the user before starting the crew execution.\",\n renderAndWaitForResponse({ status, respond }) {\n if (status === \"inProgress\" || status === \"executing\") {\n return (\n {\n setState({\n ...state,\n inputs: inputValues,\n });\n respond?.(\"Inputs submitted\");\n }}\n />\n );\n }\n return
Inputs submitted
;\n },\n });\n\n // Render the crew's state in real-time\n useCoAgentStateRender({\n name: agentName,\n render: ({ state, status }) => (\n \n ),\n });\n\n // Action to handle feedback requests from the crew\n useCopilotAction({\n name: \"crew_requesting_feedback\",\n description: \"Request feedback from the user on the crew's output\",\n renderAndWaitForResponse(props) {\n const { status, args, respond } = props;\n return (\n \n );\n },\n });\n\n // Return the output result of the crew\n return {\n output: state?.result || \"\",\n };\n};\n", - "type": "registry:hook", - "target": "hooks/use-coagents-crew-starter.tsx" - }, - { - "path": "registry/crews/crew-in-chat-input.tsx", - "content": "import React from \"react\";\n\n/**\n * Props for the CrewInChatInput component\n * \n * @property status - The current status of the crew operation\n * @property inputs - Array of input field names to render\n * @property onSubmit - Callback function triggered when form is submitted\n */\ninterface CrewInChatInputProps {\n status: \"inProgress\" | \"executing\" | string;\n inputs: string[];\n onSubmit: (inputs: Record) => Promise;\n}\n\n/**\n * A form component that renders dynamic input fields for crew interactions\n * \n * This component creates a form with input fields based on the provided\n * input names. It's designed to collect information from users in a \n * conversational interface during crew execution.\n * \n * @example\n * ```tsx\n * {\n * console.log(values); // { query: \"...\", location: \"...\" }\n * }}\n * />\n * ```\n */\nexport const CrewInChatInput: React.FC = ({\n status,\n inputs,\n onSubmit,\n}) => {\n // If not in progress or executing, show that inputs were submitted\n console.log(\"status\", status);\n if (status !== \"inProgress\" && status !== \"executing\") {\n return
Inputs submitted
;\n }\n\n return (\n ) => {\n e.preventDefault();\n const formData = new FormData(e.currentTarget);\n const inputValues = Object.fromEntries(\n inputs.map((input) => [input, formData.get(input)?.toString() || \"\"])\n );\n\n await onSubmit(inputValues);\n }}\n >\n
\n {inputs.map((input) => (\n \n \n \n
\n ))}\n \n Submit\n \n \n \n );\n};\n", - "type": "registry:component", - "target": "components/crew-in-chat-input.tsx" - }, - { - "path": "registry/crews/crew-state-renderer.tsx", - "content": "import {\n CrewsAgentState,\n CrewsResponseStatus,\n CrewsTaskStateItem,\n CrewsToolStateItem,\n} from \"@copilotkit/react-core\";\nimport { useEffect } from \"react\";\nimport { useMemo, useRef, useState } from \"react\";\n\n/**\n * Component that renders the crew's execution state in real-time\n * \n * This component visualizes:\n * - Steps being executed by the crew\n * - Tasks being performed\n * - Thoughts and results during execution\n * \n * Features:\n * - Collapsible UI to save space\n * - Auto-scrolling to newest items\n * - Highlighting of newly added items\n * \n * @param state - The current state of the crew agent\n * @param status - The response status of the crew\n */\nfunction CrewStateRenderer({\n state,\n status,\n}: {\n state: CrewsAgentState;\n status: CrewsResponseStatus;\n}) {\n const [isCollapsed, setIsCollapsed] = useState(true);\n const contentRef = useRef(null);\n const prevItemsLengthRef = useRef(0);\n const [highlightId, setHighlightId] = useState(null);\n\n // Combine and sort steps and tasks by timestamp\n const items = useMemo(() => {\n if (!state) return [];\n return [...(state.steps || []), ...(state.tasks || [])].sort(\n (a, b) =>\n new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()\n );\n }, [state]);\n\n // Handle highlighting of new items and auto-scrolling\n useEffect(() => {\n if (!state) return;\n if (items.length > prevItemsLengthRef.current) {\n const newestItem = items[items.length - 1];\n setHighlightId(newestItem.id);\n setTimeout(() => setHighlightId(null), 1500);\n\n if (contentRef.current && !isCollapsed) {\n contentRef.current.scrollTop = contentRef.current.scrollHeight;\n }\n }\n prevItemsLengthRef.current = items.length;\n }, [items, isCollapsed, state]);\n\n if (!state) {\n return
Loading crew state...
;\n }\n\n // Hide entirely if collapsed & empty & not in progress\n if (isCollapsed && items.length === 0 && status !== \"inProgress\") return null;\n\n return (\n
\n setIsCollapsed(!isCollapsed)}\n >\n {isCollapsed ? \"▶\" : \"▼\"}\n \n {status === \"inProgress\" ? \n Crew is analyzing... : \n Crew analysis\n }\n {!isCollapsed && items.length > 0 && ` (${items.length} steps)`}\n \n
\n\n {!isCollapsed && (\n \n {items.length > 0 ? (\n items.map((item) => {\n const isTool = (item as CrewsToolStateItem).tool !== undefined;\n const isHighlighted = item.id === highlightId;\n return (\n \n
\n {isTool\n ? (item as CrewsToolStateItem).tool\n : (item as CrewsTaskStateItem).name}\n
\n {\"thought\" in item && item.thought && (\n
\n Thought: {item.thought}\n
\n )}\n {\"result\" in item && item.result !== undefined && (\n
\n                      {typeof item.result === 'object' \n                        ? JSON.stringify(item.result, null, 2)\n                        : item.result}\n                    
\n )}\n {\"description\" in item && item.description && (\n
{item.description}
\n )}\n \n );\n })\n ) : (\n
No activity yet...
\n )}\n \n )}\n\n {/* Custom animation for highlighting new items */}\n \n \n );\n}\n\nexport default CrewStateRenderer;\n", - "type": "registry:component", - "target": "components/crew-state-renderer.tsx" - }, - { - "path": "registry/crews/crew-human-feedback-renderer.tsx", - "content": "import { CrewsResponseStatus, CrewsStateItem } from \"@copilotkit/react-core\";\nimport { Markdown } from \"@copilotkit/react-ui\";\nimport { useState } from \"react\";\n\n/**\n * Interface defining the feedback structure requested by the crew agents\n */\nexport interface CrewsFeedback extends CrewsStateItem {\n /**\n * Output of the task execution that requires user feedback\n */\n task_output?: string;\n}\n\n/**\n * Component that renders a UI for agent-requested user feedback\n * \n * This component presents the task output from the crew and provides\n * buttons for the user to approve or reject the proposed solution.\n * \n * @param feedback - The feedback object containing task output\n * @param respond - Callback function to send user response back to the crew\n * @param status - Current status of the feedback request\n */\nfunction CrewHumanFeedbackRenderer({\n feedback,\n respond,\n status,\n}: {\n feedback: CrewsFeedback;\n respond?: (input: string) => void;\n status: CrewsResponseStatus;\n}) {\n const [isExpanded, setIsExpanded] = useState(true);\n const [userResponse, setUserResponse] = useState(null);\n\n // If feedback request is complete, show the user's response\n if (status === \"complete\") {\n return (\n
\n {userResponse || \"Feedback submitted.\"}\n
\n );\n }\n\n // If feedback request is in progress, show the feedback UI\n if (status === \"inProgress\" || status === \"executing\") {\n return (\n
\n
\n

Feedback Required

\n setIsExpanded(!isExpanded)}\n >\n {isExpanded ? \"Hide\" : \"Show\"} Details\n \n
\n\n {isExpanded && (\n
\n \n
\n )}\n \n
\n {\n setUserResponse(\"Rejected\");\n // Send 'Reject' feedback to the crew\n respond?.(\"Reject\");\n }}\n >\n Reject\n \n {\n setUserResponse(\"Approved\");\n // Send 'Approve' feedback to the crew\n respond?.(\"Approve\");\n }}\n >\n Approve\n \n
\n
\n );\n }\n\n return null;\n}\n\nexport default CrewHumanFeedbackRenderer;\n", - "type": "registry:component", - "target": "components/crew-human-feedback-renderer.tsx" - }, - { - "path": "registry/layout/coagents-crew-starter-layout.tsx", - "content": "\"use client\";\nimport \"@copilotkit/react-ui/styles.css\";\nimport React from \"react\";\nimport { CopilotChat } from \"@copilotkit/react-ui\";\nimport { CopilotKit } from \"@copilotkit/react-core\";\n\n// Read environment variables for copilot configuration\nconst apiKey = process.env.NEXT_PUBLIC_COPILOT_API_KEY || \"\";\nconst agentName = process.env.NEXT_PUBLIC_COPILOTKIT_AGENT_NAME || \"DefaultAgent\";\n\n/**\n * Layout component for the CopilotKit interface\n * \n * This component creates a two-column layout:\n * 1. Left column (60%): Chat interface for user interaction\n * 2. Right column (40%): Results panel to display crew output\n * \n * It configures CopilotKit with environment variables for API key and agent name\n * and provides optimized styling for both light and dark modes.\n */\nexport default function CoagentsCrewStarterLayout({\n children,\n}: {\n children: React.ReactNode;\n}) {\n return (\n \n
\n
\n {/* Chat Column */}\n
\n \n
\n\n {/* Results Column */}\n
\n
\n {children}\n
\n
\n
\n
\n
\n );\n}\n", - "type": "registry:component", - "target": "app/copilotkit/layout.tsx" - }, - { - "path": "registry/quickstarts/coagents-crew-starter.tsx", - "content": "\"use client\";\nimport React from \"react\";\nimport { useCoagentsCrewStarter } from \"@/hooks/use-coagents-crew-starter\";\n\n/**\n * Format text output from the Crew for better readability\n * \n * This utility function:\n * - Preserves existing formatting if present\n * - Converts markdown bold (**text**) to HTML tags\n * - Adds appropriate spacing for listed items\n * - Handles both pre-formatted and unformatted text\n * \n * @param text - The raw text output from the crew\n * @returns Formatted HTML string ready for display\n */\nfunction formatText(text: string): string {\n if (!text) return \"\";\n\n // Check if text already has formatting (multiple consecutive newlines)\n const hasFormatting = /\\n\\s*\\n/.test(text);\n\n // Process markdown elements\n let formatted = text;\n\n // Convert markdown bold to HTML bold\n formatted = formatted.replace(/\\*\\*([^*]+)\\*\\*/g, \"$1\");\n \n // Convert markdown lists to proper HTML with spacing\n formatted = formatted.replace(/^- (.+)$/gm, \"
  • $1
  • \");\n formatted = formatted.replace(/(
  • .+<\\/li>\\n)+/g, \"
      $&
    \");\n\n if (hasFormatting) {\n // Just convert newlines to
    tags for pre-formatted text\n return formatted.replace(/\\n/g, \"
    \");\n } else {\n // For unformatted text, add proper spacing\n // Add double line breaks before numbered items\n formatted = formatted.replace(/(\\d+\\.)/g, \"

    $1\");\n\n // Add single line breaks before properties\n formatted = formatted.replace(/(\\s-\\s)/g, \"
    $1\");\n\n return formatted;\n }\n}\n\n/**\n * Main component for the Copilot Crew interface\n * \n * This component:\n * 1. Initializes the crew with required input fields\n * 2. Renders the formatted output from the crew\n * 3. Provides a clean, readable interface for users\n */\nexport default function CoagentsCrewStarter() {\n const { output } = useCoagentsCrewStarter({\n /**\n * Define the input fields needed to start your crew.\n * These will be presented as a form in the chat interface.\n */\n inputs: [\"YOUR_INPUTS_HERE\"],\n });\n\n return (\n
    \n

    \n Crew Results\n

    \n \n {!output || output === \"Crew result will appear here...\" ? (\n
    \n
    \n Waiting for input...\n
    \n
    \n Results will appear here after providing inputs in the chat\n
    \n
    \n ) : (\n
    \n
    \n
    \n )}\n
    \n );\n}\n", - "type": "registry:page", - "target": "app/copilotkit/page.tsx" - } - ] -} \ No newline at end of file diff --git a/registry/public/r/coagents-starter-crewai-flows.json b/registry/public/r/coagents-starter-crewai-flows.json deleted file mode 100644 index 85fef1c65d..0000000000 --- a/registry/public/r/coagents-starter-crewai-flows.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "$schema": "https://ui.shadcn.com/schema/registry-item.json", - "name": "coagents-starter-crewai-flows", - "type": "registry:page", - "title": "Coagents Starter CrewAI Flows", - "description": "Quickstart for CopilotKit and CrewAI Flows on CrewAI Enterprise.", - "dependencies": [ - "@copilotkit/react-core", - "@copilotkit/react-ui" - ], - "files": [ - { - "path": "registry/quickstarts/coagents-starter-crewai-flows/page.tsx", - "content": "\"use client\";\nimport \"@copilotkit/react-ui/styles.css\";\nimport { CopilotKit, useCopilotAction } from \"@copilotkit/react-core\";\nimport { CopilotChat } from \"@copilotkit/react-ui\";\nimport React, { useState } from \"react\";\n\nconst publicApiKey = process.env.NEXT_PUBLIC_COPILOT_API_KEY;\nconst agentName = process.env.NEXT_PUBLIC_COPILOTKIT_AGENT_NAME;\n\nif (!publicApiKey || !agentName) {\n throw new Error(\"Missing environment variables\");\n}\n\n// Main Chat Component: Handles chat interface and background customization\nconst Chat = () => {\n const [background, setBackground] = useState(\n \"linear-gradient(135deg, #667eea 0%, #764ba2 100%)\"\n );\n\n // Action: Allow AI to change background color dynamically\n useCopilotAction({\n name: \"change_background\",\n description:\n \"Change the background color of the chat. Can be anything that the CSS background attribute accepts. Regular colors, linear of radial gradients etc.\",\n parameters: [\n {\n name: \"background\",\n type: \"string\",\n description: \"The background. Prefer gradients.\",\n },\n ],\n handler: ({ background }) => setBackground(background),\n followUp: false,\n });\n\n return (\n \n
    \n
    \n \n
    \n
    \n
    \n );\n};\n\n// App Component: Main wrapper that provides CopilotKit context\nconst CrewAIFlow: React.FC = () => (\n \n \n \n);\n\nexport default CrewAIFlow;\n", - "type": "registry:page", - "target": "app/copilotkit/page.tsx" - } - ] -} \ No newline at end of file diff --git a/registry/public/r/coagents-starter-ui.json b/registry/public/r/coagents-starter-ui.json deleted file mode 100644 index 5e9f7fd2ad..0000000000 --- a/registry/public/r/coagents-starter-ui.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "$schema": "https://ui.shadcn.com/schema/registry-item.json", - "name": "coagents-starter-ui", - "type": "registry:block", - "title": "Coagents Starter UI", - "description": "UI for interacting with the a starter coagent.", - "dependencies": [ - "@copilotkit/react-ui", - "@copilotkit/react-core" - ], - "files": [ - { - "path": "registry/quickstarts/coagents-starter.tsx", - "content": "\"use client\";\n\nimport { useCoAgent, useCopilotAction } from \"@copilotkit/react-core\";\nimport { CopilotKitCSSProperties, CopilotSidebar } from \"@copilotkit/react-ui\";\nimport { useState } from \"react\";\n\nexport default function CopilotKitPage() {\n const [themeColor, setThemeColor] = useState(\"#6366f1\");\n\n // 🪁 Frontend Actions: https://docs.copilotkit.ai/guides/frontend-actions\n useCopilotAction({\n name: \"setThemeColor\",\n parameters: [{\n name: \"themeColor\",\n description: \"The theme color to set. Make sure to pick nice colors.\",\n required: true, \n }],\n handler({ themeColor }) {\n setThemeColor(themeColor);\n },\n });\n\n return (\n
    \n \n \n
    \n );\n}\n\n// State of the agent, make sure this aligns with your agent's state.\ntype AgentState = {\n proverbs: string[];\n}\n\nfunction YourMainContent({ themeColor }: { themeColor: string }) {\n // 🪁 Shared State: https://docs.copilotkit.ai/coagents/shared-state\n const {state, setState} = useCoAgent({\n name: \"sample_agent\",\n initialState: {\n proverbs: [\n \"CopilotKit may be new, but its the best thing since sliced bread.\",\n ],\n },\n })\n\n // 🪁 Frontend Actions: https://docs.copilotkit.ai/coagents/frontend-actions\n useCopilotAction({\n name: \"addProverb\",\n parameters: [{\n name: \"proverb\",\n description: \"The proverb to add. Make it witty, short and concise.\",\n required: true,\n }],\n handler: ({ proverb }) => {\n setState({\n ...state,\n proverbs: [...state.proverbs, proverb],\n });\n },\n });\n\n //🪁 Generative UI: https://docs.copilotkit.ai/coagents/generative-ui\n useCopilotAction({\n name: \"getWeather\",\n description: \"Get the weather for a given location.\",\n available: \"disabled\",\n parameters: [\n { name: \"location\", type: \"string\", required: true },\n ],\n render: ({ args }) => {\n return \n },\n });\n\n return (\n \n
    \n

    Proverbs

    \n

    This is a demonstrative page, but it could be anything you want! 🪁

    \n
    \n
    \n {state.proverbs?.map((proverb, index) => (\n
    \n

    {proverb}

    \n \n
    \n ))}\n
    \n {state.proverbs?.length === 0 &&

    \n No proverbs yet. Ask the assistant to add some!\n

    }\n
    \n \n );\n}\n\n// Simple sun icon for the weather card\nfunction SunIcon() {\n return (\n \n \n \n \n );\n}\n\n// Weather card component where the location and themeColor are based on what the agent\n// sets via tool calls.\nfunction WeatherCard({ location, themeColor }: { location?: string, themeColor: string }) {\n return (\n \n
    \n
    \n
    \n

    {location}

    \n

    Current Weather

    \n
    \n \n
    \n \n
    \n
    70°
    \n
    Clear skies
    \n
    \n \n
    \n
    \n
    \n

    Humidity

    \n

    45%

    \n
    \n
    \n

    Wind

    \n

    5 mph

    \n
    \n
    \n

    Feels Like

    \n

    72°

    \n
    \n
    \n
    \n
    \n \n );\n}\n", - "type": "registry:page", - "target": "app/copilotkit/page.tsx" - } - ] -} \ No newline at end of file diff --git a/registry/public/r/crew-quickstart.json b/registry/public/r/crew-quickstart.json deleted file mode 100644 index 4183bde6c1..0000000000 --- a/registry/public/r/crew-quickstart.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "$schema": "https://ui.shadcn.com/schema/registry-item.json", - "name": "crew-quickstart", - "type": "registry:page", - "title": "Crew Enterprise Quickstart", - "description": "A quickstart guide for setting up CopilotKit in an enterprise environment with a crew.", - "dependencies": [ - "@copilotkit/react-core", - "@copilotkit/runtime-client-gql" - ], - "files": [ - { - "path": "hooks/crew-quickstart/use-crew-quickstart.tsx", - "content": "\"use client\";\n\nimport {\n CrewsAgentState,\n CrewsResponseStatus,\n CrewsStateItem,\n CrewsTaskStateItem,\n CrewsToolStateItem,\n useCoAgent,\n useCoAgentStateRender,\n useCopilotAction,\n useCopilotChat,\n useCopilotAdditionalInstructions,\n} from \"@copilotkit/react-core\";\nimport { useEffect, useMemo, useRef, useState } from \"react\";\n\nimport { MessageRole, TextMessage } from \"@copilotkit/runtime-client-gql\";\n\ninterface CrewsFeedback extends CrewsStateItem {\n /**\n * Output of the task execution\n */\n task_output?: string;\n}\n\n/**\n * Renders your Crew's steps & tasks in real-time.\n */\nfunction CrewStateRenderer({\n state,\n status,\n}: {\n state: CrewsAgentState;\n status: CrewsResponseStatus;\n}) {\n const [isCollapsed, setIsCollapsed] = useState(true);\n const contentRef = useRef(null);\n const prevItemsLengthRef = useRef(0);\n const [highlightId, setHighlightId] = useState(null);\n\n // Combine steps + tasks\n const items = useMemo(() => {\n if (!state) return [];\n return [...(state.steps || []), ...(state.tasks || [])].sort(\n (a, b) =>\n new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()\n );\n }, [state]);\n\n // Highlight newly added item & auto-scroll\n useEffect(() => {\n if (!state) return;\n if (items.length > prevItemsLengthRef.current) {\n const newestItem = items[items.length - 1];\n setHighlightId(newestItem.id);\n setTimeout(() => setHighlightId(null), 1500);\n\n if (contentRef.current && !isCollapsed) {\n contentRef.current.scrollTop = contentRef.current.scrollHeight;\n }\n }\n prevItemsLengthRef.current = items.length;\n }, [items, isCollapsed, state]);\n\n if (!state) {\n return
    Loading crew state...
    ;\n }\n\n // Hide entirely if collapsed & empty & not in progress\n if (isCollapsed && items.length === 0 && status !== \"inProgress\") return null;\n\n return (\n
    \n setIsCollapsed(!isCollapsed)}\n >\n {isCollapsed ? \"▶\" : \"▼\"}\n {status === \"inProgress\" ? \"Crew is analyzing...\" : \"Crew analysis\"}\n
    \n\n {!isCollapsed && (\n \n {items.length > 0 ? (\n items.map((item) => {\n const isTool = (item as CrewsToolStateItem).tool !== undefined;\n const isHighlighted = item.id === highlightId;\n return (\n \n
    \n {isTool\n ? (item as CrewsToolStateItem).tool\n : (item as CrewsTaskStateItem).name}\n
    \n {\"thought\" in item && item.thought && (\n
    \n Thought: {item.thought}\n
    \n )}\n {\"result\" in item && item.result !== undefined && (\n
    \n                      {JSON.stringify(item.result, null, 2)}\n                    
    \n )}\n {\"description\" in item && item.description && (\n
    {item.description}
    \n )}\n \n );\n })\n ) : (\n
    No activity yet...
    \n )}\n \n )}\n\n {/* Simple fadeIn animation */}\n \n \n );\n}\n\n/**\n * Renders a simple UI for agent-requested user feedback (Approve / Reject).\n */\nfunction CrewHumanFeedbackRenderer({\n feedback,\n respond,\n status,\n}: {\n feedback: CrewsFeedback;\n respond?: (input: string) => void;\n status: CrewsResponseStatus;\n}) {\n const [isExpanded, setIsExpanded] = useState(true);\n const [userResponse, setUserResponse] = useState(null);\n\n if (status === \"complete\") {\n return (\n
    \n {userResponse || \"Feedback submitted.\"}\n
    \n );\n }\n\n if (status === \"inProgress\" || status === \"executing\") {\n return (\n
    \n {isExpanded && (\n \n {feedback.task_output}\n
    \n )}\n
    \n setIsExpanded(!isExpanded)}\n >\n {isExpanded ? \"Hide\" : \"Show\"} Feedback\n \n {\n setUserResponse(\"Approved\");\n /**\n * This string is arbitrary. It can be any serializable input that will be forwarded to your Crew as feedback.\n */\n respond?.(\"Approve\");\n }}\n >\n Approve\n \n {\n setUserResponse(\"Rejected\");\n /**\n * This string is arbitrary. It can be any serializable input that will be forwarded to your Crew as feedback.\n */\n respond?.(\"Reject\");\n }}\n >\n Reject\n \n
    \n \n );\n }\n\n return null;\n}\n\n/**\n * useCrewQuickstart\n * Minimal example that:\n * 1) Sets up a crew/agent\n * 2) Handles text-based user input (get_input)\n * 3) Renders real-time crew state\n * 4) Handles \"crew_requesting_feedback\"\n */\nexport const useCrewQuickstart = ({\n crewName,\n inputs,\n}: {\n crewName: string;\n inputs: Array;\n}): {\n output: string;\n} => {\n const [initialMessageSent, setInitialMessageSent] = useState(false);\n\n const { state, setState, run } = useCoAgent<\n CrewsAgentState & {\n result: string;\n inputs: Record;\n }\n >({\n name: crewName,\n initialState: {\n inputs: {},\n result: \"Crew result will appear here...\",\n },\n });\n\n const { appendMessage, isLoading } = useCopilotChat();\n\n const instructions =\n \"INPUTS ARE ABSOLUTELY REQUIRED. Please call getInputs before proceeding with anything else.\";\n\n // Render an initial message when the chat is first loaded\n useEffect(() => {\n if (initialMessageSent || isLoading) return;\n\n setTimeout(async () => {\n await appendMessage(\n new TextMessage({\n content: \"Hi, Please provide your inputs before we get started.\",\n role: MessageRole.Developer,\n })\n );\n setInitialMessageSent(true);\n }, 0);\n }, []);\n\n useEffect(() => {\n if (!initialMessageSent && Object.values(state?.inputs || {}).length > 0) {\n appendMessage(\n new TextMessage({\n role: MessageRole.Developer,\n content: \"My inputs are: \" + JSON.stringify(state?.inputs),\n })\n ).then(() => {\n setInitialMessageSent(true);\n });\n }\n }, [initialMessageSent, state?.inputs]);\n\n useCopilotAdditionalInstructions({\n instructions,\n available:\n Object.values(state?.inputs || {}).length > 0 ? \"enabled\" : \"disabled\",\n });\n\n useCopilotAction({\n name: \"getInputs\",\n followUp: false,\n description:\n \"This action allows Crew to get required inputs from the user before starting the Crew.\",\n renderAndWaitForResponse({ status }) {\n if (status === \"inProgress\" || status === \"executing\") {\n return (\n ) => {\n e.preventDefault();\n const form = e.currentTarget;\n const input = form.elements.namedItem(\n \"input\"\n ) as HTMLTextAreaElement;\n const inputValue = input.value;\n const inputKey = input.id;\n\n setState({\n ...state,\n inputs: {\n ...state.inputs,\n [inputKey]: inputValue,\n },\n });\n setTimeout(async () => {\n console.log(\"running crew\");\n await run();\n console.log(\"crew run complete\");\n }, 0);\n }}\n >\n \n {inputs.map((input) => (\n \n \n \n \n ))}\n \n Submit\n \n \n \n );\n }\n return <>Inputs submitted;\n },\n });\n\n useCoAgentStateRender({\n name: crewName,\n render: ({ state, status }) => (\n \n ),\n });\n\n useCopilotAction({\n name: \"crew_requesting_feedback\",\n description: \"Request feedback from the user\",\n renderAndWaitForResponse(props) {\n const { status, args, respond } = props;\n return (\n \n );\n },\n });\n\n return {\n output: state?.result || \"\",\n };\n};\n", - "type": "registry:hook", - "target": "hooks/crew-quickstart/use-crew-quickstart.tsx" - }, - { - "path": "components/crew-quickstart/index.tsx", - "content": "\"use client\";\nimport React from \"react\";\nimport useCrewQuickstart from \"./use-crew-quickstart\";\nimport { useCoAgent } from \"@copilotkit/react-core\";\nimport { useEffect } from \"react\";\n\nexport default function YourApp() {\n const { output } = useCrewQuickstart({\n crewName: \"\",\n /**\n * List of input required to start your crew (location e.g)\n */\n inputs: [\"location\"],\n });\n return (\n <>\n {/* Existing markup */}\n {output ? (\n \n {output}\n \n ) : null}\n \n );\n}\n", - "type": "registry:page", - "target": "app/crew-quickstart/index.tsx" - } - ] -} \ No newline at end of file diff --git a/registry/public/r/generic-lg-starter.json b/registry/public/r/generic-lg-starter.json deleted file mode 100644 index 2b9c54ef4d..0000000000 --- a/registry/public/r/generic-lg-starter.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "$schema": "https://ui.shadcn.com/schema/registry-item.json", - "name": "generic-lg-starter", - "type": "registry:block", - "title": "Generic LG Starter", - "description": "A starter for CopilotKit with any LangGraph agent.", - "dependencies": [ - "@copilotkit/react-core", - "@copilotkit/react-ui" - ], - "registryDependencies": [ - "tooltip" - ], - "files": [ - { - "path": "registry/quickstarts/coagents-generic-lg/page.tsx", - "content": "\"use client\";\n\nimport { CatchAllActionRenderProps, useCoAgent, useCopilotAction, useLangGraphInterrupt } from \"@copilotkit/react-core\";\nimport { CopilotChat, CopilotKitCSSProperties } from \"@copilotkit/react-ui\";\nimport { ToolCall } from \"@/registry/quickstarts/coagents-generic-lg/components/tool-call\";\nimport { AgentState } from \"@/registry/quickstarts/coagents-generic-lg/components/agent-state\";\nimport { Interrupt } from \"@/registry/quickstarts/coagents-generic-lg/components/interrupt\";\nimport { HTMLAttributes } from \"react\";\n\nexport default function Page() {\n const { state, setState, running } = useCoAgent({\n name: process.env.NEXT_PUBLIC_COPILOTKIT_AGENT_NAME || \"\",\n });\n\n const chatStyles = running ? \"w-2/3 border-r border-slate-200\" : \"w-1/2 h-1/2 mx-auto my-auto\";\n\n return (\n
    \n \n\n {running}\n\n {running && (\n \n )}\n
    \n );\n}\n\nfunction Chat(props: HTMLAttributes) {\n const { running } = useCoAgent({\n name: process.env.NEXT_PUBLIC_COPILOTKIT_AGENT_NAME || \"\",\n });\n\n useLangGraphInterrupt({\n render: ({ event, result, resolve }) => \n \n });\n\n useCopilotAction({\n name: \"*\",\n render: ({ name, args, status, result }: CatchAllActionRenderProps) => { \n return (\n \n );\n },\n });\n\n return (\n
    \n { alert(\"Thumbs down\"); }}\n onThumbsUp={() => { alert(\"Thumbs up\"); }}\n className={running ? \"h-full py-6\" : \"h-full py-6 rounded-xl\"}\n labels={{\n initial: \"Hi! I'm connected to your LangGraph agent. Ask me anything, and I'll show what's happening behind the scenes.\",\n placeholder: \"Type your message...\",\n }}\n />\n
    \n );\n}\n", - "type": "registry:page", - "target": "app/copilotkit/page.tsx" - }, - { - "path": "registry/quickstarts/coagents-generic-lg/layout.tsx", - "content": "import type { Metadata } from \"next\";\n\nimport { CopilotKit } from \"@copilotkit/react-core\";\n\nimport \"@copilotkit/react-ui/styles.css\";\n\nexport const metadata: Metadata = {\n title: \"CoAgents Starter\",\n description: \"CoAgents Starter\",\n};\n\nexport default function RootLayout({ children }: { children: React.ReactNode }) {\n return (\n \n {children}\n \n );\n}", - "type": "registry:page", - "target": "app/copilotkit/layout.tsx" - }, - { - "path": "registry/quickstarts/coagents-generic-lg/components/agent-state.tsx", - "content": "import React from 'react';\n\nexport type AgentStateProps = {\n state: State;\n setState?: (state: State) => void;\n className: string;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type State = any;\n\nexport function AgentState({ state, setState, className}: AgentStateProps) {\n const { messages, ...rest } = state;\n const [editableState, setEditableState] = React.useState(JSON.stringify(rest, null, 2));\n const [isEditing, setIsEditing] = React.useState(false);\n const [hasChanges, setHasChanges] = React.useState(false);\n \n // Update editable state when the actual state changes (if not currently editing)\n React.useEffect(() => {\n if (!isEditing) {\n setEditableState(JSON.stringify(rest, null, 2));\n setHasChanges(false); \n }\n }, [rest, isEditing]);\n\n const handleEditToggle = () => {\n setIsEditing(!isEditing);\n if (isEditing) {\n // Reset to original state when canceling edit\n setEditableState(JSON.stringify(rest, null, 2));\n setHasChanges(false);\n }\n };\n\n const handleStateChange = (e: React.ChangeEvent) => {\n setEditableState(e.target.value);\n setHasChanges(true);\n };\n\n const handleSubmit = () => {\n try {\n const newState = JSON.parse(editableState);\n if (setState) setState({ ...newState, messages });\n setIsEditing(false);\n setHasChanges(false);\n } catch (error) {\n alert(`Invalid JSON format. Please check your input. \\n\\n${error}`);\n }\n };\n\n const wrapperStyles = `${className} bg-gradient-to-r from-indigo-200 to-pink-200 shadow-inner overflow-auto`;\n const headerStyles = `sticky top-0 p-4 pt-4 pb-2 bg-indigo-500/50 backdrop-blur-sm shadow-md flex justify-between items-center z-10`;\n const headerTextStyles = `text-white text-lg font-medium p-4`;\n const agentNameStyles = `underline`;\n const buttonStyles = `px-3 py-1 text-sm rounded bg-slate-200 hover:bg-slate-300 transition-colors text-slate-700`;\n const textareaStyles = `w-full h-[calc(100vh-120px)] font-mono text-sm p-3 bg-white border border-slate-300 bg-opacity-80 backdrop-blur-sm rounded resize-none focus:ring-1 focus:ring-purple-500 focus:border-purple-500 outline-none`;\n const preStyles = `font-mono text-slate-700 h-[calc(100vh-120px)] whitespace-pre-wrap break-words text-sm w-full overflow-x-hidden p-3 bg-white border border-slate-200 rounded`;\n \n return (\n
    \n
    \n

    \n {process.env.NEXT_PUBLIC_AGENT_NAME} state\n

    \n
    \n {hasChanges && (\n \n Save\n \n )}\n \n
    \n
    \n \n
    \n {isEditing ? (\n
    \n \n
    \n ) : (\n
    \n            {editableState}\n          
    \n )}\n
    \n
    \n );\n}", - "type": "registry:component", - "target": "components/agent-state.tsx" - }, - { - "path": "registry/quickstarts/coagents-generic-lg/components/tool-call.tsx", - "content": "import {\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from \"@/components/ui/tooltip\";\nimport { CatchAllActionRenderProps } from \"@copilotkit/react-core\";\n\nexport function ToolCall(toolCallProps: CatchAllActionRenderProps) {\n const triggerStyles = \"inline-flex rounded-xl items-center gap-2 p-2 rounded bg-indigo-500/60 text-white cursor-pointer m-1\";\n const contentStyles = \"bg-white rounded-xl min-w-[300px] max-w-[500px] p-4 border\";\n const statusStyles = \"text-xs px-2 py-0.5 rounded-full bg-pink-200/40 text-white\";\n\n return (\n \n \n \n
    \n 🔧 {toolCallProps.name}\n {toolCallProps.status}\n
    \n
    \n \n \n \n
    \n
    \n );\n}\n\nconst ToolCallInformation = (toolCallProps: CatchAllActionRenderProps) => {\n const { name, args, status, result } = toolCallProps;\n\n const wrapperStyles = \"flex flex-col gap-2 max-h-[400px] overflow-y-auto text-black\";\n const titleStyles = \"flex flex-col gap-1\";\n const contentStyles = \"flex flex-col gap-1\";\n const preStyles = \"bg-indigo-500/10 p-2 rounded text-sm overflow-auto max-h-[200px] m-0 whitespace-pre-wrap break-words\";\n\n return (\n
    \n
    \n Name: {name}\n
    \n
    \n Arguments: \n
    \n          {JSON.stringify(args, null, 2)}\n        
    \n
    \n
    \n Status: {status}\n
    \n
    \n Result: \n
    \n          {JSON.stringify(result, null, 2)}\n        
    \n
    \n
    \n );\n}", - "type": "registry:component", - "target": "components/tool-call.tsx" - }, - { - "path": "registry/quickstarts/coagents-generic-lg/components/interrupt.tsx", - "content": "import { LangGraphInterruptEvent } from \"@copilotkit/runtime-client-gql\";\nimport { useState } from \"react\";\n\nexport interface InterruptProps {\n event: LangGraphInterruptEvent;\n result: unknown;\n resolve: (resolution: string) => void;\n}\n\nexport function Interrupt({ event, resolve }: InterruptProps) {\n const [response, setResponse] = useState(\"\");\n\n const wrapperStyles = \"flex flex-col justify-center items-center h-full w-full bg-indigo-600/50 my-4 rounded-xl\";\n const titleStyles = \"text-lg font-semibold text-white\";\n const subtitleStyles = \"text-sm font-normal text-gray-200 mb-1\";\n const contentStyles = \"flex flex-col gap-2 p-4 w-full\";\n const textareaStyles = \"w-full p-2 rounded-xl focus:outline-purple-500 border shadow-inner min-h-[100px] bg-white\";\n const buttonStyles = \"px-4 py-2 bg-black text-white rounded-xl hover:bg-gray-700 transition-colors\";\n const eventStyles = \"h-[300px] w-full overflow-auto p-3\";\n const preStyles = \"whitespace-pre-wrap break-words text-sm m-0 w-full\";\n\n return (\n
    \n
    \n

    \n 🙋 {event.name} ({event.type})\n

    \n \n

    The agent wants you to see...

    \n
    \n
    \n
    {JSON.stringify(event.value, null, 2)}
    \n
    \n
    \n\n

    How do you want to respond?

    \n + } + } + + + @if (toolbarTemplate || toolbarComponent()) { + + + } @else { +
    +
    + @if (addFileButtonTemplate || addFileButtonComponent()) { + + + } @else { + + + } + @if (computedToolsMenu().length > 0) { + @if (toolsButtonTemplate || toolsButtonComponent()) { + + + } @else { + + + } + } + @if (additionalToolbarItems()) { + + } +
    +
    + @if (computedMode() === "transcribe") { + @if ( + cancelTranscribeButtonTemplate || + cancelTranscribeButtonComponent() + ) { + + + } @else { + + + } + @if ( + finishTranscribeButtonTemplate || + finishTranscribeButtonComponent() + ) { + + + } @else { + + + } + } @else { + @if ( + startTranscribeButtonTemplate || + startTranscribeButtonComponent() + ) { + + + } @else { + + + } + + @if (sendButtonTemplate || sendButtonComponent()) { + + + } @else { +
    + +
    + } + } +
    +
    + } +
    + `, + styles: [ + ` + :host { + display: block; + width: 100%; + } + .shadow-\\[0_4px_4px_0_\\#0000000a\\2c_0_0_1px_0_\\#0000009e\\] { + box-shadow: + 0 4px 4px 0 #0000000a, + 0 0 1px 0 #0000009e !important; + } + `, + ], +}) +export class CopilotChatInput implements AfterViewInit, OnDestroy { + @ViewChild(CopilotChatTextarea, { read: CopilotChatTextarea }) + textAreaRef?: CopilotChatTextarea; + + @ViewChild(CopilotChatAudioRecorder) + audioRecorderRef?: CopilotChatAudioRecorder; + + // Capture templates from content projection + @ContentChild("sendButton", { read: TemplateRef }) + sendButtonTemplate?: TemplateRef; + @ContentChild("toolbar", { read: TemplateRef }) + toolbarTemplate?: TemplateRef; + @ContentChild("textArea", { read: TemplateRef }) + textAreaTemplate?: TemplateRef; + @ContentChild("audioRecorder", { read: TemplateRef }) + audioRecorderTemplate?: TemplateRef; + @ContentChild("startTranscribeButton", { read: TemplateRef }) + startTranscribeButtonTemplate?: TemplateRef; + @ContentChild("cancelTranscribeButton", { read: TemplateRef }) + cancelTranscribeButtonTemplate?: TemplateRef; + @ContentChild("finishTranscribeButton", { read: TemplateRef }) + finishTranscribeButtonTemplate?: TemplateRef; + @ContentChild("addFileButton", { read: TemplateRef }) + addFileButtonTemplate?: TemplateRef; + @ContentChild("toolsButton", { read: TemplateRef }) + toolsButtonTemplate?: TemplateRef; + + // Class inputs for styling default components + sendButtonClass = input(undefined); + toolbarClass = input(undefined); + textAreaClass = input(undefined); + textAreaMaxRows = input(undefined); + textAreaPlaceholder = input(undefined); + audioRecorderClass = input(undefined); + startTranscribeButtonClass = input(undefined); + cancelTranscribeButtonClass = input(undefined); + finishTranscribeButtonClass = input(undefined); + addFileButtonClass = input(undefined); + toolsButtonClass = input(undefined); + + // Component inputs for overrides + sendButtonComponent = input | undefined>(undefined); + toolbarComponent = input | undefined>(undefined); + textAreaComponent = input | undefined>(undefined); + audioRecorderComponent = input | undefined>(undefined); + startTranscribeButtonComponent = input | undefined>(undefined); + cancelTranscribeButtonComponent = input | undefined>(undefined); + finishTranscribeButtonComponent = input | undefined>(undefined); + addFileButtonComponent = input | undefined>(undefined); + toolsButtonComponent = input | undefined>(undefined); + + // Regular inputs + mode = input(undefined); + toolsMenu = input<(ToolsMenuItem | "-")[] | undefined>(undefined); + autoFocus = input(undefined); + value = input(undefined); + inputClass = input(undefined); + // Note: Prefer host `class` for styling this component; + // keep only `inputClass` to style the internal wrapper if needed. + additionalToolbarItems = input | undefined>(undefined); + + // Output events + submitMessage = output(); + startTranscribe = output(); + cancelTranscribe = output(); + finishTranscribe = output(); + addFile = output(); + valueChange = output(); + + // Icons and default classes + readonly ArrowUpIcon = ArrowUp; + readonly defaultButtonClass = cn( + // Base button styles + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium", + "transition-all disabled:pointer-events-none disabled:opacity-50", + "shrink-0 outline-none", + "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]", + // chatInputToolbarPrimary variant + "cursor-pointer", + "bg-black text-white", + "dark:bg-white dark:text-black dark:focus-visible:outline-white", + "rounded-full h-9 w-9", + "transition-colors", + "focus:outline-none", + "hover:opacity-70 disabled:hover:opacity-100", + "disabled:cursor-not-allowed disabled:bg-[#00000014] disabled:text-[rgb(13,13,13)]", + "dark:disabled:bg-[#454545] dark:disabled:text-white" + ); + + // Services + readonly labels = injectChatLabels(); + // readonly chatConfig = injectChatConfig(); + readonly chatState = injectChatState(); + + // Signals + modeSignal = signal("input"); + toolsMenuSignal = signal<(ToolsMenuItem | "-")[]>([]); + autoFocusSignal = signal(true); + customClass = signal(undefined); + + // Default components + // Note: CopilotChatTextarea uses attribute selector but is a component + defaultAudioRecorder = CopilotChatAudioRecorder; + defaultSendButton: any = null; // Will be set to avoid circular dependency + CopilotChatToolbar = CopilotChatToolbar; + CopilotChatAddFileButton = CopilotChatAddFileButton; + CopilotChatToolsMenu = CopilotChatToolsMenu; + CopilotChatCancelTranscribeButton = CopilotChatCancelTranscribeButton; + CopilotChatFinishTranscribeButton = CopilotChatFinishTranscribeButton; + CopilotChatStartTranscribeButton = CopilotChatStartTranscribeButton; + + // Computed values + computedMode = computed(() => this.modeSignal()); + computedToolsMenu = computed(() => this.toolsMenu() ?? []); + computedAutoFocus = computed(() => this.autoFocus() ?? true); + computedValue = computed(() => { + const customValue = this.value() ?? ""; + const configValue = this.chatState.inputValue(); + return customValue || configValue || ""; + }); + + computedClass = computed(() => { + const baseClasses = cn( + // Layout + "flex w-full flex-col items-center justify-center", + // Interaction + "cursor-text", + // Overflow and clipping + "overflow-visible bg-clip-padding contain-inline-size", + // Background + "bg-white dark:bg-[#303030]", + // Visual effects + "shadow-[0_4px_4px_0_#0000000a,0_0_1px_0_#0000009e] rounded-[28px]" + ); + return cn(baseClasses, this.customClass()); + }); + + // Context for slots (reactive via signals) + sendButtonContext = computed(() => ({ + send: () => this.send(), + disabled: + !this.computedValue().trim() || this.computedMode() === "processing", + value: this.computedValue(), + })); + + toolbarContext = computed(() => ({ + mode: this.computedMode(), + value: this.computedValue(), + })); + + textAreaContext = computed(() => ({ + value: this.computedValue(), + autoFocus: this.computedAutoFocus(), + disabled: this.computedMode() === "processing", + maxRows: this.textAreaMaxRows(), + placeholder: this.textAreaPlaceholder(), + inputClass: this.textAreaClass(), + onKeyDown: (event: KeyboardEvent) => this.handleKeyDown(event), + onChange: (value: string) => this.handleValueChange(value), + })); + + audioRecorderContext = computed(() => ({ + inputShowControls: true, + })); + + // Button contexts removed - now using outputs map for click handlers + + toolsContext = computed(() => ({ + inputToolsMenu: this.computedToolsMenu(), + inputDisabled: this.computedMode() === "transcribe", + })); + + constructor() { + // Effect to handle mode changes (no signal writes) + effect(() => { + const currentMode = this.computedMode(); + if (currentMode === "transcribe" && this.audioRecorderRef) { + this.audioRecorderRef.start().catch(console.error); + } else if (this.audioRecorderRef?.getState() === "recording") { + this.audioRecorderRef.stop().catch(console.error); + } + }); + } + + // Output maps for slots + addFileButtonOutputs = { clicked: () => this.handleAddFile() }; + cancelTranscribeButtonOutputs = { + clicked: () => this.handleCancelTranscribe(), + }; + finishTranscribeButtonOutputs = { + clicked: () => this.handleFinishTranscribe(), + }; + startTranscribeButtonOutputs = { + clicked: () => this.handleStartTranscribe(), + }; + // Support both `clicked` (idiomatic in our slots) and `click` (legacy) + sendButtonOutputs = { clicked: () => this.send(), click: () => this.send() }; + + ngAfterViewInit(): void { + // Auto-focus if needed + if (this.computedAutoFocus() && this.textAreaRef) { + setTimeout(() => { + this.textAreaRef?.focus(); + }); + } + } + + ngOnDestroy(): void { + // Clean up any resources + if (this.audioRecorderRef?.getState() === "recording") { + this.audioRecorderRef?.stop().catch(console.error); + } + } + + handleKeyDown(event: KeyboardEvent): void { + if (event.key === "Enter" && !event.shiftKey) { + event.preventDefault(); + this.send(); + } + } + + handleValueChange(value: string): void { + this.valueChange.emit(value); + if (this.chatState) this.chatState.changeInput(value); + } + + send(): void { + const trimmed = this.computedValue().trim(); + if (trimmed) { + this.submitMessage.emit(trimmed); + + this.chatState.submitInput(trimmed); + + if (this.chatState) this.chatState.changeInput(""); + if (this.textAreaRef) this.textAreaRef.setValue(""); + + // Refocus input + if (this.textAreaRef) { + setTimeout(() => { + this.textAreaRef?.focus(); + }); + } + } + } + + handleStartTranscribe(): void { + this.startTranscribe.emit(); + this.modeSignal.set("transcribe"); + } + + handleCancelTranscribe(): void { + this.cancelTranscribe.emit(); + this.modeSignal.set("input"); + } + + handleFinishTranscribe(): void { + this.finishTranscribe.emit(); + this.modeSignal.set("input"); + } + + handleAddFile(): void { + this.addFile.emit(); + } +} diff --git a/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-input.types.ts b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-input.types.ts new file mode 100644 index 0000000000..b6da757ab6 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-input.types.ts @@ -0,0 +1,148 @@ +import type { Type, TemplateRef } from "@angular/core"; + +/** + * Mode of the chat input component + */ +export type CopilotChatInputMode = "input" | "transcribe" | "processing"; + +/** + * Represents a menu item in the tools menu + */ +export type ToolsMenuItem = { + label: string; +} & ( + | { + action: () => void; + items?: never; + } + | { + action?: never; + items: (ToolsMenuItem | "-")[]; + } +); + +/** + * Audio recorder state + */ +export type AudioRecorderState = "idle" | "recording" | "processing"; + +/** + * Error class for audio recorder failures + */ +export class AudioRecorderError extends Error { + constructor(message: string) { + super(message); + this.name = "AudioRecorderError"; + } +} + +/** + * Props for textarea component + */ +export interface CopilotChatTextareaProps { + value?: string; + placeholder?: string; + maxRows?: number; + autoFocus?: boolean; + disabled?: boolean; + onChange?: (value: string) => void; + onKeyDown?: (event: KeyboardEvent) => void; + inputClass?: string; + style?: any; + rows?: number; + cols?: number; + readonly?: boolean; + spellcheck?: boolean; + wrap?: "hard" | "soft" | "off"; +} + +/** + * Props for button components + */ +export interface CopilotChatButtonProps { + disabled?: boolean; + onClick?: () => void; + inputClass?: string; + style?: any; + type?: "button" | "submit" | "reset"; + ariaLabel?: string; + ariaPressed?: boolean; + ariaExpanded?: boolean; + title?: string; +} + +/** + * Props for toolbar button with tooltip + */ +export interface CopilotChatToolbarButtonProps extends CopilotChatButtonProps { + icon?: TemplateRef; + tooltip?: string; + variant?: "primary" | "secondary"; +} + +/** + * Props for tools menu button + */ +export interface CopilotChatToolsButtonProps extends CopilotChatButtonProps { + toolsMenu?: (ToolsMenuItem | "-")[]; +} + +/** + * Props for audio recorder + */ +export interface CopilotChatAudioRecorderProps { + inputClass?: string; + style?: any; + onStateChange?: (state: AudioRecorderState) => void; + showControls?: boolean; + maxDuration?: number; +} + +/** + * Props for toolbar + */ +export interface CopilotChatToolbarProps { + inputClass?: string; + style?: any; + position?: "top" | "bottom"; + alignment?: "left" | "center" | "right" | "space-between"; +} + +/** + * Slot configuration for chat input + */ +export interface CopilotChatInputSlots { + textArea?: Type | TemplateRef; + sendButton?: Type | TemplateRef; + startTranscribeButton?: Type | TemplateRef; + cancelTranscribeButton?: Type | TemplateRef; + finishTranscribeButton?: Type | TemplateRef; + addFileButton?: Type | TemplateRef; + toolsButton?: Type | TemplateRef; + toolbar?: Type | TemplateRef; + audioRecorder?: Type | TemplateRef; +} + +/** + * Input configuration for the chat input component + */ +export interface CopilotChatInputConfig { + mode?: CopilotChatInputMode; + toolsMenu?: (ToolsMenuItem | "-")[]; + autoFocus?: boolean; + additionalToolbarItems?: TemplateRef; + value?: string; + class?: string; +} + +/** + * Output events for the chat input component + */ +export interface CopilotChatInputOutputs { + submitMessage: (value: string) => void; + startTranscribe: () => void; + cancelTranscribe: () => void; + finishTranscribe: () => void; + addFile: () => void; + changeValue: (value: string) => void; +} diff --git a/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-message-view-cursor.ts b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-message-view-cursor.ts new file mode 100644 index 0000000000..27cc145178 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-message-view-cursor.ts @@ -0,0 +1,33 @@ +import { + Component, + input, + ChangeDetectionStrategy, + ViewEncapsulation, + computed, +} from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { cn } from "../../utils"; + +/** + * Cursor component that matches the React implementation exactly. + * Shows a pulsing dot animation to indicate activity. + */ +@Component({ + selector: "copilot-chat-message-view-cursor", + standalone: true, + imports: [CommonModule], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: `
    `, +}) +export class CopilotChatMessageViewCursor { + inputClass = input(); + + // Computed class that matches React exactly: w-[11px] h-[11px] rounded-full bg-foreground animate-pulse-cursor ml-1 + computedClass = computed(() => + cn( + "w-[11px] h-[11px] rounded-full bg-foreground animate-pulse-cursor ml-1", + this.inputClass() + ) + ); +} diff --git a/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-message-view.ts b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-message-view.ts new file mode 100644 index 0000000000..73013db069 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-message-view.ts @@ -0,0 +1,222 @@ +import { + Component, + input, + output, + ContentChild, + TemplateRef, + Type, + ChangeDetectionStrategy, + ViewEncapsulation, + computed, +} from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { CopilotSlot } from "../../slots/copilot-slot"; +import type { Message } from "@ag-ui/core"; +import { CopilotChatAssistantMessage } from "./copilot-chat-assistant-message"; +import { CopilotChatUserMessage } from "./copilot-chat-user-message"; +import { CopilotChatMessageViewCursor } from "./copilot-chat-message-view-cursor"; +import { cn } from "../../utils"; + +/** + * CopilotChatMessageView component - Angular port of the React component. + * Renders a list of chat messages with support for custom slots and layouts. + * DOM structure and Tailwind classes match the React implementation exactly. + */ +@Component({ + selector: "copilot-chat-message-view", + standalone: true, + imports: [ + CommonModule, + CopilotSlot, + CopilotChatAssistantMessage, + CopilotChatUserMessage, + CopilotChatMessageViewCursor, + ], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` + + @if (customLayoutTemplate) { + + } @else { + +
    + + @for ( + message of messagesValue(); + track trackByMessageId($index, message) + ) { + @if (message && message.role === "assistant") { + + @if (assistantMessageComponent() || assistantMessageTemplate()) { + + + } @else { + + + } + } @else if (message && message.role === "user") { + + @if (userMessageComponent() || userMessageTemplate()) { + + + } @else { + + + } + } + } + + + @if (showCursorValue()) { + @if (cursorComponent() || cursorTemplate()) { + + + } @else { + + + } + } +
    + } + `, +}) +export class CopilotChatMessageView { + // Core inputs matching React props + messages = input([]); + showCursor = input(false); + isLoading = input(false); + inputClass = input(); + + // Handler availability handled via DI service + + // Assistant message slot inputs + assistantMessageComponent = input | undefined>(); + assistantMessageTemplate = input | undefined>(); + assistantMessageClass = input(); + + // User message slot inputs + userMessageComponent = input | undefined>(); + userMessageTemplate = input | undefined>(); + userMessageClass = input(); + + // Cursor slot inputs + cursorComponent = input | undefined>(); + cursorTemplate = input | undefined>(); + cursorClass = input(); + + // Custom layout template (render prop pattern) + @ContentChild("customLayout") customLayoutTemplate?: TemplateRef; + + // Output events (bubbled from child components) + assistantMessageThumbsUp = output<{ message: Message }>(); + assistantMessageThumbsDown = output<{ message: Message }>(); + assistantMessageReadAloud = output<{ message: Message }>(); + assistantMessageRegenerate = output<{ message: Message }>(); + userMessageCopy = output<{ message: Message }>(); + userMessageEdit = output<{ message: Message }>(); + + // Default components for slots + protected readonly defaultAssistantComponent = CopilotChatAssistantMessage; + protected readonly defaultUserComponent = CopilotChatUserMessage; + protected readonly defaultCursorComponent = CopilotChatMessageViewCursor; + + // Derived values from inputs + protected messagesValue = computed(() => this.messages()); + protected showCursorValue = computed(() => this.showCursor()); + protected isLoadingValue = computed(() => this.isLoading()); + + // Computed class matching React: twMerge("flex flex-col", className) + computedClass = computed(() => cn("flex flex-col", this.inputClass())); + + // Layout context for custom templates (render prop pattern) + layoutContext = computed(() => ({ + isLoading: this.isLoadingValue(), + messages: this.messagesValue(), + showCursor: this.showCursorValue(), + messageElements: this.messagesValue().filter( + (m) => m && (m.role === "assistant" || m.role === "user") + ), + })); + + // Slot resolution computed signals + assistantMessageSlot = computed( + () => this.assistantMessageComponent() || this.assistantMessageClass() + ); + + userMessageSlot = computed( + () => this.userMessageComponent() || this.userMessageClass() + ); + + cursorSlot = computed(() => this.cursorComponent() || this.cursorClass()); + + // Props merging helpers + mergeAssistantProps(message: Message) { + return { + message, + messages: this.messagesValue(), + isLoading: this.isLoadingValue(), + inputClass: this.assistantMessageClass(), + }; + } + + mergeUserProps(message: Message) { + return { + message, + inputClass: this.userMessageClass(), + }; + } + + // TrackBy function for performance optimization + trackByMessageId(index: number, message: Message): string { + return message?.id || `index-${index}`; + } + + constructor() {} + + // Event handlers - just pass them through + handleAssistantThumbsUp(event: { message: Message }): void { + this.assistantMessageThumbsUp.emit(event); + } + + handleAssistantThumbsDown(event: { message: Message }): void { + this.assistantMessageThumbsDown.emit(event); + } + + handleAssistantReadAloud(event: { message: Message }): void { + this.assistantMessageReadAloud.emit(event); + } + + handleAssistantRegenerate(event: { message: Message }): void { + this.assistantMessageRegenerate.emit(event); + } +} diff --git a/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-message-view.types.ts b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-message-view.types.ts new file mode 100644 index 0000000000..c912053529 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-message-view.types.ts @@ -0,0 +1,39 @@ +import { Message } from "@ag-ui/client"; +import { Type, TemplateRef } from "@angular/core"; + +// Context interfaces for template slots +export interface MessageViewContext { + showCursor: boolean; + messages: Message[]; + messageElements: any[]; // Will be populated with rendered elements +} + +// eslint-disable-next-line @typescript-eslint/no-empty-object-type +export interface CursorContext { + // Empty for now, can be extended if needed +} + +// Component input props interface +export interface CopilotChatMessageViewProps { + messages?: Message[]; + showCursor?: boolean; + inputClass?: string; + + // Assistant message slots + assistantMessageComponent?: Type; + assistantMessageTemplate?: TemplateRef; + assistantMessageClass?: string; + + // User message slots + userMessageComponent?: Type; + userMessageTemplate?: TemplateRef; + userMessageClass?: string; + + // Cursor slots + cursorComponent?: Type; + cursorTemplate?: TemplateRef; + cursorClass?: string; +} + +// Re-export for convenience +export type { Message }; diff --git a/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-textarea.ts b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-textarea.ts new file mode 100644 index 0000000000..9f70bdfc50 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-textarea.ts @@ -0,0 +1,186 @@ +import { + Component, + input, + output, + ElementRef, + AfterViewInit, + signal, + computed, + inject, + ChangeDetectionStrategy, + ViewEncapsulation, +} from "@angular/core"; +import { cn } from "../../utils"; +import { injectChatLabels } from "../../chat-config"; +import { injectChatState } from "../../chat-state"; + +@Component({ + selector: "textarea[copilotChatTextarea]", + standalone: true, + imports: [], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + host: { + "[value]": "computedValue()", + "[placeholder]": "placeholder()", + "[disabled]": "disabled()", + "[class]": "computedClass()", + "[style.max-height.px]": "maxHeight()", + "[style.overflow]": "'auto'", + "[style.resize]": "'none'", + "(input)": "onInput($event)", + "(keydown)": "onKeyDown($event)", + "[attr.rows]": "1", + }, + template: "", + styles: [], +}) +export class CopilotChatTextarea implements AfterViewInit { + private elementRef = inject(ElementRef); + get textareaRef() { + return this.elementRef; + } + + inputValue = input(); + inputPlaceholder = input(); + inputMaxRows = input(); + inputAutoFocus = input(); + inputDisabled = input(); + inputClass = input(); + + valueChange = output(); + keyDown = output(); + + readonly chatLabels = injectChatLabels(); + readonly chatState = injectChatState(); + + // Internal signals + maxHeight = signal(0); + + // Computed values + computedValue = computed( + () => this.inputValue() ?? this.chatState.inputValue() ?? "" + ); + placeholder = computed( + () => this.inputPlaceholder() || this.chatLabels.chatInputPlaceholder + ); + disabled = computed(() => this.inputDisabled() ?? false); + + computedClass = computed(() => { + const baseClasses = cn( + // Layout and sizing + "w-full p-5 pb-0", + // Behavior + "outline-none resize-none", + // Background + "bg-transparent", + // Typography + "antialiased font-regular leading-relaxed text-[16px]", + // Placeholder styles + "placeholder:text-[#00000077] dark:placeholder:text-[#fffc]" + ); + return cn(baseClasses, this.inputClass()); + }); + + constructor() {} + + ngAfterViewInit(): void { + this.calculateMaxHeight(); + this.adjustHeight(); + + if (this.inputAutoFocus() ?? true) { + setTimeout(() => { + this.elementRef.nativeElement.focus(); + }); + } + } + + onInput(event: Event): void { + const textarea = event.target as HTMLTextAreaElement; + const newValue = textarea.value; + this.valueChange.emit(newValue); + + this.chatState.changeInput(newValue); + + this.adjustHeight(); + } + + onKeyDown(event: KeyboardEvent): void { + // Check for Enter key without Shift + if (event.key === "Enter" && !event.shiftKey) { + event.preventDefault(); + this.keyDown.emit(event); + } else { + this.keyDown.emit(event); + } + } + + private calculateMaxHeight(): void { + const textarea = this.elementRef.nativeElement; + const maxRowsValue = this.inputMaxRows() ?? 5; + + // Save current value + const currentValue = textarea.value; + + // Clear content to measure single row height + textarea.value = ""; + textarea.style.height = "auto"; + + // Get computed styles to account for padding + const computedStyle = window.getComputedStyle(textarea); + const paddingTop = parseFloat(computedStyle.paddingTop); + const paddingBottom = parseFloat(computedStyle.paddingBottom); + + // Calculate actual content height (without padding) + const contentHeight = textarea.scrollHeight - paddingTop - paddingBottom; + + // Calculate max height: content height for maxRows + padding + const calculatedMaxHeight = + contentHeight * maxRowsValue + paddingTop + paddingBottom; + this.maxHeight.set(calculatedMaxHeight); + + // Restore original value + textarea.value = currentValue; + + // Adjust height after calculating maxHeight + if (currentValue) { + this.adjustHeight(); + } + } + + private adjustHeight(): void { + const textarea = this.elementRef.nativeElement; + const maxHeightValue = this.maxHeight(); + + if (maxHeightValue > 0) { + textarea.style.height = "auto"; + textarea.style.height = `${Math.min(textarea.scrollHeight, maxHeightValue)}px`; + } + } + + /** + * Public method to focus the textarea + */ + focus(): void { + this.elementRef.nativeElement.focus(); + } + + /** + * Public method to get current value + */ + getValue(): string { + return this.elementRef.nativeElement.value; + } + + /** + * Public method to set value programmatically + */ + setValue(value: string): void { + this.elementRef.nativeElement.value = value; + this.valueChange.emit(value); + + this.chatState.changeInput(value); + + setTimeout(() => this.adjustHeight()); + } +} diff --git a/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-tool-calls-view.ts b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-tool-calls-view.ts new file mode 100644 index 0000000000..080ecca720 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-tool-calls-view.ts @@ -0,0 +1,24 @@ +import { Component, ChangeDetectionStrategy, input } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import type { AssistantMessage, Message } from "@ag-ui/core"; +import { RenderToolCalls } from "../../render-tool-calls"; + +@Component({ + standalone: true, + selector: "copilot-chat-tool-calls-view", + imports: [CommonModule, RenderToolCalls], + changeDetection: ChangeDetectionStrategy.OnPush, + template: ` + + + `, +}) +export class CopilotChatToolCallsView { + readonly message = input.required(); + readonly messages = input.required(); + readonly isLoading = input(false); +} diff --git a/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-toolbar.ts b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-toolbar.ts new file mode 100644 index 0000000000..5756f74145 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-toolbar.ts @@ -0,0 +1,31 @@ +import { + Component, + input, + computed, + ChangeDetectionStrategy, + ViewEncapsulation, +} from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { cn } from "../../utils"; + +@Component({ + selector: "div[copilotChatToolbar]", + standalone: true, + imports: [CommonModule], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + host: { + "[class]": "computedClass()", + }, + template: ``, + styles: [], +}) +export class CopilotChatToolbar { + readonly inputClass = input(); + + readonly computedClass = computed(() => { + const baseClasses = + "w-full h-[60px] bg-transparent flex items-center justify-between"; + return cn(baseClasses, this.inputClass()); + }); +} diff --git a/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-tools-menu.ts b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-tools-menu.ts new file mode 100644 index 0000000000..d84969bbf0 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-tools-menu.ts @@ -0,0 +1,184 @@ +import { + Component, + input, + computed, + ChangeDetectionStrategy, + ViewEncapsulation, +} from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { CdkMenuModule } from "@angular/cdk/menu"; +import { OverlayModule } from "@angular/cdk/overlay"; +import { LucideAngularModule, Settings2, ChevronRight } from "lucide-angular"; +import type { ToolsMenuItem } from "./copilot-chat-input.types"; +import { cn } from "../../utils"; +import { injectChatLabels } from "../../chat-config"; + +@Component({ + selector: "copilot-chat-tools-menu", + standalone: true, + imports: [CommonModule, CdkMenuModule, OverlayModule, LucideAngularModule], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` + @if (hasItems()) { + + + +
    + @for (item of toolsMenu(); track $index) { + @if (item === "-") { +
    + } @else if (isMenuItem(item)) { + @if (item.items && item.items.length > 0) { + + + + + +
    + @for (subItem of item.items; track $index) { + @if (subItem === "-") { +
    + } @else if (isMenuItem(subItem)) { + + } + } +
    +
    + } @else { + + + } + } + } +
    +
    + } + `, + styles: [ + ` + /* CDK Overlay styles for positioning */ + .cdk-overlay-pane { + position: absolute; + pointer-events: auto; + z-index: 1000; + } + + /* Ensure menu appears above other content */ + .cdk-overlay-container { + position: fixed; + z-index: 1000; + } + + /* Menu animation */ + [cdkMenu] { + animation: menuFadeIn 0.15s ease-out; + } + + @keyframes menuFadeIn { + from { + opacity: 0; + transform: translateY(4px); + } + to { + opacity: 1; + transform: translateY(0); + } + } + `, + ], +}) +export class CopilotChatToolsMenu { + readonly Settings2Icon = Settings2; + readonly ChevronRightIcon = ChevronRight; + inputToolsMenu = input<(ToolsMenuItem | "-")[] | undefined>(); + inputDisabled = input(); + inputClass = input(); + + private labels = injectChatLabels(); + + // Derive state from inputs + toolsMenu = computed(() => this.inputToolsMenu() ?? []); + disabled = computed(() => this.inputDisabled() ?? false); + customClass = computed(() => this.inputClass()); + + hasItems = computed(() => this.toolsMenu().length > 0); + + readonly label = this.labels.chatInputToolbarToolsButtonLabel; + + buttonClass = computed(() => { + const baseClasses = cn( + // Base button styles + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-full text-sm font-medium", + "transition-all disabled:pointer-events-none disabled:opacity-50", + "shrink-0 outline-none", + "focus-visible:ring-[3px]", + // chatInputToolbarSecondary variant + "cursor-pointer", + "bg-transparent text-[#444444]", + "dark:text-white dark:border-[#404040]", + "transition-colors", + "focus:outline-none", + "hover:bg-[#f8f8f8] hover:text-[#333333]", + "dark:hover:bg-[#404040] dark:hover:text-[#FFFFFF]", + "disabled:cursor-not-allowed disabled:opacity-50", + "disabled:hover:bg-transparent disabled:hover:text-[#444444]", + "dark:disabled:hover:bg-transparent dark:disabled:hover:text-[#CCCCCC]", + // Size + "h-9 px-3 gap-2 font-normal" + ); + return cn(baseClasses, this.customClass()); + }); + + isMenuItem(item: any): item is ToolsMenuItem { + return item && typeof item === "object" && "label" in item; + } + + handleItemClick(item: ToolsMenuItem): void { + if (item.action) { + item.action(); + } + } +} diff --git a/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-user-message-branch-navigation.ts b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-user-message-branch-navigation.ts new file mode 100644 index 0000000000..9bf5e5832c --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-user-message-branch-navigation.ts @@ -0,0 +1,88 @@ +import { Component, input, output, ChangeDetectionStrategy, ViewEncapsulation, computed } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { LucideAngularModule, ChevronLeft, ChevronRight } from "lucide-angular"; +import { type CopilotChatUserMessageOnSwitchToBranchProps } from "./copilot-chat-user-message.types"; +import { cn } from "../../utils"; +import { UserMessage } from "@ag-ui/core"; + +@Component({ + standalone: true, + selector: "copilot-chat-user-message-branch-navigation", + imports: [CommonModule, LucideAngularModule], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` + @if (showNavigation()) { +
    + + + {{ currentBranch() + 1 }}/{{ numberOfBranches() }} + + +
    + } + `, +}) +export class CopilotChatUserMessageBranchNavigation { + currentBranch = input(0); + numberOfBranches = input(1); + message = input(); + inputClass = input(); + switchToBranch = output(); + + readonly ChevronLeftIcon = ChevronLeft; + readonly ChevronRightIcon = ChevronRight; + + readonly buttonClass = cn( + // Flex centering + "inline-flex items-center justify-center", + // Cursor + "cursor-pointer", + // Background and text + "p-0 text-[rgb(93,93,93)] hover:bg-[#E8E8E8]", + // Dark mode + "dark:text-[rgb(243,243,243)] dark:hover:bg-[#303030]", + // Shape and sizing + "h-6 w-6 rounded-md", + // Interactions + "transition-colors", + // Disabled state + "disabled:opacity-50 disabled:cursor-not-allowed", + ); + + showNavigation = computed(() => this.numberOfBranches() > 1); + + canGoPrev = computed(() => this.currentBranch() > 0); + + canGoNext = computed(() => this.currentBranch() < this.numberOfBranches() - 1); + + computedClass = computed(() => { + return cn("flex items-center gap-1", this.inputClass()); + }); + + handlePrevious(): void { + if (this.canGoPrev()) { + const newIndex = this.currentBranch() - 1; + this.switchToBranch.emit({ + branchIndex: newIndex, + numberOfBranches: this.numberOfBranches(), + message: this.message()!, + }); + } + } + + handleNext(): void { + if (this.canGoNext()) { + const newIndex = this.currentBranch() + 1; + this.switchToBranch.emit({ + branchIndex: newIndex, + numberOfBranches: this.numberOfBranches(), + message: this.message()!, + }); + } + } +} diff --git a/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-user-message-buttons.ts b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-user-message-buttons.ts new file mode 100644 index 0000000000..d70d4a5f11 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-user-message-buttons.ts @@ -0,0 +1,161 @@ +import { + Component, + input, + output, + signal, + computed, + ChangeDetectionStrategy, + ViewEncapsulation, +} from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { LucideAngularModule, Copy, Check, Edit } from "lucide-angular"; +import { CopilotTooltip } from "../../directives/tooltip"; +import { cn } from "../../utils"; +import { injectChatLabels } from "../../chat-config"; + +// Base toolbar button component +@Component({ + selector: "button[copilotChatUserMessageToolbarButton]", + standalone: true, + imports: [CommonModule], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` `, + host: { + "[class]": "computedClass()", + "[attr.disabled]": "disabled() ? true : null", + type: "button", + "[attr.aria-label]": "title()", + }, + hostDirectives: [ + { + directive: CopilotTooltip, + inputs: ["copilotTooltip: title", "tooltipPosition", "tooltipDelay"], + }, + ], +}) +export class CopilotChatUserMessageToolbarButton { + title = input(""); + disabled = input(false); + inputClass = input(); + + computedClass = computed(() => { + return cn( + // Flex centering + "inline-flex items-center justify-center", + // Cursor + "cursor-pointer", + // Background and text + "p-0 text-[rgb(93,93,93)] hover:bg-[#E8E8E8]", + // Dark mode + "dark:text-[rgb(243,243,243)] dark:hover:bg-[#303030]", + // Shape and sizing + "h-8 w-8 rounded-md", + // Interactions + "transition-colors", + // Hover states + "hover:text-[rgb(93,93,93)]", + "dark:hover:text-[rgb(243,243,243)]", + // Focus states + "focus:outline-none focus:ring-2 focus:ring-offset-2", + // Disabled state + "disabled:opacity-50 disabled:cursor-not-allowed", + this.inputClass() + ); + }); +} + +// Copy button component +@Component({ + standalone: true, + selector: "copilot-chat-user-message-copy-button", + imports: [ + CommonModule, + LucideAngularModule, + CopilotChatUserMessageToolbarButton, + ], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` + + `, +}) +export class CopilotChatUserMessageCopyButton { + readonly title = input(); + readonly disabled = input(false); + readonly inputClass = input(); + readonly content = input(); + readonly clicked = output(); + readonly CopyIcon = Copy; + readonly CheckIcon = Check; + readonly copied = signal(false); + readonly labels = injectChatLabels(); + + handleCopy(): void { + if (!this.content()) return; + + // Set copied immediately for instant feedback + this.copied.set(true); + setTimeout(() => this.copied.set(false), 2000); + + // Copy to clipboard (fire and forget) + navigator.clipboard.writeText(this.content()!).then( + () => this.clicked.emit(), + (err) => { + console.error("Failed to copy message:", err); + this.copied.set(false); + } + ); + } +} + +// Edit button component +@Component({ + selector: "copilot-chat-user-message-edit-button", + standalone: true, + imports: [ + CommonModule, + LucideAngularModule, + CopilotChatUserMessageToolbarButton, + ], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` + + `, +}) +export class CopilotChatUserMessageEditButton { + title = input(); + disabled = input(false); + inputClass = input(); + clicked = output(); + + readonly EditIcon = Edit; + readonly labels = injectChatLabels(); + + handleEdit(): void { + if (!this.disabled()) { + this.clicked.emit(); + } + } +} diff --git a/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-user-message-renderer.ts b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-user-message-renderer.ts new file mode 100644 index 0000000000..aac08b4be6 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-user-message-renderer.ts @@ -0,0 +1,32 @@ +import { + Component, + input, + ChangeDetectionStrategy, + ViewEncapsulation, + computed, +} from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { cn } from "../../utils"; + +@Component({ + selector: "copilot-chat-user-message-renderer", + standalone: true, + imports: [CommonModule], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + host: { + "[class]": "computedClass()", + }, + template: `{{ content() }}`, +}) +export class CopilotChatUserMessageRenderer { + readonly content = input(""); + readonly inputClass = input(); + + readonly computedClass = computed(() => { + return cn( + "prose dark:prose-invert bg-muted relative max-w-[80%] rounded-[18px] px-4 py-1.5 data-[multiline]:py-3 inline-block whitespace-pre-wrap", + this.inputClass() + ); + }); +} diff --git a/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-user-message-toolbar.ts b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-user-message-toolbar.ts new file mode 100644 index 0000000000..cbaadb15d8 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-user-message-toolbar.ts @@ -0,0 +1,31 @@ +import { + Component, + input, + computed, + ChangeDetectionStrategy, + ViewEncapsulation, +} from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { cn } from "../../utils"; + +@Component({ + selector: "div[copilotChatUserMessageToolbar]", + standalone: true, + imports: [CommonModule], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` `, + host: { + "[class]": "computedClass()", + }, +}) +export class CopilotChatUserMessageToolbar { + readonly inputClass = input(); + + readonly computedClass = computed(() => + cn( + "w-full bg-transparent flex items-center justify-end mt-[4px] invisible group-hover:visible", + this.inputClass() + ) + ); +} diff --git a/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-user-message.ts b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-user-message.ts new file mode 100644 index 0000000000..eaad605ef2 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-user-message.ts @@ -0,0 +1,261 @@ +import { + Component, + input, + output, + TemplateRef, + ContentChild, + computed, + Type, + ChangeDetectionStrategy, + ViewEncapsulation, +} from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { CopilotSlot } from "../../slots/copilot-slot"; +import { + type CopilotChatUserMessageOnEditMessageProps, + type CopilotChatUserMessageOnSwitchToBranchProps, + type MessageRendererContext, + type CopyButtonContext, + type EditButtonContext, + type BranchNavigationContext, + type UserMessageToolbarContext, +} from "./copilot-chat-user-message.types"; +import { CopilotChatUserMessageRenderer } from "./copilot-chat-user-message-renderer"; +import { + CopilotChatUserMessageCopyButton, + CopilotChatUserMessageEditButton, +} from "./copilot-chat-user-message-buttons"; +import { CopilotChatUserMessageToolbar } from "./copilot-chat-user-message-toolbar"; +import { CopilotChatUserMessageBranchNavigation } from "./copilot-chat-user-message-branch-navigation"; +import { cn } from "../../utils"; +import { UserMessage } from "@ag-ui/core"; + +function flattenUserMessageContent(content?: UserMessage["content"]): string { + if (!content) { + return ""; + } + + if (typeof content === "string") { + return content; + } + + return content + .map((part) => { + if ( + part && + typeof part === "object" && + "type" in part && + (part as { type?: unknown }).type === "text" && + typeof (part as { text?: unknown }).text === "string" + ) { + return (part as { text: string }).text; + } + return ""; + }) + .filter((text) => text.length > 0) + .join("\n"); +} + +@Component({ + standalone: true, + selector: "copilot-chat-user-message", + imports: [ + CommonModule, + CopilotSlot, + CopilotChatUserMessageRenderer, + CopilotChatUserMessageCopyButton, + CopilotChatUserMessageEditButton, + CopilotChatUserMessageToolbar, + CopilotChatUserMessageBranchNavigation, + ], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` +
    + + @if (messageRendererTemplate || messageRendererComponent()) { + + + } @else { + + + } + + + @if (toolbarTemplate || toolbarComponent()) { + + + } @else { +
    +
    + + @if (additionalToolbarItems()) { + + } + + + @if (copyButtonTemplate || copyButtonComponent()) { + + + } @else { + + + } + + + @if (true) { + @if (editButtonTemplate || editButtonComponent()) { + + + } @else { + + + } + } + + + @if (showBranchNavigation()) { + @if (branchNavigationTemplate || branchNavigationComponent()) { + + + } @else { + + + } + } +
    +
    + } +
    + `, + styles: [ + ` + :host { + display: block; + width: 100%; + } + `, + ], +}) +export class CopilotChatUserMessage { + // Capture templates from content projection + @ContentChild("messageRenderer", { read: TemplateRef }) + messageRendererTemplate?: TemplateRef; + @ContentChild("toolbar", { read: TemplateRef }) + toolbarTemplate?: TemplateRef; + @ContentChild("copyButton", { read: TemplateRef }) + copyButtonTemplate?: TemplateRef; + @ContentChild("editButton", { read: TemplateRef }) + editButtonTemplate?: TemplateRef; + @ContentChild("branchNavigation", { read: TemplateRef }) + branchNavigationTemplate?: TemplateRef; + + // Props for tweaking default components + messageRendererClass = input(); + toolbarClass = input(); + copyButtonClass = input(); + editButtonClass = input(); + branchNavigationClass = input(); + + // Component inputs for overrides + messageRendererComponent = input | undefined>(); + toolbarComponent = input | undefined>(); + copyButtonComponent = input | undefined>(); + editButtonComponent = input | undefined>(); + branchNavigationComponent = input | undefined>(); + + // Regular inputs + message = input(); + branchIndex = input(); + numberOfBranches = input(); + additionalToolbarItems = input | undefined>(); + inputClass = input(); + + // Output events + editMessage = output(); + switchToBranch = output(); + + // Derived values + branchIndexValue = computed(() => this.branchIndex() ?? 0); + numberOfBranchesValue = computed(() => this.numberOfBranches() ?? 1); + + // Default components + CopilotChatUserMessageRenderer = CopilotChatUserMessageRenderer; + CopilotChatUserMessageToolbar = CopilotChatUserMessageToolbar; + CopilotChatUserMessageCopyButton = CopilotChatUserMessageCopyButton; + CopilotChatUserMessageEditButton = CopilotChatUserMessageEditButton; + CopilotChatUserMessageBranchNavigation = CopilotChatUserMessageBranchNavigation; + + // Computed values + showBranchNavigation = computed(() => (this.numberOfBranches() ?? 1) > 1); + + computedClass = computed(() => cn("flex flex-col items-end group pt-10", this.inputClass())); + + // Context for slots (reactive via signals) + flattenedContent = computed(() => flattenUserMessageContent(this.message()?.content)); + + messageRendererContext = computed(() => ({ + content: this.flattenedContent(), + })); + + // Output maps for slots + copyButtonOutputs = { clicked: () => this.handleCopy() }; + editButtonOutputs = { clicked: () => this.handleEdit() }; + + branchNavigationContext = computed(() => ({ + currentBranch: this.branchIndexValue(), + numberOfBranches: this.numberOfBranchesValue(), + onSwitchToBranch: (props) => this.handleSwitchToBranch(props), + message: this.message()!, + })); + + toolbarContext = computed(() => ({ + children: null, // Will be populated by the toolbar content + })); + + handleCopy(): void { + // Copy is handled by the button component itself + // This is just for any additional logic if needed + } + + handleEdit(): void { + this.editMessage.emit({ message: this.message()! }); + } + + handleSwitchToBranch(props: CopilotChatUserMessageOnSwitchToBranchProps): void { + this.switchToBranch.emit(props); + } + constructor() {} +} diff --git a/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-user-message.types.ts b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-user-message.types.ts new file mode 100644 index 0000000000..a733e1f237 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-user-message.types.ts @@ -0,0 +1,36 @@ +import type { UserMessage } from "@ag-ui/core"; + +export interface CopilotChatUserMessageOnEditMessageProps { + message: UserMessage; +} + +export interface CopilotChatUserMessageOnSwitchToBranchProps { + message: UserMessage; + branchIndex: number; + numberOfBranches: number; +} + +// Context interfaces for slots +export interface MessageRendererContext { + content: string; +} + +export interface CopyButtonContext { + content?: string; + copied?: boolean; +} + +export interface EditButtonContext { + // Empty context - click handled via outputs map +} + +export interface BranchNavigationContext { + currentBranch: number; + numberOfBranches: number; + onSwitchToBranch?: (props: CopilotChatUserMessageOnSwitchToBranchProps) => void; + message: UserMessage; +} + +export interface UserMessageToolbarContext { + children?: any; +} diff --git a/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-view-disclaimer.ts b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-view-disclaimer.ts new file mode 100644 index 0000000000..3455fbbe2a --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-view-disclaimer.ts @@ -0,0 +1,50 @@ +import { + Component, + input, + ChangeDetectionStrategy, + ViewEncapsulation, +} from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { cn } from "../..//utils"; +import { injectChatLabels } from "../../chat-config"; + +/** + * Disclaimer component for CopilotChatView + * Shows configurable disclaimer text below the input + * Integrates with CopilotChatConfigurationService for labels + */ +@Component({ + selector: "copilot-chat-view-disclaimer", + standalone: true, + imports: [CommonModule], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` +
    + {{ disclaimerText }} +
    + `, +}) +export class CopilotChatViewDisclaimer { + inputClass = input(); + text = input(); + + readonly labels = injectChatLabels(); + + // Get disclaimer text from input or configuration + get disclaimerText(): string { + if (this.text()) { + return this.text() as string; + } + + return this.labels.chatDisclaimerText; + } + + // Computed class matching React exactly + get computedClass(): string { + return cn( + "text-center text-xs text-muted-foreground py-3 px-4 max-w-3xl mx-auto", + this.inputClass() + ); + } +} diff --git a/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-view-feather.ts b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-view-feather.ts new file mode 100644 index 0000000000..af2f3ba3cf --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-view-feather.ts @@ -0,0 +1,42 @@ +import { + Component, + input, + ChangeDetectionStrategy, + ViewEncapsulation, +} from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { cn } from "../../utils"; + +/** + * Feather component for CopilotChatView + * Creates a gradient overlay effect between messages and input + * Matches React implementation exactly with same Tailwind classes + */ +@Component({ + selector: "copilot-chat-view-feather", + standalone: true, + imports: [CommonModule], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: `
    `, +}) +export class CopilotChatViewFeather { + inputClass = input(); + style = input<{ [key: string]: any } | undefined>(); + + // Computed class matching React exactly + get computedClass(): string { + return cn( + // Positioning + "absolute bottom-0 left-0 right-4 h-24 pointer-events-none z-10", + // Gradient + "bg-gradient-to-t", + // Light mode colors + "from-white via-white to-transparent", + // Dark mode colors + "dark:from-[rgb(33,33,33)] dark:via-[rgb(33,33,33)]", + // Custom classes + this.inputClass() + ); + } +} diff --git a/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-view-handlers.ts b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-view-handlers.ts new file mode 100644 index 0000000000..5c5340d13a --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-view-handlers.ts @@ -0,0 +1,14 @@ +import { Injectable, signal } from "@angular/core"; + +@Injectable({ providedIn: "root" }) +export class CopilotChatViewHandlers { + // Assistant message handler availability + hasAssistantThumbsUpHandler = signal(false); + hasAssistantThumbsDownHandler = signal(false); + hasAssistantReadAloudHandler = signal(false); + hasAssistantRegenerateHandler = signal(false); + + // User message handler availability + hasUserCopyHandler = signal(false); + hasUserEditHandler = signal(false); +} diff --git a/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-view-input-container.ts b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-view-input-container.ts new file mode 100644 index 0000000000..3b6f2f4400 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-view-input-container.ts @@ -0,0 +1,80 @@ +import { + Component, + input, + ChangeDetectionStrategy, + ViewEncapsulation, + forwardRef, + ElementRef, +} from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { CopilotSlot } from "../../slots/copilot-slot"; +import { CopilotChatInput } from "./copilot-chat-input"; +import { CopilotChatViewDisclaimer } from "./copilot-chat-view-disclaimer"; +import { cn } from "../../utils"; + +/** + * InputContainer component for CopilotChatView + * Container for input and disclaimer components + * Uses ForwardRef for DOM access + */ +@Component({ + standalone: true, + selector: "copilot-chat-view-input-container", + imports: [CommonModule, CopilotSlot], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + providers: [ + { + provide: ElementRef, + useExisting: forwardRef(() => CopilotChatViewInputContainer), + }, + ], + template: ` +
    + +
    + + +
    + + + + +
    + `, +}) +export class CopilotChatViewInputContainer extends ElementRef { + inputContainerClass = input(); + + // Input slot configuration + input = input(); + inputClass = input(); + + // Disclaimer slot configuration + disclaimer = input(); + disclaimerText = input(); + disclaimerClass = input(); + + // Default components + protected readonly defaultInputComponent = CopilotChatInput; + protected readonly defaultDisclaimerComponent = CopilotChatViewDisclaimer; + + constructor(elementRef: ElementRef) { + super(elementRef.nativeElement); + } + + get computedClass(): string { + return cn( + "absolute bottom-0 left-0 right-0 z-20", + this.inputContainerClass() + ); + } +} diff --git a/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-view-scroll-to-bottom-button.ts b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-view-scroll-to-bottom-button.ts new file mode 100644 index 0000000000..facd5a307a --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-view-scroll-to-bottom-button.ts @@ -0,0 +1,80 @@ +import { + Component, + input, + output, + ChangeDetectionStrategy, + ViewEncapsulation, +} from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { LucideAngularModule, ChevronDown } from "lucide-angular"; +import { cn } from "../../utils"; + +/** + * ScrollToBottomButton component for CopilotChatView + * Matches React implementation exactly with same Tailwind classes + */ +@Component({ + standalone: true, + selector: "copilot-chat-view-scroll-to-bottom-button", + imports: [CommonModule, LucideAngularModule], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` + + `, +}) +export class CopilotChatViewScrollToBottomButton { + inputClass = input(); + disabled = input(false); + // Support function-style click handler via slot context + onClick = input<(() => void) | undefined>(); + + // Simple, idiomatic Angular output + clicked = output(); + + // Icon reference + protected readonly ChevronDown = ChevronDown; + + // Computed class matching React exactly + get computedClass(): string { + return cn( + // Base button styles + "rounded-full w-10 h-10 p-0", + // Background colors + "bg-white dark:bg-gray-900", + // Border and shadow + "shadow-lg border border-gray-200 dark:border-gray-700", + // Hover states + "hover:bg-gray-50 dark:hover:bg-gray-800", + // Layout + "flex items-center justify-center cursor-pointer", + // Transition + "transition-colors", + // Focus states + "focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2", + // Custom classes + this.inputClass() + ); + } + + handleClick(): void { + if (!this.disabled()) { + // Call input handler if provided (slot-style) + if (this.onClick()) { + this.onClick()!(); + } + this.clicked.emit(); + } + } +} diff --git a/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-view-scroll-view.ts b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-view-scroll-view.ts new file mode 100644 index 0000000000..a3c750073e --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-view-scroll-view.ts @@ -0,0 +1,343 @@ +import { + Component, + input, + output, + ViewChild, + ElementRef, + ChangeDetectionStrategy, + ViewEncapsulation, + signal, + computed, + OnInit, + AfterViewInit, + OnDestroy, + inject, + PLATFORM_ID, + ChangeDetectorRef, +} from "@angular/core"; +import { CommonModule, isPlatformBrowser } from "@angular/common"; +import { ScrollingModule } from "@angular/cdk/scrolling"; +import { CopilotSlot } from "../../slots/copilot-slot"; +import { CopilotChatMessageView } from "./copilot-chat-message-view"; +import { CopilotChatViewScrollToBottomButton } from "./copilot-chat-view-scroll-to-bottom-button"; +import { StickToBottom } from "../../directives/stick-to-bottom"; +import { ScrollPosition } from "../../scroll-position"; +import { Message } from "@ag-ui/client"; +import { cn } from "../../utils"; +import { Subject } from "rxjs"; +import { takeUntil } from "rxjs/operators"; + +/** + * ScrollView component for CopilotChatView + * Handles auto-scrolling and scroll position management + */ +@Component({ + standalone: true, + selector: "copilot-chat-view-scroll-view", + imports: [ + CommonModule, + ScrollingModule, + CopilotSlot, + CopilotChatMessageView, + StickToBottom, + ], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + providers: [ScrollPosition], + template: ` + @if (!hasMounted()) { + +
    +
    + +
    +
    + } @else if (!autoScroll()) { + +
    +
    +
    + +
    +
    + @if (messageView()) { + + + } @else { + + + } +
    +
    +
    +
    + + + @if (showScrollButton() && !isResizing()) { +
    + + +
    + } +
    + } @else { + +
    +
    + +
    + +
    +
    + @if (messageView()) { + + + } @else { + + + } +
    +
    +
    +
    + + + @if (!isAtBottom() && !isResizing()) { +
    + + +
    + } +
    + } + `, +}) +export class CopilotChatViewScrollView + implements OnInit, AfterViewInit, OnDestroy +{ + private cdr = inject(ChangeDetectorRef); + + autoScroll = input(true); + + inputContainerHeight = input(0); + + isResizing = input(false); + inputClass = input(); + messages = input([]); + messageView = input(); + messageViewClass = input(); + showCursor = input(false); + + // Handler availability flags removed in favor of DI service + + // Slot inputs + scrollToBottomButton = input(); + scrollToBottomButtonClass = input(); + + // Output events (bubbled from message view) + assistantMessageThumbsUp = output<{ message: Message }>(); + assistantMessageThumbsDown = output<{ message: Message }>(); + assistantMessageReadAloud = output<{ message: Message }>(); + assistantMessageRegenerate = output<{ message: Message }>(); + userMessageCopy = output<{ message: Message }>(); + userMessageEdit = output<{ message: Message }>(); + + // ViewChild references + @ViewChild("scrollContainer", { read: ElementRef }) + scrollContainer?: ElementRef; + @ViewChild("contentContainer", { read: ElementRef }) + contentContainer?: ElementRef; + @ViewChild(StickToBottom) stickToBottomDirective?: StickToBottom; + + // Default components + protected readonly defaultMessageViewComponent = CopilotChatMessageView; + protected readonly defaultScrollToBottomButtonComponent = + CopilotChatViewScrollToBottomButton; + + // Signals + protected hasMounted = signal(false); + protected showScrollButton = signal(false); + protected isAtBottom = signal(true); + protected paddingBottom = computed(() => this.inputContainerHeight() + 32); + + // Computed class + protected computedClass = computed(() => cn(this.inputClass())); + + private destroy$ = new Subject(); + private platformId = inject(PLATFORM_ID); + private scrollPositionService = inject(ScrollPosition); + + // No mirroring of inputs; derive directly via computed() + + ngOnInit(): void { + // Check if we're in the browser + if (isPlatformBrowser(this.platformId)) { + // Set mounted after a tick to allow for hydration + setTimeout(() => { + this.hasMounted.set(true); + }, 0); + } + } + + ngAfterViewInit(): void { + if (!this.autoScroll()) { + // Wait for the view to be fully rendered after hasMounted is set + setTimeout(() => { + if (this.scrollContainer) { + // Check initial scroll position + const initialState = this.scrollPositionService.getScrollState( + this.scrollContainer.nativeElement, + 10 + ); + this.showScrollButton.set(!initialState.isAtBottom); + + // Monitor scroll position for manual mode + this.scrollPositionService + .monitorScrollPosition(this.scrollContainer, 10) + .pipe(takeUntil(this.destroy$)) + .subscribe((state) => { + this.showScrollButton.set(!state.isAtBottom); + }); + } + }, 100); + } + } + + /** + * Handle isAtBottom change from StickToBottom directive + */ + onIsAtBottomChange(isAtBottom: boolean): void { + this.isAtBottom.set(isAtBottom); + } + + /** + * Scroll to bottom for manual mode + */ + scrollToBottom(): void { + if (this.scrollContainer) { + this.scrollPositionService.scrollToBottom(this.scrollContainer, true); + } + } + + /** + * Scroll to bottom for stick-to-bottom mode + */ + scrollToBottomFromStick(): void { + if (this.stickToBottomDirective) { + this.stickToBottomDirective.scrollToBottom("smooth"); + } + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + // Output maps for slots + scrollToBottomOutputs = { clicked: () => this.scrollToBottom() }; + scrollToBottomFromStickOutputs = { + clicked: () => this.scrollToBottomFromStick(), + }; + + // Context methods for templates + messageViewContext(): any { + return { + messages: this.messages(), + inputClass: this.messageViewClass(), + showCursor: this.showCursor(), + }; + } + + scrollToBottomContext(): any { + return { + inputClass: this.scrollToBottomButtonClass(), + onClick: () => this.scrollToBottom(), + }; + } + + scrollToBottomFromStickContext(): any { + return { + inputClass: this.scrollToBottomButtonClass(), + onClick: () => this.scrollToBottomFromStick(), + }; + } +} diff --git a/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-view.ts b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-view.ts new file mode 100644 index 0000000000..c5670423b8 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-view.ts @@ -0,0 +1,409 @@ +import { + Component, + ContentChild, + TemplateRef, + Type, + ViewChild, + ElementRef, + ChangeDetectionStrategy, + ChangeDetectorRef, + ViewEncapsulation, + computed, + signal, + OnInit, + OnChanges, + OnDestroy, + AfterViewInit, + input, + output, +} from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { CopilotSlot } from "../../slots/copilot-slot"; +import { CopilotChatViewScrollView } from "./copilot-chat-view-scroll-view"; +import { CopilotChatViewScrollToBottomButton } from "./copilot-chat-view-scroll-to-bottom-button"; +import { CopilotChatViewFeather } from "./copilot-chat-view-feather"; +import { CopilotChatViewInputContainer } from "./copilot-chat-view-input-container"; +import { CopilotChatViewDisclaimer } from "./copilot-chat-view-disclaimer"; +import { Message } from "@ag-ui/client"; +import { cn } from "../../utils"; +import { ResizeObserverService } from "../../resize-observer"; +import { CopilotChatViewHandlers } from "./copilot-chat-view-handlers"; +import { Subject } from "rxjs"; +import { takeUntil } from "rxjs/operators"; + +/** + * CopilotChatView component - Angular port of the React component. + * A complete chat interface with message feed and input components. + * + * @example + * ```html + * + * + * ``` + */ +@Component({ + standalone: true, + selector: "copilot-chat-view", + imports: [CommonModule, CopilotSlot, CopilotChatViewScrollView], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + providers: [ResizeObserverService, CopilotChatViewHandlers], + template: ` + + @if (customLayoutTemplate) { + + } @else { + +
    + + + + + + + + + + + +
    + } + `, +}) +export class CopilotChatView + implements OnInit, OnChanges, AfterViewInit, OnDestroy +{ + // Core inputs matching React props + messages = input([]); + autoScroll = input(true); + showCursor = input(false); + + // MessageView slot inputs + messageViewComponent = input | undefined>(undefined); + messageViewTemplate = input | undefined>(undefined); + messageViewClass = input(undefined); + + // ScrollView slot inputs + scrollViewComponent = input | undefined>(undefined); + scrollViewTemplate = input | undefined>(undefined); + scrollViewClass = input(undefined); + + // ScrollToBottomButton slot inputs + scrollToBottomButtonComponent = input | undefined>(undefined); + scrollToBottomButtonTemplate = input | undefined>(undefined); + scrollToBottomButtonClass = input(undefined); + + // Input slot inputs + inputComponent = input | undefined>(undefined); + inputTemplate = input | undefined>(undefined); + + // InputContainer slot inputs + inputContainerComponent = input | undefined>(undefined); + inputContainerTemplate = input | undefined>(undefined); + inputContainerClass = input(undefined); + + // Feather slot inputs + featherComponent = input | undefined>(undefined); + featherTemplate = input | undefined>(undefined); + featherClass = input(undefined); + + // Disclaimer slot inputs + disclaimerComponent = input | undefined>(undefined); + disclaimerTemplate = input | undefined>(undefined); + disclaimerClass = input(undefined); + disclaimerText = input(undefined); + + // Custom layout template (render prop pattern) + @ContentChild("customLayout") customLayoutTemplate?: TemplateRef; + + // Named template slots for deep customization + @ContentChild("sendButton") sendButtonTemplate?: TemplateRef; + @ContentChild("toolbar") toolbarTemplate?: TemplateRef; + @ContentChild("textArea") textAreaTemplate?: TemplateRef; + @ContentChild("audioRecorder") audioRecorderTemplate?: TemplateRef; + @ContentChild("assistantMessageMarkdownRenderer") + assistantMessageMarkdownRendererTemplate?: TemplateRef; + @ContentChild("thumbsUpButton") thumbsUpButtonTemplate?: TemplateRef; + @ContentChild("thumbsDownButton") thumbsDownButtonTemplate?: TemplateRef; + @ContentChild("readAloudButton") readAloudButtonTemplate?: TemplateRef; + @ContentChild("regenerateButton") regenerateButtonTemplate?: TemplateRef; + + // Output events for assistant message actions (bubbled from child components) + assistantMessageThumbsUp = output<{ message: Message }>(); + assistantMessageThumbsDown = output<{ message: Message }>(); + assistantMessageReadAloud = output<{ message: Message }>(); + assistantMessageRegenerate = output<{ message: Message }>(); + + // Output events for user message actions (if applicable) + userMessageCopy = output<{ message: Message }>(); + userMessageEdit = output<{ message: Message }>(); + + // ViewChild references + @ViewChild("inputContainerSlotRef", { read: ElementRef }) + inputContainerSlotRef?: ElementRef; + + // Default components for slots + protected readonly defaultScrollViewComponent = CopilotChatViewScrollView; + protected readonly defaultScrollToBottomButtonComponent = + CopilotChatViewScrollToBottomButton; + protected readonly defaultInputContainerComponent = + CopilotChatViewInputContainer; + protected readonly defaultFeatherComponent = CopilotChatViewFeather; + protected readonly defaultDisclaimerComponent = CopilotChatViewDisclaimer; + + // Signals for reactive state + protected messagesValue = computed(() => this.messages()); + protected autoScrollSignal = computed(() => this.autoScroll()); + protected showCursorSignal = computed(() => this.showCursor()); + protected disclaimerTextSignal = computed(() => this.disclaimerText()); + protected disclaimerClassSignal = computed(() => this.disclaimerClass()); + protected inputContainerHeight = signal(0); + protected isResizing = signal(false); + protected contentPaddingBottom = computed( + () => this.inputContainerHeight() + 32 + ); + + // Computed signals + protected computedClass = computed(() => cn("relative h-full")); + + // Slot resolution computed signals + protected messageViewSlot = computed( + () => this.messageViewTemplate() || this.messageViewComponent() + ); + + protected scrollViewSlot = computed( + () => this.scrollViewTemplate() || this.scrollViewComponent() + ); + + protected scrollToBottomButtonSlot = computed( + () => + this.scrollToBottomButtonTemplate() || + this.scrollToBottomButtonComponent() + ); + + protected inputSlot = computed( + () => this.inputTemplate() || this.inputComponent() + ); + + protected inputContainerSlot = computed( + () => this.inputContainerTemplate() || this.inputContainerComponent() + ); + + protected featherSlot = computed( + () => this.featherTemplate() || this.featherComponent() + ); + + protected disclaimerSlot = computed( + () => this.disclaimerTemplate() || this.disclaimerComponent() + ); + + // Context objects for slots + protected scrollViewContext = computed(() => ({ + autoScroll: this.autoScrollSignal(), + scrollToBottomButton: this.scrollToBottomButtonSlot(), + scrollToBottomButtonClass: this.scrollToBottomButtonClass(), + inputContainerHeight: this.inputContainerHeight(), + isResizing: this.isResizing(), + messages: this.messagesValue(), + messageView: this.messageViewSlot(), + messageViewClass: this.messageViewClass(), + })); + + // Removed scrollViewPropsComputed - no longer needed + + protected inputContainerContext = computed(() => ({ + input: this.inputSlot(), + disclaimer: this.disclaimerSlot(), + disclaimerText: this.disclaimerTextSignal(), + disclaimerClass: this.disclaimerClassSignal(), + inputContainerClass: this.inputContainerClass(), + })); + + // Removed inputContainerPropsComputed - no longer needed + + // Layout context for custom templates (render prop pattern) + protected layoutContext = computed(() => ({ + messageView: this.messageViewSlot(), + input: this.inputSlot(), + scrollView: this.scrollViewSlot(), + scrollToBottomButton: this.scrollToBottomButtonSlot(), + feather: this.featherSlot(), + inputContainer: this.inputContainerSlot(), + disclaimer: this.disclaimerSlot(), + })); + + private destroy$ = new Subject(); + private resizeTimeoutRef?: number; + + constructor( + private resizeObserverService: ResizeObserverService, + private cdr: ChangeDetectorRef, + private handlers: CopilotChatViewHandlers + ) { + // Clear any pending resize timeout when toggling isResizing, without signal writes here + } + + ngOnInit(): void { + // Initialize handler availability in the view-scoped service + // OutputEmitterRef doesn't expose 'observed'; default to true to enable UI affordances + this.handlers.hasAssistantThumbsUpHandler.set(true); + this.handlers.hasAssistantThumbsDownHandler.set(true); + this.handlers.hasAssistantReadAloudHandler.set(true); + this.handlers.hasAssistantRegenerateHandler.set(true); + this.handlers.hasUserCopyHandler.set(true); + this.handlers.hasUserEditHandler.set(true); + } + + ngOnChanges(): void { + // Keep handler availability in sync (assume available) + this.handlers.hasAssistantThumbsUpHandler.set(true); + this.handlers.hasAssistantThumbsDownHandler.set(true); + this.handlers.hasAssistantReadAloudHandler.set(true); + this.handlers.hasAssistantRegenerateHandler.set(true); + this.handlers.hasUserCopyHandler.set(true); + this.handlers.hasUserEditHandler.set(true); + } + + ngAfterViewInit(): void { + // Don't set a default height - measure it dynamically + + // Set up input container height monitoring + const measureAndObserve = () => { + if ( + !this.inputContainerSlotRef || + !this.inputContainerSlotRef.nativeElement + ) { + return false; + } + + // The slot ref points to the copilot-slot element + // We need to find the actual input container component inside it + const slotElement = this.inputContainerSlotRef.nativeElement; + const componentElement = slotElement.querySelector( + "copilot-chat-view-input-container" + ); + + if (!componentElement) { + return false; + } + + // Look for the absolute positioned div that contains the input + let innerDiv = componentElement.querySelector( + "div.absolute" + ) as HTMLElement; + + // If not found by class, try first child + if (!innerDiv) { + innerDiv = componentElement.firstElementChild as HTMLElement; + } + + if (!innerDiv) { + return false; + } + + // Measure the actual height + const measuredHeight = innerDiv.offsetHeight; + + if (measuredHeight === 0) { + return false; + } + + // Success! Set the initial height + this.inputContainerHeight.set(measuredHeight); + this.cdr.detectChanges(); + + // Create an ElementRef wrapper for ResizeObserver + const innerDivRef = new ElementRef(innerDiv); + + // Set up ResizeObserver to track changes + this.resizeObserverService + .observeElement(innerDivRef, 0, 250) + .pipe(takeUntil(this.destroy$)) + .subscribe((state) => { + const newHeight = state.height; + + if (newHeight !== this.inputContainerHeight() && newHeight > 0) { + this.inputContainerHeight.set(newHeight); + this.isResizing.set(true); + this.cdr.detectChanges(); + + // Clear existing timeout + if (this.resizeTimeoutRef) { + clearTimeout(this.resizeTimeoutRef); + } + + // Set isResizing to false after a short delay + this.resizeTimeoutRef = window.setTimeout(() => { + this.isResizing.set(false); + this.resizeTimeoutRef = undefined; + this.cdr.detectChanges(); + }, 250); + } + }); + + return true; + }; + + // Try to measure immediately + if (!measureAndObserve()) { + // If failed, retry with increasing delays + let attempts = 0; + const maxAttempts = 10; + + const retry = () => { + attempts++; + if (measureAndObserve()) { + // Successfully measured + } else if (attempts < maxAttempts) { + // Exponential backoff: 50ms, 100ms, 200ms, 400ms, etc. + const delay = 50 * Math.pow(2, Math.min(attempts - 1, 4)); + setTimeout(retry, delay); + } else { + // Failed to measure after max attempts + } + }; + + // Start retry with first delay + setTimeout(retry, 50); + } + } + + ngOnDestroy(): void { + if (this.resizeTimeoutRef) { + clearTimeout(this.resizeTimeoutRef); + } + this.destroy$.next(); + this.destroy$.complete(); + } +} diff --git a/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-view.types.ts b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-view.types.ts new file mode 100644 index 0000000000..dc9b8b23ea --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat-view.types.ts @@ -0,0 +1,52 @@ +import { Type, TemplateRef } from "@angular/core"; +import { Message } from "@ag-ui/client"; + +/** + * Props for CopilotChatView component + */ +export interface CopilotChatViewProps { + messages?: Message[]; + autoScroll?: boolean; + + // Slot configurations + messageViewComponent?: Type; + messageViewTemplate?: TemplateRef; + messageViewClass?: string; + + scrollViewComponent?: Type; + scrollViewTemplate?: TemplateRef; + scrollViewClass?: string; + + scrollToBottomButtonComponent?: Type; + scrollToBottomButtonTemplate?: TemplateRef; + scrollToBottomButtonClass?: string; + + inputComponent?: Type; + inputTemplate?: TemplateRef; + + inputContainerComponent?: Type; + inputContainerTemplate?: TemplateRef; + inputContainerClass?: string; + + featherComponent?: Type; + featherTemplate?: TemplateRef; + featherClass?: string; + + disclaimerComponent?: Type; + disclaimerTemplate?: TemplateRef; + disclaimerClass?: string; + disclaimerText?: string; +} + +/** + * Context for custom layout template + */ +export interface CopilotChatViewLayoutContext { + messageView: any; + input: any; + scrollView: any; + scrollToBottomButton: any; + feather: any; + inputContainer: any; + disclaimer: any; +} diff --git a/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat.ts b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat.ts new file mode 100644 index 0000000000..48cf655ca8 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/components/chat/copilot-chat.ts @@ -0,0 +1,178 @@ +import { + Component, + input, + ChangeDetectionStrategy, + ViewEncapsulation, + signal, + effect, + ChangeDetectorRef, + Injector, + Type, + computed, + inject, +} from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { CopilotChatView } from "./copilot-chat-view"; + +import { DEFAULT_AGENT_ID, randomUUID } from "@copilotkitnext/shared"; +import { Message, AbstractAgent } from "@ag-ui/client"; +import { injectAgentStore } from "../../agent"; +import { ChatState } from "../../chat-state"; + +/** + * CopilotChat component - Angular equivalent of React's + * Provides a complete chat interface that wires an agent to the chat view + * + * @example + * ```html + * + * ``` + */ +@Component({ + selector: "copilot-chat", + standalone: true, + imports: [CommonModule, CopilotChatView], + changeDetection: ChangeDetectionStrategy.OnPush, + encapsulation: ViewEncapsulation.None, + template: ` + + + `, + providers: [ + { + provide: ChatState, + useExisting: CopilotChat, + }, + ], +}) +export class CopilotChat implements ChatState { + readonly inputValue = signal(""); + readonly agentId = input(); + readonly threadId = input(); + readonly inputComponent = input | undefined>(); + private readonly resolvedAgentId = computed( + () => this.agentId() ?? DEFAULT_AGENT_ID + ); + readonly agentStore = injectAgentStore(this.resolvedAgentId); + // readonly chatConfig = injectChatConfig(); + readonly cdr = inject(ChangeDetectorRef); + readonly injector = inject(Injector); + + protected messages = computed(() => this.agentStore()?.messages()); + protected isRunning = computed(() => this.agentStore()?.isRunning()); + protected showCursor = signal(false); + + private generatedThreadId: string = randomUUID(); + private hasConnectedOnce = false; + + constructor() { + // Connect once when agent becomes available + // Connect once when agent becomes available + effect( + () => { + const a = this.agentStore()?.agent; + if (!a) return; + // Apply thread id when agent is available + a.threadId = this.threadId() || this.generatedThreadId; + if (!this.hasConnectedOnce) { + this.hasConnectedOnce = true; + if ("isCopilotKitAgent" in (a as any)) { + this.connectToAgent(a); + } else { + // Non-CopilotKit agent: nothing to connect; keep default cursor state + } + } + }, + { allowSignalWrites: true } + ); + + // Keep agent threadId in sync with input + effect(() => { + const a = this.agentStore()?.agent; + if (a) { + a.threadId = this.threadId() || this.generatedThreadId; + } + }); + } + + private async connectToAgent(agent: AbstractAgent): Promise { + if (!agent) return; + + this.showCursor.set(true); + this.cdr.markForCheck(); + + try { + await agent.runAgent( + { forwardedProps: { __copilotkitConnect: true } }, + { + onTextMessageStartEvent: () => { + this.showCursor.set(false); + this.cdr.detectChanges(); + }, + onToolCallStartEvent: () => { + this.showCursor.set(false); + this.cdr.detectChanges(); + }, + } + ); + this.showCursor.set(false); + this.cdr.markForCheck(); + } catch (error) { + console.error("Failed to connect to agent:", error); + this.showCursor.set(false); + this.cdr.markForCheck(); + } + } + + async submitInput(value: string): Promise { + const agent = this.agentStore()?.agent; + if (!agent || !value.trim()) return; + + // Add user message + const userMessage: Message = { + id: randomUUID(), + role: "user", + content: value, + }; + agent.addMessage(userMessage); + + // Clear the input + this.inputValue.set(""); + + // Show cursor while processing + this.showCursor.set(true); + this.cdr.markForCheck(); + + // Run the agent with named subscriber callbacks + try { + await agent.runAgent( + {}, + { + onTextMessageStartEvent: () => { + this.showCursor.set(false); + this.cdr.detectChanges(); + }, + onToolCallStartEvent: () => { + this.showCursor.set(false); + this.cdr.detectChanges(); + }, + } + ); + } catch (error) { + console.error("Agent run error:", error); + } finally { + this.showCursor.set(false); + this.cdr.markForCheck(); + } + } + + changeInput(value: string): void { + this.inputValue.set(value); + } +} diff --git a/src/v2.x/packages/angular/src/lib/config.ts b/src/v2.x/packages/angular/src/lib/config.ts new file mode 100644 index 0000000000..502ea1c5e6 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/config.ts @@ -0,0 +1,31 @@ +import { inject, InjectionToken, Provider } from "@angular/core"; +import { AbstractAgent } from "@ag-ui/client"; +import { + ClientTool, + FrontendToolConfig, + HumanInTheLoopConfig, + RenderToolCallConfig, +} from "./tools"; + +export interface CopilotKitConfig { + runtimeUrl?: string; + headers?: Record; + properties?: Record; + agents?: Record; + tools?: ClientTool[]; + renderToolCalls?: RenderToolCallConfig[]; + frontendTools?: FrontendToolConfig[]; + humanInTheLoop?: HumanInTheLoopConfig[]; +} + +export const COPILOT_KIT_CONFIG = new InjectionToken( + "COPILOT_KIT_CONFIG" +); + +export function injectCopilotKitConfig(): CopilotKitConfig { + return inject(COPILOT_KIT_CONFIG); +} + +export function provideCopilotKit(config: CopilotKitConfig): Provider { + return { provide: COPILOT_KIT_CONFIG, useValue: config }; +} diff --git a/src/v2.x/packages/angular/src/lib/copilotkit.spec.ts b/src/v2.x/packages/angular/src/lib/copilotkit.spec.ts new file mode 100644 index 0000000000..27b80f1e55 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/copilotkit.spec.ts @@ -0,0 +1,240 @@ +import { Component, Injector, signal } from "@angular/core"; +import { TestBed } from "@angular/core/testing"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { z } from "zod"; +import { CopilotKit } from "./copilotkit"; +import { provideCopilotKit } from "./config"; +import { HumanInTheLoop } from "./human-in-the-loop"; + +const mockSubscribe = vi.fn(); +const mockAddTool = vi.fn(); +const mockRemoveTool = vi.fn(); +const mockSetRuntimeUrl = vi.fn(); +const mockSetHeaders = vi.fn(); +const mockSetProperties = vi.fn(); +const mockSetAgents = vi.fn(); +const mockGetAgent = vi.fn(); + +let lastCoreInstance: any; +let lastCoreConfig: any; + +vi.mock("@copilotkitnext/core", () => { + class MockCopilotKitCore { + readonly subscribe = mockSubscribe; + readonly addTool = mockAddTool; + readonly removeTool = mockRemoveTool; + readonly setRuntimeUrl = mockSetRuntimeUrl; + readonly setHeaders = mockSetHeaders; + readonly setProperties = mockSetProperties; + readonly setAgents__unsafe_dev_only = mockSetAgents; + readonly getAgent = mockGetAgent; + agents: Record = {}; + listener?: Parameters[0]; + + constructor(config: any) { + lastCoreConfig = config; + lastCoreInstance = this; + mockSubscribe.mockImplementationOnce((listener: any) => { + this.listener = listener; + return { unsubscribe: vi.fn() }; + }); + } + } + + return { CopilotKitCore: MockCopilotKitCore } as any; +}); + +describe("CopilotKit", () => { + beforeEach(() => { + TestBed.resetTestingModule(); + vi.clearAllMocks(); + }); + + it("initialises core with transformed tool and renderer config", () => { + @Component({ + standalone: true, + selector: "dummy-tool", + template: "", + }) + class DummyToolComponent { + toolCall = signal({} as any); + } + + TestBed.configureTestingModule({ + providers: [ + provideCopilotKit({ + runtimeUrl: "https://runtime.local", + headers: { Authorization: "token" }, + properties: { region: "eu" }, + tools: [ + { + name: "search", + description: "Search something", + parameters: z.object({ query: z.string() }), + handler: async () => "done", + renderer: DummyToolComponent, + }, + ], + renderToolCalls: [ + { + name: "custom", + args: z.object({ query: z.string() }), + component: DummyToolComponent, + agentId: "agent-a", + }, + ], + }), + ], + }); + + const copilotKit = TestBed.inject(CopilotKit); + + expect(lastCoreConfig.runtimeUrl).toBe("https://runtime.local"); + expect(lastCoreConfig.headers).toEqual({ + Authorization: "token", + }); + expect(lastCoreConfig.tools).toEqual([ + expect.objectContaining({ + name: "search", + description: "Search something", + parameters: expect.anything(), + handler: expect.any(Function), + renderer: DummyToolComponent, + }), + ]); + + expect(copilotKit.toolCallRenderConfigs()).toEqual([ + { + name: "custom", + args: expect.anything(), + component: DummyToolComponent, + agentId: "agent-a", + }, + { + name: "search", + args: expect.anything(), + component: DummyToolComponent, + agentId: undefined, + }, + ]); + }); + + it("tracks client tools and executes handlers within injection context", async () => { + const handlerSpy = vi.fn().mockResolvedValue("handled"); + TestBed.configureTestingModule({ + providers: [provideCopilotKit({})], + }); + + const copilotKit = TestBed.inject(CopilotKit); + const injector = TestBed.inject(Injector); + + copilotKit.addFrontendTool({ + name: "client", + description: "Client tool", + args: z.object({ value: z.string() }), + component: class { toolCall = signal({} as any); }, + handler: handlerSpy, + injector, + }); + + expect(mockAddTool).toHaveBeenCalledWith( + expect.objectContaining({ name: "client" }) + ); + expect(copilotKit.clientToolCallRenderConfigs()).toHaveLength(1); + + const tool = mockAddTool.mock.calls.at(-1)![0]; + await tool.handler({ value: "ok" }); + expect(handlerSpy).toHaveBeenCalledWith({ value: "ok" }); + }); + + it("registers human-in-the-loop tools and delegates responses", async () => { + const onResultSpy = vi + .spyOn(HumanInTheLoop.prototype, "onResult") + .mockResolvedValue("result"); + + TestBed.configureTestingModule({ + providers: [provideCopilotKit({})], + }); + + const copilotKit = TestBed.inject(CopilotKit); + + const toolConfig = { + name: "approval", + args: z.object({ summary: z.string() }), + component: class { toolCall = signal({} as any); }, + toolCall: vi.fn(), + agentId: "agent-1", + } as const; + + copilotKit.addHumanInTheLoop(toolConfig); + + expect(copilotKit.humanInTheLoopToolRenderConfigs()).toEqual([ + toolConfig, + ]); + expect(mockAddTool).toHaveBeenCalledWith( + expect.objectContaining({ name: "approval" }) + ); + + const tool = mockAddTool.mock.calls.at(-1)![0]; + await tool.handler({}, { id: "call-1", function: { name: "approval" } }); + expect(onResultSpy).toHaveBeenCalledWith("call-1", "approval"); + + onResultSpy.mockRestore(); + }); + + it("removes tools and renderer configs", () => { + TestBed.configureTestingModule({ + providers: [provideCopilotKit({})], + }); + + const copilotKit = TestBed.inject(CopilotKit); + + copilotKit.addRenderToolCall({ + name: "temp", + args: z.object({}), + component: class { toolCall = signal({} as any); }, + agentId: undefined, + }); + + copilotKit.removeTool("temp"); + + expect(mockRemoveTool).toHaveBeenCalledWith("temp"); + expect(copilotKit.toolCallRenderConfigs()).toEqual([]); + }); + + it("updates runtime configuration via core methods", () => { + TestBed.configureTestingModule({ + providers: [provideCopilotKit({})], + }); + + const copilotKit = TestBed.inject(CopilotKit); + + copilotKit.updateRuntime({ + runtimeUrl: "https://other", + headers: { Authorization: "different" }, + properties: { locale: "en" }, + agents: { a: {} as any }, + }); + + expect(mockSetRuntimeUrl).toHaveBeenCalledWith("https://other"); + expect(mockSetHeaders).toHaveBeenCalledWith({ Authorization: "different" }); + expect(mockSetProperties).toHaveBeenCalledWith({ locale: "en" }); + expect(mockSetAgents).toHaveBeenCalledWith({ a: {} }); + }); + + it("reflects agent updates from core subscriptions", () => { + TestBed.configureTestingModule({ + providers: [provideCopilotKit({})], + }); + + const copilotKit = TestBed.inject(CopilotKit); + const core = lastCoreInstance!; + + core.agents = { + agent1: { id: "agent1" }, + } as any; + + core.listener!.onAgentsChanged(); + expect(copilotKit.agents()).toEqual(core.agents); + }); +}); diff --git a/src/v2.x/packages/angular/src/lib/copilotkit.ts b/src/v2.x/packages/angular/src/lib/copilotkit.ts new file mode 100644 index 0000000000..0520b94c4f --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/copilotkit.ts @@ -0,0 +1,154 @@ +import { AbstractAgent } from "@ag-ui/client"; +import { FrontendTool, CopilotKitCore } from "@copilotkitnext/core"; +import { Injectable, Injector, Signal, WritableSignal, runInInjectionContext, signal, inject } from "@angular/core"; +import { FrontendToolConfig, HumanInTheLoopConfig, RenderToolCallConfig } from "./tools"; +import { injectCopilotKitConfig } from "./config"; +import { HumanInTheLoop } from "./human-in-the-loop"; + +@Injectable({ providedIn: "root" }) +export class CopilotKit { + readonly #config = injectCopilotKitConfig(); + readonly #hitl = inject(HumanInTheLoop); + readonly #rootInjector = inject(Injector); + readonly #agents = signal>(this.#config.agents ?? {}); + readonly agents = this.#agents.asReadonly(); + + readonly core = new CopilotKitCore({ + runtimeUrl: this.#config.runtimeUrl, + headers: this.#config.headers, + properties: this.#config.properties, + agents__unsafe_dev_only: this.#config.agents, + tools: this.#config.tools, + }); + + readonly #toolCallRenderConfigs: WritableSignal = signal([]); + readonly #clientToolCallRenderConfigs: WritableSignal = signal([]); + readonly #humanInTheLoopToolRenderConfigs: WritableSignal = signal([]); + + readonly toolCallRenderConfigs: Signal = this.#toolCallRenderConfigs.asReadonly(); + readonly clientToolCallRenderConfigs: Signal = this.#clientToolCallRenderConfigs.asReadonly(); + readonly humanInTheLoopToolRenderConfigs: Signal = + this.#humanInTheLoopToolRenderConfigs.asReadonly(); + + constructor() { + this.#config.renderToolCalls?.forEach((renderConfig) => { + this.addRenderToolCall(renderConfig); + }); + + this.#config.tools?.forEach((tool) => { + if (tool.renderer && tool.parameters) { + this.addRenderToolCall({ + name: tool.name, + args: tool.parameters, + component: tool.renderer, + agentId: tool.agentId, + }); + } + }); + + this.#config.frontendTools?.forEach((clientTool) => { + this.addFrontendTool({ ...clientTool, injector: this.#rootInjector }); + }); + + this.#config.humanInTheLoop?.forEach((humanInTheLoopTool) => { + this.addHumanInTheLoop(humanInTheLoopTool); + }); + + this.core.subscribe({ + onAgentsChanged: () => { + this.#agents.set(this.core.agents); + }, + }); + } + + #bindClientTool( + clientToolWithInjector: FrontendToolConfig & { + injector: Injector; + }, + ): FrontendTool { + const { injector, handler, ...frontendCandidate } = clientToolWithInjector; + + return { + ...frontendCandidate, + handler: (args) => runInInjectionContext(injector, () => handler(args)), + }; + } + + addFrontendTool( + clientToolWithInjector: FrontendToolConfig & { + injector: Injector; + }, + ): void { + const tool = this.#bindClientTool(clientToolWithInjector); + + this.core.addTool(tool); + + this.#clientToolCallRenderConfigs.update((current) => [...current, clientToolWithInjector]); + } + + addRenderToolCall(renderConfig: RenderToolCallConfig): void { + this.#toolCallRenderConfigs.update((current) => [...current, renderConfig]); + } + + #bindHumanInTheLoopTool(humanInTheLoopTool: HumanInTheLoopConfig): FrontendTool { + return { + ...humanInTheLoopTool, + handler: (args, toolCall) => { + return this.#hitl.onResult(toolCall.id, humanInTheLoopTool.name); + }, + }; + } + + addHumanInTheLoop(humanInTheLoopTool: HumanInTheLoopConfig): void { + this.#humanInTheLoopToolRenderConfigs.update((current) => [...current, humanInTheLoopTool]); + + const tool = this.#bindHumanInTheLoopTool(humanInTheLoopTool); + + this.core.addTool(tool); + } + + #isSameAgentId(target: T, agentId?: string): boolean { + if (agentId) { + return target.agentId === agentId; + } + + return true; + } + + removeTool(toolName: string, agentId?: string): void { + this.core.removeTool(toolName); + this.#clientToolCallRenderConfigs.update((current) => + current.filter((renderConfig) => renderConfig.name !== toolName && this.#isSameAgentId(renderConfig, agentId)), + ); + this.#humanInTheLoopToolRenderConfigs.update((current) => + current.filter((renderConfig) => renderConfig.name !== toolName && this.#isSameAgentId(renderConfig, agentId)), + ); + this.#toolCallRenderConfigs.update((current) => + current.filter((renderConfig) => renderConfig.name !== toolName && this.#isSameAgentId(renderConfig, agentId)), + ); + } + + getAgent(agentId: string): AbstractAgent | undefined { + return this.core.getAgent(agentId); + } + + updateRuntime(options: { + runtimeUrl?: string; + headers?: Record; + properties?: Record; + agents?: Record; + }): void { + if (options.runtimeUrl !== undefined) { + this.core.setRuntimeUrl(options.runtimeUrl); + } + if (options.headers !== undefined) { + this.core.setHeaders(options.headers); + } + if (options.properties !== undefined) { + this.core.setProperties(options.properties); + } + if (options.agents !== undefined) { + this.core.setAgents__unsafe_dev_only(options.agents); + } + } +} diff --git a/src/v2.x/packages/angular/src/lib/directives/__tests__/copilotkit-agent-context.directive.spec.ts b/src/v2.x/packages/angular/src/lib/directives/__tests__/copilotkit-agent-context.directive.spec.ts new file mode 100644 index 0000000000..0fe4b43a2b --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/directives/__tests__/copilotkit-agent-context.directive.spec.ts @@ -0,0 +1,84 @@ +import { Component } from "@angular/core"; +import { TestBed } from "@angular/core/testing"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { CopilotKitAgentContext } from "../copilotkit-agent-context"; +import { CopilotKit } from "../../copilotkit"; + +class CopilotKitCoreStub { + addContext = vi.fn(() => "ctx-1"); + removeContext = vi.fn(); +} + +class CopilotKitStub { + core = new CopilotKitCoreStub(); +} + +describe("CopilotKitAgentContext", () => { + beforeEach(() => { + TestBed.resetTestingModule(); + TestBed.configureTestingModule({ + providers: [{ provide: CopilotKit, useClass: CopilotKitStub }], + }); + }); + + it("adds and removes context for separate description/value inputs", () => { + @Component({ + standalone: true, + imports: [CopilotKitAgentContext], + template: ` +
    + `, + }) + class HostComponent { + description = "Initial"; + value = { foo: "bar" }; + } + + const fixture = TestBed.createComponent(HostComponent); + fixture.detectChanges(); + + const core = TestBed.inject(CopilotKit).core as CopilotKitCoreStub; + expect(core.addContext).toHaveBeenCalledWith({ + description: "Initial", + value: { foo: "bar" }, + }); + + fixture.componentInstance.description = "Updated"; + fixture.detectChanges(); + + expect(core.removeContext).toHaveBeenCalledWith("ctx-1"); + expect(core.addContext).toHaveBeenLastCalledWith({ + description: "Updated", + value: { foo: "bar" }, + }); + + fixture.destroy(); + expect(core.removeContext).toHaveBeenLastCalledWith("ctx-1"); + }); + + it("supports passing full context object via directive binding", () => { + @Component({ + standalone: true, + imports: [CopilotKitAgentContext], + template: ` +
    + `, + }) + class ObjectHostComponent { + context = { description: "All", value: 42 }; + } + + const fixture = TestBed.createComponent(ObjectHostComponent); + fixture.detectChanges(); + + const core = TestBed.inject(CopilotKit).core as CopilotKitCoreStub; + expect(core.addContext).toHaveBeenCalledWith({ + description: "All", + value: 42, + }); + }); +}); diff --git a/src/v2.x/packages/angular/src/lib/directives/__tests__/copilotkit-chat-config.directive.spec.ts b/src/v2.x/packages/angular/src/lib/directives/__tests__/copilotkit-chat-config.directive.spec.ts new file mode 100644 index 0000000000..84803de969 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/directives/__tests__/copilotkit-chat-config.directive.spec.ts @@ -0,0 +1,44 @@ +import { Component } from "@angular/core"; +import { TestBed } from "@angular/core/testing"; +import { describe, expect, it } from "vitest"; +import { + COPILOT_CHAT_DEFAULT_LABELS, + injectChatLabels, + provideCopilotChatLabels, +} from "../../chat-config"; + +describe("Copilot chat labels", () => { + it("returns default labels when no provider is registered", () => { + @Component({ standalone: true, template: "" }) + class HostComponent { + labels = injectChatLabels(); + } + + TestBed.configureTestingModule({}); + const fixture = TestBed.createComponent(HostComponent); + + expect(fixture.componentInstance.labels).toEqual(COPILOT_CHAT_DEFAULT_LABELS); + }); + + it("merges provided labels with defaults", () => { + @Component({ standalone: true, template: "" }) + class HostComponent { + labels = injectChatLabels(); + } + + TestBed.configureTestingModule({ + providers: [ + provideCopilotChatLabels({ + chatInputPlaceholder: "Override", + }), + ], + }); + + const fixture = TestBed.createComponent(HostComponent); + expect(fixture.componentInstance.labels.chatInputPlaceholder).toBe( + "Override" + ); + expect(fixture.componentInstance.labels.assistantMessageToolbarCopyCodeLabel) + .toBe(COPILOT_CHAT_DEFAULT_LABELS.assistantMessageToolbarCopyCodeLabel); + }); +}); diff --git a/src/v2.x/packages/angular/src/lib/directives/__tests__/copilotkit-config.directive.spec.ts b/src/v2.x/packages/angular/src/lib/directives/__tests__/copilotkit-config.directive.spec.ts new file mode 100644 index 0000000000..11ab02c9a3 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/directives/__tests__/copilotkit-config.directive.spec.ts @@ -0,0 +1,30 @@ +import { Component } from "@angular/core"; +import { TestBed } from "@angular/core/testing"; +import { describe, expect, it } from "vitest"; +import { provideCopilotKit, injectCopilotKitConfig } from "../../config"; + +describe("CopilotKit config", () => { + it("provides configuration via DI", () => { + @Component({ standalone: true, template: "" }) + class HostComponent { + config = injectCopilotKitConfig(); + } + + const headers = { Authorization: "token" }; + + TestBed.configureTestingModule({ + providers: [ + provideCopilotKit({ + runtimeUrl: "https://example.com", + headers, + }), + ], + }); + + const fixture = TestBed.createComponent(HostComponent); + expect(fixture.componentInstance.config.runtimeUrl).toBe( + "https://example.com" + ); + expect(fixture.componentInstance.config.headers).toBe(headers); + }); +}); diff --git a/src/v2.x/packages/angular/src/lib/directives/__tests__/copilotkit-frontend-tool-simple.directive.spec.ts.disabled b/src/v2.x/packages/angular/src/lib/directives/__tests__/copilotkit-frontend-tool-simple.directive.spec.ts.disabled new file mode 100644 index 0000000000..39bd89e600 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/directives/__tests__/copilotkit-frontend-tool-simple.directive.spec.ts.disabled @@ -0,0 +1,68 @@ +import { Component } from "@angular/core"; +import { TestBed } from "@angular/core/testing"; +import { CopilotKitFrontendTool } from "../copilotkit-frontend-tool"; +import { CopilotKit } from "../../core/copilotkit"; +import { provideCopilotKit } from "../../core/copilotkit.providers"; +import { z } from "zod"; + +// Mock CopilotKitCore +vi.mock("@copilotkitnext/core", () => ({ + CopilotKitCore: vi.fn().mockImplementation(() => { + const runtimeUrlSetter = vi.fn(); + return { + addTool: vi.fn(), + removeTool: vi.fn(), + setHeaders: vi.fn(), + setProperties: vi.fn(), + setAgents: vi.fn(), + subscribe: vi.fn(() => () => {}), + setRuntimeUrl: vi.fn((url: string | undefined) => { + runtimeUrlSetter(url); + }), + get runtimeUrl() { + return undefined; + }, + __runtimeUrlSetter: runtimeUrlSetter, + }; + }), +})); + +describe("CopilotKitFrontendTool - Simple", () => { + let service: CopilotKit; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [provideCopilotKit({})], + }); + service = TestBed.inject(CopilotKit); + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + it.skip("should create directive instance", () => { + // Cannot test direct instantiation with inject() + expect(true).toBe(true); + }); + + it.skip("should have required inputs", () => { + // Cannot test direct instantiation with inject() + expect(true).toBe(true); + }); + + it.skip("should register tool on init", () => { + // Cannot test direct instantiation with inject() + expect(true).toBe(true); + }); + + it.skip("should unregister tool on destroy", () => { + // Cannot test direct instantiation with inject() + expect(true).toBe(true); + }); + + it.skip("should warn if name is missing", () => { + // Cannot test direct instantiation with inject() + expect(true).toBe(true); + }); +}); diff --git a/src/v2.x/packages/angular/src/lib/directives/__tests__/copilotkit-frontend-tool.directive.spec.ts b/src/v2.x/packages/angular/src/lib/directives/__tests__/copilotkit-frontend-tool.directive.spec.ts new file mode 100644 index 0000000000..c2ff06f0b4 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/directives/__tests__/copilotkit-frontend-tool.directive.spec.ts @@ -0,0 +1,108 @@ +import { Component, signal, Type } from "@angular/core"; +import { TestBed } from "@angular/core/testing"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { + registerRenderToolCall, + registerFrontendTool, + registerHumanInTheLoop, +} from "../../tools"; +import { CopilotKit } from "../../copilotkit"; +import { z } from "zod"; + +class CopilotKitStub { + addRenderToolCall = vi.fn(); + addFrontendTool = vi.fn(); + removeTool = vi.fn(); + addHumanInTheLoop = vi.fn(); +} + +@Component({ standalone: true, template: "", selector: "dummy-tool" }) +class DummyToolComponent {} + +describe("tool registration helpers", () => { + let copilotKitStub: CopilotKitStub; + + beforeEach(() => { + TestBed.resetTestingModule(); + copilotKitStub = new CopilotKitStub(); + TestBed.configureTestingModule({ + providers: [{ provide: CopilotKit, useValue: copilotKitStub }], + }); + }); + + it("registers and cleans up renderers", () => { + @Component({ standalone: true, template: "" }) + class HostComponent { + constructor() { + registerRenderToolCall({ + name: "tool", + args: z.object({ value: z.string() }), + component: DummyToolComponent as Type, + }); + } + } + + const fixture = TestBed.createComponent(HostComponent); + expect(copilotKitStub.addRenderToolCall).toHaveBeenCalledWith( + expect.objectContaining({ name: "tool" }) + ); + + fixture.destroy(); + expect(copilotKitStub.removeTool).toHaveBeenCalledWith("tool", undefined); + }); + + it("registers client tools and removes them on destroy", async () => { + const handler = vi.fn(async () => "handled"); + + @Component({ standalone: true, template: "" }) + class HostComponent { + constructor() { + registerFrontendTool({ + name: "client-tool", + description: "", + args: z.object({}), + component: DummyToolComponent as Type, + handler, + }); + } + } + + const fixture = TestBed.createComponent(HostComponent); + expect(copilotKitStub.addFrontendTool).toHaveBeenCalledWith( + expect.objectContaining({ name: "client-tool" }) + ); + + const added = copilotKitStub.addFrontendTool.mock.calls.at(-1)![0]; + await added.handler({}); + expect(handler).toHaveBeenCalled(); + + fixture.destroy(); + expect(copilotKitStub.removeTool).toHaveBeenCalledWith("client-tool"); + }); + + it("registers human-in-the-loop tools and removes them on destroy", () => { + @Component({ standalone: true, template: "" }) + class HostComponent { + constructor() { + registerHumanInTheLoop({ + name: "approval", + args: z.object({}), + component: DummyToolComponent as Type, + toolCall: signal({ + args: {}, + status: "in-progress", + result: undefined, + }), + }); + } + } + + const fixture = TestBed.createComponent(HostComponent); + expect(copilotKitStub.addHumanInTheLoop).toHaveBeenCalledWith( + expect.objectContaining({ name: "approval" }) + ); + + fixture.destroy(); + expect(copilotKitStub.removeTool).toHaveBeenCalledWith("approval"); + }); +}); diff --git a/src/v2.x/packages/angular/src/lib/directives/__tests__/copilotkit-human-in-the-loop.directive.spec.ts b/src/v2.x/packages/angular/src/lib/directives/__tests__/copilotkit-human-in-the-loop.directive.spec.ts new file mode 100644 index 0000000000..801be7fe1b --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/directives/__tests__/copilotkit-human-in-the-loop.directive.spec.ts @@ -0,0 +1,42 @@ +import { TestBed } from "@angular/core/testing"; +import { describe, expect, it } from "vitest"; +import { HumanInTheLoop } from "../../human-in-the-loop"; + +describe("HumanInTheLoop service", () => { + it("resolves when matching result is provided", async () => { + TestBed.configureTestingModule({ providers: [HumanInTheLoop] }); + const service = TestBed.inject(HumanInTheLoop); + + const promise = service.onResult("call-1", "approval"); + service.addResult("call-1", "approval", { status: "ok" }); + + await expect(promise).resolves.toEqual({ + toolCallId: "call-1", + toolName: "approval", + result: { status: "ok" }, + }); + }); + + it("ignores non-matching results until criteria matches", async () => { + TestBed.configureTestingModule({ providers: [HumanInTheLoop] }); + const service = TestBed.inject(HumanInTheLoop); + + const promise = service.onResult("call-2", "verify"); + + service.addResult("call-2", "other", "nope"); + + const race = Promise.race([ + promise, + new Promise((resolve) => setTimeout(() => resolve("pending"), 20)), + ]); + + await expect(race).resolves.toBe("pending"); + + service.addResult("call-2", "verify", "ok"); + await expect(promise).resolves.toEqual({ + toolCallId: "call-2", + toolName: "verify", + result: "ok", + }); + }); +}); diff --git a/src/v2.x/packages/angular/src/lib/directives/copilotkit-agent-context.ts b/src/v2.x/packages/angular/src/lib/directives/copilotkit-agent-context.ts new file mode 100644 index 0000000000..7466d16d31 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/directives/copilotkit-agent-context.ts @@ -0,0 +1,134 @@ +import { + Directive, + Input, + OnInit, + OnChanges, + OnDestroy, + SimpleChanges, + Inject, +} from "@angular/core"; +import { CopilotKit } from "../copilotkit"; +import type { Context } from "@ag-ui/client"; + +/** + * Directive to manage agent context in CopilotKit. + * Automatically adds context on init, updates on changes, and removes on destroy. + * + * @example + * ```html + * + *
    + *
    + * + * + *
    + *
    + * + * + *
    + *
    + * ``` + */ +@Directive({ + selector: "[copilotkitAgentContext]", + standalone: true, +}) +export class CopilotKitAgentContext implements OnInit, OnChanges, OnDestroy { + private contextId?: string; + + constructor(@Inject(CopilotKit) private readonly copilotkit: CopilotKit) {} + + /** + * Context object containing both description and value. + * If provided, this takes precedence over individual inputs. + */ + @Input("copilotkitAgentContext") context?: Context; + + /** + * Description of the context. + * Used when context object is not provided. + */ + @Input() description?: string; + + /** + * Value of the context. + * Used when context object is not provided. + */ + @Input() value?: any; + + ngOnInit(): void { + this.addContext(); + } + + ngOnChanges(changes: SimpleChanges): void { + // Check if any relevant input has changed + const hasContextChange = "context" in changes; + const hasDescriptionChange = "description" in changes; + const hasValueChange = "value" in changes; + + if (hasContextChange || hasDescriptionChange || hasValueChange) { + // Skip the first change as ngOnInit handles initial setup + if (this.contextId) { + this.updateContext(); + } + } + } + + ngOnDestroy(): void { + this.removeContext(); + } + + /** + * Adds the context to CopilotKit + */ + private addContext(): void { + const contextToAdd = this.getContext(); + + if (contextToAdd) { + this.contextId = this.copilotkit.core.addContext(contextToAdd); + } + } + + /** + * Updates the context by removing the old one and adding a new one + */ + private updateContext(): void { + this.removeContext(); + this.addContext(); + } + + /** + * Removes the current context from CopilotKit + */ + private removeContext(): void { + if (this.contextId) { + this.copilotkit.core.removeContext(this.contextId); + this.contextId = undefined; + } + } + + /** + * Gets the context object from inputs + */ + private getContext(): Context | null { + // If context object is provided, use it + if (this.context) { + return this.context; + } + + // Otherwise, build from individual inputs + // Note: null is a valid value, but undefined means not set + if (this.description !== undefined && this.value !== undefined) { + return { + description: this.description, + value: this.value, + }; + } + + return null; + } +} diff --git a/src/v2.x/packages/angular/src/lib/directives/stick-to-bottom.ts b/src/v2.x/packages/angular/src/lib/directives/stick-to-bottom.ts new file mode 100644 index 0000000000..707c41b874 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/directives/stick-to-bottom.ts @@ -0,0 +1,218 @@ +import { + Directive, + ElementRef, + OnInit, + OnDestroy, + AfterViewInit, + inject, + input, + output, +} from "@angular/core"; +import { ScrollPosition } from "../scroll-position"; +import { ResizeObserverService } from "../resize-observer"; +import { Subject } from "rxjs"; +import { + takeUntil, + debounceTime, + filter, + distinctUntilChanged, +} from "rxjs/operators"; + +export type ScrollBehavior = "smooth" | "instant" | "auto"; + +/** + * Directive for implementing stick-to-bottom scroll behavior + * Similar to the React use-stick-to-bottom library + * + * @example + * ```html + *
    + * + *
    + * ``` + */ +@Directive({ + standalone: true, + selector: "[copilotStickToBottom]", + providers: [ScrollPosition, ResizeObserverService], +}) +export class StickToBottom implements OnInit, AfterViewInit, OnDestroy { + enabled = input(true); + threshold = input(10); + initialBehavior = input("smooth"); + resizeBehavior = input("smooth"); + debounceMs = input(100); + + isAtBottomChange = output(); + scrollToBottomRequested = output(); + + private elementRef = inject(ElementRef); + private scrollService = inject(ScrollPosition); + private resizeService = inject(ResizeObserverService); + + private destroy$ = new Subject(); + private contentElement?: HTMLElement; + private wasAtBottom = true; + private hasInitialized = false; + private userHasScrolled = false; + + ngOnInit(): void { + // Setup will happen in ngAfterViewInit + } + + ngAfterViewInit(): void { + const element = this.elementRef.nativeElement as HTMLElement; + + // Find or create content wrapper + this.contentElement = element.querySelector( + "[data-stick-to-bottom-content]" + ) as HTMLElement; + if (!this.contentElement) { + this.contentElement = element; + } + + this.setupScrollMonitoring(); + this.setupResizeMonitoring(); + this.setupContentMutationObserver(); + + // Initial scroll to bottom if enabled + setTimeout(() => { + this.hasInitialized = true; + if (this.enabled()) { + this.scrollToBottom(this.initialBehavior()); + } + }, 0); + } + + private setupScrollMonitoring(): void { + if (!this.enabled()) return; + + const element = this.elementRef.nativeElement; + + // Monitor scroll position + this.scrollService + .monitorScrollPosition(element, this.threshold()) + .pipe( + takeUntil(this.destroy$), + debounceTime(this.debounceMs()), + distinctUntilChanged((a, b) => a.isAtBottom === b.isAtBottom) + ) + .subscribe((state) => { + const wasAtBottom = this.wasAtBottom; + this.wasAtBottom = state.isAtBottom; + + // Detect user scroll + if (!state.isAtBottom && wasAtBottom && this.hasInitialized) { + this.userHasScrolled = true; + } else if (state.isAtBottom) { + this.userHasScrolled = false; + } + + // Emit change + this.isAtBottomChange.emit(state.isAtBottom); + }); + } + + private setupResizeMonitoring(): void { + if (!this.enabled() || !this.contentElement) return; + + // Monitor content resize + this.resizeService + .observeElement(this.contentElement, 0, 250) + .pipe( + takeUntil(this.destroy$), + filter(() => this.enabled() && !this.userHasScrolled) + ) + .subscribe((state) => { + // Auto-scroll on resize if we were at bottom + if (this.wasAtBottom && !state.isResizing) { + this.scrollToBottom(this.resizeBehavior()); + } + }); + + // Monitor container resize + const element = this.elementRef.nativeElement; + this.resizeService + .observeElement(element, 0, 250) + .pipe( + takeUntil(this.destroy$), + filter( + () => this.enabled() && !this.userHasScrolled && this.wasAtBottom + ) + ) + .subscribe(() => { + // Adjust scroll on container resize + this.scrollToBottom(this.resizeBehavior()); + }); + } + + private setupContentMutationObserver(): void { + if (!this.enabled() || !this.contentElement) return; + + const mutationObserver = new MutationObserver(() => { + if (this.enabled() && this.wasAtBottom && !this.userHasScrolled) { + // Content changed, scroll to bottom if we were there + requestAnimationFrame(() => { + this.scrollToBottom(this.resizeBehavior()); + }); + } + }); + + mutationObserver.observe(this.contentElement, { + childList: true, + subtree: true, + characterData: true, + }); + + // Cleanup on destroy + this.destroy$.subscribe(() => { + mutationObserver.disconnect(); + }); + } + + /** + * Public method to scroll to bottom + * Can be called from parent component + */ + public scrollToBottom(behavior: ScrollBehavior = "smooth"): void { + const element = this.elementRef.nativeElement; + const smooth = behavior === "smooth"; + + this.scrollService.scrollToBottom(element, smooth); + this.userHasScrolled = false; + this.scrollToBottomRequested.emit(); + } + + /** + * Check if currently at bottom + */ + public isAtBottom(): boolean { + return this.scrollService.isAtBottom( + this.elementRef.nativeElement, + this.threshold() + ); + } + + /** + * Get current scroll state + */ + public getScrollState() { + const element = this.elementRef.nativeElement; + return { + scrollTop: element.scrollTop, + scrollHeight: element.scrollHeight, + clientHeight: element.clientHeight, + isAtBottom: this.isAtBottom(), + }; + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } +} diff --git a/src/v2.x/packages/angular/src/lib/directives/tooltip.ts b/src/v2.x/packages/angular/src/lib/directives/tooltip.ts new file mode 100644 index 0000000000..3c87401e66 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/directives/tooltip.ts @@ -0,0 +1,307 @@ +import { + Directive, + Input, + ElementRef, + HostListener, + OnDestroy, + inject, + ViewContainerRef, +} from "@angular/core"; +import { + Overlay, + OverlayRef, + OverlayPositionBuilder, + ConnectedPosition, +} from "@angular/cdk/overlay"; +import { ComponentPortal } from "@angular/cdk/portal"; +import { + Component, + ChangeDetectionStrategy, + ChangeDetectorRef, +} from "@angular/core"; + +@Component({ + selector: "copilot-tooltip-content", + standalone: true, + template: ` +
    +
    + {{ text }} +
    +
    +
    + `, + styles: [ + ` + .copilot-tooltip-wrapper { + position: relative; + display: inline-block; + animation: fadeIn 0.15s ease-in-out; + } + + .copilot-tooltip { + background-color: #1a1a1a; + color: white; + padding: 6px 10px; + border-radius: 6px; + font-size: 12px; + font-weight: 500; + white-space: nowrap; + max-width: 200px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + } + + .copilot-tooltip-arrow { + position: absolute; + width: 0; + height: 0; + border-style: solid; + } + + /* Arrow for tooltip below element (arrow points up to tooltip) */ + .copilot-tooltip-wrapper[data-position="below"] .copilot-tooltip-arrow { + top: -4px; + left: 50%; + transform: translateX(-50%); + border-width: 0 4px 4px 4px; + border-color: transparent transparent #1a1a1a transparent; + } + + /* Arrow for tooltip above element (arrow points down to element) */ + .copilot-tooltip-wrapper[data-position="above"] .copilot-tooltip-arrow { + bottom: -4px; + left: 50%; + transform: translateX(-50%); + border-width: 4px 4px 0 4px; + border-color: #1a1a1a transparent transparent transparent; + } + + /* Arrow for tooltip to the left */ + .copilot-tooltip-wrapper[data-position="left"] .copilot-tooltip-arrow { + right: -4px; + top: 50%; + transform: translateY(-50%); + border-width: 4px 0 4px 4px; + border-color: transparent transparent transparent #1a1a1a; + } + + /* Arrow for tooltip to the right */ + .copilot-tooltip-wrapper[data-position="right"] .copilot-tooltip-arrow { + left: -4px; + top: 50%; + transform: translateY(-50%); + border-width: 4px 4px 4px 0; + border-color: transparent #1a1a1a transparent transparent; + } + + @keyframes fadeIn { + from { + opacity: 0; + transform: translateY(2px); + } + to { + opacity: 1; + transform: translateY(0); + } + } + `, + ], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class TooltipContent { + text = ""; + private _position: "above" | "below" | "left" | "right" = "below"; + + get position(): "above" | "below" | "left" | "right" { + return this._position; + } + + set position(value: "above" | "below" | "left" | "right") { + this._position = value; + this.cdr.markForCheck(); + } + + constructor(private cdr: ChangeDetectorRef) {} +} + +@Directive({ + selector: "[copilotTooltip]", + standalone: true, +}) +export class CopilotTooltip implements OnDestroy { + @Input("copilotTooltip") tooltipText = ""; + @Input() tooltipPosition: "above" | "below" | "left" | "right" = "below"; + @Input() tooltipDelay = 500; // milliseconds + + private overlay = inject(Overlay); + private overlayPositionBuilder = inject(OverlayPositionBuilder); + private elementRef = inject(ElementRef); + private viewContainerRef = inject(ViewContainerRef); + + private overlayRef?: OverlayRef; + private tooltipTimeout?: number; + private originalTitle?: string; + + @HostListener("mouseenter") + onMouseEnter(): void { + if (!this.tooltipText) return; + + // Store and remove native title to prevent OS tooltip + const element = this.elementRef.nativeElement; + if (element.hasAttribute("title")) { + this.originalTitle = element.getAttribute("title"); + element.removeAttribute("title"); + } + + // Clear any existing timeout + if (this.tooltipTimeout) { + clearTimeout(this.tooltipTimeout); + } + + // Set timeout to show tooltip after delay + this.tooltipTimeout = window.setTimeout(() => { + this.show(); + }, this.tooltipDelay); + } + + @HostListener("mouseleave") + onMouseLeave(): void { + // Clear timeout if mouse leaves before tooltip shows + if (this.tooltipTimeout) { + clearTimeout(this.tooltipTimeout); + this.tooltipTimeout = undefined; + } + + // Restore original title if it existed + if (this.originalTitle !== undefined) { + this.elementRef.nativeElement.setAttribute("title", this.originalTitle); + this.originalTitle = undefined; + } + + // Hide tooltip if it's showing + this.hide(); + } + + private show(): void { + if (this.overlayRef) { + return; + } + + // Create overlay + const positionStrategy = this.overlayPositionBuilder + .flexibleConnectedTo(this.elementRef) + .withPositions(this.getPositions()) + .withPush(false); + + this.overlayRef = this.overlay.create({ + positionStrategy, + scrollStrategy: this.overlay.scrollStrategies.close(), + hasBackdrop: false, + }); + + // Create component portal and attach + const portal = new ComponentPortal(TooltipContent, this.viewContainerRef); + const componentRef = this.overlayRef.attach(portal); + componentRef.instance.text = this.tooltipText; + + // Detect actual position after overlay is positioned + setTimeout(() => { + if (this.overlayRef && this.elementRef.nativeElement) { + const tooltipRect = + this.overlayRef.overlayElement.getBoundingClientRect(); + const elementRect = + this.elementRef.nativeElement.getBoundingClientRect(); + + let actualPosition: "above" | "below" | "left" | "right" = "below"; + + // Determine actual position based on relative positions + if (tooltipRect.bottom <= elementRect.top) { + actualPosition = "above"; + } else if (tooltipRect.top >= elementRect.bottom) { + actualPosition = "below"; + } else if (tooltipRect.right <= elementRect.left) { + actualPosition = "left"; + } else if (tooltipRect.left >= elementRect.right) { + actualPosition = "right"; + } + + componentRef.instance.position = actualPosition; + } + }, 0); + } + + private hide(): void { + if (this.overlayRef) { + this.overlayRef.dispose(); + this.overlayRef = undefined; + } + } + + private getPositions(): ConnectedPosition[] { + const positions: Record = { + above: [ + { + originX: "center", + originY: "top", + overlayX: "center", + overlayY: "bottom", + offsetY: -12, + }, + ], + below: [ + { + originX: "center", + originY: "bottom", + overlayX: "center", + overlayY: "top", + offsetY: 12, + }, + ], + left: [ + { + originX: "start", + originY: "center", + overlayX: "end", + overlayY: "center", + offsetX: -12, + }, + ], + right: [ + { + originX: "end", + originY: "center", + overlayX: "start", + overlayY: "center", + offsetX: 12, + }, + ], + }; + + // Prefer below position, but use above as fallback + const primary = positions[this.tooltipPosition] || positions.below; + // For below position, add above as first fallback + const fallbacks = + this.tooltipPosition === "below" + ? [ + ...(positions.above || []), + ...(positions.left || []), + ...(positions.right || []), + ] + : Object.values(positions) + .filter((p) => p !== primary) + .flat(); + + return [...(primary || []), ...fallbacks]; + } + + ngOnDestroy(): void { + if (this.tooltipTimeout) { + clearTimeout(this.tooltipTimeout); + } + // Restore original title if it existed + if (this.originalTitle !== undefined) { + this.elementRef.nativeElement.setAttribute("title", this.originalTitle); + } + this.hide(); + } +} diff --git a/src/v2.x/packages/angular/src/lib/human-in-the-loop.ts b/src/v2.x/packages/angular/src/lib/human-in-the-loop.ts new file mode 100644 index 0000000000..633241963c --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/human-in-the-loop.ts @@ -0,0 +1,27 @@ +import { Injectable } from "@angular/core"; +import { filter, lastValueFrom, Subject, take } from "rxjs"; + +@Injectable({ providedIn: "root" }) +export class HumanInTheLoop { + results = new Subject<{ + toolCallId: string; + toolName: string; + result: unknown; + }>(); + + addResult(toolCallId: string, toolName: string, result: unknown) { + this.results.next({ toolCallId, toolName, result }); + } + + onResult(toolCallId: string, toolName: string): Promise { + return lastValueFrom( + this.results.pipe( + filter( + (result) => + result.toolCallId === toolCallId && result.toolName === toolName + ), + take(1) + ) + ); + } +} diff --git a/src/v2.x/packages/angular/src/lib/render-tool-calls.ts b/src/v2.x/packages/angular/src/lib/render-tool-calls.ts new file mode 100644 index 0000000000..5b25e1076f --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/render-tool-calls.ts @@ -0,0 +1,157 @@ +import { NgComponentOutlet } from "@angular/common"; +import { Component, inject, input } from "@angular/core"; +import { AssistantMessage, Message, ToolCall, ToolMessage } from "@ag-ui/client"; +import { CopilotKit } from "./copilotkit"; +import { + FrontendToolConfig, + HumanInTheLoopToolCall, + HumanInTheLoopConfig, + AngularToolCall, + RenderToolCallConfig, +} from "./tools"; +import { partialJSONParse } from "@copilotkitnext/shared"; +import { HumanInTheLoop } from "./human-in-the-loop"; + +type RendererToolCallHandler = { + type: "renderer"; + config: RenderToolCallConfig; +}; +type ClientToolCallHandler = { + type: "clientTool"; + config: FrontendToolConfig; +}; +type HumanInTheLoopToolCallHandler = { + type: "humanInTheLoopTool"; + config: HumanInTheLoopConfig; +}; + +type ToolCallHandler = RendererToolCallHandler | ClientToolCallHandler | HumanInTheLoopToolCallHandler; + +@Component({ + selector: "copilot-render-tool-calls", + standalone: true, + imports: [NgComponentOutlet], + template: ` + @for (toolCall of message().toolCalls ?? []; track toolCall.id) { + @let renderConfig = pickRenderer(toolCall.function.name); + @if (renderConfig && renderConfig.type !== "humanInTheLoopTool" && renderConfig.config.component) { + + } + @if (renderConfig && renderConfig.type === "humanInTheLoopTool" && renderConfig.config.component) { + + } + } + `, +}) +export class RenderToolCalls { + readonly #copilotKit = inject(CopilotKit); + readonly #hitl = inject(HumanInTheLoop); + readonly message = input.required(); + readonly messages = input.required(); + readonly isLoading = input(false); + + protected pickRenderer(name: string): ToolCallHandler | undefined { + type AssistantMessageWithAgent = AssistantMessage & { + agentId?: string; + }; + const messageAgentId = (this.message() as AssistantMessageWithAgent).agentId; + const renderers = this.#copilotKit.toolCallRenderConfigs(); + const clientTools = this.#copilotKit.clientToolCallRenderConfigs(); + const humanInTheLoopTools = this.#copilotKit.humanInTheLoopToolRenderConfigs(); + + const renderer = renderers.find( + (candidate) => + candidate.name === name && (candidate.agentId === undefined || candidate.agentId === messageAgentId), + ); + + if (renderer) return { type: "renderer", config: renderer }; + + const clientTool = clientTools.find( + (candidate) => + candidate.name === name && (candidate.agentId === undefined || candidate.agentId === messageAgentId), + ); + if (clientTool) return { type: "clientTool", config: clientTool }; + + const humanInTheLoopTool = humanInTheLoopTools.find( + (candidate) => + candidate.name === name && (candidate.agentId === undefined || candidate.agentId === messageAgentId), + ); + if (humanInTheLoopTool) return { type: "humanInTheLoopTool", config: humanInTheLoopTool }; + + const starRenderer = renderers.find((candidate) => candidate.name === "*"); + if (starRenderer) return { type: "renderer", config: starRenderer }; + + return undefined; + } + + protected buildToolCall>(toolCall: ToolCall): AngularToolCall { + const args = partialJSONParse(toolCall.function.arguments); + const message = this.#getToolMessage(toolCall.id); + + if (message) { + return { + args, + status: "complete", + result: message.content, + }; + } else if (this.isLoading()) { + return { + args, + status: "in-progress", + result: undefined, + }; + } else { + return { + args, + status: "executing", + result: undefined, + }; + } + } + + protected buildHumanInTheLoopToolCall>( + toolCall: ToolCall, + ): HumanInTheLoopToolCall { + const args = partialJSONParse(toolCall.function.arguments); + const message = this.#getToolMessage(toolCall.id); + const respond = (result: unknown) => { + this.#hitl.addResult(toolCall.id, toolCall.function.name, result); + }; + + if (message) { + return { + args, + status: "complete", + result: message.content!, + respond, + }; + } else if (this.isLoading()) { + return { + args, + status: "in-progress", + result: undefined, + respond, + }; + } else { + return { + args, + status: "executing", + result: undefined, + respond, + }; + } + } + + #getToolMessage(toolCallId: string): ToolMessage | undefined { + const message = this.messages().find((m): m is ToolMessage => m.role === "tool" && m.toolCallId === toolCallId); + + return message; + } +} diff --git a/src/v2.x/packages/angular/src/lib/resize-observer.ts b/src/v2.x/packages/angular/src/lib/resize-observer.ts new file mode 100644 index 0000000000..6b70d2f9f4 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/resize-observer.ts @@ -0,0 +1,187 @@ +import { Injectable, ElementRef, NgZone, OnDestroy } from "@angular/core"; +import { Observable, Subject, BehaviorSubject } from "rxjs"; +import { debounceTime, takeUntil, distinctUntilChanged } from "rxjs/operators"; + +export interface ResizeState { + width: number; + height: number; + isResizing: boolean; +} + +@Injectable({ + providedIn: "root", +}) +export class ResizeObserverService implements OnDestroy { + private destroy$ = new Subject(); + private observers = new Map(); + private resizeStates = new Map>(); + private resizeTimeouts = new Map(); + + constructor(private ngZone: NgZone) {} + + /** + * Observe element resize with debouncing and resizing state + * @param element Element to observe + * @param debounceMs Debounce time (default 250ms) + * @param resizingDurationMs How long to show "isResizing" state (default 250ms) + */ + observeElement( + element: ElementRef | HTMLElement, + debounceMs: number = 0, + resizingDurationMs: number = 250 + ): Observable { + const el = element instanceof ElementRef ? element.nativeElement : element; + + // Return existing observer if already observing + if (this.resizeStates.has(el)) { + return this.resizeStates.get(el)!.asObservable(); + } + + // Create new subject for this element + const resizeState$ = new BehaviorSubject({ + width: el.offsetWidth, + height: el.offsetHeight, + isResizing: false, + }); + + this.resizeStates.set(el, resizeState$); + + // Create ResizeObserver + const resizeObserver = new ResizeObserver((entries) => { + if (entries.length === 0) return; + + const entry = entries[0]; + if (!entry) return; + + const { width, height } = entry.contentRect; + + this.ngZone.run(() => { + // Clear existing timeout + const existingTimeout = this.resizeTimeouts.get(el); + if (existingTimeout) { + clearTimeout(existingTimeout); + } + + // Update state with isResizing = true + resizeState$.next({ + width, + height, + isResizing: true, + }); + + // Set timeout to clear isResizing flag + if (resizingDurationMs > 0) { + const timeout = window.setTimeout(() => { + resizeState$.next({ + width, + height, + isResizing: false, + }); + this.resizeTimeouts.delete(el); + }, resizingDurationMs); + + this.resizeTimeouts.set(el, timeout); + } else { + // If no duration, immediately set isResizing to false + resizeState$.next({ + width, + height, + isResizing: false, + }); + } + }); + }); + + // Start observing + resizeObserver.observe(el); + this.observers.set(el, resizeObserver); + + // Return observable with debouncing if specified + const observable = resizeState$.asObservable().pipe( + debounceMs > 0 ? debounceTime(debounceMs) : (source) => source, + distinctUntilChanged( + (a, b) => + a.width === b.width && + a.height === b.height && + a.isResizing === b.isResizing + ), + takeUntil(this.destroy$) + ); + + return observable; + } + + /** + * Stop observing an element + * @param element Element to stop observing + */ + unobserve(element: ElementRef | HTMLElement): void { + const el = element instanceof ElementRef ? element.nativeElement : element; + + // Clear timeout if exists + const timeout = this.resizeTimeouts.get(el); + if (timeout) { + clearTimeout(timeout); + this.resizeTimeouts.delete(el); + } + + // Disconnect observer + const observer = this.observers.get(el); + if (observer) { + observer.disconnect(); + this.observers.delete(el); + } + + // Complete and remove subject + const subject = this.resizeStates.get(el); + if (subject) { + subject.complete(); + this.resizeStates.delete(el); + } + } + + /** + * Get current size of element + * @param element Element to measure + */ + getCurrentSize(element: ElementRef | HTMLElement): { + width: number; + height: number; + } { + const el = element instanceof ElementRef ? element.nativeElement : element; + return { + width: el.offsetWidth, + height: el.offsetHeight, + }; + } + + /** + * Get current resize state of element + * @param element Element to check + */ + getCurrentState( + element: ElementRef | HTMLElement + ): ResizeState | null { + const el = element instanceof ElementRef ? element.nativeElement : element; + const subject = this.resizeStates.get(el); + return subject ? subject.value : null; + } + + ngOnDestroy(): void { + // Clear all timeouts + this.resizeTimeouts.forEach((timeout) => clearTimeout(timeout)); + this.resizeTimeouts.clear(); + + // Disconnect all observers + this.observers.forEach((observer) => observer.disconnect()); + this.observers.clear(); + + // Complete all subjects + this.resizeStates.forEach((subject) => subject.complete()); + this.resizeStates.clear(); + + // Complete destroy subject + this.destroy$.next(); + this.destroy$.complete(); + } +} diff --git a/src/v2.x/packages/angular/src/lib/scroll-position.ts b/src/v2.x/packages/angular/src/lib/scroll-position.ts new file mode 100644 index 0000000000..83a928a1c9 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/scroll-position.ts @@ -0,0 +1,173 @@ +import { Injectable, ElementRef, NgZone, OnDestroy } from "@angular/core"; +import { ScrollDispatcher, ViewportRuler } from "@angular/cdk/scrolling"; +import { + Observable, + Subject, + BehaviorSubject, + fromEvent, + merge, + animationFrameScheduler, +} from "rxjs"; +import { + takeUntil, + debounceTime, + throttleTime, + distinctUntilChanged, + map, + startWith, +} from "rxjs/operators"; + +export interface ScrollState { + isAtBottom: boolean; + scrollTop: number; + scrollHeight: number; + clientHeight: number; +} + +@Injectable({ + providedIn: "root", +}) +export class ScrollPosition implements OnDestroy { + private destroy$ = new Subject(); + private scrollStateSubject = new BehaviorSubject({ + isAtBottom: true, + scrollTop: 0, + scrollHeight: 0, + clientHeight: 0, + }); + + public scrollState$ = this.scrollStateSubject.asObservable(); + + constructor( + private scrollDispatcher: ScrollDispatcher, + private viewportRuler: ViewportRuler, + private ngZone: NgZone + ) {} + + /** + * Monitor scroll position of an element + * @param element The element to monitor + * @param threshold Pixels from bottom to consider "at bottom" (default 10) + */ + monitorScrollPosition( + element: ElementRef | HTMLElement, + threshold: number = 10 + ): Observable { + const el = element instanceof ElementRef ? element.nativeElement : element; + + // Create scroll observable + const scroll$ = merge( + fromEvent(el, "scroll"), + this.viewportRuler.change(150) // Monitor viewport changes + ).pipe( + startWith(null), // Emit initial state + throttleTime(16, animationFrameScheduler, { trailing: true }), // ~60fps + map(() => this.getScrollState(el, threshold)), + distinctUntilChanged( + (a, b) => + a.isAtBottom === b.isAtBottom && + a.scrollTop === b.scrollTop && + a.scrollHeight === b.scrollHeight + ), + takeUntil(this.destroy$) + ); + + // Subscribe and update subject + scroll$.subscribe((state) => { + this.scrollStateSubject.next(state); + }); + + return scroll$; + } + + /** + * Scroll element to bottom with smooth animation + * @param element The element to scroll + * @param smooth Whether to use smooth scrolling + */ + scrollToBottom( + element: ElementRef | HTMLElement, + smooth: boolean = true + ): void { + const el = element instanceof ElementRef ? element.nativeElement : element; + + this.ngZone.runOutsideAngular(() => { + if (smooth && "scrollBehavior" in document.documentElement.style) { + el.scrollTo({ + top: el.scrollHeight, + behavior: "smooth", + }); + } else { + el.scrollTop = el.scrollHeight; + } + }); + } + + /** + * Check if element is at bottom + * @param element The element to check + * @param threshold Pixels from bottom to consider "at bottom" + */ + isAtBottom( + element: ElementRef | HTMLElement, + threshold: number = 10 + ): boolean { + const el = element instanceof ElementRef ? element.nativeElement : element; + return this.getScrollState(el, threshold).isAtBottom; + } + + /** + * Get current scroll state of element + */ + public getScrollState(element: HTMLElement, threshold: number): ScrollState { + const scrollTop = element.scrollTop; + const scrollHeight = element.scrollHeight; + const clientHeight = element.clientHeight; + const distanceFromBottom = scrollHeight - scrollTop - clientHeight; + const isAtBottom = distanceFromBottom <= threshold; + + return { + isAtBottom, + scrollTop, + scrollHeight, + clientHeight, + }; + } + + /** + * Create a ResizeObserver for element size changes + * @param element The element to observe + * @param debounceMs Debounce time in milliseconds + */ + observeResize( + element: ElementRef | HTMLElement, + debounceMs: number = 250 + ): Observable { + const el = element instanceof ElementRef ? element.nativeElement : element; + const resize$ = new Subject(); + + const resizeObserver = new ResizeObserver((entries) => { + const entry = entries[0]; + if (entry) { + this.ngZone.run(() => { + resize$.next(entry); + }); + } + }); + + resizeObserver.observe(el); + + // Cleanup on destroy + this.destroy$.subscribe(() => { + resizeObserver.disconnect(); + }); + + return resize$.pipe(debounceTime(debounceMs), takeUntil(this.destroy$)); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + this.scrollStateSubject.complete(); + } +} diff --git a/src/v2.x/packages/angular/src/lib/slots/__tests__/slot.utils.spec.ts b/src/v2.x/packages/angular/src/lib/slots/__tests__/slot.utils.spec.ts new file mode 100644 index 0000000000..b8bbac6300 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/slots/__tests__/slot.utils.spec.ts @@ -0,0 +1,217 @@ +import { + Component, + Input, + TemplateRef, + ViewChild, + ViewContainerRef, + createEnvironmentInjector, + EnvironmentInjector, + runInInjectionContext, +} from "@angular/core"; +import { TestBed } from "@angular/core/testing"; +import { beforeEach, describe, expect, it } from "vitest"; +import { + renderSlot, + isComponentType, + isSlotValue, + normalizeSlotValue, + createSlotConfig, + provideSlots, + getSlotConfig, + createSlotRenderer, +} from "../slot.utils"; +import { SLOT_CONFIG } from "../slot.types"; + +@Component({ + standalone: true, + selector: "default-component", + template: `
    {{ text }}
    `, +}) +class DefaultComponent { + @Input() text = "Default"; +} + +@Component({ + standalone: true, + selector: "custom-component", + template: `
    {{ text }}
    `, +}) +class CustomComponent { + @Input() text = "Custom"; +} + +describe("slot utils", () => { + beforeEach(() => { + TestBed.resetTestingModule(); + }); + + describe("renderSlot", () => { + it("renders default component when no slot provided", () => { + @Component({ + standalone: true, + template: `
    `, + imports: [DefaultComponent], + }) + class HostComponent { + @ViewChild("container", { read: ViewContainerRef }) + container!: ViewContainerRef; + } + + const fixture = TestBed.createComponent(HostComponent); + fixture.detectChanges(); + + const ref = renderSlot(fixture.componentInstance.container, { + defaultComponent: DefaultComponent, + }); + + expect(ref).toBeTruthy(); + expect( + (ref as any).location.nativeElement.querySelector(".default") + ).toBeTruthy(); + }); + + it("renders template slot with provided context", () => { + @Component({ + standalone: true, + template: ` +
    + + {{ props?.value }} + + `, + }) + class HostComponent { + @ViewChild("container", { read: ViewContainerRef }) + container!: ViewContainerRef; + @ViewChild("tpl") tpl!: TemplateRef; + } + + const fixture = TestBed.createComponent(HostComponent); + fixture.detectChanges(); + + renderSlot(fixture.componentInstance.container, { + defaultComponent: DefaultComponent, + slot: fixture.componentInstance.tpl, + props: { value: "from template" }, + }); + fixture.detectChanges(); + + const span = fixture.nativeElement.querySelector(".template"); + expect(span?.textContent?.trim()).toBe("from template"); + }); + + it("applies inputs using setInput", () => { + @Component({ + standalone: true, + template: `
    `, + imports: [DefaultComponent], + }) + class HostComponent { + @ViewChild("container", { read: ViewContainerRef }) + container!: ViewContainerRef; + } + + const fixture = TestBed.createComponent(HostComponent); + fixture.detectChanges(); + + const ref = renderSlot(fixture.componentInstance.container, { + defaultComponent: DefaultComponent, + props: { text: "Updated" }, + }); + + expect(ref).toBeTruthy(); + expect((ref as any).instance.text).toBe("Updated"); + }); + }); + + describe("type guards", () => { + it("detects component types", () => { + expect(isComponentType(DefaultComponent)).toBe(true); + expect(isComponentType(() => {})).toBe(false); + expect(isComponentType(null)).toBe(false); + }); + + it("detects slot values", () => { + @Component({ + standalone: true, + template: ``, + }) + class HostComponent { + @ViewChild("tpl") tpl!: TemplateRef; + } + + const fixture = TestBed.createComponent(HostComponent); + fixture.detectChanges(); + + expect(isSlotValue(DefaultComponent)).toBe(true); + expect(isSlotValue(fixture.componentInstance.tpl)).toBe(true); + expect(isSlotValue("string")).toBe(false); + }); + }); + + describe("configuration helpers", () => { + it("normalises slot overrides to registry entries", () => { + expect(normalizeSlotValue(undefined, DefaultComponent)).toEqual({ + component: DefaultComponent, + }); + expect(normalizeSlotValue(CustomComponent, DefaultComponent)).toEqual({ + component: CustomComponent, + }); + }); + + it("creates slot configuration map with defaults", () => { + const config = createSlotConfig( + { button: CustomComponent }, + { button: DefaultComponent, toolbar: DefaultComponent } + ); + + expect(config.get("button")).toEqual({ component: CustomComponent }); + expect(config.get("toolbar")).toEqual({ component: DefaultComponent }); + }); + + it("provides and retrieves slot configuration via DI", () => { + const slots = new Map([["button", { component: CustomComponent }]]); + TestBed.configureTestingModule({ + providers: [{ provide: SLOT_CONFIG, useValue: slots }], + }); + + @Component({ standalone: true, template: "" }) + class HostComponent { + config = getSlotConfig(); + } + + const fixture = TestBed.createComponent(HostComponent); + expect(fixture.componentInstance.config).toBe(slots); + }); + + it("createSlotRenderer uses DI overrides when slot name provided", () => { + const parent = TestBed.inject(EnvironmentInjector); + const env = createEnvironmentInjector( + [provideSlots({ button: CustomComponent })], + parent + ); + + const renderer = runInInjectionContext(env, () => + createSlotRenderer(DefaultComponent, "button") + ); + + @Component({ + standalone: true, + template: `
    `, + imports: [DefaultComponent, CustomComponent], + }) + class HostComponent { + @ViewChild("container", { read: ViewContainerRef }) + container!: ViewContainerRef; + } + + const fixture = TestBed.createComponent(HostComponent); + fixture.detectChanges(); + + const ref = renderer(fixture.componentInstance.container); + expect( + (ref as any).location.nativeElement.querySelector(".custom") + ).toBeTruthy(); + }); + }); +}); diff --git a/src/v2.x/packages/angular/src/lib/slots/copilot-slot.ts b/src/v2.x/packages/angular/src/lib/slots/copilot-slot.ts new file mode 100644 index 0000000000..37caedb107 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/slots/copilot-slot.ts @@ -0,0 +1,150 @@ +import { + Component, + TemplateRef, + ViewContainerRef, + OnInit, + OnChanges, + SimpleChanges, + Inject, + ChangeDetectionStrategy, + ChangeDetectorRef, + input, + ViewChild, +} from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { renderSlot } from "./slot.utils"; +import { Type } from "@angular/core"; + +/** + * @internal - This component is for internal use only. + * Simple slot component for rendering custom content or defaults. + * Supports templates and components only. + * + * @example + * ```html + * + * + * + * + * ``` + */ +@Component({ + standalone: true, + selector: "copilot-slot", + imports: [CommonModule], + template: ` + + @if (slot() && isTemplate(slot)) { + + + } + + + + + + @if (!slot && !defaultComponent) { + + } + `, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class CopilotSlot implements OnInit, OnChanges { + slot = input | Type | undefined>(undefined); + context = input(undefined); + defaultComponent = input | undefined>(undefined); + outputs = input void> | undefined>(undefined); + + @ViewChild("slotContainer", { read: ViewContainerRef, static: true }) + private slotContainer!: ViewContainerRef; + + private componentRef?: any; + + constructor( + @Inject(ViewContainerRef) private viewContainer: ViewContainerRef, + private cdr: ChangeDetectorRef + ) {} + + ngOnInit(): void { + this.renderSlot(); + } + + ngOnChanges(changes: SimpleChanges): void { + if (changes["slot"]) { + // Slot changed, need to re-render completely + this.renderSlot(); + } else if (changes["context"] && this.componentRef) { + // Just context changed, update existing component + this.updateComponentProps(); + this.cdr.detectChanges(); + } else if (changes["context"]) { + // No component ref yet, render the slot + this.renderSlot(); + } + } + + isTemplate(value: any): value is TemplateRef { + return value instanceof TemplateRef; + } + + private renderSlot(): void { + // Skip if it's a template (handled by ngTemplateOutlet) + if (this.slot() && this.isTemplate(this.slot())) { + this.componentRef = null; + return; + } + + // Clear previous content + this.slotContainer.clear(); + this.componentRef = null; + + // Skip if no slot and no default component + if (!this.slot() && !this.defaultComponent()) { + return; + } + + // Use the utility to render other slot types + if (this.slot() || this.defaultComponent()) { + this.componentRef = renderSlot(this.slotContainer, { + slot: this.slot(), + defaultComponent: this.defaultComponent()!, + props: this.context(), + outputs: this.outputs(), + }); + } + } + + private updateComponentProps(): void { + if (!this.componentRef || !this.componentRef.instance) { + return; + } + + const props = this.context(); + + // Update props using setInput, only for declared inputs + if (props) { + const ctor = this.componentRef.instance.constructor as any; + const cmpDef: any = ctor?.ɵcmp; + const declaredInputs = new Set(Object.keys(cmpDef?.inputs ?? {})); + + if (declaredInputs.has("props")) { + this.componentRef.setInput("props", props); + } else { + for (const key in props) { + if (declaredInputs.has(key)) { + const value = props[key]; + this.componentRef.setInput(key, value); + } + } + } + } + + // Trigger change detection + if (this.componentRef.changeDetectorRef) { + this.componentRef.changeDetectorRef.detectChanges(); + } + } +} diff --git a/src/v2.x/packages/angular/src/lib/slots/index.ts b/src/v2.x/packages/angular/src/lib/slots/index.ts new file mode 100644 index 0000000000..468c3aca7a --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/slots/index.ts @@ -0,0 +1,3 @@ +export * from "./slot.types"; +export * from "./slot.utils"; +export { CopilotSlot } from "./copilot-slot"; diff --git a/src/v2.x/packages/angular/src/lib/slots/slot.types.ts b/src/v2.x/packages/angular/src/lib/slots/slot.types.ts new file mode 100644 index 0000000000..6883f57296 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/slots/slot.types.ts @@ -0,0 +1,64 @@ +import { Type, TemplateRef, InjectionToken } from "@angular/core"; + +/** + * Represents a value that can be used as a slot override. + * Can be a component type or template reference only. + * @internal - This type is for internal use only + */ +export type SlotValue = Type | TemplateRef; + +/** + * Configuration for a slot + * @internal - This interface is for internal use only + */ +export interface SlotConfig { + value?: SlotValue; + default?: Type; +} + +/** + * Context passed to slot templates + */ +export interface SlotContext { + $implicit: T; + props?: Partial; + [key: string]: any; +} + +/** + * Slot registry entry + * @internal - This interface is for internal use only + */ +export interface SlotRegistryEntry { + component?: Type; + template?: TemplateRef; +} + +/** + * Options for rendering a slot + */ +export interface RenderSlotOptions { + slot?: SlotValue; + defaultComponent: Type; + props?: Partial; + injector?: any; + outputs?: Record void>; +} + +/** + * Injection token for slot configuration + */ +export const SLOT_CONFIG = new InjectionToken< + ReadonlyMap +>("SLOT_CONFIG"); + +/** + * Type for components with slots + */ +export type WithSlots>, Rest = object> = { + [K in keyof S as `${string & K}Component`]?: Type; +} & { + [K in keyof S as `${string & K}Template`]?: TemplateRef; +} & { + [K in keyof S as `${string & K}Class`]?: string; +} & Rest; diff --git a/src/v2.x/packages/angular/src/lib/slots/slot.utils.ts b/src/v2.x/packages/angular/src/lib/slots/slot.utils.ts new file mode 100644 index 0000000000..46a77dce4e --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/slots/slot.utils.ts @@ -0,0 +1,306 @@ +import { + Type, + TemplateRef, + ViewContainerRef, + ComponentRef, + EmbeddedViewRef, + Injector, + inject, +} from "@angular/core"; +import { + SlotValue, + RenderSlotOptions, + SlotRegistryEntry, + SLOT_CONFIG, +} from "./slot.types"; + +/** + * Renders a slot value into a ViewContainerRef. + * This is the core utility for slot rendering. + * + * @param viewContainer - The ViewContainerRef to render into + * @param options - Options for rendering the slot + * @returns The created component or embedded view reference + * + * @example + * ```typescript + * export class MyComponent { + * @ViewChild('container', { read: ViewContainerRef }) container!: ViewContainerRef; + * + * renderButton() { + * renderSlot(this.container, { + * slot: this.buttonOverride, + * defaultComponent: DefaultButton, + * props: { text: 'Click me' }, + * outputs: { click: (event) => this.handleClick(event) } + * }); + * } + * } + * ``` + */ +export function renderSlot( + viewContainer: ViewContainerRef, + options: RenderSlotOptions +): ComponentRef | EmbeddedViewRef | null { + const { slot, defaultComponent, props, injector, outputs } = options; + + viewContainer.clear(); + + const effectiveSlot = slot ?? defaultComponent; + const effectiveInjector = injector ?? viewContainer.injector; + + if (effectiveSlot instanceof TemplateRef) { + // TemplateRef: render template + return viewContainer.createEmbeddedView(effectiveSlot, { + $implicit: props ?? {}, + props: props ?? {}, + } as any); + } else if (isComponentType(effectiveSlot)) { + // Component type - wrap in try/catch for safety + try { + return createComponent( + viewContainer, + effectiveSlot as Type, + props, + effectiveInjector, + outputs + ); + } catch (error) { + console.warn("Failed to create component:", effectiveSlot, error); + // Fall through to default component + } + } + + // Default: render default component if provided + return defaultComponent + ? createComponent( + viewContainer, + defaultComponent, + props, + effectiveInjector, + outputs + ) + : null; +} + +/** + * Creates a component and applies properties. + */ +function createComponent( + viewContainer: ViewContainerRef, + component: Type, + props?: Partial, + injector?: Injector, + outputs?: Record void> +): ComponentRef { + const componentRef = viewContainer.createComponent(component, { + injector, + }); + + if (props) { + // Apply props using setInput, but only for declared inputs + const cmpDef: any = (component as any).ɵcmp; + const declaredInputs = new Set(Object.keys(cmpDef?.inputs ?? {})); + + if (declaredInputs.has("props")) { + componentRef.setInput("props", props as any); + } else { + for (const key in props) { + if (declaredInputs.has(key)) { + const value = (props as any)[key]; + componentRef.setInput(key, value); + } + } + } + } + + if (outputs) { + // Wire up output event handlers with proper cleanup + const instance = componentRef.instance as any; + const subscriptions: any[] = []; + + for (const [eventName, handler] of Object.entries(outputs)) { + if (instance[eventName]?.subscribe) { + const subscription = instance[eventName].subscribe(handler); + subscriptions.push(subscription); + } + } + + // Register cleanup on component destroy + componentRef.onDestroy(() => { + subscriptions.forEach((sub) => sub.unsubscribe()); + }); + } + + // Trigger change detection + componentRef.changeDetectorRef.detectChanges(); + + return componentRef; +} + +/** + * Checks if a value is a component type. + * Simplified check - rely on try/catch for actual validation. + */ +export function isComponentType(value: any): boolean { + // Arrow functions and regular functions without a prototype are not components + return typeof value === "function" && !!value.prototype; +} + +/** + * Checks if a value is a valid slot value. + */ +export function isSlotValue(value: any): value is SlotValue { + return value instanceof TemplateRef || isComponentType(value); +} + +/** + * Normalizes a slot value to a consistent format. + */ +export function normalizeSlotValue( + value: SlotValue | undefined, + defaultComponent: Type | undefined +): SlotRegistryEntry { + if (!value) { + return { component: defaultComponent }; + } + + if (value instanceof TemplateRef) { + return { template: value }; + } + + if (isComponentType(value)) { + return { component: value as Type }; + } + + return { component: defaultComponent }; +} + +/** + * Creates a slot configuration map for a component. + * + * @example + * ```typescript + * const slots = createSlotConfig({ + standalone: true, +* sendButton: CustomSendButton, + * toolbar: 'custom-toolbar-class', + * footer: footerTemplate + * }, { + * sendButton: DefaultSendButton, + * toolbar: DefaultToolbar, + * footer: DefaultFooter + * }); + * ``` + */ +export function createSlotConfig>>( + overrides: Partial>, + defaults: T +): Map { + const config = new Map(); + + for (const key in defaults) { + const override = overrides[key]; + const defaultComponent = defaults[key]; + config.set(key, normalizeSlotValue(override, defaultComponent)); + } + + return config; +} + +/** + * Provides slot configuration to child components via DI. + * + * @example + * ```typescript + * @Component({ + standalone: true, +* providers: [ + * provideSlots({ + * sendButton: CustomSendButton, + * toolbar: CustomToolbar + * }) + * ] + * }) + * ``` + */ +export function provideSlots(slots: Record>) { + const slotMap = new Map(); + + // Only accept component types in DI (templates lack view context) + for (const [key, value] of Object.entries(slots)) { + if (isComponentType(value)) { + slotMap.set(key, { component: value as Type }); + } + } + + return { + provide: SLOT_CONFIG, + useValue: slotMap, + }; +} + +/** + * Gets slot configuration from DI. + * Must be called within an injection context. + * + * @example + * ```typescript + * export class MyComponent { + * slots = getSlotConfig(); + * + * ngOnInit() { + * const sendButton = this.slots?.get('sendButton'); + * } + * } + * ``` + */ +export function getSlotConfig(): ReadonlyMap | null { + return inject(SLOT_CONFIG, { optional: true }); +} + +/** + * Creates a render function for a specific slot. + * Useful for creating reusable slot renderers. + * + * @example + * ```typescript + * const renderSendButton = createSlotRenderer( + * DefaultSendButton, + * 'sendButton' + * ); + * + * // Later in template + * renderSendButton(this.viewContainer, this.sendButtonOverride); + * ``` + */ +export function createSlotRenderer( + defaultComponent: Type, + slotName?: string +) { + // Get config in the injection context when the renderer is created + const config = slotName ? getSlotConfig() : null; + + return ( + viewContainer: ViewContainerRef, + slot?: SlotValue, + props?: Partial, + outputs?: Record void> + ) => { + // Check DI for overrides if slot name provided + if (slotName && !slot && config) { + const entry = config.get(slotName); + if (entry) { + if (entry.component) slot = entry.component; + else if (entry.template) slot = entry.template; + } + } + + return renderSlot(viewContainer, { + slot, + defaultComponent, + props, + outputs, + }); + }; +} diff --git a/src/v2.x/packages/angular/src/lib/tools.ts b/src/v2.x/packages/angular/src/lib/tools.ts new file mode 100644 index 0000000000..934527028a --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/tools.ts @@ -0,0 +1,123 @@ +import { DestroyRef, Injector, Signal, Type, inject } from "@angular/core"; +import { FrontendTool } from "@copilotkitnext/core"; +import { z } from "zod"; +import { CopilotKit } from "./copilotkit"; + +export type AngularToolCall = Record> = + | { + args: Partial; + status: "in-progress"; + result: undefined; + } + | { + args: Args; + status: "executing"; + result: undefined; + } + | { + args: Args; + status: "complete"; + result: string; + }; + +export type HumanInTheLoopToolCall = Record> = + | { + args: Partial; + status: "in-progress"; + result: undefined; + respond: (result: unknown) => void; + } + | { + args: Args; + status: "executing"; + result: undefined; + respond: (result: unknown) => void; + } + | { + args: Args; + status: "complete"; + result: string; + respond: (result: unknown) => void; + }; + +export interface ToolRenderer = Record> { + toolCall: Signal>; +} + +export interface HumanInTheLoopToolRenderer = Record> { + toolCall: Signal>; +} + +export type ClientTool = Record> = Omit< + FrontendTool, + "handler" +> & { + renderer?: Type>; +}; + +export interface RenderToolCallConfig = Record> { + name: string; + args: z.ZodType; + component: Type>; + agentId?: string; +} + +export interface FrontendToolConfig = Record> { + name: string; + description: string; + parameters: z.ZodType; + component?: Type>; + handler: (args: Args) => Promise; + agentId?: string; +} + +export interface HumanInTheLoopConfig = Record> { + name: string; + description: string; + parameters: z.ZodType; + component: Type>; + agentId?: string; +} + +export function registerRenderToolCall = Record>( + renderToolCall: RenderToolCallConfig, +): void { + const copilotKit = inject(CopilotKit); + const destroyRef = inject(DestroyRef); + + copilotKit.addRenderToolCall(renderToolCall); + + destroyRef.onDestroy(() => { + copilotKit.removeTool(renderToolCall.name, renderToolCall.agentId); + }); +} + +export function registerFrontendTool = Record>( + frontendTool: FrontendToolConfig, +): void { + const injector = inject(Injector); + const destroyRef = inject(DestroyRef); + const copilotKit = inject(CopilotKit); + + copilotKit.addFrontendTool({ + ...(frontendTool as FrontendToolConfig), + injector, + }); + + destroyRef.onDestroy(() => { + copilotKit.removeTool(frontendTool.name); + }); +} + +export function registerHumanInTheLoop = Record>( + humanInTheLoop: HumanInTheLoopConfig, +): void { + const destroyRef = inject(DestroyRef); + const copilotKit = inject(CopilotKit); + + copilotKit.addHumanInTheLoop(humanInTheLoop); + + destroyRef.onDestroy(() => { + copilotKit.removeTool(humanInTheLoop.name); + }); +} diff --git a/src/v2.x/packages/angular/src/lib/utils.ts b/src/v2.x/packages/angular/src/lib/utils.ts new file mode 100644 index 0000000000..23b96ff122 --- /dev/null +++ b/src/v2.x/packages/angular/src/lib/utils.ts @@ -0,0 +1,10 @@ +import { clsx, type ClassValue } from "clsx"; +import { twMerge } from "tailwind-merge"; + +/** + * Utility function to merge Tailwind CSS classes + * Combines clsx for conditional classes and tailwind-merge for proper Tailwind class merging + */ +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/src/v2.x/packages/angular/src/public-api.ts b/src/v2.x/packages/angular/src/public-api.ts new file mode 100644 index 0000000000..0a97d13ae6 --- /dev/null +++ b/src/v2.x/packages/angular/src/public-api.ts @@ -0,0 +1,50 @@ +export * from "./lib/config"; +export * from "./lib/copilotkit"; +export * from "./lib/tools"; +export * from "./lib/render-tool-calls"; +export * from "./lib/agent"; +export * from "./lib/chat-config"; +export * from "./lib/chat-state"; +export * from "./lib/scroll-position"; +export * from "./lib/resize-observer"; +export * from "./lib/utils"; +export * from "./lib/agent-context"; + +export * from "./lib/slots"; + +export * from "./lib/directives/copilotkit-agent-context"; +export * from "./lib/directives/stick-to-bottom"; +export * from "./lib/directives/tooltip"; + +export * from "./lib/components/chat/copilot-chat"; +export * from "./lib/components/chat/copilot-chat-assistant-message"; +export * from "./lib/components/chat/copilot-chat-assistant-message-buttons"; +export * from "./lib/components/chat/copilot-chat-assistant-message-renderer"; +export * from "./lib/components/chat/copilot-chat-assistant-message-toolbar"; +export * from "./lib/components/chat/copilot-chat-assistant-message.types"; +export * from "./lib/components/chat/copilot-chat-audio-recorder"; +export * from "./lib/components/chat/copilot-chat-buttons"; +export * from "./lib/components/chat/copilot-chat-input"; +export * from "./lib/components/chat/copilot-chat-input-defaults"; +export * from "./lib/components/chat/copilot-chat-input.types"; +export * from "./lib/components/chat/copilot-chat-message-view"; +export * from "./lib/components/chat/copilot-chat-message-view-cursor"; +export * from "./lib/components/chat/copilot-chat-message-view.types"; +export * from "./lib/components/chat/copilot-chat-textarea"; +export * from "./lib/components/chat/copilot-chat-tool-calls-view"; +export * from "./lib/components/chat/copilot-chat-toolbar"; +export * from "./lib/components/chat/copilot-chat-tools-menu"; +export * from "./lib/components/chat/copilot-chat-user-message"; +export * from "./lib/components/chat/copilot-chat-user-message-branch-navigation"; +export * from "./lib/components/chat/copilot-chat-user-message-buttons"; +export * from "./lib/components/chat/copilot-chat-user-message-renderer"; +export * from "./lib/components/chat/copilot-chat-user-message-toolbar"; +export * from "./lib/components/chat/copilot-chat-user-message.types"; +export * from "./lib/components/chat/copilot-chat-view"; +export * from "./lib/components/chat/copilot-chat-view-disclaimer"; +export * from "./lib/components/chat/copilot-chat-view-feather"; +export * from "./lib/components/chat/copilot-chat-view-handlers"; +export * from "./lib/components/chat/copilot-chat-view-input-container"; +export * from "./lib/components/chat/copilot-chat-view-scroll-to-bottom-button"; +export * from "./lib/components/chat/copilot-chat-view-scroll-view"; +export * from "./lib/components/chat/copilot-chat-view.types"; diff --git a/src/v2.x/packages/angular/src/styles/globals.css b/src/v2.x/packages/angular/src/styles/globals.css new file mode 100644 index 0000000000..bd1d74362d --- /dev/null +++ b/src/v2.x/packages/angular/src/styles/globals.css @@ -0,0 +1,266 @@ +@import "tailwindcss"; +@plugin "@tailwindcss/typography"; +@source "../**/*.{ts,html}"; +@source "../../apps/angular/storybook/stories/**/*.{ts,html}"; + +@import "tw-animate-css"; + +@custom-variant dark (&:is(.dark *)); + +:root { + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --destructive-foreground: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --radius: 0.625rem; + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); +} + +.dark { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.145 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.145 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.985 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.396 0.141 25.723); + --destructive-foreground: oklch(0.637 0.237 25.331); + --border: oklch(0.269 0 0); + --input: oklch(0.269 0 0); + --ring: oklch(0.556 0 0); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(0.269 0 0); + --sidebar-ring: oklch(0.439 0 0); +} + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-destructive-foreground: var(--destructive-foreground); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); +} + +@theme { + --animate-pulse-cursor: pulse-cursor 0.9s cubic-bezier(0.4, 0, 0.2, 1) + infinite; + @keyframes pulse-cursor { + 0%, + 100% { + transform: scale(1); + opacity: 1; + } + 50% { + transform: scale(1.5); + opacity: 0.8; + } + } +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } +} + +/* Shiki styles removed - Angular uses highlight.js instead */ + +.prose { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.prose + :where(code):not( + :where([class~="not-prose"], [class~="not-prose"] *) + )::before { + content: none; +} + +.prose + :where(code):not( + :where([class~="not-prose"], [class~="not-prose"] *) + )::after { + content: none; +} + +.prose :where(code):not(:where([class~="not-prose"], [class~="not-prose"] *)) { + font-weight: 400; +} + +.prose h1 { + margin-block-end: 8px; + margin-bottom: 8px; + font-size: 24px; + font-weight: 600; +} + +.prose h2 { + margin-block-end: 4px; + margin-block-start: 16px; + margin-top: 16px; + margin-bottom: 4px; + font-size: 20px; + font-weight: 600; +} + +.prose h3 { + margin-block-end: 4px; + margin-block-start: 16px; + margin-top: 16px; + margin-bottom: 4px; + font-size: 18px; + font-weight: 600; +} + +.prose p { + margin-block-start: 8px; + margin-block-end: 4px; + margin-top: 4px; + margin-bottom: 8px; +} + +.prose a { + color: #2964aa; + text-decoration: none; +} + +.prose a:hover { + color: #749ac8; +} + +.prose + :where(blockquote p:first-of-type):not( + :where([class~="not-prose"], [class~="not-prose"] *) + )::before { + content: none; +} + +.prose blockquote { + font-style: normal; + font-weight: 400; +} + +.prose input[type="checkbox"] { + appearance: none; + background-color: #fff; + background-origin: border-box; + border-color: #9b9b9b; + border-width: 1px; + color: #004f99; + display: inline-block; + flex-shrink: 0; + height: 1rem; + padding: 0; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; + -webkit-user-select: none; + user-select: none; + vertical-align: middle; + width: 1rem; + border-radius: 2px; +} + +.prose input[type="checkbox"]:checked { + background-color: #004f99; + border-color: #004f99; + background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 16 16' fill='%23fff' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12.207 4.793a1 1 0 0 1 0 1.414l-5 5a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L6.5 9.086l4.293-4.293a1 1 0 0 1 1.414 0z'/%3E%3C/svg%3E"); + background-position: center; + background-repeat: no-repeat; + background-size: 100% 100%; +} + +.prose em { + @apply text-foreground; +} + +.prose hr { + margin-block-start: 0px; + margin-block-end: 0px; + margin-top: 28px; + margin-bottom: 28px; + color: rgb(13, 13, 13); +} + +.prose td { + @apply text-foreground; +} diff --git a/src/v2.x/packages/angular/src/styles/index.css b/src/v2.x/packages/angular/src/styles/index.css new file mode 100644 index 0000000000..b5c61c9567 --- /dev/null +++ b/src/v2.x/packages/angular/src/styles/index.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/src/v2.x/packages/angular/src/test-setup.ts b/src/v2.x/packages/angular/src/test-setup.ts new file mode 100644 index 0000000000..6c02928e2b --- /dev/null +++ b/src/v2.x/packages/angular/src/test-setup.ts @@ -0,0 +1,106 @@ +// Angular + Zone - Using AnalogJS setup for proper Zone.js integration +import '@angular/compiler'; +import '@analogjs/vitest-angular/setup-zone'; + +import { getTestBed } from '@angular/core/testing'; +import { Injector } from '@angular/core'; +import { + BrowserDynamicTestingModule, + platformBrowserDynamicTesting, +} from '@angular/platform-browser-dynamic/testing'; + +// JSDOM polyfills commonly needed by Angular/CDK/components +// ResizeObserver +if (!(globalThis as any).ResizeObserver) { + class RO { + callback: ResizeObserverCallback; + constructor(cb: ResizeObserverCallback) { this.callback = cb; } + observe() { /* noop */ } + unobserve() { /* noop */ } + disconnect() { /* noop */ } + } + (globalThis as any).ResizeObserver = RO as any; +} + +// IntersectionObserver +if (!(globalThis as any).IntersectionObserver) { + class IO { + constructor(_: IntersectionObserverCallback) {} + observe() {} + unobserve() {} + disconnect() {} + takeRecords() { return []; } + root = null; rootMargin = ''; thresholds: number[] = []; + } + (globalThis as any).IntersectionObserver = IO as any; +} + +// matchMedia +if (!window.matchMedia) { + (window as any).matchMedia = () => ({ + matches: false, + media: '', + onchange: null, + addListener: () => {}, + removeListener: () => {}, + addEventListener: () => {}, + removeEventListener: () => {}, + dispatchEvent: () => false, + }); +} + +// requestAnimationFrame +if (!globalThis.requestAnimationFrame) { + (globalThis as any).requestAnimationFrame = (cb: FrameRequestCallback) => setTimeout(() => cb(Date.now()), 16) as unknown as number; + (globalThis as any).cancelAnimationFrame = (id: number) => clearTimeout(id); +} + +// Canvas context - provide a mock implementation for testing +Object.defineProperty(HTMLCanvasElement.prototype, 'getContext', { + value: function(contextType: string) { + // Return mock context for testing + return { + fillRect: () => {}, clearRect: () => {}, getImageData: () => ({ data: [] }), + putImageData: () => {}, createImageData: () => [], setTransform: () => {}, + drawImage: () => {}, save: () => {}, fillText: () => {}, restore: () => {}, + beginPath: () => {}, moveTo: () => {}, lineTo: () => {}, closePath: () => {}, + stroke: () => {}, translate: () => {}, scale: () => {}, rotate: () => {}, + arc: () => {}, fill: () => {}, measureText: () => ({ width: 0 }), + transform: () => {}, rect: () => {}, clip: () => {}, + lineWidth: 1, strokeStyle: '#000', fillStyle: '#000', + canvas: this, + }; + }, + writable: true, + configurable: true +}); + +// DOMRect +if (!(globalThis as any).DOMRect) { + (globalThis as any).DOMRect = class { constructor(public x=0, public y=0, public width=0, public height=0) {} } as any; +} + +// Initialize Angular testing environment once per worker +console.info('[vitest] test-setup.ts running in pid', process.pid); + +const testBed = getTestBed(); + +// Store platform instance globally to reuse across test files +const globalAny = globalThis as any; +if (!globalAny.__ANGULAR_TEST_PLATFORM__) { + console.info('[vitest] Creating Angular test platform'); + globalAny.__ANGULAR_TEST_PLATFORM__ = platformBrowserDynamicTesting(); +} + +// Check if TestBed has already been initialized by checking the platform +if (!testBed.platform) { + console.info('[vitest] Initializing TestBed'); + testBed.initTestEnvironment( + BrowserDynamicTestingModule, + globalAny.__ANGULAR_TEST_PLATFORM__, + { teardown: { destroyAfterEach: false } } // Don't tear down after each test + ); + console.info('[vitest] TestBed initialized'); +} else { + console.info('[vitest] TestBed already initialized, skipping'); +} \ No newline at end of file diff --git a/src/v2.x/packages/angular/tsconfig.json b/src/v2.x/packages/angular/tsconfig.json new file mode 100644 index 0000000000..d53ba473c1 --- /dev/null +++ b/src/v2.x/packages/angular/tsconfig.json @@ -0,0 +1,33 @@ +{ + "extends": "@copilotkitnext/typescript-config/base.json", + "compilerOptions": { + "outDir": "./dist", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "lib": ["ES2022", "DOM"], + "target": "ES2022", + "module": "ES2022", + "moduleResolution": "node", + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "importHelpers": true, + "types": ["node"], + "paths": { + "@copilotkitnext/core": ["../core/src/index.ts"], + "@copilotkitnext/shared": ["../shared/src/index.ts"] + } + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "**/*.spec.ts", "**/*.test.ts"], + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/src/v2.x/packages/angular/tsconfig.spec.json b/src/v2.x/packages/angular/tsconfig.spec.json new file mode 100644 index 0000000000..8e906fc59f --- /dev/null +++ b/src/v2.x/packages/angular/tsconfig.spec.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "target": "es2016", + "types": ["vitest/globals", "node"] + }, + "files": ["src/test-setup.ts"], + "include": [ + "src/**/*.spec.ts", + "src/**/*.test.ts", + "src/**/*.d.ts" + ], + "exclude": ["node_modules", "dist"] +} \ No newline at end of file diff --git a/src/v2.x/packages/angular/vitest.config.mts b/src/v2.x/packages/angular/vitest.config.mts new file mode 100644 index 0000000000..868f5c5cde --- /dev/null +++ b/src/v2.x/packages/angular/vitest.config.mts @@ -0,0 +1,45 @@ +/// +import { defineConfig } from 'vite'; +import angular from '@analogjs/vite-plugin-angular'; +import { fileURLToPath } from 'node:url'; +import { dirname, resolve } from 'node:path'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const r = (...p: string[]) => resolve(__dirname, ...p); + +export default defineConfig(({ mode }) => ({ + plugins: [angular()], + resolve: { + dedupe: [ + '@angular/core', + '@angular/common', + '@angular/platform-browser', + '@angular/platform-browser-dynamic', + '@angular/compiler', + '@angular/core/testing', + ], + }, + test: { + globals: true, + environment: 'jsdom', + setupFiles: [r('src/test-setup.ts')], // Use absolute path + include: ['src/**/*.{spec,test}.{ts,tsx}'], + pool: 'threads', + poolOptions: { threads: { singleThread: true } }, + coverage: { + provider: 'v8', + reporter: ['text', 'json', 'html'], + exclude: [ + 'node_modules/', + 'dist/', + '*.config.*', + 'src/test-setup.ts', + 'src/index.ts', + 'src/public-api.ts', + ], + }, + }, + define: { + 'import.meta.vitest': mode !== 'production', + }, +})); \ No newline at end of file diff --git a/src/v2.x/packages/core/.npmignore b/src/v2.x/packages/core/.npmignore new file mode 100644 index 0000000000..e349b68043 --- /dev/null +++ b/src/v2.x/packages/core/.npmignore @@ -0,0 +1,36 @@ +# Source files (only publish dist) +src/ +tsup.config.ts +tsconfig.json +vitest.config.ts + +# Turbo cache +.turbo/ + +# Test files +**/*.test.ts +**/*.spec.ts +**/__tests__/ + +# Config files +.eslintrc.js +.prettierrc + +# IDE +.vscode/ +.idea/ + +# Logs +*.log + +# OS files +.DS_Store +Thumbs.db + +# Documentation +*.md +!README.md + +# Git +.git/ +.gitignore \ No newline at end of file diff --git a/src/v2.x/packages/core/eslint.config.mjs b/src/v2.x/packages/core/eslint.config.mjs new file mode 100644 index 0000000000..daa75ba7a7 --- /dev/null +++ b/src/v2.x/packages/core/eslint.config.mjs @@ -0,0 +1,3 @@ +import { config as baseConfig } from "@copilotkitnext/eslint-config/base"; + +export default [...baseConfig]; diff --git a/src/v2.x/packages/core/package.json b/src/v2.x/packages/core/package.json new file mode 100644 index 0000000000..0fc45dbae7 --- /dev/null +++ b/src/v2.x/packages/core/package.json @@ -0,0 +1,48 @@ +{ + "name": "@copilotkitnext/core", + "version": "0.0.33", + "description": "Core web utilities for CopilotKit2", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "require": "./dist/index.js" + } + }, + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "tsup", + "prepublishOnly": "pnpm run build", + "dev": "tsup --watch", + "lint": "eslint . --max-warnings 0", + "check-types": "tsc --noEmit", + "clean": "rm -rf dist", + "test": "vitest run", + "test:watch": "vitest", + "test:coverage": "vitest run --coverage" + }, + "devDependencies": { + "@copilotkitnext/eslint-config": "workspace:*", + "@copilotkitnext/typescript-config": "workspace:*", + "@types/node": "^22.15.3", + "@vitest/coverage-v8": "^3.2.4", + "eslint": "^9.30.0", + "tsup": "^8.5.0", + "typescript": "5.8.2", + "vitest": "^3.2.4" + }, + "dependencies": { + "@ag-ui/client": "0.0.42", + "@copilotkitnext/shared": "workspace:*", + "rxjs": "7.8.1", + "zod": "^3.25.75", + "zod-to-json-schema": "^3.24.6" + }, + "engines": { + "node": ">=18" + } +} diff --git a/src/v2.x/packages/core/src/__tests__/core-agent-constraints.test.ts b/src/v2.x/packages/core/src/__tests__/core-agent-constraints.test.ts new file mode 100644 index 0000000000..017f37be70 --- /dev/null +++ b/src/v2.x/packages/core/src/__tests__/core-agent-constraints.test.ts @@ -0,0 +1,90 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { CopilotKitCore } from '../core'; +import { FrontendTool } from '../types'; +import { MockAgent, createAssistantMessage } from './test-utils'; + +describe('CopilotKitCore - Agent Constraints', () => { + it('should add tool with agentId', () => { + const core = new CopilotKitCore({ + headers: {}, + properties: {}, + }); + + const tool: FrontendTool = { + name: 'testTool', + handler: vi.fn(), + agentId: 'agent1', + }; + + core.addTool(tool); + const retrievedTool = core.getTool({ toolName: 'testTool', agentId: 'agent1' }); + expect(retrievedTool).toBeDefined(); + expect(retrievedTool?.agentId).toBe('agent1'); + }); + + it('should add multiple tools with different agentIds', () => { + const core = new CopilotKitCore({ + headers: {}, + properties: {}, + }); + + const globalTool: FrontendTool = { + name: 'globalTool', + handler: vi.fn(), + }; + + const agent1Tool: FrontendTool = { + name: 'agent1Tool', + handler: vi.fn(), + agentId: 'agent1', + }; + + const agent2Tool: FrontendTool = { + name: 'agent2Tool', + handler: vi.fn(), + agentId: 'agent2', + }; + + core.addTool(globalTool); + core.addTool(agent1Tool); + core.addTool(agent2Tool); + + const retrievedGlobalTool = core.getTool({ toolName: 'globalTool' }); + expect(retrievedGlobalTool).toBeDefined(); + expect(retrievedGlobalTool?.agentId).toBeUndefined(); + + const retrievedAgent1Tool = core.getTool({ toolName: 'agent1Tool', agentId: 'agent1' }); + expect(retrievedAgent1Tool).toBeDefined(); + expect(retrievedAgent1Tool?.agentId).toBe('agent1'); + + const retrievedAgent2Tool = core.getTool({ toolName: 'agent2Tool', agentId: 'agent2' }); + expect(retrievedAgent2Tool).toBeDefined(); + expect(retrievedAgent2Tool?.agentId).toBe('agent2'); + }); + + it('should preserve all FrontendTool properties including agentId', () => { + const core = new CopilotKitCore({ + headers: {}, + properties: {}, + }); + + const handler = vi.fn(async () => 'result'); + const tool: FrontendTool = { + name: 'fullTool', + description: 'A complete tool', + handler, + followUp: false, + agentId: 'specificAgent', + }; + + core.addTool(tool); + + const addedTool = core.getTool({ toolName: 'fullTool', agentId: 'specificAgent' }); + expect(addedTool).toBeDefined(); + expect(addedTool?.name).toBe('fullTool'); + expect(addedTool?.description).toBe('A complete tool'); + expect(addedTool?.handler).toBe(handler); + expect(addedTool?.followUp).toBe(false); + expect(addedTool?.agentId).toBe('specificAgent'); + }); +}); \ No newline at end of file diff --git a/src/v2.x/packages/core/src/__tests__/core-agent-id-validation.test.ts b/src/v2.x/packages/core/src/__tests__/core-agent-id-validation.test.ts new file mode 100644 index 0000000000..61a33fae03 --- /dev/null +++ b/src/v2.x/packages/core/src/__tests__/core-agent-id-validation.test.ts @@ -0,0 +1,132 @@ +import { describe, expect, it } from "vitest"; +import { CopilotKitCore } from "../core"; +import { HttpAgent } from "@ag-ui/client"; + +describe("CopilotKitCore agent ID validation", () => { + it("should assign agent ID from registration key if agent ID is undefined", () => { + const agent = new HttpAgent({ url: "https://example.com" }); + expect(agent.agentId).toBeUndefined(); + + const core = new CopilotKitCore({ + runtimeUrl: undefined, + agents__unsafe_dev_only: { + myAgent: agent, + }, + }); + + expect(agent.agentId).toBe("myAgent"); + expect(core.getAgent("myAgent")).toBe(agent); + }); + + it("should allow registration when agent ID matches registration key", () => { + const agent = new HttpAgent({ url: "https://example.com" }); + agent.agentId = "myAgent"; + + const core = new CopilotKitCore({ + runtimeUrl: undefined, + agents__unsafe_dev_only: { + myAgent: agent, + }, + }); + + expect(agent.agentId).toBe("myAgent"); + expect(core.getAgent("myAgent")).toBe(agent); + }); + + it("should throw error when agent ID doesn't match registration key", () => { + const agent = new HttpAgent({ url: "https://example.com" }); + agent.agentId = "differentId"; + + expect(() => { + new CopilotKitCore({ + runtimeUrl: undefined, + agents__unsafe_dev_only: { + myAgent: agent, + }, + }); + }).toThrow( + 'Agent registration mismatch: Agent with ID "differentId" cannot be registered under key "myAgent". ' + + "The agent ID must match the registration key or be undefined." + ); + }); + + it("should validate agent ID when using addAgent__unsafe_dev_only", () => { + const core = new CopilotKitCore({ + runtimeUrl: undefined, + }); + + const agent1 = new HttpAgent({ url: "https://example.com" }); + core.addAgent__unsafe_dev_only({ id: "agent1", agent: agent1 }); + expect(agent1.agentId).toBe("agent1"); + + const agent2 = new HttpAgent({ url: "https://example.com" }); + agent2.agentId = "agent2"; + core.addAgent__unsafe_dev_only({ id: "agent2", agent: agent2 }); + expect(agent2.agentId).toBe("agent2"); + + const agent3 = new HttpAgent({ url: "https://example.com" }); + agent3.agentId = "wrongId"; + expect(() => { + core.addAgent__unsafe_dev_only({ id: "agent3", agent: agent3 }); + }).toThrow( + 'Agent registration mismatch: Agent with ID "wrongId" cannot be registered under key "agent3". ' + + "The agent ID must match the registration key or be undefined." + ); + }); + + it("should validate agent IDs when using setAgents__unsafe_dev_only", () => { + const core = new CopilotKitCore({ + runtimeUrl: undefined, + }); + + const agent1 = new HttpAgent({ url: "https://example.com" }); + const agent2 = new HttpAgent({ url: "https://example.com" }); + agent2.agentId = "agent2"; + + core.setAgents__unsafe_dev_only({ + agent1: agent1, + agent2: agent2, + }); + + expect(agent1.agentId).toBe("agent1"); + expect(agent2.agentId).toBe("agent2"); + + const agent3 = new HttpAgent({ url: "https://example.com" }); + agent3.agentId = "wrongId"; + + expect(() => { + core.setAgents__unsafe_dev_only({ + agent3: agent3, + }); + }).toThrow( + 'Agent registration mismatch: Agent with ID "wrongId" cannot be registered under key "agent3". ' + + "The agent ID must match the registration key or be undefined." + ); + }); + + it("should handle multiple agents with proper ID validation", () => { + const agent1 = new HttpAgent({ url: "https://example.com" }); + const agent2 = new HttpAgent({ url: "https://example.com" }); + const agent3 = new HttpAgent({ url: "https://example.com" }); + + agent2.agentId = "secondAgent"; + // agent1 and agent3 have undefined IDs + + const core = new CopilotKitCore({ + runtimeUrl: undefined, + agents__unsafe_dev_only: { + firstAgent: agent1, + secondAgent: agent2, + thirdAgent: agent3, + }, + }); + + expect(agent1.agentId).toBe("firstAgent"); + expect(agent2.agentId).toBe("secondAgent"); + expect(agent3.agentId).toBe("thirdAgent"); + + expect(core.getAgent("firstAgent")).toBe(agent1); + expect(core.getAgent("secondAgent")).toBe(agent2); + expect(core.getAgent("thirdAgent")).toBe(agent3); + }); +}); \ No newline at end of file diff --git a/src/v2.x/packages/core/src/__tests__/core-basic-functionality.test.ts b/src/v2.x/packages/core/src/__tests__/core-basic-functionality.test.ts new file mode 100644 index 0000000000..90c8d3a293 --- /dev/null +++ b/src/v2.x/packages/core/src/__tests__/core-basic-functionality.test.ts @@ -0,0 +1,145 @@ +import { describe, it, expect, beforeEach, vi, afterEach } from "vitest"; +import { CopilotKitCore } from "../core"; +import { + MockAgent, + createMessage, + createAssistantMessage, + createToolCallMessage, + createTool, +} from "./test-utils"; + +describe("CopilotKitCore.runAgent - Basic Functionality", () => { + let copilotKitCore: CopilotKitCore; + + beforeEach(() => { + copilotKitCore = new CopilotKitCore({}); + vi.clearAllMocks(); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + it("should run agent without tools", async () => { + const messages = [ + createMessage({ content: "Hello" }), + createAssistantMessage({ content: "Hi there!" }), + ]; + const agent = new MockAgent({ newMessages: messages }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + const result = await copilotKitCore.runAgent({ agent: agent as any }); + + expect(result.newMessages).toEqual(messages); + expect(agent.runAgentCalls).toHaveLength(1); + expect(agent.runAgentCalls[0].forwardedProps).toEqual({}); + }); + + it("should forward properties to agent.runAgent", async () => { + const properties = { apiKey: "test-key", model: "gpt-4" }; + copilotKitCore = new CopilotKitCore({ properties }); + const agent = new MockAgent({ newMessages: [] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + await copilotKitCore.runAgent({ agent: agent as any }); + + expect(agent.runAgentCalls[0].forwardedProps).toEqual(properties); + }); + + it("should handle empty newMessages array", async () => { + const agent = new MockAgent({ newMessages: [] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + const result = await copilotKitCore.runAgent({ agent: agent as any }); + + expect(result.newMessages).toEqual([]); + expect(agent.runAgentCalls).toHaveLength(1); + }); + + it("should ignore non-assistant messages for tool processing", async () => { + const messages = [ + createMessage({ role: "user", content: "User message" }), + createMessage({ role: "system", content: "System message" }), + createMessage({ role: "tool", content: "Tool result", toolCallId: "123" }), + ]; + const agent = new MockAgent({ newMessages: messages }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + const result = await copilotKitCore.runAgent({ agent: agent as any }); + + expect(result.newMessages).toEqual(messages); + expect(agent.runAgentCalls).toHaveLength(1); + }); + + it("should handle messages with undefined toolCalls", async () => { + const message = createAssistantMessage({ + content: "Response", + toolCalls: undefined + }); + const agent = new MockAgent({ newMessages: [message] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + const result = await copilotKitCore.runAgent({ agent: agent as any }); + + expect(result.newMessages).toEqual([message]); + expect(agent.runAgentCalls).toHaveLength(1); + }); + + it("should handle tool returning undefined as empty string", async () => { + const toolName = "undefinedTool"; + const tool = createTool({ + name: toolName, + handler: vi.fn(async () => undefined), + }); + copilotKitCore.addTool(tool); + + const message = createToolCallMessage(toolName); + const agent = new MockAgent({ newMessages: [message] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + await copilotKitCore.runAgent({ agent: agent as any }); + + expect(agent.messages).toContainEqual( + expect.objectContaining({ + role: "tool", + content: "", // Should be empty string, not "undefined" + }) + ); + }); + + it("should handle tool returning null as empty string", async () => { + const toolName = "nullTool"; + const tool = createTool({ + name: toolName, + handler: vi.fn(async () => null), + }); + copilotKitCore.addTool(tool); + + const message = createToolCallMessage(toolName); + const agent = new MockAgent({ newMessages: [message] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + await copilotKitCore.runAgent({ agent: agent as any }); + + expect(agent.messages).toContainEqual( + expect.objectContaining({ + role: "tool", + content: "", // Should be empty string, not "null" + }) + ); + }); + + it("should return correct result structure", async () => { + const newMessages = [ + createAssistantMessage({ content: "Test" }) + ]; + const agent = new MockAgent({ newMessages }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + const result = await copilotKitCore.runAgent({ agent: agent as any }); + + expect(result).toHaveProperty("newMessages"); + expect(Array.isArray(result.newMessages)).toBe(true); + expect(result.newMessages).toEqual(newMessages); + }); +}); \ No newline at end of file diff --git a/src/v2.x/packages/core/src/__tests__/core-basic.test.ts b/src/v2.x/packages/core/src/__tests__/core-basic.test.ts new file mode 100644 index 0000000000..8a3c27cae5 --- /dev/null +++ b/src/v2.x/packages/core/src/__tests__/core-basic.test.ts @@ -0,0 +1,27 @@ +import { describe, it, expect, beforeEach, vi } from "vitest"; +import { CopilotKitCore } from "../core"; + +describe("CopilotKitCore Basic", () => { + let copilotKitCore: CopilotKitCore; + + beforeEach(() => { + copilotKitCore = new CopilotKitCore({}); + }); + + it("should create an instance", () => { + expect(copilotKitCore).toBeDefined(); + expect(copilotKitCore.agents).toEqual({}); + expect(copilotKitCore.tools).toEqual([]); + }); + + it("should add a tool", () => { + const tool = { + name: "testTool", + handler: vi.fn(), + }; + + copilotKitCore.addTool(tool); + + expect(copilotKitCore.getTool({ toolName: "testTool" })).toBe(tool); + }); +}); diff --git a/src/v2.x/packages/core/src/__tests__/core-edge-cases.test.ts b/src/v2.x/packages/core/src/__tests__/core-edge-cases.test.ts new file mode 100644 index 0000000000..e1dc65f7c1 --- /dev/null +++ b/src/v2.x/packages/core/src/__tests__/core-edge-cases.test.ts @@ -0,0 +1,173 @@ +import { describe, it, expect, beforeEach, vi, afterEach } from "vitest"; +import { CopilotKitCore } from "../core"; +import { + MockAgent, + createAssistantMessage, + createToolCallMessage, + createToolResultMessage, + createTool, +} from "./test-utils"; + +describe("CopilotKitCore.runAgent - Edge Cases", () => { + let copilotKitCore: CopilotKitCore; + + beforeEach(() => { + copilotKitCore = new CopilotKitCore({}); + vi.clearAllMocks(); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + it("should skip tool call when result already exists in newMessages", async () => { + const tool = createTool({ + name: "alreadyProcessedTool", + handler: vi.fn(async () => "Should not be called"), + }); + copilotKitCore.addTool(tool); + + const toolCallId = "processed-call"; + const assistantMsg = createToolCallMessage("alreadyProcessedTool"); + if (assistantMsg.role === 'assistant' && assistantMsg.toolCalls && assistantMsg.toolCalls[0]) { + assistantMsg.toolCalls[0].id = toolCallId; + } + const existingResult = createToolResultMessage(toolCallId, "Already processed"); + + const agent = new MockAgent({ + newMessages: [assistantMsg, existingResult], + }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + await copilotKitCore.runAgent({ agent: agent as any }); + + expect(tool.handler).not.toHaveBeenCalled(); + }); + + it("should handle empty tool function name", async () => { + const message = createAssistantMessage({ + content: "", + toolCalls: [{ + id: "empty-name-call", + type: "function", + function: { + name: "", + arguments: "{}", + }, + }], + }); + + const agent = new MockAgent({ newMessages: [message] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + await copilotKitCore.runAgent({ agent: agent as any }); + + expect(agent.messages.filter(m => m.role === "tool")).toHaveLength(0); + }); + + it("should handle tool arguments as empty string", async () => { + const tool = createTool({ + name: "emptyArgsTool", + handler: vi.fn(async (args) => `Received: ${JSON.stringify(args)}`), + }); + copilotKitCore.addTool(tool); + + const message = createAssistantMessage({ + content: "", + toolCalls: [{ + id: "empty-args-call", + type: "function", + function: { + name: "emptyArgsTool", + arguments: "", + }, + }], + }); + + const agent = new MockAgent({ newMessages: [message] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + await expect(copilotKitCore.runAgent({ agent: agent as any })).rejects.toThrow(); + }); + + it("should handle very large tool result", async () => { + const largeResult = "x".repeat(100000); // 100KB string + const tool = createTool({ + name: "largeTool", + handler: vi.fn(async () => largeResult), + followUp: false, + }); + copilotKitCore.addTool(tool); + + const message = createToolCallMessage("largeTool"); + const agent = new MockAgent({ newMessages: [message] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + await copilotKitCore.runAgent({ agent: agent as any }); + + const toolMessage = agent.messages.find(m => m.role === "tool"); + expect(toolMessage?.content).toBe(largeResult); + }); + + it("should handle tool handler modifying agent state", async () => { + const tool = createTool({ + name: "stateTool", + handler: vi.fn(async () => { + // Try to modify agent messages during execution + agent.messages.push(createAssistantMessage({ content: "Injected" })); + return "Result"; + }), + followUp: false, + }); + copilotKitCore.addTool(tool); + + const message = createToolCallMessage("stateTool"); + const agent = new MockAgent({ newMessages: [message] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + await copilotKitCore.runAgent({ agent: agent as any }); + + // The injected message should be present + expect(agent.messages.some(m => m.content === "Injected")).toBe(true); + // Tool result should still be added correctly + expect(agent.messages.some(m => m.role === "tool" && m.content === "Result")).toBe(true); + }); + + it("should propagate errors from agent.runAgent", async () => { + const errorMessage = "Agent execution failed"; + const agent = new MockAgent({ + error: new Error(errorMessage) + }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + await expect(copilotKitCore.runAgent({ agent: agent as any })) + .rejects + .toThrow(errorMessage); + }); + + it("should handle tool with invalid JSON arguments", async () => { + const toolName = "invalidJsonTool"; + const tool = createTool({ + name: toolName, + handler: vi.fn(async () => "Should not be called"), + }); + copilotKitCore.addTool(tool); + + const message = createAssistantMessage({ + content: "", + toolCalls: [{ + id: "tool-call-1", + type: "function", + function: { + name: toolName, + arguments: "{ invalid json", + }, + }], + }); + const agent = new MockAgent({ newMessages: [message] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + await expect(copilotKitCore.runAgent({ agent: agent as any })).rejects.toThrow(); + expect(tool.handler).not.toHaveBeenCalled(); + }); +}); \ No newline at end of file diff --git a/src/v2.x/packages/core/src/__tests__/core-error-handling.test.ts b/src/v2.x/packages/core/src/__tests__/core-error-handling.test.ts new file mode 100644 index 0000000000..f9c3df139e --- /dev/null +++ b/src/v2.x/packages/core/src/__tests__/core-error-handling.test.ts @@ -0,0 +1,238 @@ +import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; +import { CopilotKitCore, CopilotKitCoreErrorCode } from "../core"; +import { ProxiedCopilotRuntimeAgent } from "../agent"; +import { createAssistantMessage } from "./test-utils"; + +describe("CopilotKitCore error handling", () => { + describe("agent error events", () => { + it("emits AGENT_RUN_ERROR_EVENT when agent sends RunError event", async () => { + const core = new CopilotKitCore({}); + const errors: Array<{ code: CopilotKitCoreErrorCode; error: Error; context: any }> = []; + const sub = core.subscribe({ onError: (e) => void errors.push(e) }); + + // Minimal agent that triggers onRunErrorEvent via the provided subscriber + const agent = { + agentId: "agent1", + threadId: "t1", + messages: [] as any[], + state: {}, + // Simulate HttpAgent-like API surface expected by core + addMessages: (_m: any[]) => {}, + addMessage: (_m: any) => {}, + abortRun: () => {}, + clone: () => agent, + subscribe: () => ({ unsubscribe() {} }), + async runAgent(_params: any, subscriber?: any) { + const event = { + type: "RUN_ERROR", + threadId: this.threadId, + runId: "r1", + message: "runtime error happened", + code: "bad_request", + rawEvent: { error: "bad_request" }, + } as any; + await subscriber?.onRunErrorEvent?.({ + event, + agent: this, + messages: this.messages, + state: this.state, + input: { threadId: this.threadId, runId: "r1", messages: this.messages, state: this.state }, + }); + return { newMessages: [] }; + }, + } as any; + + // Register agent to avoid suggestion engine warnings + core.addAgent__unsafe_dev_only({ id: agent.agentId, agent: agent as any }); + await core.runAgent({ agent }); + + expect(errors.some((e) => e.code === CopilotKitCoreErrorCode.AGENT_RUN_ERROR_EVENT)).toBe(true); + const evt = errors.find((e) => e.code === CopilotKitCoreErrorCode.AGENT_RUN_ERROR_EVENT)!; + expect(evt.context.agentId).toBe("agent1"); + expect(evt.context.event).toBeDefined(); + + sub.unsubscribe(); + }); + + it("emits AGENT_RUN_FAILED_EVENT when agent triggers onRunFailed", async () => { + const core = new CopilotKitCore({}); + const errors: Array<{ code: CopilotKitCoreErrorCode; error: Error; context: any }> = []; + const sub = core.subscribe({ onError: (e) => void errors.push(e) }); + + const agent = { + agentId: "agent2", + threadId: "t2", + messages: [] as any[], + state: {}, + addMessages: (_m: any[]) => {}, + addMessage: (_m: any) => {}, + abortRun: () => {}, + clone: () => agent, + subscribe: () => ({ unsubscribe() {} }), + async runAgent(_params: any, subscriber?: any) { + await subscriber?.onRunFailed?.({ error: new Error("agent failed") }); + return { newMessages: [] }; + }, + } as any; + + core.addAgent__unsafe_dev_only({ id: agent.agentId, agent: agent as any }); + await core.runAgent({ agent }); + + expect(errors.some((e) => e.code === CopilotKitCoreErrorCode.AGENT_RUN_FAILED_EVENT)).toBe(true); + const evt = errors.find((e) => e.code === CopilotKitCoreErrorCode.AGENT_RUN_FAILED_EVENT)!; + expect(evt.context.agentId).toBe("agent2"); + expect(evt.context.source).toBe("onRunFailed"); + + sub.unsubscribe(); + }); + }); + + describe("http errors", () => { + const originalFetch = global.fetch; + const originalWindow = (globalThis as { window?: unknown }).window; + + beforeEach(() => { + // Simulate browser environment to allow updateRuntimeConnection to proceed. + (globalThis as { window?: unknown }).window = {}; + }); + + afterEach(() => { + vi.restoreAllMocks(); + global.fetch = originalFetch; + if (originalWindow === undefined) { + delete (globalThis as { window?: unknown }).window; + } else { + (globalThis as { window?: unknown }).window = originalWindow; + } + }); + + it("emits RUNTIME_INFO_FETCH_FAILED when runtime info sync fails", async () => { + const fetchMock = vi.fn().mockRejectedValue(new Error("network failure")); + global.fetch = fetchMock; + + const core = new CopilotKitCore({ runtimeUrl: "https://runtime.example/rest", runtimeTransport: "rest" }); + const errors: Array<{ code: CopilotKitCoreErrorCode; error: Error; context: any }> = []; + const sub = core.subscribe({ onError: (e) => void errors.push(e) }); + + await vi.waitFor(() => { + expect(errors.some((e) => e.code === CopilotKitCoreErrorCode.RUNTIME_INFO_FETCH_FAILED)).toBe(true); + }); + + sub.unsubscribe(); + }); + + it("emits AGENT_RUN_FAILED when proxied runtime agent run fails (REST)", async () => { + const runtimeUrl = "https://runtime.example/rest"; + const fetchMock = vi.fn().mockRejectedValue(new Error("fetch failure")); + global.fetch = fetchMock; + + const core = new CopilotKitCore({}); + const errors: Array<{ code: CopilotKitCoreErrorCode; error: Error; context: any }> = []; + const sub = core.subscribe({ onError: (e) => void errors.push(e) }); + + const agent = new ProxiedCopilotRuntimeAgent({ runtimeUrl, agentId: "agent-http", transport: "rest" }); + + await expect(core.runAgent({ agent })).rejects.toBeDefined(); + + expect(errors.some((e) => e.code === CopilotKitCoreErrorCode.AGENT_RUN_FAILED)).toBe(true); + const evt = errors.find((e) => e.code === CopilotKitCoreErrorCode.AGENT_RUN_FAILED)!; + expect(evt.context.agentId).toBe("agent-http"); + + sub.unsubscribe(); + }); + }); + + describe("internal processing errors (tools)", () => { + it("emits TOOL_ARGUMENT_PARSE_FAILED then AGENT_RUN_FAILED when tool args JSON is invalid", async () => { + const core = new CopilotKitCore({}); + const errors: Array<{ code: CopilotKitCoreErrorCode; error: Error; context: any }> = []; + const sub = core.subscribe({ onError: (e) => void errors.push(e) }); + + const toolName = "parseFail"; + core.addTool({ name: toolName, description: "", handler: async () => "ok" }); + + // Assistant message with a tool call and invalid JSON arguments + const assistant = createAssistantMessage({ + content: "", + toolCalls: [ + { + id: "tc1", + type: "function", + function: { name: toolName, arguments: "not-json" }, + } as any, + ], + } as any); + + const agent = { + agentId: "agent-tools-1", + threadId: "t1", + messages: [] as any[], + state: {}, + addMessages: (m: any[]) => agent.messages.push(...m), + addMessage: (m: any) => agent.messages.push(m), + abortRun: () => {}, + clone: () => agent, + subscribe: () => ({ unsubscribe() {} }), + async runAgent() { + return { newMessages: [assistant] }; + }, + } as any; + + core.addAgent__unsafe_dev_only({ id: agent.agentId, agent: agent as any }); + await expect(core.runAgent({ agent })).rejects.toBeDefined(); + + // Argument parse error captured + expect(errors.some((e) => e.code === CopilotKitCoreErrorCode.TOOL_ARGUMENT_PARSE_FAILED)).toBe(true); + // The run rejects; current implementation does not emit AGENT_RUN_FAILED for this path + + sub.unsubscribe(); + }); + + it("emits TOOL_HANDLER_FAILED and continues run when tool handler throws", async () => { + const core = new CopilotKitCore({}); + const errors: Array<{ code: CopilotKitCoreErrorCode; error: Error; context: any }> = []; + const sub = core.subscribe({ onError: (e) => void errors.push(e) }); + + const toolName = "boom"; + core.addTool({ name: toolName, description: "", handler: async () => { throw new Error("boom"); } }); + + const assistant = createAssistantMessage({ + content: "", + toolCalls: [ + { + id: "tc2", + type: "function", + function: { name: toolName, arguments: JSON.stringify({ a: 1 }) }, + } as any, + ], + } as any); + + const agent = { + agentId: "agent-tools-2", + threadId: "t2", + messages: [] as any[], + state: {}, + addMessages: (m: any[]) => agent.messages.push(...m), + addMessage: (m: any) => agent.messages.push(m), + abortRun: () => {}, + clone: () => agent, + subscribe: () => ({ unsubscribe() {} }), + async runAgent() { + return { newMessages: [assistant] }; + }, + } as any; + + core.addAgent__unsafe_dev_only({ id: agent.agentId, agent: agent as any }); + const result = await core.runAgent({ agent }); + + // Handler error should be reported + expect(errors.some((e) => e.code === CopilotKitCoreErrorCode.TOOL_HANDLER_FAILED)).toBe(true); + // Run should not fail; tool result message should contain the error string + expect(Array.isArray(result.newMessages)).toBe(true); + // After run, the tool result is inserted into agent.messages with "Error: boom" + expect(agent.messages.some((m: any) => m.role === "tool" && typeof m.content === "string" && m.content.includes("Error: boom"))).toBe(true); + + sub.unsubscribe(); + }); + }); +}); diff --git a/src/v2.x/packages/core/src/__tests__/core-follow-up.test.ts b/src/v2.x/packages/core/src/__tests__/core-follow-up.test.ts new file mode 100644 index 0000000000..d14015c751 --- /dev/null +++ b/src/v2.x/packages/core/src/__tests__/core-follow-up.test.ts @@ -0,0 +1,223 @@ +import { describe, it, expect, beforeEach, vi, afterEach } from "vitest"; +import { CopilotKitCore } from "../core"; +import { FrontendTool } from "../types"; +import { + MockAgent, + createAssistantMessage, + createToolCallMessage, + createMultipleToolCallsMessage, + createTool, +} from "./test-utils"; + +describe("CopilotKitCore.runAgent - Follow-up Logic", () => { + let copilotKitCore: CopilotKitCore; + + beforeEach(() => { + copilotKitCore = new CopilotKitCore({}); + vi.clearAllMocks(); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + it("should trigger recursive call when tool.followUp is true", async () => { + const tool = createTool({ + name: "followUpTool", + handler: vi.fn(async () => "Result"), + followUp: true, + }); + copilotKitCore.addTool(tool); + + const message = createToolCallMessage("followUpTool"); + const followUpMessage = createAssistantMessage({ content: "Follow-up response" }); + + const agent = new MockAgent({ newMessages: [message] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + let callCount = 0; + agent.runAgentCallback = () => { + callCount++; + if (callCount === 2) { + agent.setNewMessages([followUpMessage]); + } + }; + + const result = await copilotKitCore.runAgent({ agent: agent as any }); + + expect(agent.runAgentCalls).toHaveLength(2); + expect(result.newMessages).toContain(followUpMessage); + }); + + it("should not trigger recursive call when tool.followUp is false", async () => { + const tool = createTool({ + name: "noFollowUpTool", + handler: vi.fn(async () => "Result"), + followUp: false, + }); + copilotKitCore.addTool(tool); + + const message = createToolCallMessage("noFollowUpTool"); + const agent = new MockAgent({ newMessages: [message] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + await copilotKitCore.runAgent({ agent: agent as any }); + + expect(agent.runAgentCalls).toHaveLength(1); + }); + + it("should trigger recursive call when tool.followUp is undefined (default)", async () => { + const tool: FrontendTool = { + name: "defaultFollowUpTool", + handler: vi.fn(async () => "Result"), + // followUp is undefined + }; + copilotKitCore.addTool(tool); + + const message = createToolCallMessage("defaultFollowUpTool"); + const followUpMessage = createAssistantMessage({ content: "Follow-up" }); + + const agent = new MockAgent({ newMessages: [message] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + let callCount = 0; + agent.runAgentCallback = () => { + callCount++; + if (callCount === 2) { + agent.setNewMessages([followUpMessage]); + } + }; + + await copilotKitCore.runAgent({ agent: agent as any }); + + expect(agent.runAgentCalls).toHaveLength(2); + }); + + it("should trigger follow-up when at least one tool needs it", async () => { + const tool1 = createTool({ + name: "tool1", + handler: vi.fn(async () => "Result 1"), + followUp: false, + }); + const tool2 = createTool({ + name: "tool2", + handler: vi.fn(async () => "Result 2"), + followUp: true, + }); + const tool3 = createTool({ + name: "tool3", + handler: vi.fn(async () => "Result 3"), + followUp: false, + }); + copilotKitCore.addTool(tool1); + copilotKitCore.addTool(tool2); + copilotKitCore.addTool(tool3); + + const message = createMultipleToolCallsMessage([ + { name: "tool1" }, + { name: "tool2" }, + { name: "tool3" }, + ]); + + const agent = new MockAgent({ newMessages: [message] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + let callCount = 0; + agent.runAgentCallback = () => { + callCount++; + if (callCount === 2) { + agent.setNewMessages([]); + } + }; + + await copilotKitCore.runAgent({ agent: agent as any }); + + expect(agent.runAgentCalls).toHaveLength(2); + }); + + it("should not trigger follow-up when all tools have followUp=false", async () => { + const tool1 = createTool({ + name: "tool1", + handler: vi.fn(async () => "Result 1"), + followUp: false, + }); + const tool2 = createTool({ + name: "tool2", + handler: vi.fn(async () => "Result 2"), + followUp: false, + }); + copilotKitCore.addTool(tool1); + copilotKitCore.addTool(tool2); + + const message = createMultipleToolCallsMessage([ + { name: "tool1" }, + { name: "tool2" }, + ]); + + const agent = new MockAgent({ newMessages: [message] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + await copilotKitCore.runAgent({ agent: agent as any }); + + expect(agent.runAgentCalls).toHaveLength(1); + }); + + it("should return final result after recursive follow-up", async () => { + const tool = createTool({ + name: "recursiveTool", + handler: vi.fn(async () => "Tool result"), + followUp: true, + }); + copilotKitCore.addTool(tool); + + const initialMessage = createToolCallMessage("recursiveTool"); + const finalMessage = createAssistantMessage({ content: "Final response" }); + + const agent = new MockAgent({ newMessages: [initialMessage] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + let callCount = 0; + agent.runAgentCallback = () => { + callCount++; + if (callCount === 2) { + agent.setNewMessages([finalMessage]); + } + }; + + const result = await copilotKitCore.runAgent({ agent: agent as any }); + + expect(result.newMessages).toEqual([finalMessage]); + }); + + it("should handle multiple recursive follow-ups (chain)", async () => { + const tool1 = createTool({ + name: "chainTool1", + handler: vi.fn(async () => "Result 1"), + followUp: true, + }); + const tool2 = createTool({ + name: "chainTool2", + handler: vi.fn(async () => "Result 2"), + followUp: true, + }); + copilotKitCore.addTool(tool1); + copilotKitCore.addTool(tool2); + + const msg1 = createToolCallMessage("chainTool1"); + const msg2 = createToolCallMessage("chainTool2"); + const finalMsg = createAssistantMessage({ content: "Done" }); + + const agent = new MockAgent({ newMessages: [msg1] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + let callCount = 0; + agent.runAgentCallback = () => { + callCount++; + if (callCount === 2) { + agent.setNewMessages([msg2]); + } else if (callCount === 3) { + agent.setNewMessages([finalMsg]); + } + }; + + const result = await copilotKitCore.runAgent({ agent: agent as any }); + + expect(agent.runAgentCalls).toHaveLength(3); + expect(result.newMessages).toEqual([finalMsg]); + }); +}); \ No newline at end of file diff --git a/src/v2.x/packages/core/src/__tests__/core-full.test.ts b/src/v2.x/packages/core/src/__tests__/core-full.test.ts new file mode 100644 index 0000000000..6e04244bcc --- /dev/null +++ b/src/v2.x/packages/core/src/__tests__/core-full.test.ts @@ -0,0 +1,335 @@ +import { describe, it, expect, beforeEach, vi, afterEach } from "vitest"; +import { CopilotKitCore } from "../core"; +import { FrontendTool } from "../types"; +import { + MockAgent, + createMessage, + createAssistantMessage, + createToolCallMessage, + createToolResultMessage, + createTool, + createMultipleToolCallsMessage, +} from "./test-utils"; + +describe("CopilotKitCore.runAgent - Full Test Suite", () => { + let copilotKitCore: CopilotKitCore; + + beforeEach(() => { + copilotKitCore = new CopilotKitCore({}); + vi.clearAllMocks(); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + describe("Tests that should pass", () => { + it("TEST 1: should run agent without tools", async () => { + const messages = [createMessage({ content: "Hello" }), createAssistantMessage({ content: "Hi there!" })]; + const agent = new MockAgent({ newMessages: messages }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + const result = await copilotKitCore.runAgent({ agent: agent as any }); + + expect(result.newMessages).toEqual(messages); + expect(agent.runAgentCalls).toHaveLength(1); + }); + + it("TEST 2: should execute tool with string result", async () => { + const toolName = "stringTool"; + const tool = createTool({ + name: toolName, + handler: vi.fn(async () => "String result"), + followUp: false, + }); + copilotKitCore.addTool(tool); + + const message = createToolCallMessage(toolName, { input: "test" }); + const agent = new MockAgent({ newMessages: [message] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + await copilotKitCore.runAgent({ agent: agent as any }); + + expect(tool.handler).toHaveBeenCalledWith( + { input: "test" }, + expect.objectContaining({ + id: expect.any(String), + function: expect.objectContaining({ + name: toolName, + arguments: '{"input":"test"}', + }), + }), + ); + expect(agent.messages.some((m) => m.role === "tool")).toBe(true); + }); + + it("TEST 3: should skip tool when not found", async () => { + const message = createToolCallMessage("nonExistentTool"); + const agent = new MockAgent({ newMessages: [message] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + await copilotKitCore.runAgent({ agent: agent as any }); + + expect(agent.messages.filter((m) => m.role === "tool")).toHaveLength(0); + }); + }); + + describe("Tests that might reveal problems", () => { + it("TEST 4: should handle follow-up with recursion", async () => { + console.log("TEST 4: Starting follow-up test"); + const tool = createTool({ + name: "followUpTool", + handler: vi.fn(async () => "Result"), + followUp: true, // This should trigger recursion + }); + copilotKitCore.addTool(tool); + + const message = createToolCallMessage("followUpTool"); + const followUpMessage = createAssistantMessage({ content: "Follow-up response" }); + + const agent = new MockAgent({ newMessages: [message] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + let callCount = 0; + agent.runAgentCallback = () => { + callCount++; + console.log(`TEST 4: Call count: ${callCount}`); + if (callCount === 2) { + agent.setNewMessages([followUpMessage]); + } + }; + + try { + const result = await copilotKitCore.runAgent({ agent: agent as any }); + console.log(`TEST 4: Success - calls: ${agent.runAgentCalls.length}`); + expect(agent.runAgentCalls).toHaveLength(2); + expect(result.newMessages).toContain(followUpMessage); + } catch (error) { + console.log(`TEST 4: Error - ${error}`); + throw error; + } + }); + + it("TEST 5: should handle multiple tools with at least one follow-up", async () => { + console.log("TEST 5: Starting multiple tools test"); + const tool1 = createTool({ + name: "tool1", + handler: vi.fn(async () => "Result 1"), + followUp: false, + }); + const tool2 = createTool({ + name: "tool2", + handler: vi.fn(async () => "Result 2"), + followUp: true, // This one needs follow-up + }); + copilotKitCore.addTool(tool1); + copilotKitCore.addTool(tool2); + + const message = createMultipleToolCallsMessage([{ name: "tool1" }, { name: "tool2" }]); + + const agent = new MockAgent({ newMessages: [message] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + let callCount = 0; + agent.runAgentCallback = () => { + callCount++; + console.log(`TEST 5: Call count: ${callCount}`); + if (callCount === 2) { + agent.setNewMessages([]); + } + }; + + try { + await copilotKitCore.runAgent({ agent: agent as any }); + console.log(`TEST 5: Success - calls: ${agent.runAgentCalls.length}`); + expect(agent.runAgentCalls).toHaveLength(2); + } catch (error) { + console.log(`TEST 5: Error - ${error}`); + throw error; + } + }); + + it("TEST 6: should handle tool with undefined follow-up (defaults to true)", async () => { + console.log("TEST 6: Starting undefined follow-up test"); + const tool: FrontendTool = { + name: "defaultFollowUpTool", + handler: vi.fn(async () => "Result"), + // followUp is undefined - should default to true + }; + copilotKitCore.addTool(tool); + + const message = createToolCallMessage("defaultFollowUpTool"); + const followUpMessage = createAssistantMessage({ content: "Follow-up" }); + + const agent = new MockAgent({ newMessages: [message] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + let callCount = 0; + agent.runAgentCallback = () => { + callCount++; + console.log(`TEST 6: Call count: ${callCount}`); + if (callCount === 2) { + agent.setNewMessages([followUpMessage]); + } + }; + + try { + await copilotKitCore.runAgent({ agent: agent as any }); + console.log(`TEST 6: Success - calls: ${agent.runAgentCalls.length}`); + expect(agent.runAgentCalls).toHaveLength(2); + } catch (error) { + console.log(`TEST 6: Error - ${error}`); + throw error; + } + }); + + it("TEST 7: should handle invalid JSON in tool arguments", async () => { + console.log("TEST 7: Starting invalid JSON test"); + const toolName = "invalidJsonTool"; + const tool = createTool({ + name: toolName, + handler: vi.fn(async () => "Should not be called"), + }); + copilotKitCore.addTool(tool); + + const message = createAssistantMessage({ + content: "", + toolCalls: [ + { + id: "tool-call-1", + type: "function", + function: { + name: toolName, + arguments: "{ invalid json", + }, + }, + ], + }); + const agent = new MockAgent({ newMessages: [message] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + try { + await copilotKitCore.runAgent({ agent: agent as any }); + console.log("TEST 7: ERROR - Should have thrown!"); + expect(true).toBe(false); // Should not reach here + } catch (error) { + console.log(`TEST 7: Success - caught error: ${error}`); + expect(tool.handler).not.toHaveBeenCalled(); + } + }); + + it("TEST 8: should handle empty string arguments", async () => { + console.log("TEST 8: Starting empty arguments test"); + const tool = createTool({ + name: "emptyArgsTool", + handler: vi.fn(async (args) => `Received: ${JSON.stringify(args)}`), + }); + copilotKitCore.addTool(tool); + + const message = createAssistantMessage({ + content: "", + toolCalls: [ + { + id: "empty-args-call", + type: "function", + function: { + name: "emptyArgsTool", + arguments: "", + }, + }, + ], + }); + + const agent = new MockAgent({ newMessages: [message] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + try { + await copilotKitCore.runAgent({ agent: agent as any }); + console.log("TEST 8: ERROR - Should have thrown on empty string!"); + expect(true).toBe(false); + } catch (error) { + console.log(`TEST 8: Success - caught error: ${error}`); + expect(tool.handler).not.toHaveBeenCalled(); + } + }); + + it("TEST 9: should handle chain of follow-ups", async () => { + console.log("TEST 9: Starting chain test"); + const tool1 = createTool({ + name: "chainTool1", + handler: vi.fn(async () => "Result 1"), + followUp: true, + }); + const tool2 = createTool({ + name: "chainTool2", + handler: vi.fn(async () => "Result 2"), + followUp: true, + }); + copilotKitCore.addTool(tool1); + copilotKitCore.addTool(tool2); + + const msg1 = createToolCallMessage("chainTool1"); + const msg2 = createToolCallMessage("chainTool2"); + const finalMsg = createAssistantMessage({ content: "Done" }); + + const agent = new MockAgent({ newMessages: [msg1] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + let callCount = 0; + agent.runAgentCallback = () => { + callCount++; + console.log(`TEST 9: Call count: ${callCount}`); + if (callCount === 2) { + agent.setNewMessages([msg2]); + } else if (callCount === 3) { + agent.setNewMessages([finalMsg]); + } + }; + + try { + const result = await copilotKitCore.runAgent({ agent: agent as any }); + console.log(`TEST 9: Success - calls: ${agent.runAgentCalls.length}`); + expect(agent.runAgentCalls).toHaveLength(3); + expect(result.newMessages).toEqual([finalMsg]); + } catch (error) { + console.log(`TEST 9: Error - ${error}`); + throw error; + } + }); + + it("TEST 10: should handle concurrent tool calls", async () => { + console.log("TEST 10: Starting concurrent tools test"); + const delays = [50, 30, 70]; + const tools = delays.map((delay, i) => + createTool({ + name: `concurrentTool${i}`, + handler: vi.fn(async () => { + await new Promise((resolve) => setTimeout(resolve, delay)); + return `Result ${i} after ${delay}ms`; + }), + followUp: false, + }), + ); + + tools.forEach((tool) => copilotKitCore.addTool(tool)); + + const message = createMultipleToolCallsMessage(delays.map((_, i) => ({ name: `concurrentTool${i}` }))); + const agent = new MockAgent({ newMessages: [message] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + const startTime = Date.now(); + try { + await copilotKitCore.runAgent({ agent: agent as any }); + const duration = Date.now() - startTime; + console.log(`TEST 10: Success - duration: ${duration}ms`); + + // Should execute sequentially + const expectedMinDuration = delays.reduce((a, b) => a + b, 0); + expect(duration).toBeGreaterThanOrEqual(expectedMinDuration - 10); + + tools.forEach((tool) => { + expect(tool.handler).toHaveBeenCalled(); + }); + } catch (error) { + console.log(`TEST 10: Error - ${error}`); + throw error; + } + }); + }); +}); diff --git a/src/v2.x/packages/core/src/__tests__/core-headers.test.ts b/src/v2.x/packages/core/src/__tests__/core-headers.test.ts new file mode 100644 index 0000000000..3aecff9fc0 --- /dev/null +++ b/src/v2.x/packages/core/src/__tests__/core-headers.test.ts @@ -0,0 +1,266 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { CopilotKitCore } from "../core"; +import { HttpAgent } from "@ag-ui/client"; +import { waitForCondition } from "./test-utils"; + +describe("CopilotKitCore headers", () => { + const originalFetch = global.fetch; + const originalWindow = (global as any).window; + + beforeEach(() => { + vi.restoreAllMocks(); + // Mock window to simulate browser environment + (global as any).window = {}; + }); + + afterEach(() => { + if (originalFetch) { + global.fetch = originalFetch; + } else { + delete (global as typeof globalThis & { fetch?: typeof fetch }).fetch; + } + // Restore window + if (originalWindow === undefined) { + delete (global as any).window; + } else { + (global as any).window = originalWindow; + } + }); + + it("includes provided headers when fetching runtime info", async () => { + const fetchMock = vi.fn().mockResolvedValue({ + json: vi.fn().mockResolvedValue({ version: "1.0.0", agents: {} }), + }); + global.fetch = fetchMock as unknown as typeof fetch; + + const headers = { + Authorization: "Bearer test-token", + "X-Custom-Header": "custom-value", + }; + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const core = new CopilotKitCore({ + runtimeUrl: "https://runtime.example", + headers, + }); + + await waitForCondition(() => fetchMock.mock.calls.length >= 1); + + expect(fetchMock).toHaveBeenCalledWith( + "https://runtime.example/info", + expect.objectContaining({ + headers: expect.objectContaining(headers), + }) + ); + }); + + it("uses updated headers for subsequent runtime requests", async () => { + const fetchMock = vi.fn().mockResolvedValue({ + json: vi.fn().mockResolvedValue({ version: "1.0.0", agents: {} }), + }); + global.fetch = fetchMock as unknown as typeof fetch; + + const core = new CopilotKitCore({ + runtimeUrl: "https://runtime.example", + headers: { Authorization: "Bearer initial" }, + }); + + await waitForCondition(() => fetchMock.mock.calls.length >= 1); + + core.setHeaders({ Authorization: "Bearer updated", "X-Trace": "123" }); + core.setRuntimeUrl(undefined); + core.setRuntimeUrl("https://runtime.example"); + + await waitForCondition(() => fetchMock.mock.calls.length >= 2); + + const secondCall = fetchMock.mock.calls[1]; + expect(secondCall?.[1]?.headers).toMatchObject({ + Authorization: "Bearer updated", + "X-Trace": "123", + }); + }); + + it("passes configured headers to HttpAgent runs", async () => { + const recorded: Array> = []; + + class RecordingHttpAgent extends HttpAgent { + constructor() { + super({ url: "https://runtime.example" }); + } + + async connectAgent(...args: Parameters) { + recorded.push({ ...this.headers }); + return Promise.resolve({ newMessages: [] }) as ReturnType; + } + + async runAgent(...args: Parameters) { + recorded.push({ ...this.headers }); + return Promise.resolve({ newMessages: [] }) as ReturnType; + } + } + + const agent = new RecordingHttpAgent(); + + const core = new CopilotKitCore({ + runtimeUrl: undefined, + headers: { Authorization: "Bearer cfg", "X-Team": "angular" }, + agents__unsafe_dev_only: { default: agent }, + }); + + await agent.runAgent(); + await core.connectAgent({ agent }); + await core.runAgent({ agent }); + + expect(recorded).toHaveLength(3); + for (const headers of recorded) { + expect(headers).toMatchObject({ + Authorization: "Bearer cfg", + "X-Team": "angular", + }); + } + }); + + it("applies updated headers to existing HttpAgent instances", () => { + const agent = new HttpAgent({ url: "https://runtime.example" }); + + const core = new CopilotKitCore({ + runtimeUrl: undefined, + headers: { Authorization: "Bearer cfg" }, + agents__unsafe_dev_only: { default: agent }, + }); + + expect(agent.headers).toMatchObject({ + Authorization: "Bearer cfg", + }); + + core.setHeaders({ + Authorization: "Bearer updated", + "X-Trace": "123", + }); + + expect(agent.headers).toMatchObject({ + Authorization: "Bearer updated", + "X-Trace": "123", + }); + }); + + it("applies headers to agents provided via setAgents", () => { + const originalAgent = new HttpAgent({ url: "https://runtime.example/original" }); + const replacementAgent = new HttpAgent({ + url: "https://runtime.example/replacement", + }); + + const core = new CopilotKitCore({ + runtimeUrl: undefined, + headers: { Authorization: "Bearer cfg" }, + agents__unsafe_dev_only: { original: originalAgent }, + }); + + expect(originalAgent.headers).toMatchObject({ + Authorization: "Bearer cfg", + }); + + core.setAgents__unsafe_dev_only({ replacement: replacementAgent }); + + expect(replacementAgent.headers).toMatchObject({ + Authorization: "Bearer cfg", + }); + }); + + it("applies headers when agents are added dynamically", () => { + const core = new CopilotKitCore({ + runtimeUrl: undefined, + headers: { Authorization: "Bearer cfg" }, + }); + + const addedAgent = new HttpAgent({ url: "https://runtime.example/new" }); + + core.setAgents__unsafe_dev_only({ added: addedAgent }); + + expect(addedAgent.headers).toMatchObject({ + Authorization: "Bearer cfg", + }); + }); + + it("uses the latest headers when running HttpAgent instances", async () => { + const recorded: Array> = []; + + class RecordingHttpAgent extends HttpAgent { + constructor() { + super({ url: "https://runtime.example" }); + } + + async runAgent(...args: Parameters) { + recorded.push({ ...this.headers }); + return Promise.resolve({ newMessages: [] }) as ReturnType; + } + } + + const agent = new RecordingHttpAgent(); + + const core = new CopilotKitCore({ + runtimeUrl: undefined, + headers: { Authorization: "Bearer initial" }, + agents__unsafe_dev_only: { default: agent }, + }); + + await core.runAgent({ agent }); + + core.setHeaders({ Authorization: "Bearer updated", "X-Trace": "123" }); + + await core.runAgent({ agent }); + + expect(recorded).toHaveLength(2); + expect(recorded[0]).toMatchObject({ Authorization: "Bearer initial" }); + expect(recorded[1]).toMatchObject({ + Authorization: "Bearer updated", + "X-Trace": "123", + }); + }); + + it("applies headers to remote agents fetched from runtime info", async () => { + const fetchMock = vi.fn().mockResolvedValue({ + json: vi.fn().mockResolvedValue({ + version: "1.0.0", + agents: { + remote: { + name: "Remote Agent", + className: "RemoteClass", + description: "Remote description", + }, + }, + }), + }); + global.fetch = fetchMock as unknown as typeof fetch; + + const core = new CopilotKitCore({ + runtimeUrl: "https://runtime.example", + headers: { Authorization: "Bearer cfg", "X-Team": "angular" }, + }); + + await waitForCondition(() => core.getAgent("remote") !== undefined); + + const remoteAgent = core.getAgent("remote") as HttpAgent | undefined; + expect(remoteAgent).toBeDefined(); + expect(remoteAgent?.headers).toMatchObject({ + Authorization: "Bearer cfg", + "X-Team": "angular", + }); + + core.setHeaders({ Authorization: "Bearer updated" }); + + expect(remoteAgent?.headers).toMatchObject({ + Authorization: "Bearer updated", + }); + + expect(fetchMock).toHaveBeenCalledWith( + "https://runtime.example/info", + expect.objectContaining({ + headers: expect.objectContaining({ + Authorization: "Bearer cfg", + "X-Team": "angular", + }), + }) + ); + }); +}); diff --git a/src/v2.x/packages/core/src/__tests__/core-simple.test.ts b/src/v2.x/packages/core/src/__tests__/core-simple.test.ts new file mode 100644 index 0000000000..1bf0dd66bb --- /dev/null +++ b/src/v2.x/packages/core/src/__tests__/core-simple.test.ts @@ -0,0 +1,25 @@ +import { describe, it, expect, beforeEach, vi } from "vitest"; +import { CopilotKitCore } from "../core"; +import { MockAgent, createMessage, createAssistantMessage } from "./test-utils"; + +describe("CopilotKitCore.runAgent Simple", () => { + let copilotKitCore: CopilotKitCore; + + beforeEach(() => { + copilotKitCore = new CopilotKitCore({}); + }); + + it("should run agent without tools", async () => { + const messages = [ + createMessage({ content: "Hello" }), + createAssistantMessage({ content: "Hi there!" }), + ]; + const agent = new MockAgent({ newMessages: messages }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + const result = await copilotKitCore.runAgent({ agent: agent as any }); + + expect(result.newMessages).toEqual(messages); + expect(agent.runAgentCalls).toHaveLength(1); + }); +}); \ No newline at end of file diff --git a/src/v2.x/packages/core/src/__tests__/core-suggestions-e2e.test.ts b/src/v2.x/packages/core/src/__tests__/core-suggestions-e2e.test.ts new file mode 100644 index 0000000000..ee70c12cb6 --- /dev/null +++ b/src/v2.x/packages/core/src/__tests__/core-suggestions-e2e.test.ts @@ -0,0 +1,1482 @@ +import { describe, it, expect, beforeEach, vi } from "vitest"; +import { CopilotKitCore } from "../core"; +import { Suggestion } from "../types"; +import { MockAgent, createSuggestionsConfig, createMessage, createAssistantMessage } from "./test-utils"; + +describe("CopilotKitCore - Suggestions E2E", () => { + let copilotKitCore: CopilotKitCore; + + beforeEach(() => { + copilotKitCore = new CopilotKitCore({}); + }); + + describe("Basic suggestion generation flow", () => { + it("should generate suggestions by calling copilotkitSuggest tool", async () => { + // Setup provider and consumer agents + const providerAgent = new MockAgent({ agentId: "default" }); + const consumerAgent = new MockAgent({ + agentId: "consumer", + messages: [createMessage({ content: "User asked something" })], + }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + // Setup suggestions config + const config = createSuggestionsConfig({ + instructions: "Suggest helpful actions", + minSuggestions: 2, + maxSuggestions: 3, + consumerAgentId: "consumer", + }); + copilotKitCore.addSuggestionsConfig(config); + + // Mock the provider agent to return suggestion tool call + // Note: arguments should be an array of strings for streaming support + // Each string is a chunk that can be partial JSON + const suggestionToolCall = createAssistantMessage({ + content: "", + toolCalls: [ + { + id: "suggest-1", + type: "function", + function: { + name: "copilotkitSuggest", + arguments: [ + '{"suggestions":[', + '{"title":"Action 1","message":"Do action 1"}', + ',{"title":"Action 2","message":"Do action 2"}', + "]}", + ] as any, + }, + }, + ], + } as any); + + providerAgent.setNewMessages([suggestionToolCall]); + + // Track subscriber calls + const onSuggestionsChanged = vi.fn(); + copilotKitCore.subscribe({ onSuggestionsChanged }); + + // Trigger suggestion generation + copilotKitCore.reloadSuggestions("consumer"); + + // Wait for suggestions to be generated + await vi.waitFor(() => { + const result = copilotKitCore.getSuggestions("consumer"); + expect(result.suggestions.length).toBeGreaterThan(0); + }); + + // Verify suggestions were generated + const result = copilotKitCore.getSuggestions("consumer"); + expect(result.suggestions).toHaveLength(2); + expect(result.suggestions[0]).toEqual({ title: "Action 1", message: "Do action 1", isLoading: false }); + expect(result.suggestions[1]).toEqual({ title: "Action 2", message: "Do action 2", isLoading: false }); + + // Verify subscriber was notified + await vi.waitFor(() => { + expect(onSuggestionsChanged).toHaveBeenCalled(); + }); + }); + + it("should include instructions and constraints in suggestion prompt", async () => { + const providerAgent = new MockAgent({ agentId: "default" }); + const consumerAgent = new MockAgent({ agentId: "consumer" }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const config = createSuggestionsConfig({ + instructions: "Focus on data analysis tasks", + minSuggestions: 2, + maxSuggestions: 4, + }); + copilotKitCore.addSuggestionsConfig(config); + + // Mock a response to trigger completion + providerAgent.setNewMessages([ + createAssistantMessage({ + toolCalls: [ + { + id: "s1", + type: "function", + function: { + name: "copilotkitSuggest", + arguments: ['{"suggestions":[{"title":"Test","message":"Test"}]}'] as any, + }, + }, + ], + }), + ]); + + copilotKitCore.reloadSuggestions("consumer"); + + // Check that the cloned agent received the correct prompt + await vi.waitFor(() => { + expect(providerAgent.addMessage).toHaveBeenCalled(); + }); + + const addMessageCalls = providerAgent.addMessage.mock.calls; + expect(addMessageCalls.length).toBeGreaterThan(0); + + const promptMessage = addMessageCalls[0][0]; + expect(promptMessage.role).toBe("user"); + expect(promptMessage.content).toContain("copilotkitSuggest"); + expect(promptMessage.content).toContain("at least 2"); + expect(promptMessage.content).toContain("at most 4"); + expect(promptMessage.content).toContain("Focus on data analysis tasks"); + }); + + it("should force toolChoice to copilotkitSuggest", async () => { + const providerAgent = new MockAgent({ agentId: "default" }); + const consumerAgent = new MockAgent({ agentId: "consumer" }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const config = createSuggestionsConfig(); + copilotKitCore.addSuggestionsConfig(config); + + // Mock a response to trigger completion + providerAgent.setNewMessages([ + createAssistantMessage({ + toolCalls: [ + { + id: "s1", + type: "function", + function: { + name: "copilotkitSuggest", + arguments: ['{"suggestions":[{"title":"Test","message":"Test"}]}'] as any, + }, + }, + ], + }), + ]); + + copilotKitCore.reloadSuggestions("consumer"); + + // Verify runAgent was called with forced tool choice + await vi.waitFor(() => { + expect(providerAgent.runAgentCalls.length).toBeGreaterThan(0); + }); + + const runAgentCall = providerAgent.runAgentCalls[0]; + expect(runAgentCall.forwardedProps.toolChoice).toEqual({ + type: "function", + function: { name: "copilotkitSuggest" }, + }); + }); + }); + + describe("Agent ID filtering patterns", () => { + it("should apply suggestions to specific consumer agent ID", async () => { + const providerAgent = new MockAgent({ agentId: "default" }); + const targetAgent = new MockAgent({ agentId: "target" }); + const otherAgent = new MockAgent({ agentId: "other" }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "target", agent: targetAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "other", agent: otherAgent as any }); + + const config = createSuggestionsConfig({ + consumerAgentId: "target", + }); + copilotKitCore.addSuggestionsConfig(config); + + // Mock a response to trigger completion + providerAgent.setNewMessages([ + createAssistantMessage({ + toolCalls: [ + { + id: "s1", + type: "function", + function: { + name: "copilotkitSuggest", + arguments: ['{"suggestions":[{"title":"Test","message":"Test"}]}'] as any, + }, + }, + ], + }), + ]); + + // Reload for target agent - should generate + copilotKitCore.reloadSuggestions("target"); + + await vi.waitFor(() => { + expect(providerAgent.runAgentCalls.length).toBeGreaterThan(0); + }); + + // Reload for other agent - should NOT generate + providerAgent.runAgentCalls = []; + copilotKitCore.reloadSuggestions("other"); + + // Give it a moment + await new Promise((resolve) => setTimeout(resolve, 50)); + + expect(providerAgent.runAgentCalls.length).toBe(0); + }); + + it("should apply suggestions to all agents when consumer ID is *", async () => { + const providerAgent = new MockAgent({ agentId: "default" }); + const agent1 = new MockAgent({ agentId: "agent1" }); + const agent2 = new MockAgent({ agentId: "agent2" }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "agent1", agent: agent1 as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "agent2", agent: agent2 as any }); + + const config = createSuggestionsConfig({ + consumerAgentId: "*", + }); + copilotKitCore.addSuggestionsConfig(config); + + // Mock a response to trigger completion + providerAgent.setNewMessages([ + createAssistantMessage({ + toolCalls: [ + { + id: "s1", + type: "function", + function: { + name: "copilotkitSuggest", + arguments: ['{"suggestions":[{"title":"Test","message":"Test"}]}'] as any, + }, + }, + ], + }), + ]); + + // Should generate for both agents + copilotKitCore.reloadSuggestions("agent1"); + await vi.waitFor(() => { + expect(providerAgent.runAgentCalls.length).toBeGreaterThan(0); + }); + + const callCountAfterFirst = providerAgent.runAgentCalls.length; + + copilotKitCore.reloadSuggestions("agent2"); + await vi.waitFor(() => { + expect(providerAgent.runAgentCalls.length).toBeGreaterThan(callCountAfterFirst); + }); + }); + + it("should apply suggestions when consumer ID is undefined", async () => { + const providerAgent = new MockAgent({ agentId: "default" }); + const consumerAgent = new MockAgent({ agentId: "consumer" }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const config = createSuggestionsConfig({ + consumerAgentId: undefined, + }); + copilotKitCore.addSuggestionsConfig(config); + + // Mock a response to trigger completion + providerAgent.setNewMessages([ + createAssistantMessage({ + toolCalls: [ + { + id: "s1", + type: "function", + function: { + name: "copilotkitSuggest", + arguments: ['{"suggestions":[{"title":"Test","message":"Test"}]}'] as any, + }, + }, + ], + }), + ]); + + copilotKitCore.reloadSuggestions("consumer"); + + await vi.waitFor(() => { + expect(providerAgent.runAgentCalls.length).toBeGreaterThan(0); + }); + }); + }); + + describe("Streaming and partial JSON suggestions", () => { + it("should handle streaming suggestions with incomplete JSON", async () => { + const providerAgent = new MockAgent({ agentId: "default" }); + const consumerAgent = new MockAgent({ agentId: "consumer" }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const config = createSuggestionsConfig(); + copilotKitCore.addSuggestionsConfig(config); + + // Track subscriber calls to see streaming updates + const suggestionUpdates: Suggestion[][] = []; + copilotKitCore.subscribe({ + onSuggestionsChanged: ({ suggestions }) => { + suggestionUpdates.push([...suggestions]); + }, + }); + + // Simulate streaming tool call with partial JSON + const partialToolCall = createAssistantMessage({ + content: "", + toolCalls: [ + { + id: "stream-1", + type: "function", + function: { + name: "copilotkitSuggest", + // Incomplete JSON - missing closing bracket + arguments: ['{"suggestions":[{"title":"First","message":"First action"}'] as any, + }, + }, + ], + }); + + providerAgent.setNewMessages([partialToolCall]); + copilotKitCore.reloadSuggestions("consumer"); + + await vi.waitFor(() => { + expect(suggestionUpdates.length).toBeGreaterThanOrEqual(2); + }); + + // Find the update with suggestions (skip empty initial updates) + const streamingUpdate = suggestionUpdates.find(update => update.length > 0 && update[0].isLoading === true); + expect(streamingUpdate).toBeDefined(); + expect(streamingUpdate![0]).toMatchObject({ + title: "First", + message: "First action", + isLoading: true, + }); + + // After finalization, isLoading should be false + await vi.waitFor(() => { + const lastUpdate = suggestionUpdates[suggestionUpdates.length - 1]; + expect(lastUpdate.length).toBeGreaterThan(0); + expect(lastUpdate[0].isLoading).toBe(false); + }); + + const finalUpdate = suggestionUpdates[suggestionUpdates.length - 1]; + expect(finalUpdate[0]).toMatchObject({ + title: "First", + message: "First action", + isLoading: false, + }); + }); + + it("should update suggestions as more JSON chunks arrive", async () => { + const providerAgent = new MockAgent({ agentId: "default" }); + const consumerAgent = new MockAgent({ agentId: "consumer" }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const config = createSuggestionsConfig(); + copilotKitCore.addSuggestionsConfig(config); + + const suggestionUpdates: number[] = []; + copilotKitCore.subscribe({ + onSuggestionsChanged: ({ suggestions }) => { + suggestionUpdates.push(suggestions.length); + }, + }); + + // Simulate multiple chunks arriving + const streamingToolCall = createAssistantMessage({ + content: "", + toolCalls: [ + { + id: "stream-2", + type: "function", + function: { + name: "copilotkitSuggest", + arguments: [ + '{"suggestions":[', + '{"title":"First","message":"msg1"},', + '{"title":"Second","message":"msg2"}', + "]}", + ] as any, + }, + }, + ], + }); + + providerAgent.setNewMessages([streamingToolCall]); + copilotKitCore.reloadSuggestions("consumer"); + + await vi.waitFor(() => { + const result = copilotKitCore.getSuggestions("consumer"); + expect(result.suggestions.length).toBe(2); + }); + + const result = copilotKitCore.getSuggestions("consumer"); + expect(result.suggestions).toHaveLength(2); + expect(result.suggestions[0].title).toBe("First"); + expect(result.suggestions[1].title).toBe("Second"); + }); + }); + + describe("Multiple configs and concurrent generation", () => { + it("should generate suggestions for multiple configs", async () => { + const providerAgent = new MockAgent({ agentId: "default" }); + const consumerAgent = new MockAgent({ agentId: "consumer" }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + // Add two different suggestion configs + const config1 = createSuggestionsConfig({ + instructions: "Suggest actions", + }); + const config2 = createSuggestionsConfig({ + instructions: "Suggest questions", + }); + + copilotKitCore.addSuggestionsConfig(config1); + copilotKitCore.addSuggestionsConfig(config2); + + // Mock provider to return different suggestions + const toolCall1 = createAssistantMessage({ + content: "", + toolCalls: [ + { + id: "suggest-1", + type: "function", + function: { + name: "copilotkitSuggest", + arguments: ['{"suggestions":[{"title":"Action","message":"Do action"}]}'] as any, + }, + }, + ], + }); + + providerAgent.setNewMessages([toolCall1]); + + copilotKitCore.reloadSuggestions("consumer"); + + // Should have called runAgent twice (once per config) + await vi.waitFor(() => { + expect(providerAgent.runAgentCalls.length).toBeGreaterThanOrEqual(2); + }); + }); + + it("should handle concurrent suggestion generation for different agents", async () => { + const providerAgent = new MockAgent({ agentId: "default" }); + const agent1 = new MockAgent({ agentId: "agent1" }); + const agent2 = new MockAgent({ agentId: "agent2" }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "agent1", agent: agent1 as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "agent2", agent: agent2 as any }); + + const config = createSuggestionsConfig({ + consumerAgentId: "*", + }); + copilotKitCore.addSuggestionsConfig(config); + + const toolCall = createAssistantMessage({ + content: "", + toolCalls: [ + { + id: "suggest-x", + type: "function", + function: { + name: "copilotkitSuggest", + arguments: ['{"suggestions":[{"title":"Test","message":"Test msg"}]}'] as any, + }, + }, + ], + }); + + providerAgent.setNewMessages([toolCall]); + + // Trigger both concurrently + copilotKitCore.reloadSuggestions("agent1"); + copilotKitCore.reloadSuggestions("agent2"); + + // Both should have suggestions + await vi.waitFor(() => { + const result1 = copilotKitCore.getSuggestions("agent1"); + const result2 = copilotKitCore.getSuggestions("agent2"); + expect(result1.suggestions.length).toBeGreaterThan(0); + expect(result2.suggestions.length).toBeGreaterThan(0); + }); + }); + + it("should generate independent suggestion sets for same agent", async () => { + const providerAgent = new MockAgent({ agentId: "default" }); + const consumerAgent = new MockAgent({ agentId: "consumer" }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const config1 = createSuggestionsConfig({ instructions: "Type 1" }); + const config2 = createSuggestionsConfig({ instructions: "Type 2" }); + + copilotKitCore.addSuggestionsConfig(config1); + copilotKitCore.addSuggestionsConfig(config2); + + // Set messages that will be returned for both configs + providerAgent.setNewMessages([ + createAssistantMessage({ + toolCalls: [ + { + id: "s1", + type: "function", + function: { + name: "copilotkitSuggest", + arguments: ['{"suggestions":[{"title":"Suggestion 1","message":"msg1"}]}'] as any, + }, + }, + ], + }), + ]); + + copilotKitCore.reloadSuggestions("consumer"); + + // Wait for both configs to be called + await vi.waitFor(() => { + expect(providerAgent.runAgentCalls.length).toBeGreaterThanOrEqual(2); + }); + + // Both config invocations should have generated suggestions + // Since they share the same response, we should have at least 1 suggestion + // (Could be 2 if both succeeded, but timing might cause only one) + const result = copilotKitCore.getSuggestions("consumer"); + expect(result.suggestions.length).toBeGreaterThanOrEqual(1); + }); + }); + + describe("Context and properties forwarding", () => { + it("should forward context to suggestion provider agent", async () => { + const providerAgent = new MockAgent({ agentId: "default" }); + const consumerAgent = new MockAgent({ agentId: "consumer" }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + // Add context + const contextId = copilotKitCore.addContext({ + description: "User preferences", + value: { theme: "dark", language: "en" } as any, + }); + + const config = createSuggestionsConfig(); + copilotKitCore.addSuggestionsConfig(config); + + // Mock a response to trigger completion + providerAgent.setNewMessages([ + createAssistantMessage({ + toolCalls: [ + { + id: "s1", + type: "function", + function: { + name: "copilotkitSuggest", + arguments: ['{"suggestions":[{"title":"Test","message":"Test"}]}'] as any, + }, + }, + ], + }), + ]); + + copilotKitCore.reloadSuggestions("consumer"); + + await vi.waitFor(() => { + expect(providerAgent.runAgentCalls.length).toBeGreaterThan(0); + }); + + const runAgentCall = providerAgent.runAgentCalls[0]; + expect(runAgentCall.context).toEqual([ + { description: "User preferences", value: { theme: "dark", language: "en" } }, + ]); + + // Cleanup + copilotKitCore.removeContext(contextId); + }); + + it("should forward properties to suggestion provider agent", async () => { + const providerAgent = new MockAgent({ agentId: "default" }); + const consumerAgent = new MockAgent({ agentId: "consumer" }); + + const core = new CopilotKitCore({ + properties: { userId: "123", sessionId: "abc" }, + }); + + core.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + core.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const config = createSuggestionsConfig(); + core.addSuggestionsConfig(config); + + // Mock a response to trigger completion + providerAgent.setNewMessages([ + createAssistantMessage({ + toolCalls: [ + { + id: "s1", + type: "function", + function: { + name: "copilotkitSuggest", + arguments: ['{"suggestions":[{"title":"Test","message":"Test"}]}'] as any, + }, + }, + ], + }), + ]); + + core.reloadSuggestions("consumer"); + + await vi.waitFor(() => { + expect(providerAgent.runAgentCalls.length).toBeGreaterThan(0); + }); + + const runAgentCall = providerAgent.runAgentCalls[0]; + expect(runAgentCall.forwardedProps).toMatchObject({ + userId: "123", + sessionId: "abc", + }); + }); + + it("should include available frontend tools in suggestion prompt", async () => { + const providerAgent = new MockAgent({ agentId: "default" }); + const consumerAgent = new MockAgent({ agentId: "consumer" }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + // Add some frontend tools + copilotKitCore.addTool({ + name: "searchTool", + description: "Search for information", + }); + copilotKitCore.addTool({ + name: "analyzeTool", + description: "Analyze data", + }); + + const config = createSuggestionsConfig(); + copilotKitCore.addSuggestionsConfig(config); + + // Mock a response to trigger completion + providerAgent.setNewMessages([ + createAssistantMessage({ + toolCalls: [ + { + id: "s1", + type: "function", + function: { + name: "copilotkitSuggest", + arguments: ['{"suggestions":[{"title":"Test","message":"Test"}]}'] as any, + }, + }, + ], + }), + ]); + + copilotKitCore.reloadSuggestions("consumer"); + + await vi.waitFor(() => { + expect(providerAgent.addMessage).toHaveBeenCalled(); + }); + + const promptMessage = providerAgent.addMessage.mock.calls[0][0]; + expect(promptMessage.content).toContain("searchTool"); + expect(promptMessage.content).toContain("analyzeTool"); + }); + }); + + describe("Agent cloning", () => { + it("should clone provider agent for suggestion generation", async () => { + const providerAgent = new MockAgent({ + agentId: "default", + state: { original: true }, + }); + const consumerAgent = new MockAgent({ agentId: "consumer" }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const config = createSuggestionsConfig(); + copilotKitCore.addSuggestionsConfig(config); + + copilotKitCore.reloadSuggestions("consumer"); + + await vi.waitFor(() => { + expect(providerAgent.clone).toHaveBeenCalled(); + }); + }); + + it("should copy consumer agent messages to suggestion agent", async () => { + const providerAgent = new MockAgent({ agentId: "default" }); + const consumerMessages = [ + createMessage({ content: "User question" }), + createAssistantMessage({ content: "Assistant response" }), + ]; + const consumerAgent = new MockAgent({ + agentId: "consumer", + messages: consumerMessages, + }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const config = createSuggestionsConfig(); + copilotKitCore.addSuggestionsConfig(config); + + copilotKitCore.reloadSuggestions("consumer"); + + // The cloned agent should receive the consumer's message history + await vi.waitFor(() => { + expect(providerAgent.clone).toHaveBeenCalled(); + }); + + // Verify clone was called and messages would be copied + expect(providerAgent.clone).toHaveBeenCalled(); + }); + + it("should copy consumer agent state to suggestion agent", async () => { + const providerAgent = new MockAgent({ agentId: "default" }); + const consumerAgent = new MockAgent({ + agentId: "consumer", + state: { conversationContext: "important data", stepCount: 5 }, + }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const config = createSuggestionsConfig(); + copilotKitCore.addSuggestionsConfig(config); + + copilotKitCore.reloadSuggestions("consumer"); + + await vi.waitFor(() => { + expect(providerAgent.clone).toHaveBeenCalled(); + }); + + // Verify clone preserves state + expect(providerAgent.clone).toHaveBeenCalled(); + }); + + it("should assign unique agentId and threadId to suggestion agent", async () => { + const providerAgent = new MockAgent({ agentId: "default" }); + const consumerAgent = new MockAgent({ agentId: "consumer" }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const config = createSuggestionsConfig(); + copilotKitCore.addSuggestionsConfig(config); + + copilotKitCore.reloadSuggestions("consumer"); + + // The cloned agent should have a unique ID + await vi.waitFor(() => { + expect(providerAgent.clone).toHaveBeenCalled(); + }); + + // Each clone should get a unique suggestion ID + expect(providerAgent.clone).toHaveBeenCalled(); + }); + }); + + describe("Suggestion abortion and reload", () => { + it("should abort running suggestions when a user message is submitted (runAgent)", async () => { + const providerAgent = new MockAgent({ agentId: "default", runAgentDelay: 100 }); + const consumerAgent = new MockAgent({ + agentId: "consumer", + messages: [createMessage({ content: "Initial message" })], + }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const config = createSuggestionsConfig({ consumerAgentId: "consumer" }); + copilotKitCore.addSuggestionsConfig(config); + + // Begin generating suggestions and wait until the provider is cloned (run in progress) + copilotKitCore.reloadSuggestions("consumer"); + await vi.waitFor(() => { + expect(providerAgent.clone).toHaveBeenCalled(); + }); + + // Grab the cloned suggestion agent instance to assert abort + const clonedSuggestionAgent = (providerAgent.clone as any).mock.results[0]?.value; + expect(clonedSuggestionAgent).toBeDefined(); + + // Ensure loading state is on before user submits new message + await vi.waitFor(() => { + const result = copilotKitCore.getSuggestions("consumer"); + expect(result.isLoading).toBe(true); + }); + + // Additionally assert abort happens before the user's run starts + consumerAgent.runAgentCallback = () => { + expect(clonedSuggestionAgent.abortRun).toHaveBeenCalled(); + }; + + // Simulate user submitting a new message which triggers runAgent (and should clear/abort suggestions immediately) + consumerAgent.addMessages([createMessage({ content: "User follow-up" })]); + await copilotKitCore.runAgent({ + agent: consumerAgent as any, + }); + + // The in-flight suggestion run should be aborted + expect(clonedSuggestionAgent.abortRun).toHaveBeenCalled(); + + // Suggestions should be cleared and isLoading turned off + await vi.waitFor(() => { + const result = copilotKitCore.getSuggestions("consumer"); + expect(result.isLoading).toBe(false); + expect(result.suggestions).toEqual([]); + }); + }); + it("should abort running suggestions when clearSuggestions is called", async () => { + const providerAgent = new MockAgent({ agentId: "default" }); + const consumerAgent = new MockAgent({ agentId: "consumer" }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const config = createSuggestionsConfig(); + copilotKitCore.addSuggestionsConfig(config); + + // Mock a response to trigger completion + providerAgent.setNewMessages([ + createAssistantMessage({ + toolCalls: [ + { + id: "s1", + type: "function", + function: { + name: "copilotkitSuggest", + arguments: ['{"suggestions":[{"title":"Test","message":"Test"}]}'] as any, + }, + }, + ], + }), + ]); + + // Start generation + copilotKitCore.reloadSuggestions("consumer"); + + await vi.waitFor(() => { + expect(providerAgent.runAgentCalls.length).toBeGreaterThan(0); + }); + + // Clear should abort + copilotKitCore.clearSuggestions("consumer"); + + // Verify suggestions are cleared + const result = copilotKitCore.getSuggestions("consumer"); + expect(result.suggestions).toEqual([]); + expect(result.isLoading).toBe(false); + }); + + it("should create fresh agents on subsequent reloadSuggestions", async () => { + const providerAgent = new MockAgent({ agentId: "default" }); + const consumerAgent = new MockAgent({ agentId: "consumer" }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const config = createSuggestionsConfig(); + copilotKitCore.addSuggestionsConfig(config); + + // Mock a response to trigger completion + providerAgent.setNewMessages([ + createAssistantMessage({ + toolCalls: [ + { + id: "s1", + type: "function", + function: { + name: "copilotkitSuggest", + arguments: ['{"suggestions":[{"title":"Test","message":"Test"}]}'] as any, + }, + }, + ], + }), + ]); + + // First reload + copilotKitCore.reloadSuggestions("consumer"); + await vi.waitFor(() => { + expect(providerAgent.clone).toHaveBeenCalledTimes(1); + }); + + // Clear + copilotKitCore.clearSuggestions("consumer"); + + // Second reload - should clone again + copilotKitCore.reloadSuggestions("consumer"); + await vi.waitFor(() => { + expect(providerAgent.clone).toHaveBeenCalledTimes(2); + }); + }); + + it("should notify subscribers when suggestions are cleared", async () => { + const providerAgent = new MockAgent({ agentId: "default" }); + const consumerAgent = new MockAgent({ agentId: "consumer" }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const onSuggestionsChanged = vi.fn(); + copilotKitCore.subscribe({ onSuggestionsChanged }); + + const config = createSuggestionsConfig(); + copilotKitCore.addSuggestionsConfig(config); + + // Mock a response to trigger completion + providerAgent.setNewMessages([ + createAssistantMessage({ + toolCalls: [ + { + id: "s1", + type: "function", + function: { + name: "copilotkitSuggest", + arguments: ['{"suggestions":[{"title":"Test","message":"Test"}]}'] as any, + }, + }, + ], + }), + ]); + + copilotKitCore.reloadSuggestions("consumer"); + + await vi.waitFor(() => { + expect(providerAgent.runAgentCalls.length).toBeGreaterThan(0); + }); + + // Clear and verify notification + copilotKitCore.clearSuggestions("consumer"); + + await vi.waitFor(() => { + const clearCall = onSuggestionsChanged.mock.calls.find( + (call) => call[0].suggestions.length === 0 && call[0].agentId === "consumer", + ); + expect(clearCall).toBeDefined(); + }); + }); + }); + + describe("Error handling during suggestion generation", () => { + it("should handle error when suggestion generation fails", async () => { + const providerAgent = new MockAgent({ + agentId: "default", + error: new Error("Generation failed"), + }); + const consumerAgent = new MockAgent({ agentId: "consumer" }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const config = createSuggestionsConfig(); + copilotKitCore.addSuggestionsConfig(config); + + // Should not throw, error should be caught internally + copilotKitCore.reloadSuggestions("consumer"); + + // Give it a moment + await new Promise((resolve) => setTimeout(resolve, 100)); + + // Suggestions should be empty due to error + const result = copilotKitCore.getSuggestions("consumer"); + expect(result.suggestions).toEqual([]); + expect(result.isLoading).toBe(false); + }); + + it("should handle malformed suggestion tool call", async () => { + const providerAgent = new MockAgent({ agentId: "default" }); + const consumerAgent = new MockAgent({ agentId: "consumer" }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + // Return tool call without proper structure + const malformedToolCall = createAssistantMessage({ + content: "", + toolCalls: [ + { + id: "malformed", + type: "function", + function: { + name: "copilotkitSuggest", + arguments: "not valid json", + }, + }, + ], + }); + + providerAgent.setNewMessages([malformedToolCall]); + + const config = createSuggestionsConfig(); + copilotKitCore.addSuggestionsConfig(config); + + copilotKitCore.reloadSuggestions("consumer"); + + // Should not crash + await new Promise((resolve) => setTimeout(resolve, 100)); + + // Suggestions should be empty or handle gracefully + const result = copilotKitCore.getSuggestions("consumer"); + expect(Array.isArray(result.suggestions)).toBe(true); + expect(typeof result.isLoading).toBe("boolean"); + }); + }); + + describe("Loading state E2E", () => { + it("should track isLoading state during suggestion generation lifecycle", async () => { + const providerAgent = new MockAgent({ agentId: "default", runAgentDelay: 50 }); + const consumerAgent = new MockAgent({ agentId: "consumer" }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const config = createSuggestionsConfig(); + copilotKitCore.addSuggestionsConfig(config); + + // Mock a response + providerAgent.setNewMessages([ + createAssistantMessage({ + toolCalls: [ + { + id: "s1", + type: "function", + function: { + name: "copilotkitSuggest", + arguments: ['{"suggestions":[{"title":"Test","message":"Test"}]}'] as any, + }, + }, + ], + }), + ]); + + // Before triggering, isLoading should be false + let result = copilotKitCore.getSuggestions("consumer"); + expect(result.isLoading).toBe(false); + + // Trigger suggestions + copilotKitCore.reloadSuggestions("consumer"); + + // During generation, isLoading should be true + await vi.waitFor(() => { + result = copilotKitCore.getSuggestions("consumer"); + expect(result.isLoading).toBe(true); + }); + + // After generation completes, isLoading should be false + await vi.waitFor(() => { + result = copilotKitCore.getSuggestions("consumer"); + expect(result.isLoading).toBe(false); + }); + }); + + it("should emit loading start and end events in correct order", async () => { + const providerAgent = new MockAgent({ agentId: "default" }); + const consumerAgent = new MockAgent({ agentId: "consumer" }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const events: string[] = []; + copilotKitCore.subscribe({ + onSuggestionsStartedLoading: () => { + events.push("start"); + }, + onSuggestionsFinishedLoading: () => { + events.push("end"); + }, + onSuggestionsChanged: () => { + events.push("changed"); + }, + }); + + const config = createSuggestionsConfig(); + copilotKitCore.addSuggestionsConfig(config); + + providerAgent.setNewMessages([ + createAssistantMessage({ + toolCalls: [ + { + id: "s1", + type: "function", + function: { + name: "copilotkitSuggest", + arguments: ['{"suggestions":[{"title":"Test","message":"Test"}]}'] as any, + }, + }, + ], + }), + ]); + + copilotKitCore.reloadSuggestions("consumer"); + + await vi.waitFor(() => { + expect(events).toContain("start"); + expect(events).toContain("end"); + }); + + // Start should come before end + const startIndex = events.indexOf("start"); + const endIndex = events.lastIndexOf("end"); + expect(startIndex).toBeLessThan(endIndex); + }); + + it("should handle isLoading with multiple concurrent configs", async () => { + const providerAgent = new MockAgent({ agentId: "default", runAgentDelay: 50 }); + const consumerAgent = new MockAgent({ agentId: "consumer" }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const config1 = createSuggestionsConfig({ instructions: "First" }); + const config2 = createSuggestionsConfig({ instructions: "Second" }); + copilotKitCore.addSuggestionsConfig(config1); + copilotKitCore.addSuggestionsConfig(config2); + + providerAgent.setNewMessages([ + createAssistantMessage({ + toolCalls: [ + { + id: "s1", + type: "function", + function: { + name: "copilotkitSuggest", + arguments: ['{"suggestions":[{"title":"Test","message":"Test"}]}'] as any, + }, + }, + ], + }), + ]); + + copilotKitCore.reloadSuggestions("consumer"); + + // During generation, isLoading should be true + await vi.waitFor(() => { + const result = copilotKitCore.getSuggestions("consumer"); + expect(result.isLoading).toBe(true); + }); + + // After all complete, isLoading should be false + await vi.waitFor(() => { + const result = copilotKitCore.getSuggestions("consumer"); + expect(result.isLoading).toBe(false); + }); + }); + + it("should set isLoading to false after errors", async () => { + const providerAgent = new MockAgent({ + agentId: "default", + error: new Error("Generation failed"), + }); + const consumerAgent = new MockAgent({ agentId: "consumer" }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const config = createSuggestionsConfig(); + copilotKitCore.addSuggestionsConfig(config); + + copilotKitCore.reloadSuggestions("consumer"); + + // After error, isLoading should be false + await vi.waitFor(() => { + const result = copilotKitCore.getSuggestions("consumer"); + expect(result.isLoading).toBe(false); + }); + }); + }); + + describe("SuggestionAvailability - Dynamic Suggestions", () => { + it("should show dynamic suggestions with 'before-first-message' only when messages are empty", async () => { + const providerAgent = new MockAgent({ agentId: "provider" }); + const consumerAgent = new MockAgent({ agentId: "consumer", messages: [] }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "provider", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const config = createSuggestionsConfig({ + instructions: "Suggest actions", + providerAgentId: "provider", + consumerAgentId: "consumer", + available: "before-first-message", + }); + copilotKitCore.addSuggestionsConfig(config); + + providerAgent.setNewMessages([ + createAssistantMessage({ + toolCalls: [ + { + id: "tc-1", + type: "function", + function: { + name: "copilotkitSuggest", + arguments: ['{"suggestions":[{"title":"Start","message":"Get started"}]}'] as any, + }, + }, + ], + } as any), + ]); + + // Should show suggestions when messages are empty + copilotKitCore.reloadSuggestions("consumer"); + await vi.waitFor(() => { + const result = copilotKitCore.getSuggestions("consumer"); + expect(result.suggestions.length).toBeGreaterThan(0); + }); + + const result1 = copilotKitCore.getSuggestions("consumer"); + expect(result1.suggestions).toHaveLength(1); + expect(result1.suggestions[0]?.title).toBe("Start"); + + // Add a message and reload - should not show suggestions + consumerAgent.messages = [createMessage({ content: "First message" })]; + copilotKitCore.reloadSuggestions("consumer"); + + const result2 = copilotKitCore.getSuggestions("consumer"); + expect(result2.suggestions).toHaveLength(0); + }); + + it("should show dynamic suggestions with 'after-first-message' only when messages exist", async () => { + const providerAgent = new MockAgent({ agentId: "provider" }); + const consumerAgent = new MockAgent({ agentId: "consumer", messages: [] }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "provider", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const config = createSuggestionsConfig({ + instructions: "Suggest next actions", + providerAgentId: "provider", + consumerAgentId: "consumer", + available: "after-first-message", + }); + copilotKitCore.addSuggestionsConfig(config); + + providerAgent.setNewMessages([ + createAssistantMessage({ + toolCalls: [ + { + id: "tc-2", + type: "function", + function: { + name: "copilotkitSuggest", + arguments: ['{"suggestions":[{"title":"Continue","message":"Keep going"}]}'] as any, + }, + }, + ], + } as any), + ]); + + // Should not show suggestions when messages are empty + copilotKitCore.reloadSuggestions("consumer"); + const result1 = copilotKitCore.getSuggestions("consumer"); + expect(result1.suggestions).toHaveLength(0); + + // Add a message and reload - should show suggestions + consumerAgent.messages = [createMessage({ content: "First message" })]; + copilotKitCore.reloadSuggestions("consumer"); + + await vi.waitFor(() => { + const result = copilotKitCore.getSuggestions("consumer"); + expect(result.suggestions.length).toBeGreaterThan(0); + }); + + const result2 = copilotKitCore.getSuggestions("consumer"); + expect(result2.suggestions).toHaveLength(1); + expect(result2.suggestions[0]?.title).toBe("Continue"); + }); + + it("should show dynamic suggestions with 'always' regardless of message count", async () => { + const providerAgent = new MockAgent({ agentId: "provider" }); + const consumerAgent = new MockAgent({ agentId: "consumer", messages: [] }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "provider", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const config = createSuggestionsConfig({ + instructions: "Always suggest", + providerAgentId: "provider", + consumerAgentId: "consumer", + available: "always", + }); + copilotKitCore.addSuggestionsConfig(config); + + providerAgent.setNewMessages([ + createAssistantMessage({ + toolCalls: [ + { + id: "tc-3", + type: "function", + function: { + name: "copilotkitSuggest", + arguments: ['{"suggestions":[{"title":"Always","message":"Always available"}]}'] as any, + }, + }, + ], + } as any), + ]); + + // Should show when empty + copilotKitCore.reloadSuggestions("consumer"); + await vi.waitFor(() => { + const result = copilotKitCore.getSuggestions("consumer"); + expect(result.suggestions.length).toBeGreaterThan(0); + }); + + const result1 = copilotKitCore.getSuggestions("consumer"); + expect(result1.suggestions[0]?.title).toBe("Always"); + + // Should also show when messages exist + consumerAgent.messages = [createMessage({ content: "Message" })]; + copilotKitCore.reloadSuggestions("consumer"); + + await vi.waitFor(() => { + const result = copilotKitCore.getSuggestions("consumer"); + expect(result.suggestions.length).toBeGreaterThan(0); + }); + + const result2 = copilotKitCore.getSuggestions("consumer"); + expect(result2.suggestions[0]?.title).toBe("Always"); + }); + + it("should not show dynamic suggestions with 'disabled'", async () => { + const providerAgent = new MockAgent({ agentId: "provider" }); + const consumerAgent = new MockAgent({ agentId: "consumer", messages: [] }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "provider", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const config = createSuggestionsConfig({ + instructions: "Should not appear", + providerAgentId: "provider", + consumerAgentId: "consumer", + available: "disabled", + }); + copilotKitCore.addSuggestionsConfig(config); + + // Should not show regardless of message count + copilotKitCore.reloadSuggestions("consumer"); + const result1 = copilotKitCore.getSuggestions("consumer"); + expect(result1.suggestions).toHaveLength(0); + + consumerAgent.messages = [createMessage({ content: "Message" })]; + copilotKitCore.reloadSuggestions("consumer"); + const result2 = copilotKitCore.getSuggestions("consumer"); + expect(result2.suggestions).toHaveLength(0); + }); + + }); + + describe("SuggestionAvailability - Static Suggestions", () => { + it("should show static suggestions with 'before-first-message' only when messages are empty", () => { + const consumerAgent = new MockAgent({ agentId: "consumer", messages: [] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + copilotKitCore.addSuggestionsConfig({ + suggestions: [ + { title: "Start Here", message: "Begin your journey", isLoading: false }, + { title: "Learn More", message: "Get information", isLoading: false }, + ], + consumerAgentId: "consumer", + available: "before-first-message", + }); + + // Should show when empty + copilotKitCore.reloadSuggestions("consumer"); + const result1 = copilotKitCore.getSuggestions("consumer"); + expect(result1.suggestions).toHaveLength(2); + expect(result1.suggestions[0]?.title).toBe("Start Here"); + expect(result1.isLoading).toBe(false); + + // Should not show when messages exist + consumerAgent.messages = [createMessage({ content: "Message" })]; + copilotKitCore.reloadSuggestions("consumer"); + const result2 = copilotKitCore.getSuggestions("consumer"); + expect(result2.suggestions).toHaveLength(0); + }); + + it("should show static suggestions with 'after-first-message' only when messages exist", () => { + const consumerAgent = new MockAgent({ agentId: "consumer", messages: [] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + copilotKitCore.addSuggestionsConfig({ + suggestions: [{ title: "Next Step", message: "Continue", isLoading: false }], + consumerAgentId: "consumer", + available: "after-first-message", + }); + + // Should not show when empty + copilotKitCore.reloadSuggestions("consumer"); + const result1 = copilotKitCore.getSuggestions("consumer"); + expect(result1.suggestions).toHaveLength(0); + + // Should show when messages exist + consumerAgent.messages = [createMessage({ content: "Message" })]; + copilotKitCore.reloadSuggestions("consumer"); + const result2 = copilotKitCore.getSuggestions("consumer"); + expect(result2.suggestions).toHaveLength(1); + expect(result2.suggestions[0]?.title).toBe("Next Step"); + }); + + it("should show static suggestions with 'always' regardless of message count", () => { + const consumerAgent = new MockAgent({ agentId: "consumer", messages: [] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + copilotKitCore.addSuggestionsConfig({ + suggestions: [{ title: "Persistent", message: "Always here", isLoading: false }], + consumerAgentId: "consumer", + available: "always", + }); + + // Should show when empty + copilotKitCore.reloadSuggestions("consumer"); + const result1 = copilotKitCore.getSuggestions("consumer"); + expect(result1.suggestions).toHaveLength(1); + + // Should show when messages exist + consumerAgent.messages = [createMessage({ content: "Message" })]; + copilotKitCore.reloadSuggestions("consumer"); + const result2 = copilotKitCore.getSuggestions("consumer"); + expect(result2.suggestions).toHaveLength(1); + }); + + it("should not show static suggestions with 'disabled'", () => { + const consumerAgent = new MockAgent({ agentId: "consumer", messages: [] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + copilotKitCore.addSuggestionsConfig({ + suggestions: [{ title: "Hidden", message: "Should not appear", isLoading: false }], + consumerAgentId: "consumer", + available: "disabled", + }); + + copilotKitCore.reloadSuggestions("consumer"); + const result = copilotKitCore.getSuggestions("consumer"); + expect(result.suggestions).toHaveLength(0); + }); + + it("should default to 'before-first-message' for static suggestions when availability not specified", () => { + const consumerAgent = new MockAgent({ agentId: "consumer", messages: [] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + copilotKitCore.addSuggestionsConfig({ + suggestions: [{ title: "Default Static", message: "Default behavior", isLoading: false }], + consumerAgentId: "consumer", + // No 'available' specified + }); + + // Should show when empty (default: before-first-message) + copilotKitCore.reloadSuggestions("consumer"); + const result1 = copilotKitCore.getSuggestions("consumer"); + expect(result1.suggestions).toHaveLength(1); + + // Should not show when messages exist + consumerAgent.messages = [createMessage({ content: "Message" })]; + copilotKitCore.reloadSuggestions("consumer"); + const result2 = copilotKitCore.getSuggestions("consumer"); + expect(result2.suggestions).toHaveLength(0); + }); + }); +}); diff --git a/src/v2.x/packages/core/src/__tests__/core-suggestions.test.ts b/src/v2.x/packages/core/src/__tests__/core-suggestions.test.ts new file mode 100644 index 0000000000..d33307ab97 --- /dev/null +++ b/src/v2.x/packages/core/src/__tests__/core-suggestions.test.ts @@ -0,0 +1,448 @@ +import { describe, it, expect, beforeEach, vi } from "vitest"; +import { CopilotKitCore } from "../core"; +import { DynamicSuggestionsConfig, StaticSuggestionsConfig } from "../types"; +import { MockAgent, createSuggestionsConfig, createAssistantMessage } from "./test-utils"; + +describe("CopilotKitCore - Suggestions Config Management", () => { + let copilotKitCore: CopilotKitCore; + + beforeEach(() => { + copilotKitCore = new CopilotKitCore({}); + }); + + describe("Adding suggestions configs", () => { + it("should add a dynamic suggestions config and return an ID", () => { + const config = createSuggestionsConfig({ + instructions: "Test instructions", + }); + + const id = copilotKitCore.addSuggestionsConfig(config); + + expect(id).toBeDefined(); + expect(typeof id).toBe("string"); + }); + + it("should notify subscribers when config is added", async () => { + const onConfigChanged = vi.fn(); + copilotKitCore.subscribe({ onSuggestionsConfigChanged: onConfigChanged }); + + const config = createSuggestionsConfig(); + const id = copilotKitCore.addSuggestionsConfig(config); + + await vi.waitFor(() => { + expect(onConfigChanged).toHaveBeenCalledWith({ + copilotkit: copilotKitCore, + suggestionsConfig: expect.objectContaining({ + [id]: config, + }), + }); + }); + }); + + it("should add multiple configs independently", () => { + const config1 = createSuggestionsConfig({ instructions: "First" }); + const config2 = createSuggestionsConfig({ instructions: "Second" }); + + const id1 = copilotKitCore.addSuggestionsConfig(config1); + const id2 = copilotKitCore.addSuggestionsConfig(config2); + + expect(id1).not.toBe(id2); + }); + }); + + describe("Removing suggestions configs", () => { + it("should remove a suggestions config", async () => { + const config = createSuggestionsConfig(); + const id = copilotKitCore.addSuggestionsConfig(config); + + copilotKitCore.removeSuggestionsConfig(id); + + // Config should no longer trigger suggestions + const agent = new MockAgent({ agentId: "test" }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + copilotKitCore.reloadSuggestions("test"); + + const result = copilotKitCore.getSuggestions("test"); + expect(result.suggestions).toEqual([]); + expect(result.isLoading).toBe(false); + }); + + it("should notify subscribers when config is removed", async () => { + const config = createSuggestionsConfig(); + const id = copilotKitCore.addSuggestionsConfig(config); + + const onConfigChanged = vi.fn(); + copilotKitCore.subscribe({ onSuggestionsConfigChanged: onConfigChanged }); + + copilotKitCore.removeSuggestionsConfig(id); + + await vi.waitFor(() => { + expect(onConfigChanged).toHaveBeenCalledWith({ + copilotkit: copilotKitCore, + suggestionsConfig: expect.not.objectContaining({ + [id]: expect.anything(), + }), + }); + }); + }); + }); + + describe("Getting suggestions", () => { + it("should return empty array when no suggestions exist", () => { + const result = copilotKitCore.getSuggestions("nonexistent"); + expect(result.suggestions).toEqual([]); + expect(result.isLoading).toBe(false); + }); + + it("should return empty array for agent without suggestions", () => { + const agent = new MockAgent({ agentId: "test" }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + const result = copilotKitCore.getSuggestions("test"); + expect(result.suggestions).toEqual([]); + expect(result.isLoading).toBe(false); + }); + }); + + describe("Clearing suggestions", () => { + it("should clear suggestions for an agent", () => { + const agent = new MockAgent({ agentId: "test" }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + copilotKitCore.clearSuggestions("test"); + + const result = copilotKitCore.getSuggestions("test"); + expect(result.suggestions).toEqual([]); + expect(result.isLoading).toBe(false); + }); + + it("should notify subscribers with empty suggestions array", async () => { + const onSuggestionsChanged = vi.fn(); + copilotKitCore.subscribe({ onSuggestionsChanged }); + + copilotKitCore.clearSuggestions("test"); + + await vi.waitFor(() => { + expect(onSuggestionsChanged).toHaveBeenCalledWith({ + copilotkit: copilotKitCore, + agentId: "test", + suggestions: [], + }); + }); + }); + + it("should abort running suggestion agents", () => { + const providerAgent = new MockAgent({ agentId: "default" }); + const consumerAgent = new MockAgent({ agentId: "test" }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: consumerAgent as any }); + + const config = createSuggestionsConfig(); + copilotKitCore.addSuggestionsConfig(config); + + // Start suggestion generation + copilotKitCore.reloadSuggestions("test"); + + // Clear should abort the running agent + copilotKitCore.clearSuggestions("test"); + + // Verify abortRun was called on the cloned agent + // Note: We can't directly verify this without exposing internal state, + // but we can verify that subsequent getSuggestions returns empty + const result = copilotKitCore.getSuggestions("test"); + expect(result.suggestions).toEqual([]); + expect(result.isLoading).toBe(false); + }); + + it("should handle clearing suggestions when none are running", () => { + // Should not throw + expect(() => { + copilotKitCore.clearSuggestions("nonexistent"); + }).not.toThrow(); + }); + }); + + describe("Config type discrimination", () => { + it("should recognize dynamic suggestions config", () => { + const dynamicConfig: DynamicSuggestionsConfig = { + instructions: "Test", + minSuggestions: 1, + maxSuggestions: 3, + }; + + const id = copilotKitCore.addSuggestionsConfig(dynamicConfig); + expect(id).toBeDefined(); + }); + + it("should recognize static suggestions config", () => { + const staticConfig: StaticSuggestionsConfig = { + suggestions: [ + { title: "Test 1", message: "test1", isLoading: false }, + { title: "Test 2", message: "test2", isLoading: false }, + ], + }; + + const id = copilotKitCore.addSuggestionsConfig(staticConfig); + expect(id).toBeDefined(); + }); + }); + + describe("Error handling", () => { + it("should handle error when provider agent not found", async () => { + const consumerAgent = new MockAgent({ agentId: "consumer" }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const config = createSuggestionsConfig({ + providerAgentId: "nonexistent", + consumerAgentId: "consumer", + }); + copilotKitCore.addSuggestionsConfig(config); + + // reloadSuggestions is fire-and-forget, so it doesn't throw + // The error is caught and logged inside generateSuggestions + expect(() => { + copilotKitCore.reloadSuggestions("consumer"); + }).not.toThrow(); + + // Give async operation time to complete + await new Promise((resolve) => setTimeout(resolve, 50)); + + // Verify no suggestions were generated + const result = copilotKitCore.getSuggestions("consumer"); + expect(result.suggestions).toEqual([]); + expect(result.isLoading).toBe(false); + }); + + it("should not generate suggestions when consumer agent not found", async () => { + const providerAgent = new MockAgent({ agentId: "provider" }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "provider", agent: providerAgent as any }); + + const config = createSuggestionsConfig({ + providerAgentId: "provider", + consumerAgentId: "nonexistent", + }); + copilotKitCore.addSuggestionsConfig(config); + + // Since generateSuggestions is fire-and-forget (void), errors are logged but not thrown + // The method should complete without throwing + expect(() => { + copilotKitCore.reloadSuggestions("nonexistent"); + }).not.toThrow(); + + // Give async operation time to complete + await new Promise((resolve) => setTimeout(resolve, 50)); + + // Verify no suggestions were generated + const result = copilotKitCore.getSuggestions("nonexistent"); + expect(result.suggestions).toEqual([]); + expect(result.isLoading).toBe(false); + }); + }); + + describe("Initialization with configs", () => { + it("should accept suggestions configs in constructor", () => { + const config1 = createSuggestionsConfig({ instructions: "First" }); + const config2 = createSuggestionsConfig({ instructions: "Second" }); + + const core = new CopilotKitCore({ + suggestionsConfig: [config1, config2], + }); + + expect(core).toBeDefined(); + }); + }); + + describe("Loading state", () => { + it("should return isLoading false when no suggestions are running", () => { + const result = copilotKitCore.getSuggestions("test"); + expect(result.isLoading).toBe(false); + }); + + it("should emit loading start event when suggestions generation begins", async () => { + const providerAgent = new MockAgent({ agentId: "default" }); + const consumerAgent = new MockAgent({ agentId: "consumer" }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const onLoadingStart = vi.fn(); + copilotKitCore.subscribe({ onSuggestionsStartedLoading: onLoadingStart }); + + const config = createSuggestionsConfig(); + copilotKitCore.addSuggestionsConfig(config); + + // Mock a response + providerAgent.setNewMessages([ + createAssistantMessage({ + toolCalls: [ + { + id: "s1", + type: "function", + function: { + name: "copilotkitSuggest", + arguments: ['{"suggestions":[{"title":"Test","message":"Test"}]}'] as any, + }, + }, + ], + }), + ]); + + copilotKitCore.reloadSuggestions("consumer"); + + await vi.waitFor(() => { + expect(onLoadingStart).toHaveBeenCalledWith({ + copilotkit: copilotKitCore, + agentId: "consumer", + }); + }); + }); + + it("should emit loading end event when all suggestions generation completes", async () => { + const providerAgent = new MockAgent({ agentId: "default" }); + const consumerAgent = new MockAgent({ agentId: "consumer" }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const onLoadingEnd = vi.fn(); + copilotKitCore.subscribe({ onSuggestionsFinishedLoading: onLoadingEnd }); + + const config = createSuggestionsConfig(); + copilotKitCore.addSuggestionsConfig(config); + + // Mock a response + providerAgent.setNewMessages([ + createAssistantMessage({ + toolCalls: [ + { + id: "s1", + type: "function", + function: { + name: "copilotkitSuggest", + arguments: ['{"suggestions":[{"title":"Test","message":"Test"}]}'] as any, + }, + }, + ], + }), + ]); + + copilotKitCore.reloadSuggestions("consumer"); + + await vi.waitFor(() => { + expect(onLoadingEnd).toHaveBeenCalledWith({ + copilotkit: copilotKitCore, + agentId: "consumer", + }); + }); + }); + + it("should only emit loading start once for multiple configs", async () => { + const providerAgent = new MockAgent({ agentId: "default" }); + const consumerAgent = new MockAgent({ agentId: "consumer" }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const onLoadingStart = vi.fn(); + copilotKitCore.subscribe({ onSuggestionsStartedLoading: onLoadingStart }); + + const config1 = createSuggestionsConfig({ instructions: "First" }); + const config2 = createSuggestionsConfig({ instructions: "Second" }); + copilotKitCore.addSuggestionsConfig(config1); + copilotKitCore.addSuggestionsConfig(config2); + + // Mock a response + providerAgent.setNewMessages([ + createAssistantMessage({ + toolCalls: [ + { + id: "s1", + type: "function", + function: { + name: "copilotkitSuggest", + arguments: ['{"suggestions":[{"title":"Test","message":"Test"}]}'] as any, + }, + }, + ], + }), + ]); + + copilotKitCore.reloadSuggestions("consumer"); + + await vi.waitFor(() => { + expect(providerAgent.runAgentCalls.length).toBeGreaterThanOrEqual(2); + }); + + // Should only emit once even with multiple configs + expect(onLoadingStart).toHaveBeenCalledTimes(1); + }); + + it("should only emit loading end once after all configs complete", async () => { + const providerAgent = new MockAgent({ agentId: "default" }); + const consumerAgent = new MockAgent({ agentId: "consumer" }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const onLoadingEnd = vi.fn(); + copilotKitCore.subscribe({ onSuggestionsFinishedLoading: onLoadingEnd }); + + const config1 = createSuggestionsConfig({ instructions: "First" }); + const config2 = createSuggestionsConfig({ instructions: "Second" }); + copilotKitCore.addSuggestionsConfig(config1); + copilotKitCore.addSuggestionsConfig(config2); + + // Mock a response + providerAgent.setNewMessages([ + createAssistantMessage({ + toolCalls: [ + { + id: "s1", + type: "function", + function: { + name: "copilotkitSuggest", + arguments: ['{"suggestions":[{"title":"Test","message":"Test"}]}'] as any, + }, + }, + ], + }), + ]); + + copilotKitCore.reloadSuggestions("consumer"); + + await vi.waitFor(() => { + expect(onLoadingEnd).toHaveBeenCalled(); + }); + + // Should only emit once after all complete + expect(onLoadingEnd).toHaveBeenCalledTimes(1); + }); + + it("should emit loading end even when errors occur", async () => { + const providerAgent = new MockAgent({ + agentId: "default", + error: new Error("Generation failed"), + }); + const consumerAgent = new MockAgent({ agentId: "consumer" }); + + copilotKitCore.addAgent__unsafe_dev_only({ id: "default", agent: providerAgent as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "consumer", agent: consumerAgent as any }); + + const onLoadingEnd = vi.fn(); + copilotKitCore.subscribe({ onSuggestionsFinishedLoading: onLoadingEnd }); + + const config = createSuggestionsConfig(); + copilotKitCore.addSuggestionsConfig(config); + + copilotKitCore.reloadSuggestions("consumer"); + + await vi.waitFor(() => { + expect(onLoadingEnd).toHaveBeenCalledWith({ + copilotkit: copilotKitCore, + agentId: "consumer", + }); + }); + }); + }); +}); diff --git a/src/v2.x/packages/core/src/__tests__/core-tool-minimal.test.ts b/src/v2.x/packages/core/src/__tests__/core-tool-minimal.test.ts new file mode 100644 index 0000000000..42ce797b9c --- /dev/null +++ b/src/v2.x/packages/core/src/__tests__/core-tool-minimal.test.ts @@ -0,0 +1,48 @@ +import { describe, it, expect, beforeEach, vi } from "vitest"; +import { CopilotKitCore } from "../core"; +import { MockAgent, createToolCallMessage, createTool } from "./test-utils"; + +describe("CopilotKitCore Tool Minimal", () => { + let copilotKitCore: CopilotKitCore; + + beforeEach(() => { + copilotKitCore = new CopilotKitCore({}); + }); + + it("should execute tool with string result", async () => { + const toolName = "stringTool"; + const tool = createTool({ + name: toolName, + handler: vi.fn(async () => "String result"), + }); + copilotKitCore.addTool(tool); + + const message = createToolCallMessage(toolName, { input: "test" }); + const agent = new MockAgent({ newMessages: [message] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + await copilotKitCore.runAgent({ agent: agent as any }); + + expect(tool.handler).toHaveBeenCalledWith( + { input: "test" }, + expect.objectContaining({ + id: expect.any(String), + function: expect.objectContaining({ + name: toolName, + arguments: '{"input":"test"}', + }), + }), + ); + expect(agent.messages.some((m) => m.role === "tool")).toBe(true); + }); + + it("should skip tool call when tool not found", async () => { + const message = createToolCallMessage("nonExistentTool"); + const agent = new MockAgent({ newMessages: [message] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + await copilotKitCore.runAgent({ agent: agent as any }); + + expect(agent.messages.filter((m) => m.role === "tool")).toHaveLength(0); + }); +}); diff --git a/src/v2.x/packages/core/src/__tests__/core-tool-simple.test.ts b/src/v2.x/packages/core/src/__tests__/core-tool-simple.test.ts new file mode 100644 index 0000000000..beb114289d --- /dev/null +++ b/src/v2.x/packages/core/src/__tests__/core-tool-simple.test.ts @@ -0,0 +1,46 @@ +import { describe, it, expect, beforeEach, vi } from "vitest"; +import { CopilotKitCore } from "../core"; +import { MockAgent, createToolCallMessage, createTool } from "./test-utils"; + +describe("CopilotKitCore Tool Simple", () => { + let copilotKitCore: CopilotKitCore; + + beforeEach(() => { + copilotKitCore = new CopilotKitCore({}); + }); + + it("should execute a simple tool", async () => { + console.log("Starting simple tool test"); + + const toolName = "simpleTool"; + const tool = createTool({ + name: toolName, + handler: vi.fn(async () => { + console.log("Tool handler called"); + return "Simple result"; + }), + followUp: false, // Important: no follow-up to avoid recursion + }); + copilotKitCore.addTool(tool); + + const message = createToolCallMessage(toolName, { input: "test" }); + const agent = new MockAgent({ newMessages: [message] }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + console.log("About to run agent"); + await copilotKitCore.runAgent({ agent: agent as any }); + console.log("Agent run complete"); + + expect(tool.handler).toHaveBeenCalledWith( + { input: "test" }, + expect.objectContaining({ + id: expect.any(String), + function: expect.objectContaining({ + name: toolName, + arguments: '{"input":"test"}', + }), + }), + ); + expect(agent.messages.length).toBeGreaterThan(0); + }); +}); diff --git a/src/v2.x/packages/core/src/__tests__/core-wildcard.test.ts b/src/v2.x/packages/core/src/__tests__/core-wildcard.test.ts new file mode 100644 index 0000000000..a410abd011 --- /dev/null +++ b/src/v2.x/packages/core/src/__tests__/core-wildcard.test.ts @@ -0,0 +1,46 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import { CopilotKitCore } from "../core"; +import { FrontendTool } from "../types"; + +describe("CopilotKitCore - Wildcard Tool Simple", () => { + it("should add wildcard tool", () => { + const core = new CopilotKitCore({ + headers: {}, + properties: {}, + }); + + const wildcardTool: FrontendTool = { + name: "*", + handler: vi.fn(), + }; + + core.addTool(wildcardTool); + const retrievedTool = core.getTool({ toolName: "*" }); + expect(retrievedTool).toBeDefined(); + expect(retrievedTool?.name).toBe("*"); + }); + + it("should not interfere with specific tools", () => { + const core = new CopilotKitCore({ + headers: {}, + properties: {}, + }); + + const specificTool: FrontendTool = { + name: "specific", + handler: vi.fn(), + }; + + const wildcardTool: FrontendTool = { + name: "*", + handler: vi.fn(), + }; + + core.addTool(specificTool); + core.addTool(wildcardTool); + + expect(core.getTool({ toolName: "specific" })).toBeDefined(); + expect(core.getTool({ toolName: "*" })).toBeDefined(); + expect(core.tools.length).toBe(2); + }); +}); diff --git a/src/v2.x/packages/core/src/__tests__/import.test.ts b/src/v2.x/packages/core/src/__tests__/import.test.ts new file mode 100644 index 0000000000..7aeba17509 --- /dev/null +++ b/src/v2.x/packages/core/src/__tests__/import.test.ts @@ -0,0 +1,13 @@ +import { describe, it, expect } from "vitest"; +import { Message } from "@ag-ui/client"; + +describe("Import Test", () => { + it("should import Message type", () => { + const msg: Message = { + id: "test", + role: "user", + content: "test", + }; + expect(msg.id).toBe("test"); + }); +}); \ No newline at end of file diff --git a/src/v2.x/packages/core/src/__tests__/proxied-runtime-transport.test.ts b/src/v2.x/packages/core/src/__tests__/proxied-runtime-transport.test.ts new file mode 100644 index 0000000000..4b242ed855 --- /dev/null +++ b/src/v2.x/packages/core/src/__tests__/proxied-runtime-transport.test.ts @@ -0,0 +1,381 @@ +import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; +import { ProxiedCopilotRuntimeAgent } from "../agent"; +import { CopilotKitCore } from "../core"; +import { createSuggestionsConfig, MockAgent } from "./test-utils"; + +type TransportMatrixEntry = { + label: string; + runtimeUrl: string; + transport: "rest" | "single"; +}; + +const TEST_MATRIX: TransportMatrixEntry[] = [ + { + label: "Hono REST transport", + runtimeUrl: "https://runtime.example/hono", + transport: "rest", + }, + { + label: "Hono single-route transport", + runtimeUrl: "https://runtime.example/hono-rpc", + transport: "single", + }, + { + label: "Express REST transport", + runtimeUrl: "https://runtime.example/express", + transport: "rest", + }, + { + label: "Express single-route transport", + runtimeUrl: "https://runtime.example/express-rpc", + transport: "single", + }, +]; + +const encoder = new TextEncoder(); + +describe("ProxiedCopilotRuntimeAgent transport integration", () => { + const originalFetch = global.fetch; + + TEST_MATRIX.forEach(({ label, runtimeUrl, transport }) => { + describe(label, () => { + let fetchMock: ReturnType; + + beforeEach(() => { + fetchMock = vi.fn(); + // @ts-expect-error - Node typings allow reassigning fetch in tests + global.fetch = fetchMock; + }); + + afterEach(() => { + vi.restoreAllMocks(); + global.fetch = originalFetch; + }); + + it("sends run requests with the expected payload", async () => { + const agentId = "workflow-agent"; + const agent = new ProxiedCopilotRuntimeAgent({ + runtimeUrl, + agentId, + headers: { Authorization: "Bearer test-token" }, + transport, + }); + + fetchMock.mockResolvedValueOnce(createSseResponse()); + + await expect( + agent.runAgent({ + forwardedProps: { foo: "bar" }, + }), + ).resolves.toMatchObject({ + newMessages: expect.any(Array), + }); + + expect(fetchMock).toHaveBeenCalledTimes(1); + const [url, init] = fetchMock.mock.calls[0] as [string, RequestInit]; + if (transport === "rest") { + expect(url).toBe(`${runtimeUrl}/agent/${encodeURIComponent(agentId)}/run`); + } else { + expect(url).toBe(runtimeUrl); + const body = JSON.parse(init.body as string); + expect(body).toMatchObject({ + method: "agent/run", + params: { + agentId, + }, + }); + } + + expect(init.method).toBe("POST"); + const headers = new Headers(init.headers as HeadersInit); + expect(headers.get("content-type")).toBe("application/json"); + expect(headers.get("accept")).toBe("text/event-stream"); + }); + + it("sends connect requests with the expected payload", async () => { + const agentId = "connect-agent"; + const agent = new ProxiedCopilotRuntimeAgent({ + runtimeUrl, + agentId, + headers: { Authorization: "Bearer test-token" }, + transport, + }); + + fetchMock.mockResolvedValueOnce(createSseResponse()); + + await expect(agent.connectAgent({})).resolves.toMatchObject({ + newMessages: expect.any(Array), + }); + + expect(fetchMock).toHaveBeenCalledTimes(1); + const [url, init] = fetchMock.mock.calls[0] as [string, RequestInit]; + if (transport === "rest") { + expect(url).toBe(`${runtimeUrl}/agent/${encodeURIComponent(agentId)}/connect`); + } else { + expect(url).toBe(runtimeUrl); + const body = JSON.parse(init.body as string); + expect(body).toMatchObject({ + method: "agent/connect", + params: { + agentId, + }, + }); + } + expect(init.method).toBe("POST"); + const headers = new Headers(init.headers as HeadersInit); + expect(headers.get("accept")).toBe("text/event-stream"); + }); + + it("sends stop requests with the expected payload", () => { + const agentId = "stop-agent"; + const threadId = "thread-123"; + const agent = new ProxiedCopilotRuntimeAgent({ + runtimeUrl, + agentId, + headers: { Authorization: "Bearer test-token" }, + transport, + }); + + agent.threadId = threadId; + fetchMock.mockResolvedValueOnce( + new Response(null, { + status: 200, + headers: { "content-type": "application/json" }, + }), + ); + agent.abortRun(); + + expect(fetchMock).toHaveBeenCalledTimes(1); + const [url, init] = fetchMock.mock.calls[0] as [string, RequestInit]; + if (transport === "rest") { + expect(url).toBe( + `${runtimeUrl}/agent/${encodeURIComponent(agentId)}/stop/${encodeURIComponent(threadId)}`, + ); + } else { + expect(url).toBe(runtimeUrl); + const body = JSON.parse(init.body as string); + expect(body).toMatchObject({ + method: "agent/stop", + params: { + agentId, + threadId, + }, + }); + } + expect(init.method).toBe("POST"); + const headers = new Headers(init.headers as HeadersInit); + expect(headers.get("content-type")).toBe("application/json"); + }); + }); + }); +}); + +describe("ProxiedCopilotRuntimeAgent cloning", () => { + const originalFetch = global.fetch; + const runtimeUrl = "https://runtime.example/single"; + + beforeEach(() => { + // @ts-expect-error - Node typings allow reassigning fetch in tests + global.fetch = vi.fn(() => Promise.resolve(createSseResponse())); + }); + + afterEach(() => { + vi.restoreAllMocks(); + global.fetch = originalFetch; + }); + + it("preserves single-endpoint envelope on cloned agents", async () => { + const agentId = "clone-agent"; + const agent = new ProxiedCopilotRuntimeAgent({ + runtimeUrl, + agentId, + transport: "single", + }); + + const cloned = agent.clone(); + await expect(cloned.runAgent({})).resolves.toMatchObject({ + newMessages: expect.any(Array), + }); + + const fetchMock = global.fetch as ReturnType; + expect(fetchMock).toHaveBeenCalledTimes(1); + const [url, init] = fetchMock.mock.calls[0] as [string, RequestInit]; + expect(url).toBe(runtimeUrl); + const body = JSON.parse(init.body as string); + expect(body).toMatchObject({ + method: "agent/run", + params: { + agentId, + }, + }); + }); +}); + +describe("Suggestions engine with single-endpoint runtime agents", () => { + const originalFetch = global.fetch; + const runtimeUrl = "https://runtime.example/single"; + + beforeEach(() => { + // @ts-expect-error - Node typings allow reassigning fetch in tests + global.fetch = vi.fn(() => Promise.resolve(createSseResponse())); + }); + + afterEach(() => { + vi.restoreAllMocks(); + global.fetch = originalFetch; + }); + + it("envelopes suggestion runs with method and params when cloning runtime agents", async () => { + const providerAgent = new ProxiedCopilotRuntimeAgent({ + runtimeUrl, + agentId: "provider", + transport: "single", + }); + const consumerAgent = new MockAgent({ agentId: "consumer" }) as unknown as any; + + const core = new CopilotKitCore({ + runtimeUrl, + runtimeTransport: "single", + agents__unsafe_dev_only: { + provider: providerAgent, + consumer: consumerAgent, + }, + }); + + core.addSuggestionsConfig( + createSuggestionsConfig({ + providerAgentId: "provider", + consumerAgentId: "consumer", + }), + ); + + core.reloadSuggestions("consumer"); + + const fetchMock = global.fetch as ReturnType; + await vi.waitFor(() => { + expect(fetchMock).toHaveBeenCalled(); + }); + + const [url, init] = fetchMock.mock.calls[0] as [string, RequestInit]; + expect(url).toBe(runtimeUrl); + const body = JSON.parse(init.body as string); + expect(body).toMatchObject({ + method: "agent/run", + params: { + agentId: expect.any(String), + }, + }); + }); +}); + +function createSseResponse(): Response { + const stream = new ReadableStream({ + start(controller) { + const events = [ + { + type: "RUN_STARTED", + threadId: "test-thread", + runId: "test-run", + }, + { + type: "RUN_FINISHED", + threadId: "test-thread", + runId: "test-run", + result: { newMessages: [] }, + }, + ]; + const payload = events.map((event) => `data: ${JSON.stringify(event)}\n\n`).join(""); + controller.enqueue(encoder.encode(payload)); + controller.close(); + }, + }); + + return new Response(stream, { + status: 200, + headers: { "content-type": "text/event-stream" }, + }); +} + +describe("AgentRegistry runtime info requests", () => { + const originalFetch = global.fetch; + const originalWindow = (globalThis as { window?: unknown }).window; + + beforeEach(() => { + // Simulate browser environment to allow updateRuntimeConnection to proceed. + (globalThis as { window?: unknown }).window = {}; + }); + + afterEach(() => { + vi.restoreAllMocks(); + global.fetch = originalFetch; + if (originalWindow === undefined) { + delete (globalThis as { window?: unknown }).window; + } else { + (globalThis as { window?: unknown }).window = originalWindow; + } + }); + + const infoResponse = { + version: "1.0.0", + agents: { + remote: { + description: "Remote agent", + }, + }, + }; + + const matrix: TransportMatrixEntry[] = [ + { + label: "REST runtime info", + runtimeUrl: "https://runtime.example/rest", + transport: "rest", + }, + { + label: "Single-route runtime info", + runtimeUrl: "https://runtime.example/single", + transport: "single", + }, + ]; + + matrix.forEach(({ label, runtimeUrl, transport }) => { + it(`fetches runtime info correctly for ${label}`, async () => { + const fetchMock = vi.fn().mockResolvedValue( + new Response(JSON.stringify(infoResponse), { + status: 200, + headers: { "content-type": "application/json" }, + }), + ); + // @ts-expect-error - override in test environment + global.fetch = fetchMock; + + const core = new CopilotKitCore({ + runtimeUrl, + runtimeTransport: transport, + headers: { Authorization: "Bearer token" }, + }); + + await vi.waitFor(() => { + expect(fetchMock).toHaveBeenCalled(); + }); + + const [url, init] = fetchMock.mock.calls[0] as [string, RequestInit]; + if (transport === "rest") { + expect(url).toBe(`${runtimeUrl}/info`); + expect(init.method ?? "GET").toBe("GET"); + } else { + expect(url).toBe(runtimeUrl); + expect(init.method).toBe("POST"); + const body = JSON.parse(init.body as string); + expect(body).toEqual({ method: "info" }); + } + + const headers = new Headers(init.headers as HeadersInit); + expect(headers.get("Authorization")).toBe("Bearer token"); + + // Ensure remote agent was registered using the chosen transport. + const remoteAgent = core.getAgent("remote"); + expect(remoteAgent).toBeDefined(); + expect(remoteAgent?.agentId).toBe("remote"); + }); + }); +}); diff --git a/src/v2.x/packages/core/src/__tests__/simple.test.ts b/src/v2.x/packages/core/src/__tests__/simple.test.ts new file mode 100644 index 0000000000..bb3ee7f98a --- /dev/null +++ b/src/v2.x/packages/core/src/__tests__/simple.test.ts @@ -0,0 +1,7 @@ +import { describe, it, expect } from "vitest"; + +describe("Simple Test", () => { + it("should pass", () => { + expect(1 + 1).toBe(2); + }); +}); \ No newline at end of file diff --git a/src/v2.x/packages/core/src/__tests__/state-manager.test.ts b/src/v2.x/packages/core/src/__tests__/state-manager.test.ts new file mode 100644 index 0000000000..c1aa3cf4fc --- /dev/null +++ b/src/v2.x/packages/core/src/__tests__/state-manager.test.ts @@ -0,0 +1,779 @@ +import { describe, it, expect, beforeEach, vi } from "vitest"; +import { CopilotKitCore } from "../core"; +import { AbstractAgent, Message, State, RunAgentInput, EventType } from "@ag-ui/client"; +import { randomUUID } from "@copilotkitnext/shared"; + +/** + * Mock agent that can emit events to test state management + */ +class EventEmittingMockAgent extends AbstractAgent { + private subscribers: any[] = []; + + constructor(agentId: string, threadId: string, initialState: State = {}) { + super({ + agentId, + threadId, + initialState, + }); + } + + protected run(input: RunAgentInput): any { + // Not used in these tests + throw new Error("run() should not be called in these tests"); + } + + // Expose subscribe for testing + public testSubscribe(subscriber: any) { + return this.subscribe(subscriber); + } + + // Helper to emit run started event + public async emitRunStarted(runId: string, state: State = {}) { + this.state = state; + for (const sub of this.subscribers) { + if (sub.onRunStartedEvent) { + await sub.onRunStartedEvent({ + event: { + type: EventType.RUN_STARTED, + threadId: this.threadId, + runId, + }, + messages: this.messages, + state: this.state, + agent: this, + input: this.createRunInput(runId), + }); + } + } + } + + // Helper to emit run finished event + public async emitRunFinished(runId: string, state: State = {}) { + this.state = state; + for (const sub of this.subscribers) { + if (sub.onRunFinishedEvent) { + await sub.onRunFinishedEvent({ + event: { + type: EventType.RUN_FINISHED, + threadId: this.threadId, + runId, + }, + messages: this.messages, + state: this.state, + agent: this, + input: this.createRunInput(runId), + }); + } + } + } + + // Helper to emit state snapshot event + public async emitStateSnapshot(runId: string, snapshot: State) { + for (const sub of this.subscribers) { + if (sub.onStateSnapshotEvent) { + await sub.onStateSnapshotEvent({ + event: { + type: EventType.STATE_SNAPSHOT, + snapshot, + }, + messages: this.messages, + state: this.state, + agent: this, + input: this.createRunInput(runId), + }); + } + } + } + + // Helper to emit state delta event + public async emitStateDelta(runId: string, delta: any[], currentState: State) { + this.state = currentState; + for (const sub of this.subscribers) { + if (sub.onStateDeltaEvent) { + await sub.onStateDeltaEvent({ + event: { + type: EventType.STATE_DELTA, + delta, + }, + messages: this.messages, + state: this.state, + agent: this, + input: this.createRunInput(runId), + }); + } + } + } + + // Helper to emit messages snapshot event + public async emitMessagesSnapshot(runId: string, messages: Message[]) { + for (const sub of this.subscribers) { + if (sub.onMessagesSnapshotEvent) { + await sub.onMessagesSnapshotEvent({ + event: { + type: EventType.MESSAGES_SNAPSHOT, + messages, + }, + messages: this.messages, + state: this.state, + agent: this, + input: this.createRunInput(runId), + }); + } + } + } + + // Helper to emit new message event + public async emitNewMessage(runId: string, message: Message) { + this.messages.push(message); + for (const sub of this.subscribers) { + if (sub.onNewMessage) { + await sub.onNewMessage({ + message, + messages: this.messages, + state: this.state, + agent: this, + input: this.createRunInput(runId), + }); + } + } + } + + // Override subscribe to track subscribers + public override subscribe(subscriber: any) { + this.subscribers.push(subscriber); + return { + unsubscribe: () => { + const index = this.subscribers.indexOf(subscriber); + if (index > -1) { + this.subscribers.splice(index, 1); + } + }, + }; + } + + private createRunInput(runId: string): RunAgentInput { + return { + threadId: this.threadId, + runId, + state: this.state, + messages: this.messages, + }; + } +} + +describe("StateManager - Basic State Tracking", () => { + let copilotKitCore: CopilotKitCore; + let agent: EventEmittingMockAgent; + + beforeEach(() => { + copilotKitCore = new CopilotKitCore({}); + agent = new EventEmittingMockAgent("agent1", "thread1", { count: 0 }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "agent1", agent: agent as any }); + }); + + it("should track state when run starts", async () => { + const runId = "run1"; + const state = { count: 1, user: "alice" }; + + await agent.emitRunStarted(runId, state); + + const storedState = copilotKitCore.getStateByRun("agent1", "thread1", runId); + expect(storedState).toEqual(state); + }); + + it("should track state when run finishes", async () => { + const runId = "run1"; + const finalState = { count: 5, user: "bob", completed: true }; + + await agent.emitRunStarted(runId, { count: 1 }); + await agent.emitRunFinished(runId, finalState); + + const storedState = copilotKitCore.getStateByRun("agent1", "thread1", runId); + expect(storedState).toEqual(finalState); + }); + + it("should track state snapshots during run", async () => { + const runId = "run1"; + const initialState = { count: 0 }; + const snapshot = { count: 3, intermediate: true }; + + await agent.emitRunStarted(runId, initialState); + await agent.emitStateSnapshot(runId, snapshot); + + const storedState = copilotKitCore.getStateByRun("agent1", "thread1", runId); + // State should be merged with snapshot + expect(storedState).toEqual({ count: 3, intermediate: true }); + }); + + it("should track state deltas during run", async () => { + const runId = "run1"; + const initialState = { count: 0, user: "alice" }; + const deltaState = { count: 2, user: "alice" }; + + await agent.emitRunStarted(runId, initialState); + await agent.emitStateDelta(runId, [{ op: "replace", path: "/count", value: 2 }], deltaState); + + const storedState = copilotKitCore.getStateByRun("agent1", "thread1", runId); + expect(storedState).toEqual(deltaState); + }); + + it("should return undefined for non-existent run", () => { + const storedState = copilotKitCore.getStateByRun("agent1", "thread1", "non-existent-run"); + expect(storedState).toBeUndefined(); + }); + + it("should return undefined for non-existent agent", () => { + const storedState = copilotKitCore.getStateByRun("non-existent-agent", "thread1", "run1"); + expect(storedState).toBeUndefined(); + }); + + it("should return undefined for non-existent thread", () => { + const storedState = copilotKitCore.getStateByRun("agent1", "non-existent-thread", "run1"); + expect(storedState).toBeUndefined(); + }); +}); + +describe("StateManager - Multiple Runs", () => { + let copilotKitCore: CopilotKitCore; + let agent: EventEmittingMockAgent; + + beforeEach(() => { + copilotKitCore = new CopilotKitCore({}); + agent = new EventEmittingMockAgent("agent1", "thread1"); + copilotKitCore.addAgent__unsafe_dev_only({ id: "agent1", agent: agent as any }); + }); + + it("should track multiple sequential runs independently", async () => { + const run1State = { count: 1, step: "first" }; + const run2State = { count: 2, step: "second" }; + const run3State = { count: 3, step: "third" }; + + await agent.emitRunStarted("run1", run1State); + await agent.emitRunFinished("run1", run1State); + + await agent.emitRunStarted("run2", run2State); + await agent.emitRunFinished("run2", run2State); + + await agent.emitRunStarted("run3", run3State); + await agent.emitRunFinished("run3", run3State); + + expect(copilotKitCore.getStateByRun("agent1", "thread1", "run1")).toEqual(run1State); + expect(copilotKitCore.getStateByRun("agent1", "thread1", "run2")).toEqual(run2State); + expect(copilotKitCore.getStateByRun("agent1", "thread1", "run3")).toEqual(run3State); + }); + + it("should list all run IDs for a thread", async () => { + await agent.emitRunStarted("run1", { count: 1 }); + await agent.emitRunFinished("run1", { count: 1 }); + + await agent.emitRunStarted("run2", { count: 2 }); + await agent.emitRunFinished("run2", { count: 2 }); + + await agent.emitRunStarted("run3", { count: 3 }); + await agent.emitRunFinished("run3", { count: 3 }); + + const runIds = copilotKitCore.getRunIdsForThread("agent1", "thread1"); + expect(runIds).toHaveLength(3); + expect(runIds).toContain("run1"); + expect(runIds).toContain("run2"); + expect(runIds).toContain("run3"); + }); + + it("should return empty array for thread with no runs", () => { + const runIds = copilotKitCore.getRunIdsForThread("agent1", "thread-no-runs"); + expect(runIds).toEqual([]); + }); + + it("should handle state updates in the middle of a run", async () => { + const runId = "run1"; + + await agent.emitRunStarted(runId, { count: 0, status: "started" }); + + // Simulate state changes during the run + await agent.emitStateSnapshot(runId, { count: 1, status: "processing" }); + expect(copilotKitCore.getStateByRun("agent1", "thread1", runId)).toMatchObject({ + count: 1, + status: "processing", + }); + + await agent.emitStateDelta(runId, [], { count: 2, status: "processing" }); + expect(copilotKitCore.getStateByRun("agent1", "thread1", runId)).toMatchObject({ + count: 2, + status: "processing", + }); + + await agent.emitRunFinished(runId, { count: 3, status: "completed" }); + expect(copilotKitCore.getStateByRun("agent1", "thread1", runId)).toMatchObject({ + count: 3, + status: "completed", + }); + }); +}); + +describe("StateManager - Message Tracking", () => { + let copilotKitCore: CopilotKitCore; + let agent: EventEmittingMockAgent; + + beforeEach(() => { + copilotKitCore = new CopilotKitCore({}); + agent = new EventEmittingMockAgent("agent1", "thread1"); + copilotKitCore.addAgent__unsafe_dev_only({ id: "agent1", agent: agent as any }); + }); + + it("should associate new messages with runs", async () => { + const runId = "run1"; + const message: Message = { + id: "msg1", + role: "user", + content: "Hello", + }; + + await agent.emitRunStarted(runId, {}); + await agent.emitNewMessage(runId, message); + await agent.emitRunFinished(runId, {}); + + const associatedRunId = copilotKitCore.getRunIdForMessage("agent1", "thread1", "msg1"); + expect(associatedRunId).toBe(runId); + }); + + it("should associate messages from snapshot with runs", async () => { + const runId = "run1"; + const messages: Message[] = [ + { id: "msg1", role: "user", content: "Hello" }, + { id: "msg2", role: "assistant", content: "Hi there" }, + { id: "msg3", role: "user", content: "How are you?" }, + ]; + + await agent.emitRunStarted(runId, {}); + await agent.emitMessagesSnapshot(runId, messages); + await agent.emitRunFinished(runId, {}); + + expect(copilotKitCore.getRunIdForMessage("agent1", "thread1", "msg1")).toBe(runId); + expect(copilotKitCore.getRunIdForMessage("agent1", "thread1", "msg2")).toBe(runId); + expect(copilotKitCore.getRunIdForMessage("agent1", "thread1", "msg3")).toBe(runId); + }); + + it("should track messages across multiple runs", async () => { + const msg1: Message = { id: "msg1", role: "user", content: "Run 1" }; + const msg2: Message = { id: "msg2", role: "user", content: "Run 2" }; + const msg3: Message = { id: "msg3", role: "user", content: "Run 3" }; + + await agent.emitRunStarted("run1", {}); + await agent.emitNewMessage("run1", msg1); + await agent.emitRunFinished("run1", {}); + + await agent.emitRunStarted("run2", {}); + await agent.emitNewMessage("run2", msg2); + await agent.emitRunFinished("run2", {}); + + await agent.emitRunStarted("run3", {}); + await agent.emitNewMessage("run3", msg3); + await agent.emitRunFinished("run3", {}); + + expect(copilotKitCore.getRunIdForMessage("agent1", "thread1", "msg1")).toBe("run1"); + expect(copilotKitCore.getRunIdForMessage("agent1", "thread1", "msg2")).toBe("run2"); + expect(copilotKitCore.getRunIdForMessage("agent1", "thread1", "msg3")).toBe("run3"); + }); + + it("should return undefined for non-existent message", () => { + const runId = copilotKitCore.getRunIdForMessage("agent1", "thread1", "non-existent-msg"); + expect(runId).toBeUndefined(); + }); + + it("should handle messages with tool calls", async () => { + const runId = "run1"; + const message: Message = { + id: "msg1", + role: "assistant", + content: "", + toolCalls: [ + { + id: "tool1", + type: "function", + function: { + name: "searchWeb", + arguments: JSON.stringify({ query: "test" }), + }, + }, + ], + }; + + await agent.emitRunStarted(runId, {}); + await agent.emitNewMessage(runId, message); + await agent.emitRunFinished(runId, {}); + + const associatedRunId = copilotKitCore.getRunIdForMessage("agent1", "thread1", "msg1"); + expect(associatedRunId).toBe(runId); + }); +}); + +describe("StateManager - Multiple Agents and Threads", () => { + let copilotKitCore: CopilotKitCore; + let agent1: EventEmittingMockAgent; + let agent2: EventEmittingMockAgent; + + beforeEach(() => { + copilotKitCore = new CopilotKitCore({}); + agent1 = new EventEmittingMockAgent("agent1", "thread1"); + agent2 = new EventEmittingMockAgent("agent2", "thread2"); + copilotKitCore.addAgent__unsafe_dev_only({ id: "agent1", agent: agent1 as any }); + copilotKitCore.addAgent__unsafe_dev_only({ id: "agent2", agent: agent2 as any }); + }); + + it("should track state for multiple agents independently", async () => { + const agent1State = { agentName: "agent1", count: 1 }; + const agent2State = { agentName: "agent2", count: 2 }; + + await agent1.emitRunStarted("run1", agent1State); + await agent1.emitRunFinished("run1", agent1State); + + await agent2.emitRunStarted("run1", agent2State); + await agent2.emitRunFinished("run1", agent2State); + + expect(copilotKitCore.getStateByRun("agent1", "thread1", "run1")).toEqual(agent1State); + expect(copilotKitCore.getStateByRun("agent2", "thread2", "run1")).toEqual(agent2State); + }); + + it("should track messages for multiple agents independently", async () => { + const msg1: Message = { id: "msg1", role: "user", content: "Agent 1" }; + const msg2: Message = { id: "msg2", role: "user", content: "Agent 2" }; + + await agent1.emitRunStarted("run1", {}); + await agent1.emitNewMessage("run1", msg1); + await agent1.emitRunFinished("run1", {}); + + await agent2.emitRunStarted("run1", {}); + await agent2.emitNewMessage("run1", msg2); + await agent2.emitRunFinished("run1", {}); + + expect(copilotKitCore.getRunIdForMessage("agent1", "thread1", "msg1")).toBe("run1"); + expect(copilotKitCore.getRunIdForMessage("agent2", "thread2", "msg2")).toBe("run1"); + // Cross-agent lookups should return undefined + expect(copilotKitCore.getRunIdForMessage("agent1", "thread1", "msg2")).toBeUndefined(); + expect(copilotKitCore.getRunIdForMessage("agent2", "thread2", "msg1")).toBeUndefined(); + }); + + it("should handle same agent with multiple threads", async () => { + // Create first instance with thread-a + const agent3ThreadA = new EventEmittingMockAgent("agent3", "thread-a"); + copilotKitCore.addAgent__unsafe_dev_only({ id: "agent3", agent: agent3ThreadA as any }); + + // Create second instance without agentId initially, then assign it after registration + const agent3ThreadB = new EventEmittingMockAgent("", "thread-b"); + copilotKitCore.addAgent__unsafe_dev_only({ id: "agent3-threadb", agent: agent3ThreadB as any }); + + const threadAState = { thread: "a", count: 1 }; + const threadBState = { thread: "b", count: 2 }; + + await agent3ThreadA.emitRunStarted("run1", threadAState); + await agent3ThreadA.emitRunFinished("run1", threadAState); + + await agent3ThreadB.emitRunStarted("run1", threadBState); + await agent3ThreadB.emitRunFinished("run1", threadBState); + + expect(copilotKitCore.getStateByRun("agent3", "thread-a", "run1")).toEqual(threadAState); + expect(copilotKitCore.getStateByRun("agent3-threadb", "thread-b", "run1")).toEqual(threadBState); + }); +}); + +describe("StateManager - State Isolation", () => { + let copilotKitCore: CopilotKitCore; + let agent: EventEmittingMockAgent; + + beforeEach(() => { + copilotKitCore = new CopilotKitCore({}); + agent = new EventEmittingMockAgent("agent1", "thread1"); + copilotKitCore.addAgent__unsafe_dev_only({ id: "agent1", agent: agent as any }); + }); + + it("should deep copy state to prevent external mutations", async () => { + const runId = "run1"; + const state = { nested: { count: 1 }, items: [1, 2, 3] }; + + await agent.emitRunStarted(runId, state); + + const retrievedState = copilotKitCore.getStateByRun("agent1", "thread1", runId) as any; + + // Mutate the retrieved state + retrievedState.nested.count = 999; + retrievedState.items.push(4); + + // Original stored state should be unchanged + const retrievedAgain = copilotKitCore.getStateByRun("agent1", "thread1", runId) as any; + expect(retrievedAgain.nested.count).toBe(1); + expect(retrievedAgain.items).toEqual([1, 2, 3]); + }); + + it("should handle complex nested state objects", async () => { + const runId = "run1"; + const complexState = { + user: { + id: "123", + profile: { + name: "Alice", + settings: { + theme: "dark", + notifications: true, + }, + }, + }, + conversations: [ + { id: "conv1", messages: 5 }, + { id: "conv2", messages: 3 }, + ], + metadata: { + timestamp: 1234567890, + version: "1.0", + }, + }; + + await agent.emitRunStarted(runId, complexState); + + const storedState = copilotKitCore.getStateByRun("agent1", "thread1", runId); + expect(storedState).toEqual(complexState); + }); + + it("should handle state with null and undefined values", async () => { + const runId = "run1"; + const state = { + nullValue: null, + undefinedValue: undefined, + zeroValue: 0, + emptyString: "", + falseValue: false, + }; + + await agent.emitRunStarted(runId, state); + + const storedState = copilotKitCore.getStateByRun("agent1", "thread1", runId) as any; + expect(storedState.nullValue).toBeNull(); + // Note: undefined may not survive JSON serialization + expect(storedState.zeroValue).toBe(0); + expect(storedState.emptyString).toBe(""); + expect(storedState.falseValue).toBe(false); + }); +}); + +describe("StateManager - Edge Cases", () => { + let copilotKitCore: CopilotKitCore; + + beforeEach(() => { + copilotKitCore = new CopilotKitCore({}); + }); + + it("should handle agent without agentId gracefully", async () => { + const agent = new EventEmittingMockAgent("", "thread1"); + copilotKitCore.addAgent__unsafe_dev_only({ id: "test", agent: agent as any }); + + // Agent will get assigned "test" as its agentId during registration + // So it should actually track the state under "test" + await agent.emitRunStarted("run1", { count: 1 }); + + // The state should be tracked under the assigned agentId "test" + const state = copilotKitCore.getStateByRun("test", "thread1", "run1"); + expect(state).toEqual({ count: 1 }); + }); + + it("should handle empty state object", async () => { + const agent = new EventEmittingMockAgent("agent1", "thread1"); + copilotKitCore.addAgent__unsafe_dev_only({ id: "agent1", agent: agent as any }); + + await agent.emitRunStarted("run1", {}); + await agent.emitRunFinished("run1", {}); + + const storedState = copilotKitCore.getStateByRun("agent1", "thread1", "run1"); + expect(storedState).toEqual({}); + }); + + it("should handle rapid successive runs", async () => { + const agent = new EventEmittingMockAgent("agent1", "thread1"); + copilotKitCore.addAgent__unsafe_dev_only({ id: "agent1", agent: agent as any }); + + // Fire multiple runs rapidly + const promises = []; + for (let i = 0; i < 10; i++) { + const runId = `run${i}`; + const state = { count: i }; + promises.push( + agent.emitRunStarted(runId, state).then(() => agent.emitRunFinished(runId, state)) + ); + } + + await Promise.all(promises); + + // All runs should be tracked correctly + for (let i = 0; i < 10; i++) { + const storedState = copilotKitCore.getStateByRun("agent1", "thread1", `run${i}`); + expect(storedState).toEqual({ count: i }); + } + }); + + it("should handle messages without input parameter", async () => { + const agent = new EventEmittingMockAgent("agent1", "thread1"); + copilotKitCore.addAgent__unsafe_dev_only({ id: "agent1", agent: agent as any }); + + const message: Message = { + id: "msg1", + role: "user", + content: "Test", + }; + + // Emit message without proper input (edge case) + for (const sub of (agent as any).subscribers) { + if (sub.onNewMessage) { + await sub.onNewMessage({ + message, + messages: agent.messages, + state: agent.state, + agent: agent, + // No input parameter + }); + } + } + + // Should not throw, but message won't be associated + const runId = copilotKitCore.getRunIdForMessage("agent1", "thread1", "msg1"); + expect(runId).toBeUndefined(); + }); +}); + +describe("StateManager - Real-world Scenarios", () => { + let copilotKitCore: CopilotKitCore; + let agent: EventEmittingMockAgent; + + beforeEach(() => { + copilotKitCore = new CopilotKitCore({}); + agent = new EventEmittingMockAgent("chatbot", "user-session-123"); + copilotKitCore.addAgent__unsafe_dev_only({ id: "chatbot", agent: agent as any }); + }); + + it("should track a complete conversation flow", async () => { + // First run - user asks a question + const run1Id = randomUUID(); + await agent.emitRunStarted(run1Id, { conversationContext: "greeting" }); + + const msg1: Message = { id: randomUUID(), role: "user", content: "Hello" }; + await agent.emitNewMessage(run1Id, msg1); + + const msg2: Message = { id: randomUUID(), role: "assistant", content: "Hi! How can I help?" }; + await agent.emitNewMessage(run1Id, msg2); + + await agent.emitRunFinished(run1Id, { conversationContext: "greeting", messageCount: 2 }); + + // Second run - user asks for help + const run2Id = randomUUID(); + await agent.emitRunStarted(run2Id, { conversationContext: "help_request", messageCount: 2 }); + + const msg3: Message = { id: randomUUID(), role: "user", content: "I need help with my order" }; + await agent.emitNewMessage(run2Id, msg3); + + await agent.emitStateSnapshot(run2Id, { conversationContext: "help_request", topic: "orders" }); + + const msg4: Message = { id: randomUUID(), role: "assistant", content: "I can help with that!" }; + await agent.emitNewMessage(run2Id, msg4); + + await agent.emitRunFinished(run2Id, { + conversationContext: "help_request", + topic: "orders", + messageCount: 4, + }); + + // Verify all messages are associated correctly + expect(copilotKitCore.getRunIdForMessage("chatbot", "user-session-123", msg1.id)).toBe(run1Id); + expect(copilotKitCore.getRunIdForMessage("chatbot", "user-session-123", msg2.id)).toBe(run1Id); + expect(copilotKitCore.getRunIdForMessage("chatbot", "user-session-123", msg3.id)).toBe(run2Id); + expect(copilotKitCore.getRunIdForMessage("chatbot", "user-session-123", msg4.id)).toBe(run2Id); + + // Verify states are tracked correctly + const run1State = copilotKitCore.getStateByRun("chatbot", "user-session-123", run1Id); + expect(run1State).toMatchObject({ conversationContext: "greeting", messageCount: 2 }); + + const run2State = copilotKitCore.getStateByRun("chatbot", "user-session-123", run2Id); + expect(run2State).toMatchObject({ + conversationContext: "help_request", + topic: "orders", + messageCount: 4, + }); + + // Verify we can list all runs + const runIds = copilotKitCore.getRunIdsForThread("chatbot", "user-session-123"); + expect(runIds).toHaveLength(2); + expect(runIds).toContain(run1Id); + expect(runIds).toContain(run2Id); + }); + + it("should handle tool execution flow with state changes", async () => { + const runId = randomUUID(); + + await agent.emitRunStarted(runId, { step: "initial", toolsExecuted: [] }); + + const userMsg: Message = { + id: randomUUID(), + role: "user", + content: "Search for cats", + }; + await agent.emitNewMessage(runId, userMsg); + + // Agent calls a tool + const toolCallMsg: Message = { + id: randomUUID(), + role: "assistant", + content: "", + toolCalls: [ + { + id: "tool-call-1", + type: "function", + function: { + name: "searchWeb", + arguments: JSON.stringify({ query: "cats" }), + }, + }, + ], + }; + await agent.emitNewMessage(runId, toolCallMsg); + + // Update state after tool call + await agent.emitStateDelta(runId, [], { + step: "tool_called", + toolsExecuted: ["searchWeb"], + lastToolCall: "tool-call-1", + }); + + // Tool result comes back + const toolResultMsg: Message = { + id: randomUUID(), + role: "tool", + content: "Found 10 results about cats", + toolCallId: "tool-call-1", + }; + await agent.emitNewMessage(runId, toolResultMsg); + + // Final response + const responseMsg: Message = { + id: randomUUID(), + role: "assistant", + content: "I found information about cats for you!", + }; + await agent.emitNewMessage(runId, responseMsg); + + await agent.emitRunFinished(runId, { + step: "completed", + toolsExecuted: ["searchWeb"], + totalMessages: 4, + }); + + // Verify all messages are associated + expect(copilotKitCore.getRunIdForMessage("chatbot", "user-session-123", userMsg.id)).toBe(runId); + expect(copilotKitCore.getRunIdForMessage("chatbot", "user-session-123", toolCallMsg.id)).toBe(runId); + expect(copilotKitCore.getRunIdForMessage("chatbot", "user-session-123", toolResultMsg.id)).toBe(runId); + expect(copilotKitCore.getRunIdForMessage("chatbot", "user-session-123", responseMsg.id)).toBe(runId); + + // Verify final state + const finalState = copilotKitCore.getStateByRun("chatbot", "user-session-123", runId); + expect(finalState).toMatchObject({ + step: "completed", + toolsExecuted: ["searchWeb"], + totalMessages: 4, + }); + }); +}); diff --git a/src/v2.x/packages/core/src/__tests__/test-utils.ts b/src/v2.x/packages/core/src/__tests__/test-utils.ts new file mode 100644 index 0000000000..d313edae2c --- /dev/null +++ b/src/v2.x/packages/core/src/__tests__/test-utils.ts @@ -0,0 +1,260 @@ +import { Message } from "@ag-ui/client"; +import { vi } from "vitest"; +import { DynamicSuggestionsConfig, FrontendTool } from "../types"; + +export interface MockAgentOptions { + messages?: Message[]; + newMessages?: Message[]; + error?: Error | string; + runAgentDelay?: number; + runAgentCallback?: (input: any) => void; + agentId?: string; + threadId?: string; + state?: Record; +} + +export class MockAgent { + public messages: Message[] = []; + public state: Record = {}; + public agentId?: string; + public threadId?: string; + public addMessages = vi.fn((messages: Message[]) => { + this.messages.push(...messages); + }); + public addMessage = vi.fn((message: Message) => { + this.messages.push(message); + // Also track on parent if this is a clone + if (this._parentAgent) { + this._parentAgent.addMessage(message); + } + }); + public abortRun = vi.fn(); + public clone = vi.fn(() => this._cloneImpl()); + + private newMessages: Message[]; + private error?: Error | string; + private runAgentDelay: number; + public runAgentCallback?: (input: any) => void; + public runAgentCalls: any[] = []; + private _parentAgent?: MockAgent; + + constructor(options: MockAgentOptions = {}) { + this.messages = options.messages || []; + this.newMessages = options.newMessages || []; + this.error = options.error; + this.runAgentDelay = options.runAgentDelay || 0; + this.runAgentCallback = options.runAgentCallback; + this.agentId = options.agentId; + this.threadId = options.threadId; + this.state = options.state || {}; + } + + async runAgent(input: any, subscriber?: any): Promise<{ newMessages: Message[] }> { + this.runAgentCalls.push(input); + // Also track on parent if this is a clone + if (this._parentAgent) { + this._parentAgent.runAgentCalls.push(input); + } + + if (this.runAgentCallback) { + this.runAgentCallback(input); + } + + if (this.runAgentDelay > 0) { + await new Promise(resolve => setTimeout(resolve, this.runAgentDelay)); + } + + if (this.error) { + throw this.error; + } + + // If there's a subscriber with onMessagesChanged, call it with the messages + if (subscriber?.onMessagesChanged && this.newMessages.length > 0) { + // Trigger the subscriber callback with messages + subscriber.onMessagesChanged({ messages: [...this.messages, ...this.newMessages] }); + } + + return { newMessages: this.newMessages }; + } + + private _cloneImpl(): MockAgent { + const cloned = new MockAgent({ + messages: [...this.messages], + newMessages: [...this.newMessages], + error: this.error, + runAgentDelay: this.runAgentDelay, + runAgentCallback: this.runAgentCallback, + agentId: this.agentId, + threadId: this.threadId, + state: JSON.parse(JSON.stringify(this.state)), + }); + // Link the clone back to the parent so calls are visible + cloned._parentAgent = this; + return cloned; + } + + // Provide a no-op subscribe API so core can attach state listeners + // without errors during tests. + // eslint-disable-next-line @typescript-eslint/no-unused-vars + public subscribe(_subscriber?: any): { unsubscribe: () => void } { + return { unsubscribe: () => {} }; + } + + setNewMessages(messages: Message[]): void { + this.newMessages = messages; + } +} + +export function createMessage(overrides: Partial = {}): Message { + return { + id: `msg-${Math.random().toString(36).substr(2, 9)}`, + role: "user", + content: "Test message", + ...overrides, + } as Message; +} + +export function createAssistantMessage( + overrides: Partial = {} +): Message { + return createMessage({ + role: "assistant", + content: "Assistant message", + ...overrides, + }); +} + +export function createToolCallMessage( + toolCallName: string, + args: any = {}, + overrides: Partial = {} +): Message { + const toolCallId = `tool-call-${Math.random().toString(36).substr(2, 9)}`; + return createAssistantMessage({ + content: "", + toolCalls: [ + { + id: toolCallId, + type: "function", + function: { + name: toolCallName, + arguments: JSON.stringify(args), + }, + }, + ], + ...overrides, + }); +} + +export function createToolResultMessage( + toolCallId: string, + content: string, + overrides: Partial = {} +): Message { + return createMessage({ + role: "tool", + content, + toolCallId, + ...overrides, + }); +} + +export function createTool>( + overrides: Partial> = {} +): FrontendTool { + return { + name: `tool-${Math.random().toString(36).substr(2, 9)}`, + description: "Test tool", + handler: vi.fn(async () => "Tool result"), + followUp: false, // Default to false to avoid unexpected recursion in tests + ...overrides, + }; +} + +export function createMultipleToolCallsMessage( + toolCalls: Array<{ name: string; args?: any }>, + overrides: Partial = {} +): Message { + return createAssistantMessage({ + content: "", + toolCalls: toolCalls.map((tc) => ({ + id: `tool-call-${Math.random().toString(36).substr(2, 9)}`, + type: "function", + function: { + name: tc.name, + arguments: JSON.stringify(tc.args || {}), + }, + })), + ...overrides, + }); +} + +export async function waitForCondition( + condition: () => boolean, + timeout: number = 1000, + interval: number = 10 +): Promise { + const start = Date.now(); + while (!condition()) { + if (Date.now() - start > timeout) { + throw new Error("Timeout waiting for condition"); + } + await new Promise(resolve => setTimeout(resolve, interval)); + } +} + +/** + * Helper to create a dynamic suggestions config + */ +export function createSuggestionsConfig( + overrides: Partial = {} +): DynamicSuggestionsConfig { + return { + instructions: "Suggest helpful next actions", + minSuggestions: 1, + maxSuggestions: 3, + available: "always", + providerAgentId: "default", + consumerAgentId: "*", + ...overrides, + }; +} + +/** + * Helper to create a tool call message for copilotkitSuggest + */ +export function createSuggestionToolCall( + suggestions: Array<{ title: string; message: string }>, + overrides: Partial = {} +): Message { + const toolCallId = `suggest-call-${Math.random().toString(36).substr(2, 9)}`; + return createAssistantMessage({ + content: "", + toolCalls: [ + { + id: toolCallId, + type: "function", + function: { + name: "copilotkitSuggest", + arguments: JSON.stringify({ suggestions }), + }, + }, + ], + ...overrides, + }); +} + +/** + * Helper to create streaming suggestion messages with partial JSON + * Returns an array of JSON chunks that can be assembled into complete suggestions + */ +export function createStreamingSuggestionChunks(): string[] { + return [ + '{"suggestions":[', + '{"title":"First","message":"Do first thing"}', + ',{"title":"Second",', + '"message":"Do second thing"}', + ',{"title":"Third","message":"Do third thing"}', + ']}', + ]; +} diff --git a/src/v2.x/packages/core/src/agent.ts b/src/v2.x/packages/core/src/agent.ts new file mode 100644 index 0000000000..b5fb3eb81e --- /dev/null +++ b/src/v2.x/packages/core/src/agent.ts @@ -0,0 +1,175 @@ +import { + BaseEvent, + HttpAgent, + HttpAgentConfig, + RunAgentInput, + runHttpRequest, + transformHttpEventStream, +} from "@ag-ui/client"; +import { Observable } from "rxjs"; +import { CopilotRuntimeTransport } from "./types"; + +export interface ProxiedCopilotRuntimeAgentConfig extends Omit { + runtimeUrl?: string; + transport?: CopilotRuntimeTransport; +} + +export class ProxiedCopilotRuntimeAgent extends HttpAgent { + runtimeUrl?: string; + private transport: CopilotRuntimeTransport; + private singleEndpointUrl?: string; + + constructor(config: ProxiedCopilotRuntimeAgentConfig) { + const normalizedRuntimeUrl = config.runtimeUrl ? config.runtimeUrl.replace(/\/$/, "") : undefined; + const transport = config.transport ?? "rest"; + const runUrl = + transport === "single" + ? normalizedRuntimeUrl ?? config.runtimeUrl ?? "" + : `${normalizedRuntimeUrl ?? config.runtimeUrl}/agent/${encodeURIComponent(config.agentId ?? "")}/run`; + + if (!runUrl) { + throw new Error("ProxiedCopilotRuntimeAgent requires a runtimeUrl when transport is set to 'single'."); + } + + super({ + ...config, + url: runUrl, + }); + this.runtimeUrl = normalizedRuntimeUrl ?? config.runtimeUrl; + this.transport = transport; + if (this.transport === "single") { + this.singleEndpointUrl = this.runtimeUrl; + } + } + + abortRun(): void { + if (!this.agentId || !this.threadId) { + return; + } + + if (typeof fetch === "undefined") { + return; + } + + if (this.transport === "single") { + if (!this.singleEndpointUrl) { + return; + } + + const headers = new Headers({ ...this.headers, "Content-Type": "application/json" }); + void fetch(this.singleEndpointUrl, { + method: "POST", + headers, + body: JSON.stringify({ + method: "agent/stop", + params: { + agentId: this.agentId, + threadId: this.threadId, + }, + }), + }).catch((error) => { + console.error("ProxiedCopilotRuntimeAgent: stop request failed", error); + }); + return; + } + + if (!this.runtimeUrl) { + return; + } + + const stopPath = `${this.runtimeUrl}/agent/${encodeURIComponent(this.agentId)}/stop/${encodeURIComponent(this.threadId)}`; + const origin = typeof window !== "undefined" && window.location ? window.location.origin : "http://localhost"; + const base = new URL(this.runtimeUrl, origin); + const stopUrl = new URL(stopPath, base); + + void fetch(stopUrl.toString(), { + method: "POST", + headers: { + "Content-Type": "application/json", + ...this.headers, + }, + }).catch((error) => { + console.error("ProxiedCopilotRuntimeAgent: stop request failed", error); + }); + } + + connect(input: RunAgentInput): Observable { + if (this.transport === "single") { + if (!this.singleEndpointUrl) { + throw new Error("Single endpoint transport requires a runtimeUrl"); + } + + const requestInit = this.createSingleRouteRequestInit(input, "agent/connect", { + agentId: this.agentId!, + }); + const httpEvents = runHttpRequest(this.singleEndpointUrl, requestInit); + return transformHttpEventStream(httpEvents); + } + + const httpEvents = runHttpRequest(`${this.runtimeUrl}/agent/${this.agentId}/connect`, this.requestInit(input)); + return transformHttpEventStream(httpEvents); + } + + public run(input: RunAgentInput): Observable { + if (this.transport === "single") { + if (!this.singleEndpointUrl) { + throw new Error("Single endpoint transport requires a runtimeUrl"); + } + + const requestInit = this.createSingleRouteRequestInit(input, "agent/run", { + agentId: this.agentId!, + }); + const httpEvents = runHttpRequest(this.singleEndpointUrl, requestInit); + return transformHttpEventStream(httpEvents); + } + + return super.run(input); + } + + public override clone(): ProxiedCopilotRuntimeAgent { + const cloned = super.clone() as ProxiedCopilotRuntimeAgent; + cloned.runtimeUrl = this.runtimeUrl; + cloned.transport = this.transport; + cloned.singleEndpointUrl = this.singleEndpointUrl; + return cloned; + } + + private createSingleRouteRequestInit(input: RunAgentInput, method: string, params?: Record): RequestInit { + if (!this.agentId) { + throw new Error("ProxiedCopilotRuntimeAgent requires agentId to make runtime requests"); + } + + const baseInit = super.requestInit(input); + const headers = new Headers(baseInit.headers ?? {}); + headers.set("Content-Type", "application/json"); + headers.set("Accept", headers.get("Accept") ?? "text/event-stream"); + + let originalBody: unknown = undefined; + if (typeof baseInit.body === "string") { + try { + originalBody = JSON.parse(baseInit.body); + } catch (error) { + console.warn("ProxiedCopilotRuntimeAgent: failed to parse request body for single route transport", error); + originalBody = undefined; + } + } + + const envelope: Record = { + method, + }; + + if (params && Object.keys(params).length > 0) { + envelope.params = params; + } + + if (originalBody !== undefined) { + envelope.body = originalBody; + } + + return { + ...baseInit, + headers, + body: JSON.stringify(envelope), + }; + } +} diff --git a/src/v2.x/packages/core/src/core/__tests__/run-handler-schema.test.ts b/src/v2.x/packages/core/src/core/__tests__/run-handler-schema.test.ts new file mode 100644 index 0000000000..788f8d9996 --- /dev/null +++ b/src/v2.x/packages/core/src/core/__tests__/run-handler-schema.test.ts @@ -0,0 +1,59 @@ +import { describe, expect, it } from "vitest"; +import { z } from "zod"; +import { RunHandler } from "../run-handler"; +import type { CopilotKitCore } from "../core"; + +function createRunHandler(): RunHandler { + return new RunHandler({} as CopilotKitCore); +} + +describe("RunHandler tool schema generation", () => { + it("strips boolean additionalProperties emitted by zod-to-json-schema", () => { + const runHandler = createRunHandler(); + runHandler.initialize([ + { + name: "sample", + parameters: z.object({ + foo: z.string(), + }), + }, + ]); + + const [tool] = runHandler.buildFrontendTools(); + expect(tool.parameters).toEqual({ + type: "object", + properties: { + foo: { type: "string" }, + }, + required: ["foo"], + }); + }); + + it("removes additionalProperties even for catchalls", () => { + const runHandler = createRunHandler(); + runHandler.initialize([ + { + name: "catchall", + parameters: z.object({}).catchall(z.string()), + }, + ]); + + const [tool] = runHandler.buildFrontendTools(); + expect(tool.parameters).not.toHaveProperty("additionalProperties"); + }); + + it("returns an empty schema for tools without parameters", () => { + const runHandler = createRunHandler(); + runHandler.initialize([ + { + name: "noSchema", + }, + ]); + + const [tool] = runHandler.buildFrontendTools(); + expect(tool.parameters).toEqual({ + type: "object", + properties: {}, + }); + }); +}); diff --git a/src/v2.x/packages/core/src/core/agent-registry.ts b/src/v2.x/packages/core/src/core/agent-registry.ts new file mode 100644 index 0000000000..2199a31f81 --- /dev/null +++ b/src/v2.x/packages/core/src/core/agent-registry.ts @@ -0,0 +1,323 @@ +import { AbstractAgent, HttpAgent } from "@ag-ui/client"; +import { logger, RuntimeInfo, AgentDescription } from "@copilotkitnext/shared"; +import { ProxiedCopilotRuntimeAgent } from "../agent"; +import type { CopilotKitCore } from "./core"; +import { CopilotKitCoreErrorCode, CopilotKitCoreRuntimeConnectionStatus, CopilotKitCoreFriendsAccess } from "./core"; +import { CopilotRuntimeTransport } from "../types"; + +export interface CopilotKitCoreAddAgentParams { + id: string; + agent: AbstractAgent; +} + +/** + * Manages agent registration, lifecycle, and runtime connectivity for CopilotKitCore. + * Handles both local development agents and remote runtime agents. + */ +export class AgentRegistry { + private _agents: Record = {}; + private localAgents: Record = {}; + private remoteAgents: Record = {}; + + private _runtimeUrl?: string; + private _runtimeVersion?: string; + private _runtimeConnectionStatus: CopilotKitCoreRuntimeConnectionStatus = + CopilotKitCoreRuntimeConnectionStatus.Disconnected; + private _runtimeTransport: CopilotRuntimeTransport = "rest"; + + constructor(private core: CopilotKitCore) {} + + /** + * Get all agents as a readonly record + */ + get agents(): Readonly> { + return this._agents; + } + + get runtimeUrl(): string | undefined { + return this._runtimeUrl; + } + + get runtimeVersion(): string | undefined { + return this._runtimeVersion; + } + + get runtimeConnectionStatus(): CopilotKitCoreRuntimeConnectionStatus { + return this._runtimeConnectionStatus; + } + + get runtimeTransport(): CopilotRuntimeTransport { + return this._runtimeTransport; + } + + /** + * Initialize agents from configuration + */ + initialize(agents: Record): void { + this.localAgents = this.assignAgentIds(agents); + this.applyHeadersToAgents(this.localAgents); + this._agents = this.localAgents; + } + + /** + * Set the runtime URL and update connection + */ + setRuntimeUrl(runtimeUrl: string | undefined): void { + const normalizedRuntimeUrl = runtimeUrl ? runtimeUrl.replace(/\/$/, "") : undefined; + + if (this._runtimeUrl === normalizedRuntimeUrl) { + return; + } + + this._runtimeUrl = normalizedRuntimeUrl; + void this.updateRuntimeConnection(); + } + + setRuntimeTransport(runtimeTransport: CopilotRuntimeTransport): void { + if (this._runtimeTransport === runtimeTransport) { + return; + } + + this._runtimeTransport = runtimeTransport; + void this.updateRuntimeConnection(); + } + + /** + * Set all agents at once (for development use) + */ + setAgents__unsafe_dev_only(agents: Record): void { + // Validate all agents before making any changes + Object.entries(agents).forEach(([id, agent]) => { + if (agent) { + this.validateAndAssignAgentId(id, agent); + } + }); + this.localAgents = agents; + this._agents = { ...this.localAgents, ...this.remoteAgents }; + this.applyHeadersToAgents(this._agents); + void this.notifyAgentsChanged(); + } + + /** + * Add a single agent (for development use) + */ + addAgent__unsafe_dev_only({ id, agent }: CopilotKitCoreAddAgentParams): void { + this.validateAndAssignAgentId(id, agent); + this.localAgents[id] = agent; + this.applyHeadersToAgent(agent); + this._agents = { ...this.localAgents, ...this.remoteAgents }; + void this.notifyAgentsChanged(); + } + + /** + * Remove an agent by ID (for development use) + */ + removeAgent__unsafe_dev_only(id: string): void { + delete this.localAgents[id]; + this._agents = { ...this.localAgents, ...this.remoteAgents }; + void this.notifyAgentsChanged(); + } + + /** + * Get an agent by ID + */ + getAgent(id: string): AbstractAgent | undefined { + if (id in this._agents) { + return this._agents[id] as AbstractAgent; + } + + // Silently return undefined if we're still loading runtime agents + if ( + this.runtimeUrl !== undefined && + (this.runtimeConnectionStatus === CopilotKitCoreRuntimeConnectionStatus.Disconnected || + this.runtimeConnectionStatus === CopilotKitCoreRuntimeConnectionStatus.Connecting) + ) { + return undefined; + } + + console.warn(`Agent ${id} not found`); + return undefined; + } + + /** + * Apply current headers to an agent + */ + applyHeadersToAgent(agent: AbstractAgent): void { + if (agent instanceof HttpAgent) { + agent.headers = { ...(this.core as unknown as CopilotKitCoreFriendsAccess).headers }; + } + } + + /** + * Apply current headers to all agents + */ + applyHeadersToAgents(agents: Record): void { + Object.values(agents).forEach((agent) => { + this.applyHeadersToAgent(agent); + }); + } + + /** + * Update runtime connection and fetch remote agents + */ + private async updateRuntimeConnection(): Promise { + // Skip fetching on the server (SSR) + if (typeof window === 'undefined') { + return; + } + + if (!this.runtimeUrl) { + this._runtimeConnectionStatus = CopilotKitCoreRuntimeConnectionStatus.Disconnected; + this._runtimeVersion = undefined; + this.remoteAgents = {}; + this._agents = this.localAgents; + + await this.notifyRuntimeStatusChanged(CopilotKitCoreRuntimeConnectionStatus.Disconnected); + await this.notifyAgentsChanged(); + return; + } + + this._runtimeConnectionStatus = CopilotKitCoreRuntimeConnectionStatus.Connecting; + await this.notifyRuntimeStatusChanged(CopilotKitCoreRuntimeConnectionStatus.Connecting); + + try { + const runtimeInfoResponse = await this.fetchRuntimeInfo(); + const { + version, + ...runtimeInfo + }: { + agents: Record; + version: string; + } = runtimeInfoResponse; + + const agents: Record = Object.fromEntries( + Object.entries(runtimeInfo.agents).map(([id, { description }]) => { + const agent = new ProxiedCopilotRuntimeAgent({ + runtimeUrl: this.runtimeUrl, + agentId: id, // Runtime agents always have their ID set correctly + description: description, + transport: this._runtimeTransport, + }); + this.applyHeadersToAgent(agent); + return [id, agent]; + }), + ); + + this.remoteAgents = agents; + this._agents = { ...this.localAgents, ...this.remoteAgents }; + this._runtimeConnectionStatus = CopilotKitCoreRuntimeConnectionStatus.Connected; + this._runtimeVersion = version; + + await this.notifyRuntimeStatusChanged(CopilotKitCoreRuntimeConnectionStatus.Connected); + await this.notifyAgentsChanged(); + } catch (error) { + this._runtimeConnectionStatus = CopilotKitCoreRuntimeConnectionStatus.Error; + this._runtimeVersion = undefined; + this.remoteAgents = {}; + this._agents = this.localAgents; + + await this.notifyRuntimeStatusChanged(CopilotKitCoreRuntimeConnectionStatus.Error); + await this.notifyAgentsChanged(); + + const message = error instanceof Error ? error.message : JSON.stringify(error); + logger.warn(`Failed to load runtime info (${this.runtimeUrl}/info): ${message}`); + const runtimeError = error instanceof Error ? error : new Error(String(error)); + await (this.core as unknown as CopilotKitCoreFriendsAccess).emitError({ + error: runtimeError, + code: CopilotKitCoreErrorCode.RUNTIME_INFO_FETCH_FAILED, + context: { + runtimeUrl: this.runtimeUrl, + }, + }); + } + } + + private async fetchRuntimeInfo(): Promise { + if (!this.runtimeUrl) { + throw new Error("Runtime URL is not set"); + } + + const baseHeaders = (this.core as unknown as CopilotKitCoreFriendsAccess).headers; + const headers: Record = { + ...baseHeaders, + }; + + if (this._runtimeTransport === "single") { + if (!headers["Content-Type"]) { + headers["Content-Type"] = "application/json"; + } + const response = await fetch(this.runtimeUrl, { + method: "POST", + headers, + body: JSON.stringify({ method: "info" }), + }); + if ("ok" in response && !(response as Response).ok) { + throw new Error(`Runtime info request failed with status ${response.status}`); + } + return (await response.json()) as RuntimeInfo; + } + + const response = await fetch(`${this.runtimeUrl}/info`, { + headers, + }); + if ("ok" in response && !(response as Response).ok) { + throw new Error(`Runtime info request failed with status ${response.status}`); + } + return (await response.json()) as RuntimeInfo; + } + + /** + * Assign agent IDs to a record of agents + */ + private assignAgentIds(agents: Record): Record { + Object.entries(agents).forEach(([id, agent]) => { + if (agent) { + this.validateAndAssignAgentId(id, agent); + } + }); + return agents; + } + + /** + * Validate and assign an agent ID + */ + private validateAndAssignAgentId(registrationId: string, agent: AbstractAgent): void { + if (agent.agentId && agent.agentId !== registrationId) { + throw new Error( + `Agent registration mismatch: Agent with ID "${agent.agentId}" cannot be registered under key "${registrationId}". ` + + `The agent ID must match the registration key or be undefined.`, + ); + } + if (!agent.agentId) { + agent.agentId = registrationId; + } + } + + /** + * Notify subscribers of runtime status changes + */ + private async notifyRuntimeStatusChanged(status: CopilotKitCoreRuntimeConnectionStatus): Promise { + await (this.core as unknown as CopilotKitCoreFriendsAccess).notifySubscribers( + (subscriber) => + subscriber.onRuntimeConnectionStatusChanged?.({ + copilotkit: this.core, + status, + }), + "Error in CopilotKitCore subscriber (onRuntimeConnectionStatusChanged):", + ); + } + + /** + * Notify subscribers of agent changes + */ + private async notifyAgentsChanged(): Promise { + await (this.core as unknown as CopilotKitCoreFriendsAccess).notifySubscribers( + (subscriber) => + subscriber.onAgentsChanged?.({ + copilotkit: this.core, + agents: this._agents, + }), + "Subscriber onAgentsChanged error:", + ); + } +} diff --git a/src/v2.x/packages/core/src/core/context-store.ts b/src/v2.x/packages/core/src/core/context-store.ts new file mode 100644 index 0000000000..f7baf9df5d --- /dev/null +++ b/src/v2.x/packages/core/src/core/context-store.ts @@ -0,0 +1,54 @@ +import { Context } from "@ag-ui/client"; +import { randomUUID } from "@copilotkitnext/shared"; +import type { CopilotKitCore } from "./core"; +import { CopilotKitCoreFriendsAccess } from "./core"; + +/** + * Manages context storage and lifecycle for CopilotKitCore. + * Context represents additional information available to agents during execution. + */ +export class ContextStore { + private _context: Record = {}; + + constructor(private core: CopilotKitCore) {} + + /** + * Get all context entries as a readonly record + */ + get context(): Readonly> { + return this._context; + } + + /** + * Add a new context entry + * @returns The ID of the created context entry + */ + addContext({ description, value }: Context): string { + const id = randomUUID(); + this._context[id] = { description, value }; + void this.notifySubscribers(); + return id; + } + + /** + * Remove a context entry by ID + */ + removeContext(id: string): void { + delete this._context[id]; + void this.notifySubscribers(); + } + + /** + * Notify all subscribers of context changes + */ + private async notifySubscribers(): Promise { + await (this.core as unknown as CopilotKitCoreFriendsAccess).notifySubscribers( + (subscriber) => + subscriber.onContextChanged?.({ + copilotkit: this.core, + context: this._context, + }), + "Subscriber onContextChanged error:", + ); + } +} diff --git a/src/v2.x/packages/core/src/core/core.ts b/src/v2.x/packages/core/src/core/core.ts new file mode 100644 index 0000000000..d24ca51d5e --- /dev/null +++ b/src/v2.x/packages/core/src/core/core.ts @@ -0,0 +1,442 @@ +import { AbstractAgent, Context, State } from "@ag-ui/client"; +import { FrontendTool, SuggestionsConfig, Suggestion, CopilotRuntimeTransport } from "../types"; +import { AgentRegistry, CopilotKitCoreAddAgentParams } from "./agent-registry"; +import { ContextStore } from "./context-store"; +import { SuggestionEngine } from "./suggestion-engine"; +import { + RunHandler, + CopilotKitCoreRunAgentParams, + CopilotKitCoreConnectAgentParams, + CopilotKitCoreGetToolParams, +} from "./run-handler"; +import { StateManager } from "./state-manager"; + +/** Configuration options for `CopilotKitCore`. */ +export interface CopilotKitCoreConfig { + /** The endpoint of the CopilotRuntime. */ + runtimeUrl?: string; + /** Transport style for CopilotRuntime endpoints. Defaults to REST. */ + runtimeTransport?: CopilotRuntimeTransport; + /** Mapping from agent name to its `AbstractAgent` instance. For development only - production requires CopilotRuntime. */ + agents__unsafe_dev_only?: Record; + /** Headers appended to every HTTP request made by `CopilotKitCore`. */ + headers?: Record; + /** Properties sent as `forwardedProps` to the AG-UI agent. */ + properties?: Record; + /** Ordered collection of frontend tools available to the core. */ + tools?: FrontendTool[]; + /** Suggestions config for the core. */ + suggestionsConfig?: SuggestionsConfig[]; +} + +export type { CopilotKitCoreAddAgentParams }; +export type { CopilotKitCoreRunAgentParams, CopilotKitCoreConnectAgentParams, CopilotKitCoreGetToolParams }; + +export interface CopilotKitCoreStopAgentParams { + agent: AbstractAgent; +} + +export type CopilotKitCoreGetSuggestionsResult = { + suggestions: Suggestion[]; + isLoading: boolean; +}; + +export enum CopilotKitCoreErrorCode { + RUNTIME_INFO_FETCH_FAILED = "runtime_info_fetch_failed", + AGENT_CONNECT_FAILED = "agent_connect_failed", + AGENT_RUN_FAILED = "agent_run_failed", + AGENT_RUN_FAILED_EVENT = "agent_run_failed_event", + AGENT_RUN_ERROR_EVENT = "agent_run_error_event", + TOOL_ARGUMENT_PARSE_FAILED = "tool_argument_parse_failed", + TOOL_HANDLER_FAILED = "tool_handler_failed", +} + +export interface CopilotKitCoreSubscriber { + onRuntimeConnectionStatusChanged?: (event: { + copilotkit: CopilotKitCore; + status: CopilotKitCoreRuntimeConnectionStatus; + }) => void | Promise; + onToolExecutionStart?: (event: { + copilotkit: CopilotKitCore; + toolCallId: string; + agentId: string; + toolName: string; + args: unknown; + }) => void | Promise; + onToolExecutionEnd?: (event: { + copilotkit: CopilotKitCore; + toolCallId: string; + agentId: string; + toolName: string; + result: string; + error?: string; + }) => void | Promise; + onAgentsChanged?: (event: { + copilotkit: CopilotKitCore; + agents: Readonly>; + }) => void | Promise; + onContextChanged?: (event: { + copilotkit: CopilotKitCore; + context: Readonly>; + }) => void | Promise; + onSuggestionsConfigChanged?: (event: { + copilotkit: CopilotKitCore; + suggestionsConfig: Readonly>; + }) => void | Promise; + onSuggestionsChanged?: (event: { + copilotkit: CopilotKitCore; + agentId: string; + suggestions: Suggestion[]; + }) => void | Promise; + onSuggestionsStartedLoading?: (event: { copilotkit: CopilotKitCore; agentId: string }) => void | Promise; + onSuggestionsFinishedLoading?: (event: { copilotkit: CopilotKitCore; agentId: string }) => void | Promise; + onPropertiesChanged?: (event: { + copilotkit: CopilotKitCore; + properties: Readonly>; + }) => void | Promise; + onHeadersChanged?: (event: { + copilotkit: CopilotKitCore; + headers: Readonly>; + }) => void | Promise; + onError?: (event: { + copilotkit: CopilotKitCore; + error: Error; + code: CopilotKitCoreErrorCode; + context: Record; + }) => void | Promise; +} + +// Subscription object returned by subscribe() +export interface CopilotKitCoreSubscription { + unsubscribe: () => void; +} + +export enum CopilotKitCoreRuntimeConnectionStatus { + Disconnected = "disconnected", + Connected = "connected", + Connecting = "connecting", + Error = "error", +} + +/** + * Internal interface for delegate classes to access CopilotKitCore methods. + * This provides type safety while allowing controlled access to private functionality. + */ +export interface CopilotKitCoreFriendsAccess { + // Notification methods + notifySubscribers( + handler: (subscriber: CopilotKitCoreSubscriber) => void | Promise, + errorMessage: string, + ): Promise; + + emitError(params: { error: Error; code: CopilotKitCoreErrorCode; context?: Record }): Promise; + + // Getters for internal state + readonly headers: Readonly>; + readonly properties: Readonly>; + readonly context: Readonly>; + + // Internal methods + buildFrontendTools(agentId?: string): import("@ag-ui/client").Tool[]; + getAgent(id: string): AbstractAgent | undefined; + + // References to delegate subsystems + readonly suggestionEngine: { + clearSuggestions(agentId: string): void; + reloadSuggestions(agentId: string): void; + }; +} + +export class CopilotKitCore { + private _headers: Record; + private _properties: Record; + + private subscribers: Set = new Set(); + + // Delegate classes + private agentRegistry: AgentRegistry; + private contextStore: ContextStore; + private suggestionEngine: SuggestionEngine; + private runHandler: RunHandler; + private stateManager: StateManager; + + constructor({ + runtimeUrl, + runtimeTransport = "rest", + headers = {}, + properties = {}, + agents__unsafe_dev_only = {}, + tools = [], + suggestionsConfig = [], + }: CopilotKitCoreConfig) { + this._headers = headers; + this._properties = properties; + + // Initialize delegate classes + this.agentRegistry = new AgentRegistry(this); + this.contextStore = new ContextStore(this); + this.suggestionEngine = new SuggestionEngine(this); + this.runHandler = new RunHandler(this); + this.stateManager = new StateManager(this); + + // Initialize each subsystem + this.agentRegistry.initialize(agents__unsafe_dev_only); + this.runHandler.initialize(tools); + this.suggestionEngine.initialize(suggestionsConfig); + this.stateManager.initialize(); + + this.agentRegistry.setRuntimeTransport(runtimeTransport); + this.agentRegistry.setRuntimeUrl(runtimeUrl); + + // Subscribe to agent changes to track state for new agents + this.subscribe({ + onAgentsChanged: ({ agents }) => { + Object.values(agents).forEach((agent) => { + if (agent.agentId) { + this.stateManager.subscribeToAgent(agent); + } + }); + }, + }); + } + + /** + * Internal method used by delegate classes and subclasses to notify subscribers + */ + protected async notifySubscribers( + handler: (subscriber: CopilotKitCoreSubscriber) => void | Promise, + errorMessage: string, + ): Promise { + await Promise.all( + Array.from(this.subscribers).map(async (subscriber) => { + try { + await handler(subscriber); + } catch (error) { + console.error(errorMessage, error); + } + }), + ); + } + + /** + * Internal method used by delegate classes to emit errors + */ + private async emitError({ + error, + code, + context = {}, + }: { + error: Error; + code: CopilotKitCoreErrorCode; + context?: Record; + }): Promise { + await this.notifySubscribers( + (subscriber) => + subscriber.onError?.({ + copilotkit: this, + error, + code, + context, + }), + "Subscriber onError error:", + ); + } + + /** + * Snapshot accessors + */ + get context(): Readonly> { + return this.contextStore.context; + } + + get agents(): Readonly> { + return this.agentRegistry.agents; + } + + get tools(): Readonly[]> { + return this.runHandler.tools; + } + + get runtimeUrl(): string | undefined { + return this.agentRegistry.runtimeUrl; + } + + setRuntimeUrl(runtimeUrl: string | undefined): void { + this.agentRegistry.setRuntimeUrl(runtimeUrl); + } + + get runtimeTransport(): CopilotRuntimeTransport { + return this.agentRegistry.runtimeTransport; + } + + setRuntimeTransport(runtimeTransport: CopilotRuntimeTransport): void { + this.agentRegistry.setRuntimeTransport(runtimeTransport); + } + + get runtimeVersion(): string | undefined { + return this.agentRegistry.runtimeVersion; + } + + get headers(): Readonly> { + return this._headers; + } + + get properties(): Readonly> { + return this._properties; + } + + get runtimeConnectionStatus(): CopilotKitCoreRuntimeConnectionStatus { + return this.agentRegistry.runtimeConnectionStatus; + } + + /** + * Configuration updates + */ + setHeaders(headers: Record): void { + this._headers = headers; + this.agentRegistry.applyHeadersToAgents(this.agentRegistry.agents as Record); + void this.notifySubscribers( + (subscriber) => + subscriber.onHeadersChanged?.({ + copilotkit: this, + headers: this.headers, + }), + "Subscriber onHeadersChanged error:", + ); + } + + setProperties(properties: Record): void { + this._properties = properties; + void this.notifySubscribers( + (subscriber) => + subscriber.onPropertiesChanged?.({ + copilotkit: this, + properties: this.properties, + }), + "Subscriber onPropertiesChanged error:", + ); + } + + /** + * Agent management (delegated to AgentRegistry) + */ + setAgents__unsafe_dev_only(agents: Record): void { + this.agentRegistry.setAgents__unsafe_dev_only(agents); + } + + addAgent__unsafe_dev_only(params: CopilotKitCoreAddAgentParams): void { + this.agentRegistry.addAgent__unsafe_dev_only(params); + } + + removeAgent__unsafe_dev_only(id: string): void { + this.agentRegistry.removeAgent__unsafe_dev_only(id); + } + + getAgent(id: string): AbstractAgent | undefined { + return this.agentRegistry.getAgent(id); + } + + /** + * Context management (delegated to ContextStore) + */ + addContext(context: Context): string { + return this.contextStore.addContext(context); + } + + removeContext(id: string): void { + this.contextStore.removeContext(id); + } + + /** + * Suggestions management (delegated to SuggestionEngine) + */ + addSuggestionsConfig(config: SuggestionsConfig): string { + return this.suggestionEngine.addSuggestionsConfig(config); + } + + removeSuggestionsConfig(id: string): void { + this.suggestionEngine.removeSuggestionsConfig(id); + } + + reloadSuggestions(agentId: string): void { + this.suggestionEngine.reloadSuggestions(agentId); + } + + clearSuggestions(agentId: string): void { + this.suggestionEngine.clearSuggestions(agentId); + } + + getSuggestions(agentId: string): CopilotKitCoreGetSuggestionsResult { + return this.suggestionEngine.getSuggestions(agentId); + } + + /** + * Tool management (delegated to RunHandler) + */ + addTool = Record>(tool: FrontendTool): void { + this.runHandler.addTool(tool); + } + + removeTool(id: string, agentId?: string): void { + this.runHandler.removeTool(id, agentId); + } + + getTool(params: CopilotKitCoreGetToolParams): FrontendTool | undefined { + return this.runHandler.getTool(params); + } + + setTools(tools: FrontendTool[]): void { + this.runHandler.setTools(tools); + } + + /** + * Subscription lifecycle + */ + subscribe(subscriber: CopilotKitCoreSubscriber): CopilotKitCoreSubscription { + this.subscribers.add(subscriber); + + // Return subscription with unsubscribe method + return { + unsubscribe: () => { + this.subscribers.delete(subscriber); + }, + }; + } + + /** + * Agent connectivity (delegated to RunHandler) + */ + async connectAgent(params: CopilotKitCoreConnectAgentParams): Promise { + return this.runHandler.connectAgent(params); + } + + stopAgent(params: CopilotKitCoreStopAgentParams): void { + params.agent.abortRun(); + } + + async runAgent(params: CopilotKitCoreRunAgentParams): Promise { + return this.runHandler.runAgent(params); + } + + /** + * State management (delegated to StateManager) + */ + getStateByRun(agentId: string, threadId: string, runId: string): State | undefined { + return this.stateManager.getStateByRun(agentId, threadId, runId); + } + + getRunIdForMessage(agentId: string, threadId: string, messageId: string): string | undefined { + return this.stateManager.getRunIdForMessage(agentId, threadId, messageId); + } + + getRunIdsForThread(agentId: string, threadId: string): string[] { + return this.stateManager.getRunIdsForThread(agentId, threadId); + } + + /** + * Internal method used by RunHandler to build frontend tools + */ + private buildFrontendTools(agentId?: string): import("@ag-ui/client").Tool[] { + return this.runHandler.buildFrontendTools(agentId); + } +} diff --git a/src/v2.x/packages/core/src/core/index.ts b/src/v2.x/packages/core/src/core/index.ts new file mode 100644 index 0000000000..3fc88064a2 --- /dev/null +++ b/src/v2.x/packages/core/src/core/index.ts @@ -0,0 +1,6 @@ +export * from "./core"; +export * from "./agent-registry"; +export * from "./context-store"; +export * from "./suggestion-engine"; +export * from "./run-handler"; +export * from "./state-manager"; diff --git a/src/v2.x/packages/core/src/core/run-handler.ts b/src/v2.x/packages/core/src/core/run-handler.ts new file mode 100644 index 0000000000..d8ddefc212 --- /dev/null +++ b/src/v2.x/packages/core/src/core/run-handler.ts @@ -0,0 +1,601 @@ +import { AbstractAgent, AgentSubscriber, HttpAgent, Message, RunAgentResult, Tool } from "@ag-ui/client"; +import { randomUUID, logger } from "@copilotkitnext/shared"; +import { zodToJsonSchema } from "zod-to-json-schema"; +import type { CopilotKitCore } from "./core"; +import { CopilotKitCoreErrorCode, CopilotKitCoreFriendsAccess } from "./core"; +import { FrontendTool } from "../types"; + +export interface CopilotKitCoreRunAgentParams { + agent: AbstractAgent; +} + +export interface CopilotKitCoreConnectAgentParams { + agent: AbstractAgent; +} + +export interface CopilotKitCoreGetToolParams { + toolName: string; + agentId?: string; +} + +/** + * Handles agent execution, tool calling, and agent connectivity for CopilotKitCore. + * Manages the complete lifecycle of agent runs including tool execution and follow-ups. + */ +export class RunHandler { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private _tools: FrontendTool[] = []; + + constructor(private core: CopilotKitCore) {} + + /** + * Get all tools as a readonly array + */ + get tools(): Readonly[]> { + return this._tools; + } + + /** + * Initialize with tools + */ + initialize(tools: FrontendTool[]): void { + this._tools = tools; + } + + /** + * Add a tool to the registry + */ + addTool = Record>(tool: FrontendTool): void { + // Check if a tool with the same name and agentId already exists + const existingToolIndex = this._tools.findIndex((t) => t.name === tool.name && t.agentId === tool.agentId); + + if (existingToolIndex !== -1) { + logger.warn(`Tool already exists: '${tool.name}' for agent '${tool.agentId || "global"}', skipping.`); + return; + } + + this._tools.push(tool); + } + + /** + * Remove a tool by name and optionally by agentId + */ + removeTool(id: string, agentId?: string): void { + this._tools = this._tools.filter((tool) => { + // Remove tool if both name and agentId match + if (agentId !== undefined) { + return !(tool.name === id && tool.agentId === agentId); + } + // If no agentId specified, only remove global tools with matching name + return !(tool.name === id && !tool.agentId); + }); + } + + /** + * Get a tool by name and optionally by agentId. + * If agentId is provided, it will first look for an agent-specific tool, + * then fall back to a global tool with the same name. + */ + getTool(params: CopilotKitCoreGetToolParams): FrontendTool | undefined { + const { toolName, agentId } = params; + + // If agentId is provided, first look for agent-specific tool + if (agentId) { + const agentTool = this._tools.find((tool) => tool.name === toolName && tool.agentId === agentId); + if (agentTool) { + return agentTool; + } + } + + // Fall back to global tool (no agentId) + return this._tools.find((tool) => tool.name === toolName && !tool.agentId); + } + + /** + * Set all tools at once. Replaces existing tools. + */ + setTools(tools: FrontendTool[]): void { + this._tools = [...tools]; + } + + /** + * Connect an agent (establish initial connection) + */ + async connectAgent({ agent }: CopilotKitCoreConnectAgentParams): Promise { + try { + // Detach any active run before connecting to avoid previous runs interfering + await agent.detachActiveRun(); + agent.setMessages([]); + agent.setState({}); + + if (agent instanceof HttpAgent) { + agent.headers = { ...(this.core as unknown as CopilotKitCoreFriendsAccess).headers }; + } + + const runAgentResult = await agent.connectAgent( + { + forwardedProps: (this.core as unknown as CopilotKitCoreFriendsAccess).properties, + tools: this.buildFrontendTools(agent.agentId), + }, + this.createAgentErrorSubscriber(agent), + ); + + return this.processAgentResult({ runAgentResult, agent }); + } catch (error) { + const connectError = error instanceof Error ? error : new Error(String(error)); + const context: Record = {}; + if (agent.agentId) { + context.agentId = agent.agentId; + } + await (this.core as unknown as CopilotKitCoreFriendsAccess).emitError({ + error: connectError, + code: CopilotKitCoreErrorCode.AGENT_CONNECT_FAILED, + context, + }); + throw error; + } + } + + /** + * Run an agent + */ + async runAgent({ agent }: CopilotKitCoreRunAgentParams): Promise { + // Agent ID is guaranteed to be set by validateAndAssignAgentId + if (agent.agentId) { + void (this.core as unknown as CopilotKitCoreFriendsAccess).suggestionEngine.clearSuggestions(agent.agentId); + } + + if (agent instanceof HttpAgent) { + agent.headers = { ...(this.core as unknown as CopilotKitCoreFriendsAccess).headers }; + } + + try { + const runAgentResult = await agent.runAgent( + { + forwardedProps: (this.core as unknown as CopilotKitCoreFriendsAccess).properties, + tools: this.buildFrontendTools(agent.agentId), + context: Object.values((this.core as unknown as CopilotKitCoreFriendsAccess).context), + }, + this.createAgentErrorSubscriber(agent), + ); + return this.processAgentResult({ runAgentResult, agent }); + } catch (error) { + const runError = error instanceof Error ? error : new Error(String(error)); + const context: Record = {}; + if (agent.agentId) { + context.agentId = agent.agentId; + } + await (this.core as unknown as CopilotKitCoreFriendsAccess).emitError({ + error: runError, + code: CopilotKitCoreErrorCode.AGENT_RUN_FAILED, + context, + }); + throw error; + } + } + + /** + * Process agent result and execute tools + */ + private async processAgentResult({ + runAgentResult, + agent, + }: { + runAgentResult: RunAgentResult; + agent: AbstractAgent; + }): Promise { + const { newMessages } = runAgentResult; + // Agent ID is guaranteed to be set by validateAndAssignAgentId + const agentId = agent.agentId!; + + let needsFollowUp = false; + + for (const message of newMessages) { + if (message.role === "assistant") { + for (const toolCall of message.toolCalls || []) { + if (newMessages.findIndex((m) => m.role === "tool" && m.toolCallId === toolCall.id) === -1) { + const tool = this.getTool({ + toolName: toolCall.function.name, + agentId: agent.agentId, + }); + if (tool) { + const followUp = await this.executeSpecificTool(tool, toolCall, message, agent, agentId); + if (followUp) { + needsFollowUp = true; + } + } else { + // Wildcard fallback for undefined tools + const wildcardTool = this.getTool({ toolName: "*", agentId: agent.agentId }); + if (wildcardTool) { + const followUp = await this.executeWildcardTool(wildcardTool, toolCall, message, agent, agentId); + if (followUp) { + needsFollowUp = true; + } + } + } + } + } + } + } + + if (needsFollowUp) { + return await this.runAgent({ agent }); + } + + void (this.core as unknown as CopilotKitCoreFriendsAccess).suggestionEngine.reloadSuggestions(agentId); + + return runAgentResult; + } + + /** + * Execute a specific tool + */ + private async executeSpecificTool( + tool: FrontendTool, + toolCall: any, + message: Message, + agent: AbstractAgent, + agentId: string, + ): Promise { + // Check if tool is constrained to a specific agent + if (tool?.agentId && tool.agentId !== agent.agentId) { + // Tool is not available for this agent, skip it + return false; + } + + let toolCallResult = ""; + let errorMessage: string | undefined; + let isArgumentError = false; + + if (tool?.handler) { + let parsedArgs: unknown; + try { + parsedArgs = JSON.parse(toolCall.function.arguments); + } catch (error) { + const parseError = error instanceof Error ? error : new Error(String(error)); + errorMessage = parseError.message; + isArgumentError = true; + await (this.core as unknown as CopilotKitCoreFriendsAccess).emitError({ + error: parseError, + code: CopilotKitCoreErrorCode.TOOL_ARGUMENT_PARSE_FAILED, + context: { + agentId: agentId, + toolCallId: toolCall.id, + toolName: toolCall.function.name, + rawArguments: toolCall.function.arguments, + toolType: "specific", + messageId: message.id, + }, + }); + } + + await (this.core as unknown as CopilotKitCoreFriendsAccess).notifySubscribers( + (subscriber) => + subscriber.onToolExecutionStart?.({ + copilotkit: this.core, + toolCallId: toolCall.id, + agentId: agentId, + toolName: toolCall.function.name, + args: parsedArgs, + }), + "Subscriber onToolExecutionStart error:", + ); + + if (!errorMessage) { + try { + const result = await tool.handler(parsedArgs as any, toolCall); + if (result === undefined || result === null) { + toolCallResult = ""; + } else if (typeof result === "string") { + toolCallResult = result; + } else { + toolCallResult = JSON.stringify(result); + } + } catch (error) { + const handlerError = error instanceof Error ? error : new Error(String(error)); + errorMessage = handlerError.message; + await (this.core as unknown as CopilotKitCoreFriendsAccess).emitError({ + error: handlerError, + code: CopilotKitCoreErrorCode.TOOL_HANDLER_FAILED, + context: { + agentId: agentId, + toolCallId: toolCall.id, + toolName: toolCall.function.name, + parsedArgs, + toolType: "specific", + messageId: message.id, + }, + }); + } + } + + if (errorMessage) { + toolCallResult = `Error: ${errorMessage}`; + } + + await (this.core as unknown as CopilotKitCoreFriendsAccess).notifySubscribers( + (subscriber) => + subscriber.onToolExecutionEnd?.({ + copilotkit: this.core, + toolCallId: toolCall.id, + agentId: agentId, + toolName: toolCall.function.name, + result: errorMessage ? "" : toolCallResult, + error: errorMessage, + }), + "Subscriber onToolExecutionEnd error:", + ); + + if (isArgumentError) { + throw new Error(errorMessage ?? "Tool execution failed"); + } + } + + if (!errorMessage || !isArgumentError) { + const messageIndex = agent.messages.findIndex((m) => m.id === message.id); + const toolMessage = { + id: randomUUID(), + role: "tool" as const, + toolCallId: toolCall.id, + content: toolCallResult, + }; + agent.messages.splice(messageIndex + 1, 0, toolMessage); + + if (!errorMessage && tool?.followUp !== false) { + return true; // Needs follow-up + } + } + + return false; + } + + /** + * Execute a wildcard tool + */ + private async executeWildcardTool( + wildcardTool: FrontendTool, + toolCall: any, + message: Message, + agent: AbstractAgent, + agentId: string, + ): Promise { + // Check if wildcard tool is constrained to a specific agent + if (wildcardTool?.agentId && wildcardTool.agentId !== agent.agentId) { + // Wildcard tool is not available for this agent, skip it + return false; + } + + let toolCallResult = ""; + let errorMessage: string | undefined; + let isArgumentError = false; + + if (wildcardTool?.handler) { + let parsedArgs: unknown; + try { + parsedArgs = JSON.parse(toolCall.function.arguments); + } catch (error) { + const parseError = error instanceof Error ? error : new Error(String(error)); + errorMessage = parseError.message; + isArgumentError = true; + await (this.core as unknown as CopilotKitCoreFriendsAccess).emitError({ + error: parseError, + code: CopilotKitCoreErrorCode.TOOL_ARGUMENT_PARSE_FAILED, + context: { + agentId: agentId, + toolCallId: toolCall.id, + toolName: toolCall.function.name, + rawArguments: toolCall.function.arguments, + toolType: "wildcard", + messageId: message.id, + }, + }); + } + + const wildcardArgs = { + toolName: toolCall.function.name, + args: parsedArgs, + }; + + await (this.core as unknown as CopilotKitCoreFriendsAccess).notifySubscribers( + (subscriber) => + subscriber.onToolExecutionStart?.({ + copilotkit: this.core, + toolCallId: toolCall.id, + agentId: agentId, + toolName: toolCall.function.name, + args: wildcardArgs, + }), + "Subscriber onToolExecutionStart error:", + ); + + if (!errorMessage) { + try { + const result = await wildcardTool.handler(wildcardArgs as any, toolCall); + if (result === undefined || result === null) { + toolCallResult = ""; + } else if (typeof result === "string") { + toolCallResult = result; + } else { + toolCallResult = JSON.stringify(result); + } + } catch (error) { + const handlerError = error instanceof Error ? error : new Error(String(error)); + errorMessage = handlerError.message; + await (this.core as unknown as CopilotKitCoreFriendsAccess).emitError({ + error: handlerError, + code: CopilotKitCoreErrorCode.TOOL_HANDLER_FAILED, + context: { + agentId: agentId, + toolCallId: toolCall.id, + toolName: toolCall.function.name, + parsedArgs: wildcardArgs, + toolType: "wildcard", + messageId: message.id, + }, + }); + } + } + + if (errorMessage) { + toolCallResult = `Error: ${errorMessage}`; + } + + await (this.core as unknown as CopilotKitCoreFriendsAccess).notifySubscribers( + (subscriber) => + subscriber.onToolExecutionEnd?.({ + copilotkit: this.core, + toolCallId: toolCall.id, + agentId: agentId, + toolName: toolCall.function.name, + result: errorMessage ? "" : toolCallResult, + error: errorMessage, + }), + "Subscriber onToolExecutionEnd error:", + ); + + if (isArgumentError) { + throw new Error(errorMessage ?? "Tool execution failed"); + } + } + + if (!errorMessage || !isArgumentError) { + const messageIndex = agent.messages.findIndex((m) => m.id === message.id); + const toolMessage = { + id: randomUUID(), + role: "tool" as const, + toolCallId: toolCall.id, + content: toolCallResult, + }; + agent.messages.splice(messageIndex + 1, 0, toolMessage); + + if (!errorMessage && wildcardTool?.followUp !== false) { + return true; // Needs follow-up + } + } + + return false; + } + + /** + * Build frontend tools for an agent + */ + buildFrontendTools(agentId?: string): Tool[] { + return this._tools + .filter((tool) => !tool.agentId || tool.agentId === agentId) + .map((tool) => ({ + name: tool.name, + description: tool.description ?? "", + parameters: createToolSchema(tool), + })); + } + + /** + * Create an agent error subscriber + */ + private createAgentErrorSubscriber(agent: AbstractAgent): AgentSubscriber { + const emitAgentError = async ( + error: Error, + code: CopilotKitCoreErrorCode, + extraContext: Record = {}, + ) => { + const context: Record = { ...extraContext }; + if (agent.agentId) { + context.agentId = agent.agentId; + } + await (this.core as unknown as CopilotKitCoreFriendsAccess).emitError({ + error, + code, + context, + }); + }; + + return { + onRunFailed: async ({ error }: { error: Error }) => { + await emitAgentError(error, CopilotKitCoreErrorCode.AGENT_RUN_FAILED_EVENT, { + source: "onRunFailed", + }); + }, + onRunErrorEvent: async ({ event }) => { + const eventError = + event?.rawEvent instanceof Error + ? event.rawEvent + : event?.rawEvent?.error instanceof Error + ? event.rawEvent.error + : undefined; + + const errorMessage = + typeof event?.rawEvent?.error === "string" ? event.rawEvent.error : (event?.message ?? "Agent run error"); + + const rawError = eventError ?? new Error(errorMessage); + + if (event?.code && !(rawError as any).code) { + (rawError as any).code = event.code; + } + + await emitAgentError(rawError, CopilotKitCoreErrorCode.AGENT_RUN_ERROR_EVENT, { + source: "onRunErrorEvent", + event, + runtimeErrorCode: event?.code, + }); + }, + }; + } +} + +/** + * Empty tool schema constant + */ +const EMPTY_TOOL_SCHEMA = { + type: "object", + properties: {}, +} as const satisfies Record; + +/** + * Create a JSON schema from a tool's parameters + */ +function createToolSchema(tool: FrontendTool): Record { + if (!tool.parameters) { + return { ...EMPTY_TOOL_SCHEMA }; + } + + const rawSchema = zodToJsonSchema(tool.parameters, { + $refStrategy: "none", + }); + + if (!rawSchema || typeof rawSchema !== "object") { + return { ...EMPTY_TOOL_SCHEMA }; + } + + const { $schema, ...schema } = rawSchema as Record; + + if (typeof schema.type !== "string") { + schema.type = "object"; + } + if (typeof schema.properties !== "object" || schema.properties === null) { + schema.properties = {}; + } + + stripAdditionalProperties(schema); + return schema; +} + +function stripAdditionalProperties(schema: unknown): void { + if (!schema || typeof schema !== "object") { + return; + } + + if (Array.isArray(schema)) { + schema.forEach(stripAdditionalProperties); + return; + } + + const record = schema as Record; + + if (record.additionalProperties !== undefined) { + delete record.additionalProperties; + } + + for (const value of Object.values(record)) { + stripAdditionalProperties(value); + } +} diff --git a/src/v2.x/packages/core/src/core/state-manager.ts b/src/v2.x/packages/core/src/core/state-manager.ts new file mode 100644 index 0000000000..83a40f3283 --- /dev/null +++ b/src/v2.x/packages/core/src/core/state-manager.ts @@ -0,0 +1,248 @@ +import { + AbstractAgent, + Message, + State, + RunAgentInput, + RunStartedEvent, + RunFinishedEvent, + StateSnapshotEvent, + StateDeltaEvent, + MessagesSnapshotEvent, +} from "@ag-ui/client"; +import type { CopilotKitCore } from "./core"; + +/** + * Manages state and message tracking by run for CopilotKitCore. + * Tracks agent state snapshots and message-to-run associations. + */ +export class StateManager { + // State tracking: agentId -> threadId -> runId -> state + private stateByRun: Map>> = new Map(); + + // Message tracking: agentId -> threadId -> messageId -> runId + private messageToRun: Map>> = new Map(); + + // Agent subscriptions for cleanup + private agentSubscriptions: Map void> = new Map(); + + constructor(private core: CopilotKitCore) {} + + /** + * Initialize state tracking for an agent + */ + initialize(): void { + // Will be called when CopilotKitCore is initialized + } + + /** + * Subscribe to an agent's events to track state and messages + */ + subscribeToAgent(agent: AbstractAgent): void { + if (!agent.agentId) { + return; // Skip agents without IDs + } + + const agentId = agent.agentId; + + // Unsubscribe existing subscription if any + this.unsubscribeFromAgent(agentId); + + // Subscribe to agent events + const { unsubscribe } = agent.subscribe({ + onRunStartedEvent: ({ event, state }) => { + this.handleRunStarted(agent, event, state); + }, + onRunFinishedEvent: ({ event, state }) => { + this.handleRunFinished(agent, event, state); + }, + onStateSnapshotEvent: ({ event, input, state }) => { + this.handleStateSnapshot(agent, event, input, state); + }, + onStateDeltaEvent: ({ event, input, state }) => { + this.handleStateDelta(agent, event, input, state); + }, + onMessagesSnapshotEvent: ({ event, input, messages }) => { + this.handleMessagesSnapshot(agent, event, input, messages); + }, + onNewMessage: ({ message, input }) => { + this.handleNewMessage(agent, message, input); + }, + }); + + this.agentSubscriptions.set(agentId, unsubscribe); + } + + /** + * Unsubscribe from an agent's events + */ + unsubscribeFromAgent(agentId: string): void { + const unsubscribe = this.agentSubscriptions.get(agentId); + if (unsubscribe) { + unsubscribe(); + this.agentSubscriptions.delete(agentId); + } + } + + /** + * Get state for a specific run + * Returns a deep copy to prevent external mutations + */ + getStateByRun(agentId: string, threadId: string, runId: string): State | undefined { + const state = this.stateByRun.get(agentId)?.get(threadId)?.get(runId); + if (!state) return undefined; + // Return a deep copy to prevent mutations + return JSON.parse(JSON.stringify(state)); + } + + /** + * Get runId associated with a message + */ + getRunIdForMessage(agentId: string, threadId: string, messageId: string): string | undefined { + return this.messageToRun.get(agentId)?.get(threadId)?.get(messageId); + } + + /** + * Get all states for an agent's thread + */ + getStatesForThread(agentId: string, threadId: string): Map { + return this.stateByRun.get(agentId)?.get(threadId) ?? new Map(); + } + + /** + * Get all run IDs for an agent's thread + */ + getRunIdsForThread(agentId: string, threadId: string): string[] { + const threadStates = this.stateByRun.get(agentId)?.get(threadId); + return threadStates ? Array.from(threadStates.keys()) : []; + } + + /** + * Handle run started event + */ + private handleRunStarted(agent: AbstractAgent, event: RunStartedEvent, state: State): void { + if (!agent.agentId) return; + + const { threadId, runId } = event; + this.saveState(agent.agentId, threadId, runId, state); + } + + /** + * Handle run finished event + */ + private handleRunFinished(agent: AbstractAgent, event: RunFinishedEvent, state: State): void { + if (!agent.agentId) return; + + const { threadId, runId } = event; + this.saveState(agent.agentId, threadId, runId, state); + } + + /** + * Handle state snapshot event + */ + private handleStateSnapshot( + agent: AbstractAgent, + event: StateSnapshotEvent, + input: RunAgentInput, + state: State, + ): void { + if (!agent.agentId) return; + + const { threadId, runId } = input; + // Merge snapshot into current state + const mergedState = { ...state, ...event.snapshot }; + this.saveState(agent.agentId, threadId, runId, mergedState); + } + + /** + * Handle state delta event + */ + private handleStateDelta(agent: AbstractAgent, event: StateDeltaEvent, input: RunAgentInput, state: State): void { + if (!agent.agentId) return; + + const { threadId, runId } = input; + // State is already updated by the agent, just save it + this.saveState(agent.agentId, threadId, runId, state); + } + + /** + * Handle messages snapshot event + */ + private handleMessagesSnapshot( + agent: AbstractAgent, + event: MessagesSnapshotEvent, + input: RunAgentInput, + messages: Message[], + ): void { + if (!agent.agentId) return; + + const { threadId, runId } = input; + + // Associate all messages in the snapshot with this run + for (const message of event.messages) { + this.associateMessageWithRun(agent.agentId, threadId, message.id, runId); + } + } + + /** + * Handle new message event + */ + private handleNewMessage(agent: AbstractAgent, message: Message, input?: RunAgentInput): void { + if (!agent.agentId || !input) return; + + const { threadId, runId } = input; + this.associateMessageWithRun(agent.agentId, threadId, message.id, runId); + } + + /** + * Save state for a specific run + */ + private saveState(agentId: string, threadId: string, runId: string, state: State): void { + // Ensure nested maps exist + if (!this.stateByRun.has(agentId)) { + this.stateByRun.set(agentId, new Map()); + } + const agentStates = this.stateByRun.get(agentId)!; + + if (!agentStates.has(threadId)) { + agentStates.set(threadId, new Map()); + } + const threadStates = agentStates.get(threadId)!; + + // Deep copy the state to prevent mutations + threadStates.set(runId, JSON.parse(JSON.stringify(state))); + } + + /** + * Associate a message with a run + */ + private associateMessageWithRun(agentId: string, threadId: string, messageId: string, runId: string): void { + // Ensure nested maps exist + if (!this.messageToRun.has(agentId)) { + this.messageToRun.set(agentId, new Map()); + } + const agentMessages = this.messageToRun.get(agentId)!; + + if (!agentMessages.has(threadId)) { + agentMessages.set(threadId, new Map()); + } + const threadMessages = agentMessages.get(threadId)!; + + threadMessages.set(messageId, runId); + } + + /** + * Clear all state for an agent + */ + clearAgentState(agentId: string): void { + this.stateByRun.delete(agentId); + this.messageToRun.delete(agentId); + } + + /** + * Clear all state for a thread + */ + clearThreadState(agentId: string, threadId: string): void { + this.stateByRun.get(agentId)?.delete(threadId); + this.messageToRun.get(agentId)?.delete(threadId); + } +} diff --git a/src/v2.x/packages/core/src/core/suggestion-engine.ts b/src/v2.x/packages/core/src/core/suggestion-engine.ts new file mode 100644 index 0000000000..9f68e70de8 --- /dev/null +++ b/src/v2.x/packages/core/src/core/suggestion-engine.ts @@ -0,0 +1,444 @@ +import { AbstractAgent, Message, Tool, Context } from "@ag-ui/client"; +import { randomUUID, partialJSONParse } from "@copilotkitnext/shared"; +import type { CopilotKitCore } from "./core"; +import type { CopilotKitCoreGetSuggestionsResult } from "./core"; +import { CopilotKitCoreFriendsAccess } from "./core"; +import { DynamicSuggestionsConfig, StaticSuggestionsConfig, Suggestion, SuggestionsConfig } from "../types"; + +/** + * Manages suggestion generation, streaming, and lifecycle for CopilotKitCore. + * Handles both dynamic (AI-generated) and static suggestions. + */ +export class SuggestionEngine { + private _suggestionsConfig: Record = {}; + private _suggestions: Record> = {}; + private _runningSuggestions: Record = {}; + + constructor(private core: CopilotKitCore) {} + + /** + * Initialize with suggestion configs + */ + initialize(suggestionsConfig: SuggestionsConfig[]): void { + for (const config of suggestionsConfig) { + this._suggestionsConfig[randomUUID()] = config; + } + } + + /** + * Add a suggestion configuration + * @returns The ID of the created config + */ + addSuggestionsConfig(config: SuggestionsConfig): string { + const id = randomUUID(); + this._suggestionsConfig[id] = config; + void this.notifySuggestionsConfigChanged(); + return id; + } + + /** + * Remove a suggestion configuration by ID + */ + removeSuggestionsConfig(id: string): void { + delete this._suggestionsConfig[id]; + void this.notifySuggestionsConfigChanged(); + } + + /** + * Reload suggestions for a specific agent + * This triggers generation of new suggestions based on current configs + */ + public reloadSuggestions(agentId: string): void { + this.clearSuggestions(agentId); + + // Get agent to check message count for availability filtering + const agent = (this.core as unknown as CopilotKitCoreFriendsAccess).getAgent(agentId); + if (!agent) { + return; + } + + const messageCount = agent.messages?.length ?? 0; + let hasAnySuggestions = false; + + for (const config of Object.values(this._suggestionsConfig)) { + // Check if config applies to this agent + if ( + config.consumerAgentId !== undefined && + config.consumerAgentId !== "*" && + config.consumerAgentId !== agentId + ) { + continue; + } + + // Check availability based on message count + if (!this.shouldShowSuggestions(config, messageCount)) { + continue; + } + + const suggestionId = randomUUID(); + + if (isDynamicSuggestionsConfig(config)) { + if (!hasAnySuggestions) { + hasAnySuggestions = true; + void this.notifySuggestionsStartedLoading(agentId); + } + void this.generateSuggestions(suggestionId, config, agentId); + } else if (isStaticSuggestionsConfig(config)) { + this.addStaticSuggestions(suggestionId, config, agentId); + } + } + } + + /** + * Clear all suggestions for a specific agent + */ + public clearSuggestions(agentId: string): void { + const runningAgents = this._runningSuggestions[agentId]; + if (runningAgents) { + for (const agent of runningAgents) { + agent.abortRun(); + } + delete this._runningSuggestions[agentId]; + } + this._suggestions[agentId] = {}; + + void this.notifySuggestionsChanged(agentId, []); + } + + /** + * Get current suggestions for an agent + */ + public getSuggestions(agentId: string): CopilotKitCoreGetSuggestionsResult { + const suggestions = Object.values(this._suggestions[agentId] ?? {}).flat(); + const isLoading = (this._runningSuggestions[agentId]?.length ?? 0) > 0; + return { suggestions, isLoading }; + } + + /** + * Generate suggestions using a provider agent + */ + private async generateSuggestions( + suggestionId: string, + config: DynamicSuggestionsConfig, + consumerAgentId: string, + ): Promise { + let agent: AbstractAgent | undefined = undefined; + try { + const suggestionsProviderAgent = (this.core as unknown as CopilotKitCoreFriendsAccess).getAgent( + config.providerAgentId ?? "default", + ); + if (!suggestionsProviderAgent) { + throw new Error(`Suggestions provider agent not found: ${config.providerAgentId}`); + } + const suggestionsConsumerAgent = (this.core as unknown as CopilotKitCoreFriendsAccess).getAgent(consumerAgentId); + if (!suggestionsConsumerAgent) { + throw new Error(`Suggestions consumer agent not found: ${consumerAgentId}`); + } + + const clonedAgent: AbstractAgent = suggestionsProviderAgent.clone(); + agent = clonedAgent; + //agent.agentId = suggestionId; + agent.threadId = suggestionId; + agent.messages = JSON.parse(JSON.stringify(suggestionsConsumerAgent.messages)); + agent.state = JSON.parse(JSON.stringify(suggestionsConsumerAgent.state)); + + // Initialize suggestion storage for this agent/suggestion combo + this._suggestions[consumerAgentId] = { + ...(this._suggestions[consumerAgentId] ?? {}), + [suggestionId]: [], + }; + this._runningSuggestions[consumerAgentId] = [...(this._runningSuggestions[consumerAgentId] ?? []), agent]; + + agent.addMessage({ + id: suggestionId, + role: "user", + content: [ + `Suggest what the user could say next. Provide clear, highly relevant suggestions by calling the \`copilotkitSuggest\` tool.`, + `Provide at least ${config.minSuggestions ?? 1} and at most ${config.maxSuggestions ?? 3} suggestions.`, + `The user has the following tools available: ${JSON.stringify((this.core as unknown as CopilotKitCoreFriendsAccess).buildFrontendTools(consumerAgentId))}.`, + ` ${config.instructions}`, + ].join("\n"), + }); + + await agent.runAgent( + { + context: Object.values((this.core as unknown as CopilotKitCoreFriendsAccess).context), + forwardedProps: { + ...(this.core as unknown as CopilotKitCoreFriendsAccess).properties, + toolChoice: { type: "function", function: { name: "copilotkitSuggest" } }, + }, + tools: [SUGGEST_TOOL], + }, + { + onMessagesChanged: ({ messages }: { messages: Message[] }) => { + this.extractSuggestions(messages, suggestionId, consumerAgentId, true); + }, + }, + ); + } catch (error) { + console.warn("Error generating suggestions:", error); + } finally { + // Finalize suggestions by marking them as no longer loading + this.finalizeSuggestions(suggestionId, consumerAgentId); + + // Remove this agent from running suggestions + const runningAgents = this._runningSuggestions[consumerAgentId]; + if (agent && runningAgents) { + const filteredAgents = runningAgents.filter((a) => a !== agent); + this._runningSuggestions[consumerAgentId] = filteredAgents; + + // If no more suggestions are running, emit loading end event + if (filteredAgents.length === 0) { + delete this._runningSuggestions[consumerAgentId]; + await this.notifySuggestionsFinishedLoading(consumerAgentId); + } + } + } + } + + /** + * Finalize suggestions by marking them as no longer loading + */ + private finalizeSuggestions(suggestionId: string, consumerAgentId: string): void { + const agentSuggestions = this._suggestions[consumerAgentId]; + const currentSuggestions = agentSuggestions?.[suggestionId]; + + if (agentSuggestions && currentSuggestions && currentSuggestions.length > 0) { + // Filter out empty suggestions and mark remaining as no longer loading + const finalizedSuggestions = currentSuggestions + .filter((suggestion) => suggestion.title !== "" || suggestion.message !== "") + .map((suggestion) => ({ + ...suggestion, + isLoading: false, + })); + + if (finalizedSuggestions.length > 0) { + agentSuggestions[suggestionId] = finalizedSuggestions; + } else { + delete agentSuggestions[suggestionId]; + } + + // Get all aggregated suggestions for this agent + const allSuggestions = Object.values(this._suggestions[consumerAgentId] ?? {}).flat(); + + void this.notifySuggestionsChanged(consumerAgentId, allSuggestions, "finalized"); + } + } + + /** + * Extract suggestions from messages (called during streaming) + */ + extractSuggestions(messages: Message[], suggestionId: string, consumerAgentId: string, isRunning: boolean): void { + const idx = messages.findIndex((message) => message.id === suggestionId); + if (idx == -1) { + return; + } + + const suggestions: Suggestion[] = []; + const newMessages = messages.slice(idx + 1); + + for (const message of newMessages) { + if (message.role === "assistant" && message.toolCalls) { + for (const toolCall of message.toolCalls) { + if (toolCall.function.name === "copilotkitSuggest") { + // Join all argument chunks into a single string for parsing + // arguments can be either a string or an array of strings + const fullArgs = Array.isArray(toolCall.function.arguments) + ? toolCall.function.arguments.join("") + : toolCall.function.arguments; + const parsed = partialJSONParse(fullArgs); + if (parsed && typeof parsed === "object" && "suggestions" in parsed) { + const parsedSuggestions = (parsed as any).suggestions; + if (Array.isArray(parsedSuggestions)) { + for (const item of parsedSuggestions) { + if (item && typeof item === "object" && "title" in item) { + suggestions.push({ + title: item.title ?? "", + message: item.message ?? "", + isLoading: false, // Will be set correctly below + }); + } + } + } + } + } + } + } + } + + // Set isLoading for the last suggestion if still running + if (isRunning && suggestions.length > 0) { + suggestions[suggestions.length - 1]!.isLoading = true; + } + + const agentSuggestions = this._suggestions[consumerAgentId]; + if (agentSuggestions) { + agentSuggestions[suggestionId] = suggestions; + + // Get all aggregated suggestions for this agent + const allSuggestions = Object.values(this._suggestions[consumerAgentId] ?? {}).flat(); + + void this.notifySuggestionsChanged(consumerAgentId, allSuggestions, "suggestions changed"); + } + } + + /** + * Notify subscribers of suggestions config changes + */ + private async notifySuggestionsConfigChanged(): Promise { + await (this.core as unknown as CopilotKitCoreFriendsAccess).notifySubscribers( + (subscriber) => + subscriber.onSuggestionsConfigChanged?.({ + copilotkit: this.core, + suggestionsConfig: this._suggestionsConfig, + }), + "Subscriber onSuggestionsConfigChanged error:", + ); + } + + /** + * Notify subscribers of suggestions changes + */ + private async notifySuggestionsChanged( + agentId: string, + suggestions: Suggestion[], + context: string = "", + ): Promise { + await (this.core as unknown as CopilotKitCoreFriendsAccess).notifySubscribers( + (subscriber) => + subscriber.onSuggestionsChanged?.({ + copilotkit: this.core, + agentId, + suggestions, + }), + `Subscriber onSuggestionsChanged error: ${context}`, + ); + } + + /** + * Notify subscribers that suggestions started loading + */ + private async notifySuggestionsStartedLoading(agentId: string): Promise { + await (this.core as unknown as CopilotKitCoreFriendsAccess).notifySubscribers( + (subscriber) => + subscriber.onSuggestionsStartedLoading?.({ + copilotkit: this.core, + agentId, + }), + "Subscriber onSuggestionsStartedLoading error:", + ); + } + + /** + * Notify subscribers that suggestions finished loading + */ + private async notifySuggestionsFinishedLoading(agentId: string): Promise { + await (this.core as unknown as CopilotKitCoreFriendsAccess).notifySubscribers( + (subscriber) => + subscriber.onSuggestionsFinishedLoading?.({ + copilotkit: this.core, + agentId, + }), + "Subscriber onSuggestionsFinishedLoading error:", + ); + } + + /** + * Check if suggestions should be shown based on availability and message count + */ + private shouldShowSuggestions(config: SuggestionsConfig, messageCount: number): boolean { + const availability = config.available; + + // Default behavior if no availability specified + if (!availability) { + if (isDynamicSuggestionsConfig(config)) { + return messageCount > 0; // Default: after-first-message + } else { + return messageCount === 0; // Default: before-first-message + } + } + + switch (availability) { + case "disabled": + return false; + case "before-first-message": + return messageCount === 0; + case "after-first-message": + return messageCount > 0; + case "always": + return true; + default: + return false; + } + } + + /** + * Add static suggestions directly without AI generation + */ + private addStaticSuggestions(suggestionId: string, config: StaticSuggestionsConfig, consumerAgentId: string): void { + // Mark all as not loading since they're static + const suggestions = config.suggestions.map((s) => ({ + ...s, + isLoading: false, + })); + + // Store suggestions + this._suggestions[consumerAgentId] = { + ...(this._suggestions[consumerAgentId] ?? {}), + [suggestionId]: suggestions, + }; + + // Notify subscribers + const allSuggestions = Object.values(this._suggestions[consumerAgentId] ?? {}).flat(); + + void this.notifySuggestionsChanged(consumerAgentId, allSuggestions, "static suggestions added"); + } +} + +/** + * Type guard for dynamic suggestions config + */ +function isDynamicSuggestionsConfig(config: SuggestionsConfig): config is DynamicSuggestionsConfig { + return "instructions" in config; +} + +/** + * Type guard for static suggestions config + */ +function isStaticSuggestionsConfig(config: SuggestionsConfig): config is StaticSuggestionsConfig { + return "suggestions" in config; +} + +/** + * The tool definition for AI-generated suggestions + */ +const SUGGEST_TOOL: Tool = { + name: "copilotkitSuggest", + description: "Suggest what the user could say next", + parameters: { + type: "object", + properties: { + suggestions: { + type: "array", + description: "List of suggestions shown to the user as buttons.", + items: { + type: "object", + properties: { + title: { + type: "string", + description: "The title of the suggestion. This is shown as a button and should be short.", + }, + message: { + type: "string", + description: + "The message to send when the suggestion is clicked. This should be a clear, complete sentence " + + "and will be sent as an instruction to the AI.", + }, + }, + required: ["title", "message"], + }, + }, + }, + required: ["suggestions"], + }, +}; diff --git a/src/v2.x/packages/core/src/index.ts b/src/v2.x/packages/core/src/index.ts new file mode 100644 index 0000000000..5dd8618c51 --- /dev/null +++ b/src/v2.x/packages/core/src/index.ts @@ -0,0 +1,4 @@ +export * from "./core"; +export * from "./types"; +export * from "./agent"; +export * from "./utils/markdown"; diff --git a/src/v2.x/packages/core/src/types.ts b/src/v2.x/packages/core/src/types.ts new file mode 100644 index 0000000000..cd29f4f316 --- /dev/null +++ b/src/v2.x/packages/core/src/types.ts @@ -0,0 +1,86 @@ +import { ToolCall } from "@ag-ui/client"; +import { z } from "zod"; + +/** + * Status of a tool call execution + */ +export enum ToolCallStatus { + InProgress = "inProgress", + Executing = "executing", + Complete = "complete", +} + +export type CopilotRuntimeTransport = "rest" | "single"; + +export type FrontendTool = Record> = { + name: string; + description?: string; + parameters?: z.ZodType; + handler?: (args: T, toolCall: ToolCall) => Promise; + followUp?: boolean; + /** + * Optional agent ID to constrain this tool to a specific agent. + * If specified, this tool will only be available to the specified agent. + */ + agentId?: string; +}; + +export type Suggestion = { + title: string; + message: string; + /** Indicates whether this suggestion is still being generated. */ + isLoading: boolean; +}; + +export type SuggestionAvailability = "before-first-message" | "after-first-message" | "always" | "disabled"; + +export type DynamicSuggestionsConfig = { + /** + * A prompt or instructions for the GPT to generate suggestions. + */ + instructions: string; + /** + * The minimum number of suggestions to generate. Defaults to `1`. + * @default 1 + */ + minSuggestions?: number; + /** + * The maximum number of suggestions to generate. Defaults to `3`. + * @default 1 + */ + maxSuggestions?: number; + + /** + * When the suggestions are available. Defaults to "after-first-message". + */ + available?: SuggestionAvailability; + + /** + * The agent ID of the provider of the suggestions. Defaults to `"default"`. + */ + providerAgentId?: string; + + /** + * The agent ID of the consumer of the suggestions. Defaults to `"*"` (all agents). + */ + consumerAgentId?: string; +}; + +export type StaticSuggestionsConfig = { + /** + * The suggestions to display. + */ + suggestions: Omit[]; + + /** + * When the suggestions are available. Defaults to "before-first-message". + */ + available?: SuggestionAvailability; + + /** + * The agent ID of the consumer of the suggestions. Defaults to `"*"` (all agents). + */ + consumerAgentId?: string; +}; + +export type SuggestionsConfig = DynamicSuggestionsConfig | StaticSuggestionsConfig; diff --git a/src/v2.x/packages/core/src/utils/markdown.ts b/src/v2.x/packages/core/src/utils/markdown.ts new file mode 100644 index 0000000000..6383663551 --- /dev/null +++ b/src/v2.x/packages/core/src/utils/markdown.ts @@ -0,0 +1,271 @@ +export function completePartialMarkdown(input: string): string { + let s = input; + + // Handle code fences first - use FIRST unmatched fence for proper nesting + const fenceMatches = Array.from(s.matchAll(/^(\s*)(`{3,}|~{3,})/gm)); + if (fenceMatches.length % 2 === 1) { + const [, indent, fence] = fenceMatches[0]!; + s += `\n${indent}${fence}`; + } + + // Identify incomplete links at the end and close them + const incompleteLinkMatch = s.match(/\[([^\]]*)\]\(([^)]*)$/); + if (incompleteLinkMatch) { + s += ")"; + } + + // State-based parsing + interface OpenElement { + type: string; + marker: string; + position: number; + } + + const openElements: OpenElement[] = []; + const chars = Array.from(s); + + // First pass: identify code block boundaries and inline code to avoid processing their content + const codeBlockRanges: Array<{ start: number; end: number }> = []; + const inlineCodeRanges: Array<{ start: number; end: number }> = []; + + // Find code block ranges + let tempCodeFenceCount = 0; + let currentCodeBlockStart = -1; + + for (let i = 0; i < chars.length; i++) { + if (i === 0 || chars[i - 1] === "\n") { + const lineMatch = s.substring(i).match(/^(\s*)(`{3,}|~{3,})/); + if (lineMatch) { + tempCodeFenceCount++; + if (tempCodeFenceCount % 2 === 1) { + currentCodeBlockStart = i; + } else if (currentCodeBlockStart !== -1) { + codeBlockRanges.push({ + start: currentCodeBlockStart, + end: i + lineMatch[0].length, + }); + currentCodeBlockStart = -1; + } + i += lineMatch[0].length - 1; + } + } + } + + // Find inline code ranges + for (let i = 0; i < chars.length; i++) { + if (chars[i] === "`") { + // Check if escaped + let backslashCount = 0; + for (let j = i - 1; j >= 0 && chars[j] === "\\"; j--) { + backslashCount++; + } + if (backslashCount % 2 === 0) { + // Not escaped - find the closing backtick + for (let j = i + 1; j < chars.length; j++) { + if (chars[j] === "`") { + let closingBackslashCount = 0; + for (let k = j - 1; k >= 0 && chars[k] === "\\"; k--) { + closingBackslashCount++; + } + if (closingBackslashCount % 2 === 0) { + inlineCodeRanges.push({ start: i, end: j + 1 }); + i = j; + break; + } + } + } + } + } + } + + // Helper function to check if position is in code + const isInCode = (pos: number): boolean => { + return ( + codeBlockRanges.some((range) => pos >= range.start && pos < range.end) || + inlineCodeRanges.some((range) => pos >= range.start && pos < range.end) + ); + }; + + // Second pass: process markdown elements, skipping code regions + for (let i = 0; i < chars.length; i++) { + const char = chars[i]; + const nextChar = chars[i + 1]; + const prevChar = chars[i - 1]; + + if (isInCode(i)) { + continue; + } + + // Handle brackets (but not if they're part of already-complete links) + if (char === "[") { + // Check if this is part of a complete link [text](url) + let isCompleteLink = false; + let bracketDepth = 1; + let j = i + 1; + + // Find the matching ] + while (j < chars.length && bracketDepth > 0) { + if (chars[j] === "[" && !isInCode(j)) bracketDepth++; + if (chars[j] === "]" && !isInCode(j)) bracketDepth--; + j++; + } + + // Check if followed by ( + if (bracketDepth === 0 && chars[j] === "(") { + // Find the closing ) + let parenDepth = 1; + j++; + while (j < chars.length && parenDepth > 0) { + if (chars[j] === "(" && !isInCode(j)) parenDepth++; + if (chars[j] === ")" && !isInCode(j)) parenDepth--; + j++; + } + if (parenDepth === 0) { + isCompleteLink = true; + i = j - 1; + continue; + } + } + + // This is a standalone bracket, treat as markdown + if (!isCompleteLink) { + const existingIndex = openElements.findIndex( + (el) => el.type === "bracket" + ); + if (existingIndex !== -1) { + openElements.splice(existingIndex, 1); + } else { + openElements.push({ type: "bracket", marker: "[", position: i }); + } + } + } + + // Handle double emphasis first (**, __, ~~) - these take precedence + else if (char === "*" && nextChar === "*") { + const existingIndex = openElements.findIndex( + (el) => el.type === "bold_star" + ); + if (existingIndex !== -1) { + openElements.splice(existingIndex, 1); + } else { + openElements.push({ type: "bold_star", marker: "**", position: i }); + } + i++; // Skip next character + } else if (char === "_" && nextChar === "_") { + const existingIndex = openElements.findIndex( + (el) => el.type === "bold_underscore" + ); + if (existingIndex !== -1) { + openElements.splice(existingIndex, 1); + } else { + openElements.push({ + type: "bold_underscore", + marker: "__", + position: i, + }); + } + i++; // Skip next character + } else if (char === "~" && nextChar === "~") { + const existingIndex = openElements.findIndex( + (el) => el.type === "strike" + ); + if (existingIndex !== -1) { + openElements.splice(existingIndex, 1); + } else { + openElements.push({ type: "strike", marker: "~~", position: i }); + } + i++; // Skip next character + } + + // Handle single emphasis (*, _) - only if not part of double + else if (char === "*" && prevChar !== "*" && nextChar !== "*") { + const existingIndex = openElements.findIndex( + (el) => el.type === "italic_star" + ); + if (existingIndex !== -1) { + openElements.splice(existingIndex, 1); + } else { + openElements.push({ type: "italic_star", marker: "*", position: i }); + } + } else if (char === "_" && prevChar !== "_" && nextChar !== "_") { + const existingIndex = openElements.findIndex( + (el) => el.type === "italic_underscore" + ); + if (existingIndex !== -1) { + openElements.splice(existingIndex, 1); + } else { + openElements.push({ + type: "italic_underscore", + marker: "_", + position: i, + }); + } + } + } + + // Handle remaining unmatched backticks (outside of inline code ranges) + let backtickCount = 0; + for (let i = 0; i < chars.length; i++) { + if (chars[i] === "`" && !isInCode(i)) { + backtickCount++; + } + } + if (backtickCount % 2 === 1) { + s += "`"; + } + + // Close remaining open elements in reverse order (LIFO stack semantics) + openElements.sort((a, b) => b.position - a.position); + + const closers = openElements.map((el) => { + switch (el.type) { + case "bracket": + return "]"; + case "bold_star": + return "**"; + case "bold_underscore": + return "__"; + case "strike": + return "~~"; + case "italic_star": + return "*"; + case "italic_underscore": + return "_"; + default: + return ""; + } + }); + + let result = s + closers.join(""); + + // Handle parentheses ONLY if not inside code + const finalFenceMatches = Array.from( + result.matchAll(/^(\s*)(`{3,}|~{3,})/gm) + ); + const hasUnclosedBacktick = (result.match(/`/g) || []).length % 2 === 1; + const hasUnclosedCodeFence = finalFenceMatches.length % 2 === 1; + + let shouldCloseParens = !hasUnclosedBacktick && !hasUnclosedCodeFence; + + if (shouldCloseParens) { + const lastOpenParen = result.lastIndexOf("("); + if (lastOpenParen !== -1) { + // Check if this paren is inside a backtick pair + const beforeParen = result.substring(0, lastOpenParen); + const backticksBeforeParen = (beforeParen.match(/`/g) || []).length; + if (backticksBeforeParen % 2 === 1) { + shouldCloseParens = false; + } + } + } + + if (shouldCloseParens) { + const openParens = (result.match(/\(/g) || []).length; + const closeParens = (result.match(/\)/g) || []).length; + if (openParens > closeParens) { + result += ")".repeat(openParens - closeParens); + } + } + + return result; +} \ No newline at end of file diff --git a/src/v2.x/packages/core/tsconfig.json b/src/v2.x/packages/core/tsconfig.json new file mode 100644 index 0000000000..c3ccee0fe5 --- /dev/null +++ b/src/v2.x/packages/core/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "@copilotkitnext/typescript-config/base.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "declaration": true, + "declarationMap": true, + "sourceMap": true + }, + "include": ["src/**/*"], + "exclude": ["dist", "node_modules"] +} diff --git a/src/v2.x/packages/core/tsup.config.ts b/src/v2.x/packages/core/tsup.config.ts new file mode 100644 index 0000000000..c43d98b97e --- /dev/null +++ b/src/v2.x/packages/core/tsup.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from 'tsup'; + +export default defineConfig({ + entry: ['src/index.ts'], + format: ['cjs', 'esm'], + dts: true, + sourcemap: true, + clean: true, + target: 'es2022', + outDir: 'dist', +}); \ No newline at end of file diff --git a/src/v2.x/packages/core/vitest.config.mjs b/src/v2.x/packages/core/vitest.config.mjs new file mode 100644 index 0000000000..7cd1a28456 --- /dev/null +++ b/src/v2.x/packages/core/vitest.config.mjs @@ -0,0 +1,15 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + environment: "node", + globals: true, + include: ["src/**/__tests__/**/*.{test,spec}.ts"], + coverage: { + reporter: ["text", "lcov", "html"], + include: ["src/**/*.ts"], + exclude: ["src/**/*.d.ts", "src/**/index.ts", "src/**/__tests__/**"], + }, + setupFiles: [], + }, +}); \ No newline at end of file diff --git a/src/v2.x/packages/demo-agents/package.json b/src/v2.x/packages/demo-agents/package.json new file mode 100644 index 0000000000..c0ae507b87 --- /dev/null +++ b/src/v2.x/packages/demo-agents/package.json @@ -0,0 +1,33 @@ +{ + "name": "@copilotkitnext/demo-agents", + "version": "0.0.0", + "private": true, + "main": "dist/index.js", + "module": "dist/index.mjs", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "tsup", + "dev": "tsup --watch", + "check-types": "tsc --noEmit", + "lint": "eslint .", + "test": "vitest run", + "test:watch": "vitest" + }, + "dependencies": { + "@ag-ui/client": "*", + "openai": "^4.73.1", + "rxjs": "^7.8.1" + }, + "devDependencies": { + "@copilotkitnext/eslint-config": "workspace:*", + "@copilotkitnext/typescript-config": "workspace:*", + "@types/node": "^22.10.5", + "eslint": "^9.17.0", + "tsup": "^8.0.1", + "typescript": "^5.8.2", + "vitest": "^2.1.8" + } +} \ No newline at end of file diff --git a/src/v2.x/packages/demo-agents/src/index.ts b/src/v2.x/packages/demo-agents/src/index.ts new file mode 100644 index 0000000000..14a6ff7257 --- /dev/null +++ b/src/v2.x/packages/demo-agents/src/index.ts @@ -0,0 +1,2 @@ +export { OpenAIAgent } from "./openai"; +export { SlowToolCallStreamingAgent } from "./slow-tool-call-streaming"; diff --git a/src/v2.x/packages/demo-agents/src/openai.ts b/src/v2.x/packages/demo-agents/src/openai.ts new file mode 100644 index 0000000000..492d249200 --- /dev/null +++ b/src/v2.x/packages/demo-agents/src/openai.ts @@ -0,0 +1,99 @@ +import { + AbstractAgent, + RunAgentInput, + EventType, + BaseEvent, +} from "@ag-ui/client"; +import { Observable } from "rxjs"; +import { OpenAI } from "openai"; + +export class OpenAIAgent extends AbstractAgent { + private openai: OpenAI; + + constructor(openai?: OpenAI) { + super(); + this.openai = openai ?? new OpenAI(); + } + + clone(): OpenAIAgent { + return new OpenAIAgent(this.openai); + } + + protected run(input: RunAgentInput): Observable { + return new Observable((observer) => { + observer.next({ + type: EventType.RUN_STARTED, + threadId: input.threadId, + runId: input.runId, + } as BaseEvent); + + this.openai.chat.completions + .create({ + model: "gpt-4o", + stream: true, + tools: input.tools.map((tool) => ({ + type: "function", + function: { + name: tool.name, + description: tool.description, + parameters: tool.parameters, + }, + })), + messages: input.messages.map((message) => { + if (message.role === "tool") { + return { + role: "tool" as const, + content: message.content ?? "", + tool_call_id: message.toolCallId ?? "", + }; + } else if (message.role === "assistant" && message.toolCalls) { + return { + role: "assistant" as const, + content: message.content ?? "", + tool_calls: message.toolCalls, + }; + } else { + return { + role: message.role as "system" | "user" | "assistant", + content: message.content ?? "", + }; + } + }), + }) + .then(async (response) => { + const messageId = Date.now().toString(); + for await (const chunk of response) { + if (chunk.choices[0]?.delta?.content) { + observer.next({ + type: EventType.TEXT_MESSAGE_CHUNK, + messageId, + delta: chunk.choices[0].delta.content, + } as BaseEvent); + } else if (chunk.choices[0]?.delta?.tool_calls?.[0]) { + const toolCall = chunk.choices[0].delta.tool_calls[0]; + observer.next({ + type: EventType.TOOL_CALL_CHUNK, + toolCallId: toolCall.id, + toolCallName: toolCall.function?.name, + parentMessageId: messageId, + delta: toolCall.function?.arguments, + } as BaseEvent); + } + } + observer.next({ + type: EventType.RUN_FINISHED, + threadId: input.threadId, + runId: input.runId, + } as BaseEvent); + observer.complete(); + }) + .catch((error) => { + observer.next({ + type: EventType.RUN_ERROR, + message: error.message, + } as BaseEvent); + observer.error(error); + }); + }); + } +} diff --git a/src/v2.x/packages/demo-agents/src/slow-tool-call-streaming.ts b/src/v2.x/packages/demo-agents/src/slow-tool-call-streaming.ts new file mode 100644 index 0000000000..61f9370169 --- /dev/null +++ b/src/v2.x/packages/demo-agents/src/slow-tool-call-streaming.ts @@ -0,0 +1,127 @@ +import { AbstractAgent, RunAgentInput, EventType, BaseEvent, ToolCallResultEvent } from "@ag-ui/client"; +import { Observable } from "rxjs"; + +export class SlowToolCallStreamingAgent extends AbstractAgent { + private delay = 200; // 0.2 seconds delay between chunks + + constructor(delayMs: number = 200) { + super(); + this.delay = delayMs; + } + + clone(): SlowToolCallStreamingAgent { + return new SlowToolCallStreamingAgent(this.delay); + } + + private sleep(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); + } + + protected run(input: RunAgentInput): Observable { + return new Observable((observer) => { + let cancelled = false; + + const runAsync = async () => { + await this.sleep(1000); + try { + const messageId = Date.now().toString(); + const toolCallId = `call_${Date.now()}`; + + // Start the run + observer.next({ + type: EventType.RUN_STARTED, + threadId: input.threadId, + runId: input.runId, + } as BaseEvent); + + // Stream the initial text message + const textMessage = "I'll check the weather for you. Let me fetch that information ok?."; + const chunks = textMessage.split(" "); + + for (let i = 0; i < chunks.length; i++) { + if (cancelled) return; + observer.next({ + type: EventType.TEXT_MESSAGE_CHUNK, + messageId, + delta: (i > 0 ? " " : "") + chunks[i], + } as BaseEvent); + await this.sleep(this.delay); + } + + // Stream the tool call + const toolCallArgs = JSON.stringify({ + location: "San Francisco", + unit: "celsius", + }); + const toolCallChunks: string[] = []; + for (let i = 0; i < toolCallArgs.length; i += 5) { + toolCallChunks.push(toolCallArgs.slice(i, i + 5)); + } + + for (let i = 0; i < toolCallChunks.length; i++) { + if (cancelled) return; + if (i === 0) { + // First chunk includes tool name + observer.next({ + type: EventType.TOOL_CALL_CHUNK, + toolCallId, + toolCallName: "getWeather", + parentMessageId: messageId, + delta: toolCallChunks[0], + } as BaseEvent); + } else { + // Subsequent chunks only include arguments + observer.next({ + type: EventType.TOOL_CALL_CHUNK, + toolCallId, + parentMessageId: messageId, + delta: toolCallChunks[i], + } as BaseEvent); + } + await this.sleep(this.delay); + } + + // Send tool result + if (cancelled) return; + const toolResultMessageId = `${Date.now()}_tool_result`; + observer.next({ + type: EventType.TOOL_CALL_RESULT, + toolCallId, + content: JSON.stringify({ + temperature: 18, + unit: "celsius", + condition: "partly cloudy", + humidity: 65, + windSpeed: 12, + }), + messageId: toolResultMessageId, + } as ToolCallResultEvent); + + // Complete the run + if (cancelled) return; + observer.next({ + type: EventType.RUN_FINISHED, + threadId: input.threadId, + runId: input.runId, + } as BaseEvent); + observer.complete(); + } catch (error) { + if (!cancelled) { + observer.next({ + type: EventType.RUN_ERROR, + message: error instanceof Error ? error.message : "Unknown error occurred", + } as BaseEvent); + observer.error(error); + } + } + }; + + runAsync(); + + // Cleanup function + return () => { + cancelled = true; + }; + }); + } +} diff --git a/src/v2.x/packages/demo-agents/tsconfig.json b/src/v2.x/packages/demo-agents/tsconfig.json new file mode 100644 index 0000000000..6f041104a4 --- /dev/null +++ b/src/v2.x/packages/demo-agents/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "@copilotkitnext/typescript-config/base.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src" + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "**/*.spec.ts", "**/*.test.ts"] +} \ No newline at end of file diff --git a/src/v2.x/packages/demo-agents/tsup.config.ts b/src/v2.x/packages/demo-agents/tsup.config.ts new file mode 100644 index 0000000000..44a9f8bc97 --- /dev/null +++ b/src/v2.x/packages/demo-agents/tsup.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["src/index.ts"], + format: ["cjs", "esm"], + dts: true, + clean: true, + sourcemap: true, + external: ["@ag-ui/client", "openai", "rxjs"], +}); \ No newline at end of file diff --git a/src/v2.x/packages/demo-agents/vitest.config.ts b/src/v2.x/packages/demo-agents/vitest.config.ts new file mode 100644 index 0000000000..ed9db65d17 --- /dev/null +++ b/src/v2.x/packages/demo-agents/vitest.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + environment: "node", + passWithNoTests: true, + }, +}); diff --git a/src/v2.x/packages/eslint-config/README.md b/src/v2.x/packages/eslint-config/README.md new file mode 100644 index 0000000000..8b42d901b0 --- /dev/null +++ b/src/v2.x/packages/eslint-config/README.md @@ -0,0 +1,3 @@ +# `@turbo/eslint-config` + +Collection of internal eslint configurations. diff --git a/src/v2.x/packages/eslint-config/base.js b/src/v2.x/packages/eslint-config/base.js new file mode 100644 index 0000000000..09d316efdb --- /dev/null +++ b/src/v2.x/packages/eslint-config/base.js @@ -0,0 +1,32 @@ +import js from "@eslint/js"; +import eslintConfigPrettier from "eslint-config-prettier"; +import turboPlugin from "eslint-plugin-turbo"; +import tseslint from "typescript-eslint"; +import onlyWarn from "eslint-plugin-only-warn"; + +/** + * A shared ESLint configuration for the repository. + * + * @type {import("eslint").Linter.Config[]} + * */ +export const config = [ + js.configs.recommended, + eslintConfigPrettier, + ...tseslint.configs.recommended, + { + plugins: { + turbo: turboPlugin, + }, + rules: { + "turbo/no-undeclared-env-vars": "warn", + }, + }, + { + plugins: { + onlyWarn, + }, + }, + { + ignores: ["dist/**"], + }, +]; diff --git a/src/v2.x/packages/eslint-config/next.js b/src/v2.x/packages/eslint-config/next.js new file mode 100644 index 0000000000..6bf01a74ed --- /dev/null +++ b/src/v2.x/packages/eslint-config/next.js @@ -0,0 +1,49 @@ +import js from "@eslint/js"; +import eslintConfigPrettier from "eslint-config-prettier"; +import tseslint from "typescript-eslint"; +import pluginReactHooks from "eslint-plugin-react-hooks"; +import pluginReact from "eslint-plugin-react"; +import globals from "globals"; +import pluginNext from "@next/eslint-plugin-next"; +import { config as baseConfig } from "./base.js"; + +/** + * A custom ESLint configuration for libraries that use Next.js. + * + * @type {import("eslint").Linter.Config[]} + * */ +export const nextJsConfig = [ + ...baseConfig, + js.configs.recommended, + eslintConfigPrettier, + ...tseslint.configs.recommended, + { + ...pluginReact.configs.flat.recommended, + languageOptions: { + ...pluginReact.configs.flat.recommended.languageOptions, + globals: { + ...globals.serviceworker, + }, + }, + }, + { + plugins: { + "@next/next": pluginNext, + }, + rules: { + ...pluginNext.configs.recommended.rules, + ...pluginNext.configs["core-web-vitals"].rules, + }, + }, + { + plugins: { + "react-hooks": pluginReactHooks, + }, + settings: { react: { version: "detect" } }, + rules: { + ...pluginReactHooks.configs.recommended.rules, + // React scope no longer necessary with new JSX transform. + "react/react-in-jsx-scope": "off", + }, + }, +]; diff --git a/src/v2.x/packages/eslint-config/package.json b/src/v2.x/packages/eslint-config/package.json new file mode 100644 index 0000000000..2c3a6dcdc3 --- /dev/null +++ b/src/v2.x/packages/eslint-config/package.json @@ -0,0 +1,24 @@ +{ + "name": "@copilotkitnext/eslint-config", + "version": "0.0.0", + "type": "module", + "private": true, + "exports": { + "./base": "./base.js", + "./next-js": "./next.js", + "./react-internal": "./react-internal.js" + }, + "devDependencies": { + "@eslint/js": "^9.30.0", + "@next/eslint-plugin-next": "^15.3.0", + "eslint": "^9.30.0", + "eslint-config-prettier": "^10.1.1", + "eslint-plugin-only-warn": "^1.1.0", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-turbo": "^2.5.0", + "globals": "^16.2.0", + "typescript": "^5.8.2", + "typescript-eslint": "^8.35.0" + } +} diff --git a/src/v2.x/packages/eslint-config/react-internal.js b/src/v2.x/packages/eslint-config/react-internal.js new file mode 100644 index 0000000000..daeccba24d --- /dev/null +++ b/src/v2.x/packages/eslint-config/react-internal.js @@ -0,0 +1,39 @@ +import js from "@eslint/js"; +import eslintConfigPrettier from "eslint-config-prettier"; +import tseslint from "typescript-eslint"; +import pluginReactHooks from "eslint-plugin-react-hooks"; +import pluginReact from "eslint-plugin-react"; +import globals from "globals"; +import { config as baseConfig } from "./base.js"; + +/** + * A custom ESLint configuration for libraries that use React. + * + * @type {import("eslint").Linter.Config[]} */ +export const config = [ + ...baseConfig, + js.configs.recommended, + eslintConfigPrettier, + ...tseslint.configs.recommended, + pluginReact.configs.flat.recommended, + { + languageOptions: { + ...pluginReact.configs.flat.recommended.languageOptions, + globals: { + ...globals.serviceworker, + ...globals.browser, + }, + }, + }, + { + plugins: { + "react-hooks": pluginReactHooks, + }, + settings: { react: { version: "detect" } }, + rules: { + ...pluginReactHooks.configs.recommended.rules, + // React scope no longer necessary with new JSX transform. + "react/react-in-jsx-scope": "off", + }, + }, +]; diff --git a/src/v2.x/packages/react/.npmignore b/src/v2.x/packages/react/.npmignore new file mode 100644 index 0000000000..9fd792176a --- /dev/null +++ b/src/v2.x/packages/react/.npmignore @@ -0,0 +1,40 @@ +# Source files (only publish dist) +src/ +tsup.config.ts +tsconfig.json +vitest.config.ts +tailwind.config.js +postcss.config.js + +# Turbo cache +.turbo/ + +# Test files +**/*.test.ts +**/*.test.tsx +**/*.spec.ts +**/*.spec.tsx +**/__tests__/ + +# Config files +.eslintrc.js +.prettierrc + +# IDE +.vscode/ +.idea/ + +# Logs +*.log + +# OS files +.DS_Store +Thumbs.db + +# Documentation +*.md +!README.md + +# Git +.git/ +.gitignore \ No newline at end of file diff --git a/registry/components.json b/src/v2.x/packages/react/components.json similarity index 75% rename from registry/components.json rename to src/v2.x/packages/react/components.json index dea737b85e..99ccffe54b 100644 --- a/registry/components.json +++ b/src/v2.x/packages/react/components.json @@ -4,11 +4,10 @@ "rsc": true, "tsx": true, "tailwind": { - "config": "tailwind.config.ts", - "css": "app/globals.css", + "config": "", + "css": "src/styles/globals.css", "baseColor": "neutral", - "cssVariables": true, - "prefix": "" + "cssVariables": true }, "aliases": { "components": "@/components", @@ -18,4 +17,4 @@ "hooks": "@/hooks" }, "iconLibrary": "lucide" -} \ No newline at end of file +} diff --git a/src/v2.x/packages/react/eslint.config.mjs b/src/v2.x/packages/react/eslint.config.mjs new file mode 100644 index 0000000000..8aced6f096 --- /dev/null +++ b/src/v2.x/packages/react/eslint.config.mjs @@ -0,0 +1,11 @@ +import { config as reactConfig } from "@copilotkitnext/eslint-config/react-internal"; + +export default [ + ...reactConfig, + { + rules: { + // Disable PropTypes validation since we use TypeScript for type checking + "react/prop-types": "off", + }, + }, +]; diff --git a/src/v2.x/packages/react/package.json b/src/v2.x/packages/react/package.json new file mode 100644 index 0000000000..926560f719 --- /dev/null +++ b/src/v2.x/packages/react/package.json @@ -0,0 +1,83 @@ +{ + "name": "@copilotkitnext/react", + "version": "0.0.33", + "description": "React components for CopilotKit2", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "require": "./dist/index.js" + }, + "./styles.css": "./dist/styles.css" + }, + "publishConfig": { + "access": "public" + }, + "scripts": { + "dev:css": "npx @tailwindcss/cli -i ./src/styles/globals.css -o ./dist/styles.css --watch --minify", + "build:css": "npx @tailwindcss/cli -i ./src/styles/globals.css -o ./dist/styles.css -m", + "dev": "concurrently \"npm:dev:css\" \"tsup --watch\"", + "build": "tsup && npm run build:css", + "prepublishOnly": "pnpm run build", + "lint": "eslint . --max-warnings 0", + "check-types": "tsc --noEmit", + "test": "vitest run", + "test:watch": "vitest", + "test:coverage": "vitest run --coverage", + "test:ui": "vitest --ui", + "clean": "rm -rf dist" + }, + "devDependencies": { + "@copilotkitnext/eslint-config": "workspace:*", + "@copilotkitnext/typescript-config": "workspace:*", + "@tailwindcss/cli": "^4.1.11", + "@tailwindcss/postcss": "^4.1.11", + "@tailwindcss/typography": "^0.5.16", + "@testing-library/jest-dom": "^6.0.0", + "@testing-library/react": "^16.0.0", + "@testing-library/user-event": "^14.0.0", + "@types/node": "^22.15.3", + "@types/react": "^19.1.0", + "@types/react-dom": "^19.0.2", + "@vitest/ui": "^3.2.4", + "autoprefixer": "^10.4.20", + "concurrently": "^9.1.0", + "eslint": "^9.30.0", + "jsdom": "^26.1.0", + "tailwindcss": "^4.0.8", + "rxjs": "^7.8.1", + "tsup": "^8.5.0", + "typescript": "5.8.2", + "vitest": "^3.2.4" + }, + "dependencies": { + "@ag-ui/client": "0.0.42", + "@ag-ui/core": "0.0.42", + "@copilotkitnext/core": "workspace:*", + "@copilotkitnext/shared": "workspace:*", + "@copilotkitnext/web-inspector": "workspace:*", + "@lit-labs/react": "^2.0.2", + "@radix-ui/react-dropdown-menu": "^2.1.15", + "@radix-ui/react-slot": "^1.2.3", + "@radix-ui/react-tooltip": "^1.2.7", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "katex": "^0.16.22", + "lucide-react": "^0.525.0", + "streamdown": "^1.3.0", + "tailwind-merge": "^3.3.1", + "ts-deepmerge": "^7.0.3", + "tw-animate-css": "^1.3.5", + "use-stick-to-bottom": "^1.1.1", + "zod": "^3.25.75" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + }, + "engines": { + "node": ">=18" + } +} diff --git a/src/v2.x/packages/react/postcss.config.js b/src/v2.x/packages/react/postcss.config.js new file mode 100644 index 0000000000..fe139bf16f --- /dev/null +++ b/src/v2.x/packages/react/postcss.config.js @@ -0,0 +1,7 @@ +/** @type {import('postcss-load-config').Config} */ +const config = { + plugins: { "@tailwindcss/postcss": {} }, +}; + +// eslint-disable-next-line no-undef +module.exports = config; diff --git a/src/v2.x/packages/react/src/__tests__/setup.ts b/src/v2.x/packages/react/src/__tests__/setup.ts new file mode 100644 index 0000000000..bd80a7cc97 --- /dev/null +++ b/src/v2.x/packages/react/src/__tests__/setup.ts @@ -0,0 +1,89 @@ +// Test setup file for Vitest +// Add any global test configuration here +import { afterEach, vi } from "vitest"; +import { cleanup } from "@testing-library/react"; +import React from "react"; + +// Mock ResizeObserver which is not available in jsdom +global.ResizeObserver = class ResizeObserver { + constructor(callback: ResizeObserverCallback) { + // Store callback for potential future use + this.callback = callback; + } + callback: ResizeObserverCallback; + observe() {} + unobserve() {} + disconnect() {} +}; + +// Mock scrollIntoView which is not available in jsdom +HTMLElement.prototype.scrollIntoView = vi.fn(); + +// Ensure we cleanup between tests to avoid lingering handles +afterEach(() => { + cleanup(); +}); + +// Mock canvas getContext used by audio recorder during tests +HTMLCanvasElement.prototype.getContext = function(contextId: any) { + if (contextId === '2d') { + return { + fillRect: () => {}, + clearRect: () => {}, + getImageData: () => ({ data: [] }), + putImageData: () => {}, + createImageData: () => ({ data: [] }), + setTransform: () => {}, + drawImage: () => {}, + save: () => {}, + restore: () => {}, + beginPath: () => {}, + closePath: () => {}, + moveTo: () => {}, + lineTo: () => {}, + stroke: () => {}, + translate: () => {}, + scale: () => {}, + rotate: () => {}, + arc: () => {}, + fill: () => {}, + measureText: (text: string) => ({ width: text.length * 8 }), + transform: () => {}, + rect: () => {}, + clip: () => {}, + } as unknown as CanvasRenderingContext2D; + } + return null; +} as any; + +// Simplify Radix tooltip behavior to avoid act() noise in jsdom +vi.mock("@radix-ui/react-tooltip", async () => { + const forward = ( + renderFn: React.ForwardRefRenderFunction + ) => React.forwardRef(renderFn); + + const SimpleProvider: React.FC<{ children?: React.ReactNode }> = ({ children }) => + React.createElement(React.Fragment, null, children); + + const SimplePortal: React.FC<{ children?: React.ReactNode }> = ({ children }) => + React.createElement(React.Fragment, null, children); + + const createWrapper = () => + forward(({ children, asChild, sideOffset: _sideOffset, ...rest }: any, ref) => { + if (asChild && React.isValidElement(children)) { + return React.cloneElement(children, { ref, ...rest }); + } + return React.createElement("div", { ref, ...rest }, children); + }); + + const passthrough = createWrapper(); + + return { + Provider: SimpleProvider, + Root: passthrough, + Trigger: passthrough, + Content: passthrough, + Portal: SimplePortal, + Arrow: () => null, + }; +}); diff --git a/src/v2.x/packages/react/src/__tests__/utils/test-helpers.tsx b/src/v2.x/packages/react/src/__tests__/utils/test-helpers.tsx new file mode 100644 index 0000000000..e1669eccda --- /dev/null +++ b/src/v2.x/packages/react/src/__tests__/utils/test-helpers.tsx @@ -0,0 +1,323 @@ +import React from "react"; +import { render, act } from "@testing-library/react"; +import { CopilotKitProvider } from "@/providers/CopilotKitProvider"; +import { CopilotChat } from "@/components/chat/CopilotChat"; +import { CopilotChatConfigurationProvider } from "@/providers/CopilotChatConfigurationProvider"; +import { DEFAULT_AGENT_ID } from "@copilotkitnext/shared"; +import { + AbstractAgent, + EventType, + type BaseEvent, + type RunAgentInput, +} from "@ag-ui/client"; +import { Observable, Subject } from "rxjs"; +import { ReactActivityMessageRenderer, ReactToolCallRenderer } from "@/types"; +import { ReactCustomMessageRenderer } from "@/types/react-custom-message-renderer"; + +/** + * A controllable mock agent for deterministic E2E testing. + * Exposes emit() and complete() methods to drive agent events step-by-step. + */ +export class MockStepwiseAgent extends AbstractAgent { + private subject = new Subject(); + + /** + * Emit a single agent event + */ + emit(event: BaseEvent) { + if (event.type === EventType.RUN_STARTED) { + this.isRunning = true; + } else if ( + event.type === EventType.RUN_FINISHED || + event.type === EventType.RUN_ERROR + ) { + this.isRunning = false; + } + act(() => { + this.subject.next(event); + }); + } + + /** + * Complete the agent stream + */ + complete() { + this.isRunning = false; + act(() => { + this.subject.complete(); + }); + } + + clone(): MockStepwiseAgent { + // For tests, return same instance so we can keep controlling it + return this; + } + + run(_input: RunAgentInput): Observable { + return this.subject.asObservable(); + } +} + +/** + * Helper to render components with CopilotKitProvider for E2E tests + */ +export function renderWithCopilotKit({ + agent, + agents, + renderToolCalls, + renderCustomMessages, + renderActivityMessages, + frontendTools, + humanInTheLoop, + agentId, + threadId, + children, +}: { + agent?: AbstractAgent; + agents?: Record; + renderToolCalls?: ReactToolCallRenderer[]; + renderCustomMessages?: ReactCustomMessageRenderer[]; + renderActivityMessages?: ReactActivityMessageRenderer[]; + frontendTools?: any[]; + humanInTheLoop?: any[]; + agentId?: string; + threadId?: string; + children?: React.ReactNode; +}): ReturnType { + const resolvedAgents = agents || (agent ? { default: agent } : undefined); + const resolvedAgentId = agentId ?? DEFAULT_AGENT_ID; + const resolvedThreadId = threadId ?? "test-thread"; + + return render( + + + {children || ( +
    + +
    + )} +
    +
    + ); +} + +/** + * Helper to create a RUN_STARTED event + */ +export function runStartedEvent(): BaseEvent { + return { type: EventType.RUN_STARTED } as BaseEvent; +} + +/** + * Helper to create a RUN_FINISHED event + */ +export function runFinishedEvent(): BaseEvent { + return { type: EventType.RUN_FINISHED } as BaseEvent; +} + +/** + * Helper to create a STATE_SNAPSHOT event + */ +export function stateSnapshotEvent(snapshot: unknown): BaseEvent { + return { + type: EventType.STATE_SNAPSHOT, + snapshot, + } as BaseEvent; +} + +/** + * Helper to create an ACTIVITY_SNAPSHOT event + */ +export function activitySnapshotEvent({ + messageId, + activityType, + content, +}: { + messageId: string; + activityType: string; + content: Record; +}): BaseEvent { + return { + type: EventType.ACTIVITY_SNAPSHOT, + messageId, + activityType, + content, + } as BaseEvent; +} + +/** + * Helper to start an assistant text message + */ +export function textMessageStartEvent( + messageId: string, + role: "assistant" | "developer" | "system" | "user" = "assistant", +): BaseEvent { + return { + type: EventType.TEXT_MESSAGE_START, + messageId, + role, + } as BaseEvent; +} + +/** + * Helper to stream text message content + */ +export function textMessageContentEvent(messageId: string, delta: string): BaseEvent { + return { + type: EventType.TEXT_MESSAGE_CONTENT, + messageId, + delta, + } as BaseEvent; +} + +/** + * Helper to end a text message + */ +export function textMessageEndEvent(messageId: string): BaseEvent { + return { + type: EventType.TEXT_MESSAGE_END, + messageId, + } as BaseEvent; +} + +/** + * Helper to create a TEXT_MESSAGE_CHUNK event + */ +export function textChunkEvent(messageId: string, delta: string): BaseEvent { + return { + type: EventType.TEXT_MESSAGE_CHUNK, + messageId, + delta, + } as BaseEvent; +} + +/** + * Helper to create a TOOL_CALL_CHUNK event + */ +export function toolCallChunkEvent({ + toolCallId, + toolCallName, + parentMessageId, + delta, +}: { + toolCallId: string; + toolCallName?: string; + parentMessageId: string; + delta: string; +}): BaseEvent { + return { + type: EventType.TOOL_CALL_CHUNK, + toolCallId, + toolCallName, + parentMessageId, + delta, + } as BaseEvent; +} + +/** + * Helper to create a TOOL_CALL_RESULT event + */ +export function toolCallResultEvent({ + toolCallId, + messageId, + content, +}: { + toolCallId: string; + messageId: string; + content: string; +}): BaseEvent { + return { + type: EventType.TOOL_CALL_RESULT, + toolCallId, + messageId, + content, + } as BaseEvent; +} + +/** + * Helper to generate unique IDs for tests + */ +export function testId(prefix: string): string { + return `${prefix}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; +} + +/** + * Helper to emit a complete suggestion tool call with streaming chunks + */ +export function emitSuggestionToolCall( + agent: MockStepwiseAgent, + { + toolCallId, + parentMessageId, + suggestions, + }: { + toolCallId: string; + parentMessageId: string; + suggestions: Array<{ title: string; message: string }>; + } +) { + // Convert suggestions to JSON string + const suggestionsJson = JSON.stringify({ suggestions }); + + // Emit the tool call name first + agent.emit(toolCallChunkEvent({ + toolCallId, + toolCallName: "copilotkitSuggest", + parentMessageId, + delta: "", + })); + + // Stream the JSON in chunks to simulate streaming + const chunkSize = 10; // Characters per chunk + for (let i = 0; i < suggestionsJson.length; i += chunkSize) { + const chunk = suggestionsJson.substring(i, i + chunkSize); + agent.emit(toolCallChunkEvent({ + toolCallId, + parentMessageId, + delta: chunk, + })); + } +} + +/** + * A MockStepwiseAgent that emits suggestion events when run() is called + */ +export class SuggestionsProviderAgent extends MockStepwiseAgent { + private _suggestions: Array<{ title: string; message: string }> = []; + + setSuggestions(suggestions: Array<{ title: string; message: string }>) { + this._suggestions = suggestions; + } + + run(_input: RunAgentInput): Observable { + // Call the parent's run() to get the Subject that's already set up + const parentObservable = super.run(_input); + + // Use setTimeout to emit events asynchronously through the existing subject + setTimeout(() => { + const messageId = testId("suggest-msg"); + this.emit({ type: EventType.RUN_STARTED } as BaseEvent); + + emitSuggestionToolCall(this, { + toolCallId: testId("tc"), + parentMessageId: messageId, + suggestions: this._suggestions, + }); + + this.emit({ type: EventType.RUN_FINISHED } as BaseEvent); + this.complete(); + }, 0); + + return parentObservable; + } +} diff --git a/src/v2.x/packages/react/src/components/CopilotKitInspector.tsx b/src/v2.x/packages/react/src/components/CopilotKitInspector.tsx new file mode 100644 index 0000000000..6d635abbd6 --- /dev/null +++ b/src/v2.x/packages/react/src/components/CopilotKitInspector.tsx @@ -0,0 +1,46 @@ +import * as React from "react"; +import { createComponent } from "@lit-labs/react"; +import type { CopilotKitCore } from "@copilotkitnext/core"; + +type CopilotKitInspectorBaseProps = { + core?: CopilotKitCore | null; + [key: string]: unknown; +}; + +type InspectorComponent = React.ComponentType; + +export interface CopilotKitInspectorProps extends CopilotKitInspectorBaseProps {} + +export const CopilotKitInspector: React.FC = ({ core, ...rest }) => { + const [InspectorComponent, setInspectorComponent] = React.useState(null); + + React.useEffect(() => { + let mounted = true; + + // Load the web component only on the client to keep SSR output stable. + import("@copilotkitnext/web-inspector").then((mod) => { + mod.defineWebInspector?.(); + + const Component = createComponent({ + tagName: mod.WEB_INSPECTOR_TAG, + elementClass: mod.WebInspectorElement, + react: React, + }) as InspectorComponent; + + if (mounted) { + setInspectorComponent(() => Component); + } + }); + + return () => { + mounted = false; + }; + }, []); + + // During SSR (and until the client finishes loading), render nothing to keep markup consistent. + if (!InspectorComponent) return null; + + return ; +}; + +CopilotKitInspector.displayName = "CopilotKitInspector"; diff --git a/src/v2.x/packages/react/src/components/MCPAppsActivityRenderer.tsx b/src/v2.x/packages/react/src/components/MCPAppsActivityRenderer.tsx new file mode 100644 index 0000000000..47e7beb79c --- /dev/null +++ b/src/v2.x/packages/react/src/components/MCPAppsActivityRenderer.tsx @@ -0,0 +1,390 @@ +"use client"; + +import React, { useEffect, useRef, useState, useCallback } from "react"; +import { z } from "zod"; +import type { AbstractAgent } from "@ag-ui/client"; + +// Protocol version supported +const PROTOCOL_VERSION = "2025-06-18"; + +/** + * Activity type for MCP Apps events - must match the middleware's MCPAppsActivityType + */ +export const MCPAppsActivityType = "mcp-apps"; + +// Zod schema for activity content validation +export const MCPAppsActivityContentSchema = z.object({ + result: z.object({ + content: z.array(z.any()).optional(), + structuredContent: z.any().optional(), + isError: z.boolean().optional(), + }), + resource: z.object({ + uri: z.string(), + mimeType: z.string().optional(), + text: z.string().optional(), + blob: z.string().optional(), + }), + // Server ID for proxying requests (MD5 hash of server config) + serverId: z.string().optional(), + // Original tool input arguments + toolInput: z.record(z.unknown()).optional(), +}); + +export type MCPAppsActivityContent = z.infer; + +interface JSONRPCRequest { + jsonrpc: "2.0"; + id: string | number; + method: string; + params?: Record; +} + +interface JSONRPCResponse { + jsonrpc: "2.0"; + id: string | number; + result?: unknown; + error?: { code: number; message: string }; +} + +interface JSONRPCNotification { + jsonrpc: "2.0"; + method: string; + params?: Record; +} + +type JSONRPCMessage = JSONRPCRequest | JSONRPCResponse | JSONRPCNotification; + +function isRequest(msg: JSONRPCMessage): msg is JSONRPCRequest { + return "id" in msg && "method" in msg; +} + +function isNotification(msg: JSONRPCMessage): msg is JSONRPCNotification { + return !("id" in msg) && "method" in msg; +} + +/** + * Extract HTML content from a resource object + */ +function extractHtmlFromResource(resource: MCPAppsActivityContent["resource"]): string { + if (resource.text) { + return resource.text; + } + if (resource.blob) { + // Base64 decode + return atob(resource.blob); + } + throw new Error("Resource has no text or blob content"); +} + +/** + * Props for the activity renderer component + */ +interface MCPAppsActivityRendererProps { + activityType: string; + content: MCPAppsActivityContent; + message: unknown; // ActivityMessage from @ag-ui/core + agent: AbstractAgent | undefined; +} + +/** + * MCP Apps Extension Activity Renderer + * + * Renders MCP Apps UI in a sandboxed iframe with full protocol support. + */ +export const MCPAppsActivityRenderer: React.FC = ({ content, agent }) => { + const containerRef = useRef(null); + const iframeRef = useRef(null); + const [iframeReady, setIframeReady] = useState(false); + const [error, setError] = useState(null); + const [iframeSize, setIframeSize] = useState<{ width?: number; height?: number }>({}); + + // Use refs for values that shouldn't trigger re-renders but need latest values + const contentRef = useRef(content); + contentRef.current = content; + + // Store agent in a ref for use in async handlers + const agentRef = useRef(agent); + agentRef.current = agent; + + // Callback to send a message to the iframe + const sendToIframe = useCallback((msg: JSONRPCMessage) => { + if (iframeRef.current?.contentWindow) { + console.log("[MCPAppsRenderer] Sending to iframe:", msg); + iframeRef.current.contentWindow.postMessage(msg, "*"); + } + }, []); + + // Callback to send a JSON-RPC response + const sendResponse = useCallback((id: string | number, result: unknown) => { + sendToIframe({ + jsonrpc: "2.0", + id, + result, + }); + }, [sendToIframe]); + + // Callback to send a JSON-RPC error response + const sendErrorResponse = useCallback((id: string | number, code: number, message: string) => { + sendToIframe({ + jsonrpc: "2.0", + id, + error: { code, message }, + }); + }, [sendToIframe]); + + // Callback to send a notification + const sendNotification = useCallback((method: string, params?: Record) => { + sendToIframe({ + jsonrpc: "2.0", + method, + params: params || {}, + }); + }, [sendToIframe]); + + // Effect 1: Setup sandbox proxy iframe and communication + useEffect(() => { + if (!containerRef.current) return; + + let mounted = true; + let messageHandler: ((event: MessageEvent) => void) | null = null; + + const setup = async () => { + try { + // Create sandbox proxy iframe + const iframe = document.createElement("iframe"); + iframe.style.width = "100%"; + iframe.style.height = "300px"; + iframe.style.border = "none"; + iframe.style.backgroundColor = "transparent"; + iframe.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms"); + + // Wait for sandbox proxy to be ready + const sandboxReady = new Promise((resolve) => { + const initialListener = (event: MessageEvent) => { + if (event.source === iframe.contentWindow) { + if (event.data?.method === "ui/notifications/sandbox-proxy-ready") { + window.removeEventListener("message", initialListener); + resolve(); + } + } + }; + window.addEventListener("message", initialListener); + }); + + // Set iframe source and add to DOM + iframe.src = "/sandbox.html"; + iframeRef.current = iframe; + containerRef.current?.appendChild(iframe); + + // Wait for sandbox proxy to signal ready + await sandboxReady; + if (!mounted) return; + + console.log("[MCPAppsRenderer] Sandbox proxy ready"); + + // Setup message handler for JSON-RPC messages from the inner iframe + messageHandler = async (event: MessageEvent) => { + if (event.source !== iframe.contentWindow) return; + + const msg = event.data as JSONRPCMessage; + if (!msg || typeof msg !== "object" || msg.jsonrpc !== "2.0") return; + + console.log("[MCPAppsRenderer] Received from iframe:", msg); + + // Handle requests (need response) + if (isRequest(msg)) { + switch (msg.method) { + case "ui/initialize": { + // Respond with host capabilities + sendResponse(msg.id, { + protocolVersion: PROTOCOL_VERSION, + hostInfo: { + name: "CopilotKit MCP Apps Host", + version: "1.0.0", + }, + hostCapabilities: { + openLinks: {}, + logging: {}, + }, + hostContext: { + theme: "light", + platform: "web", + }, + }); + break; + } + + case "ui/message": { + // For now, just acknowledge the message + // TODO: Hook into CopilotKit chat to add messages + console.log("[MCPAppsRenderer] ui/message request:", msg.params); + sendResponse(msg.id, { isError: false }); + break; + } + + case "ui/open-link": { + // Open URL in new tab + const url = msg.params?.url as string | undefined; + if (url) { + window.open(url, "_blank", "noopener,noreferrer"); + sendResponse(msg.id, { isError: false }); + } else { + sendErrorResponse(msg.id, -32602, "Missing url parameter"); + } + break; + } + + case "tools/call": { + // Proxy tool call to MCP server via agent.runAgent() + const { serverId } = contentRef.current; + const currentAgent = agentRef.current; + + if (!serverId) { + sendErrorResponse(msg.id, -32603, "No server ID available for proxying"); + break; + } + + if (!currentAgent) { + sendErrorResponse(msg.id, -32603, "No agent available for proxying"); + break; + } + + try { + // Use agent.runAgent() to proxy the MCP request + // The middleware will intercept forwardedProps.__proxiedMCPRequest + const runResult = await currentAgent.runAgent({ + forwardedProps: { + __proxiedMCPRequest: { + serverId, + method: "tools/call", + params: msg.params, + }, + }, + }); + + // The result from runAgent contains the MCP response + sendResponse(msg.id, runResult.result || {}); + } catch (err) { + console.error("[MCPAppsRenderer] tools/call error:", err); + sendErrorResponse(msg.id, -32603, String(err)); + } + break; + } + + default: + sendErrorResponse(msg.id, -32601, `Method not found: ${msg.method}`); + } + } + + // Handle notifications (no response needed) + if (isNotification(msg)) { + switch (msg.method) { + case "ui/notifications/initialized": { + console.log("[MCPAppsRenderer] Inner iframe initialized"); + if (mounted) { + setIframeReady(true); + } + break; + } + + case "ui/notifications/size-change": { + const { width, height } = msg.params || {}; + console.log("[MCPAppsRenderer] Size change:", { width, height }); + if (mounted) { + setIframeSize({ + width: typeof width === "number" ? width : undefined, + height: typeof height === "number" ? height : undefined, + }); + } + break; + } + + case "notifications/message": { + // Logging notification from the app + console.log("[MCPAppsRenderer] App log:", msg.params); + break; + } + } + } + }; + + window.addEventListener("message", messageHandler); + + // Send the HTML resource to the sandbox proxy + const html = extractHtmlFromResource(content.resource); + sendNotification("ui/notifications/sandbox-resource-ready", { html }); + + } catch (err) { + console.error("[MCPAppsRenderer] Setup error:", err); + if (mounted) { + setError(err instanceof Error ? err : new Error(String(err))); + } + } + }; + + setup(); + + return () => { + mounted = false; + if (messageHandler) { + window.removeEventListener("message", messageHandler); + } + // Remove iframe from DOM + if (iframeRef.current && containerRef.current?.contains(iframeRef.current)) { + containerRef.current.removeChild(iframeRef.current); + } + iframeRef.current = null; + }; + }, [content.resource, sendNotification, sendResponse, sendErrorResponse]); + + // Effect 2: Update iframe size when it changes + useEffect(() => { + if (iframeRef.current) { + if (iframeSize.width !== undefined) { + iframeRef.current.style.width = `${iframeSize.width}px`; + } + if (iframeSize.height !== undefined) { + iframeRef.current.style.height = `${iframeSize.height}px`; + } + } + }, [iframeSize]); + + // Effect 3: Send tool input when iframe ready + useEffect(() => { + if (iframeReady && content.toolInput) { + console.log("[MCPAppsRenderer] Sending tool input:", content.toolInput); + sendNotification("ui/notifications/tool-input", { + arguments: content.toolInput, + }); + } + }, [iframeReady, content.toolInput, sendNotification]); + + // Effect 4: Send tool result when iframe ready + useEffect(() => { + if (iframeReady && content.result) { + console.log("[MCPAppsRenderer] Sending tool result:", content.result); + sendNotification("ui/notifications/tool-result", content.result); + } + }, [iframeReady, content.result, sendNotification]); + + return ( +
    + {error && ( +
    + Error: {error.message} +
    + )} +
    + ); +}; diff --git a/src/v2.x/packages/react/src/components/WildcardToolCallRender.tsx b/src/v2.x/packages/react/src/components/WildcardToolCallRender.tsx new file mode 100644 index 0000000000..6dba08623f --- /dev/null +++ b/src/v2.x/packages/react/src/components/WildcardToolCallRender.tsx @@ -0,0 +1,86 @@ +import { defineToolCallRenderer } from "../types/defineToolCallRenderer"; +import { useState } from "react"; + +export const WildcardToolCallRender = defineToolCallRenderer({ + name: "*", + render: ({ args, result, name, status }) => { + const [isExpanded, setIsExpanded] = useState(false); + + const statusString = String(status) as + | "inProgress" + | "executing" + | "complete"; + const isActive = + statusString === "inProgress" || statusString === "executing"; + const isComplete = statusString === "complete"; + const statusStyles = isActive + ? "bg-amber-100 text-amber-800 dark:bg-amber-500/15 dark:text-amber-400" + : isComplete + ? "bg-emerald-100 text-emerald-800 dark:bg-emerald-500/15 dark:text-emerald-400" + : "bg-zinc-100 text-zinc-800 dark:bg-zinc-700/40 dark:text-zinc-300"; + + return ( +
    +
    +
    setIsExpanded(!isExpanded)} + > +
    + + + + + + {name} + +
    + + {String(status)} + +
    + + {isExpanded && ( +
    +
    +
    + Arguments +
    +
    +                  {JSON.stringify(args ?? {}, null, 2)}
    +                
    +
    + + {result !== undefined && ( +
    +
    + Result +
    +
    +                    {typeof result === "string"
    +                      ? result
    +                      : JSON.stringify(result, null, 2)}
    +                  
    +
    + )} +
    + )} +
    +
    + ); + }, +}); diff --git a/src/v2.x/packages/react/src/components/chat/CopilotChat.tsx b/src/v2.x/packages/react/src/components/chat/CopilotChat.tsx new file mode 100644 index 0000000000..232118560a --- /dev/null +++ b/src/v2.x/packages/react/src/components/chat/CopilotChat.tsx @@ -0,0 +1,168 @@ +import { useAgent } from "@/hooks/use-agent"; +import { useSuggestions } from "@/hooks/use-suggestions"; +import { CopilotChatView, CopilotChatViewProps } from "./CopilotChatView"; +import CopilotChatInput, { CopilotChatInputProps } from "./CopilotChatInput"; +import { + CopilotChatConfigurationProvider, + CopilotChatLabels, + useCopilotChatConfiguration, +} from "@/providers/CopilotChatConfigurationProvider"; +import { DEFAULT_AGENT_ID, randomUUID } from "@copilotkitnext/shared"; +import { Suggestion } from "@copilotkitnext/core"; +import { useCallback, useEffect, useMemo } from "react"; +import { merge } from "ts-deepmerge"; +import { useCopilotKit } from "@/providers/CopilotKitProvider"; +import { AbstractAgent, AGUIConnectNotImplementedError } from "@ag-ui/client"; +import { renderSlot, SlotValue } from "@/lib/slots"; + +export type CopilotChatProps = Omit< + CopilotChatViewProps, + "messages" | "isRunning" | "suggestions" | "suggestionLoadingIndexes" | "onSelectSuggestion" +> & { + agentId?: string; + threadId?: string; + labels?: Partial; + chatView?: SlotValue; + isModalDefaultOpen?: boolean; +}; +export function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen, ...props }: CopilotChatProps) { + // Check for existing configuration provider + const existingConfig = useCopilotChatConfiguration(); + + // Apply priority: props > existing config > defaults + const resolvedAgentId = agentId ?? existingConfig?.agentId ?? DEFAULT_AGENT_ID; + const resolvedThreadId = useMemo( + () => threadId ?? existingConfig?.threadId ?? randomUUID(), + [threadId, existingConfig?.threadId], + ); + const { agent } = useAgent({ agentId: resolvedAgentId }); + const { copilotkit } = useCopilotKit(); + + const { suggestions: autoSuggestions } = useSuggestions({ agentId: resolvedAgentId }); + + const { + inputProps: providedInputProps, + messageView: providedMessageView, + suggestionView: providedSuggestionView, + ...restProps + } = props; + + useEffect(() => { + const connect = async (agent: AbstractAgent) => { + try { + await copilotkit.connectAgent({ agent }); + } catch (error) { + if (error instanceof AGUIConnectNotImplementedError) { + // connect not implemented, ignore + } else { + throw error; + } + } + }; + agent.threadId = resolvedThreadId; + connect(agent); + return () => {}; + }, [resolvedThreadId, agent, copilotkit, resolvedAgentId]); + + const onSubmitInput = useCallback( + async (value: string) => { + agent.addMessage({ + id: randomUUID(), + role: "user", + content: value, + }); + try { + await copilotkit.runAgent({ agent }); + } catch (error) { + console.error("CopilotChat: runAgent failed", error); + } + }, + [agent, copilotkit], + ); + + const handleSelectSuggestion = useCallback( + async (suggestion: Suggestion) => { + agent.addMessage({ + id: randomUUID(), + role: "user", + content: suggestion.message, + }); + + try { + await copilotkit.runAgent({ agent }); + } catch (error) { + console.error("CopilotChat: runAgent failed after selecting suggestion", error); + } + }, + [agent, copilotkit], + ); + + const stopCurrentRun = useCallback(() => { + try { + copilotkit.stopAgent({ agent }); + } catch (error) { + console.error("CopilotChat: stopAgent failed", error); + try { + agent.abortRun(); + } catch (abortError) { + console.error("CopilotChat: abortRun fallback failed", abortError); + } + } + }, [agent, copilotkit]); + + const mergedProps = merge( + { + isRunning: agent.isRunning, + suggestions: autoSuggestions, + onSelectSuggestion: handleSelectSuggestion, + suggestionView: providedSuggestionView, + }, + { + ...restProps, + ...(typeof providedMessageView === "string" + ? { messageView: { className: providedMessageView } } + : providedMessageView !== undefined + ? { messageView: providedMessageView } + : {}), + }, + ); + + const providedStopHandler = providedInputProps?.onStop; + const hasMessages = agent.messages.length > 0; + const shouldAllowStop = agent.isRunning && hasMessages; + const effectiveStopHandler = shouldAllowStop ? (providedStopHandler ?? stopCurrentRun) : providedStopHandler; + + const finalInputProps = { + ...providedInputProps, + onSubmitMessage: onSubmitInput, + onStop: effectiveStopHandler, + isRunning: agent.isRunning, + } as Partial & { onSubmitMessage: (value: string) => void }; + + finalInputProps.mode = agent.isRunning ? "processing" : (finalInputProps.mode ?? "input"); + + const finalProps = merge(mergedProps, { + messages: agent.messages, + inputProps: finalInputProps, + }) as CopilotChatViewProps; + + // Always create a provider with merged values + // This ensures priority: props > existing config > defaults + const RenderedChatView = renderSlot(chatView, CopilotChatView, finalProps); + + return ( + + {RenderedChatView} + + ); +} + +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace CopilotChat { + export const View = CopilotChatView; +} diff --git a/src/v2.x/packages/react/src/components/chat/CopilotChatAssistantMessage.tsx b/src/v2.x/packages/react/src/components/chat/CopilotChatAssistantMessage.tsx new file mode 100644 index 0000000000..393a77da0b --- /dev/null +++ b/src/v2.x/packages/react/src/components/chat/CopilotChatAssistantMessage.tsx @@ -0,0 +1,362 @@ +import { AssistantMessage, Message } from "@ag-ui/core"; +import { useState } from "react"; +import { + Copy, + Check, + ThumbsUp, + ThumbsDown, + Volume2, + RefreshCw, +} from "lucide-react"; +import { + useCopilotChatConfiguration, + CopilotChatDefaultLabels, +} from "@/providers/CopilotChatConfigurationProvider"; +import { twMerge } from "tailwind-merge"; +import { Button } from "@/components/ui/button"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import "katex/dist/katex.min.css"; +import { WithSlots, renderSlot } from "@/lib/slots"; +import { Streamdown } from "streamdown"; +import CopilotChatToolCallsView from "./CopilotChatToolCallsView"; + +export type CopilotChatAssistantMessageProps = WithSlots< + { + markdownRenderer: typeof CopilotChatAssistantMessage.MarkdownRenderer; + toolbar: typeof CopilotChatAssistantMessage.Toolbar; + copyButton: typeof CopilotChatAssistantMessage.CopyButton; + thumbsUpButton: typeof CopilotChatAssistantMessage.ThumbsUpButton; + thumbsDownButton: typeof CopilotChatAssistantMessage.ThumbsDownButton; + readAloudButton: typeof CopilotChatAssistantMessage.ReadAloudButton; + regenerateButton: typeof CopilotChatAssistantMessage.RegenerateButton; + toolCallsView: typeof CopilotChatToolCallsView; + }, + { + onThumbsUp?: (message: AssistantMessage) => void; + onThumbsDown?: (message: AssistantMessage) => void; + onReadAloud?: (message: AssistantMessage) => void; + onRegenerate?: (message: AssistantMessage) => void; + message: AssistantMessage; + messages?: Message[]; + isRunning?: boolean; + additionalToolbarItems?: React.ReactNode; + toolbarVisible?: boolean; + } & React.HTMLAttributes +>; + +export function CopilotChatAssistantMessage({ + message, + messages, + isRunning, + onThumbsUp, + onThumbsDown, + onReadAloud, + onRegenerate, + additionalToolbarItems, + toolbarVisible = true, + markdownRenderer, + toolbar, + copyButton, + thumbsUpButton, + thumbsDownButton, + readAloudButton, + regenerateButton, + toolCallsView, + children, + className, + ...props +}: CopilotChatAssistantMessageProps) { + const boundMarkdownRenderer = renderSlot( + markdownRenderer, + CopilotChatAssistantMessage.MarkdownRenderer, + { + content: message.content || "", + + } + ); + + const boundCopyButton = renderSlot( + copyButton, + CopilotChatAssistantMessage.CopyButton, + { + onClick: async () => { + if (message.content) { + try { + await navigator.clipboard.writeText(message.content); + } catch (err) { + console.error("Failed to copy message:", err); + } + } + }, + } + ); + + const boundThumbsUpButton = renderSlot( + thumbsUpButton, + CopilotChatAssistantMessage.ThumbsUpButton, + { + onClick: onThumbsUp, + } + ); + + const boundThumbsDownButton = renderSlot( + thumbsDownButton, + CopilotChatAssistantMessage.ThumbsDownButton, + { + onClick: onThumbsDown, + } + ); + + const boundReadAloudButton = renderSlot( + readAloudButton, + CopilotChatAssistantMessage.ReadAloudButton, + { + onClick: onReadAloud, + } + ); + + const boundRegenerateButton = renderSlot( + regenerateButton, + CopilotChatAssistantMessage.RegenerateButton, + { + onClick: onRegenerate, + } + ); + + const boundToolbar = renderSlot( + toolbar, + CopilotChatAssistantMessage.Toolbar, + { + children: ( +
    + {boundCopyButton} + {(onThumbsUp || thumbsUpButton) && boundThumbsUpButton} + {(onThumbsDown || thumbsDownButton) && boundThumbsDownButton} + {(onReadAloud || readAloudButton) && boundReadAloudButton} + {(onRegenerate || regenerateButton) && boundRegenerateButton} + {additionalToolbarItems} +
    + ), + } + ); + + const boundToolCallsView = renderSlot( + toolCallsView, + CopilotChatToolCallsView, + { + message, + messages, + } + ); + + // Don't show toolbar if message has no content (only tool calls) + const hasContent = !!(message.content && message.content.trim().length > 0); + const isLatestAssistantMessage = + message.role === "assistant" && messages?.[messages.length - 1]?.id === message.id; + const shouldShowToolbar = toolbarVisible && hasContent && !(isRunning && isLatestAssistantMessage); + + if (children) { + return ( + <> + {children({ + markdownRenderer: boundMarkdownRenderer, + toolbar: boundToolbar, + toolCallsView: boundToolCallsView, + copyButton: boundCopyButton, + thumbsUpButton: boundThumbsUpButton, + thumbsDownButton: boundThumbsDownButton, + readAloudButton: boundReadAloudButton, + regenerateButton: boundRegenerateButton, + message, + messages, + isRunning, + onThumbsUp, + onThumbsDown, + onReadAloud, + onRegenerate, + additionalToolbarItems, + toolbarVisible: shouldShowToolbar, + })} + + ); + } + + return ( +
    + {boundMarkdownRenderer} + {boundToolCallsView} + {shouldShowToolbar && boundToolbar} +
    + ); +} + +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace CopilotChatAssistantMessage { + export const MarkdownRenderer: React.FC< + Omit, "children"> & { + content: string; + } + > = ({ content, className, ...props }) => ( + + {content ?? ""} + + ); + + export const Toolbar: React.FC> = ({ + className, + ...props + }) => ( +
    + ); + + export const ToolbarButton: React.FC< + React.ButtonHTMLAttributes & { + title: string; + children: React.ReactNode; + } + > = ({ title, children, ...props }) => { + return ( + + + + + +

    {title}

    +
    +
    + ); + }; + + export const CopyButton: React.FC< + React.ButtonHTMLAttributes + > = ({ className, title, onClick, ...props }) => { + const config = useCopilotChatConfiguration(); + const labels = config?.labels ?? CopilotChatDefaultLabels; + const [copied, setCopied] = useState(false); + + const handleClick = (event: React.MouseEvent) => { + setCopied(true); + setTimeout(() => setCopied(false), 2000); + + if (onClick) { + onClick(event); + } + }; + + return ( + + {copied ? ( + + ) : ( + + )} + + ); + }; + + export const ThumbsUpButton: React.FC< + React.ButtonHTMLAttributes + > = ({ title, ...props }) => { + const config = useCopilotChatConfiguration(); + const labels = config?.labels ?? CopilotChatDefaultLabels; + return ( + + + + ); + }; + + export const ThumbsDownButton: React.FC< + React.ButtonHTMLAttributes + > = ({ title, ...props }) => { + const config = useCopilotChatConfiguration(); + const labels = config?.labels ?? CopilotChatDefaultLabels; + return ( + + + + ); + }; + + export const ReadAloudButton: React.FC< + React.ButtonHTMLAttributes + > = ({ title, ...props }) => { + const config = useCopilotChatConfiguration(); + const labels = config?.labels ?? CopilotChatDefaultLabels; + return ( + + + + ); + }; + + export const RegenerateButton: React.FC< + React.ButtonHTMLAttributes + > = ({ title, ...props }) => { + const config = useCopilotChatConfiguration(); + const labels = config?.labels ?? CopilotChatDefaultLabels; + return ( + + + + ); + }; +} + +CopilotChatAssistantMessage.MarkdownRenderer.displayName = + "CopilotChatAssistantMessage.MarkdownRenderer"; +CopilotChatAssistantMessage.Toolbar.displayName = + "CopilotChatAssistantMessage.Toolbar"; +CopilotChatAssistantMessage.CopyButton.displayName = + "CopilotChatAssistantMessage.CopyButton"; +CopilotChatAssistantMessage.ThumbsUpButton.displayName = + "CopilotChatAssistantMessage.ThumbsUpButton"; +CopilotChatAssistantMessage.ThumbsDownButton.displayName = + "CopilotChatAssistantMessage.ThumbsDownButton"; +CopilotChatAssistantMessage.ReadAloudButton.displayName = + "CopilotChatAssistantMessage.ReadAloudButton"; +CopilotChatAssistantMessage.RegenerateButton.displayName = + "CopilotChatAssistantMessage.RegenerateButton"; + +export default CopilotChatAssistantMessage; diff --git a/src/v2.x/packages/react/src/components/chat/CopilotChatAudioRecorder.tsx b/src/v2.x/packages/react/src/components/chat/CopilotChatAudioRecorder.tsx new file mode 100644 index 0000000000..ce900e885d --- /dev/null +++ b/src/v2.x/packages/react/src/components/chat/CopilotChatAudioRecorder.tsx @@ -0,0 +1,157 @@ +import { useRef, useEffect, useImperativeHandle, forwardRef } from "react"; +import { twMerge } from "tailwind-merge"; + +/** Finite-state machine for every recorder implementation */ +export type AudioRecorderState = "idle" | "recording" | "processing"; + +/** Error subclass so callers can `instanceof`-guard recorder failures */ +export class AudioRecorderError extends Error { + constructor(message: string) { + super(message); + this.name = "AudioRecorderError"; + } +} + +export const CopilotChatAudioRecorder = forwardRef< + any, + React.HTMLAttributes +>((props, ref) => { + const { className, ...divProps } = props; + const canvasRef = useRef(null); + + // Generate fake waveform that moves with time + const getLoudness = (n: number): number[] => { + const elapsed = Date.now() / 1000; // Use current timestamp directly + const samples: number[] = []; + + for (let i = 0; i < n; i++) { + // Create a position that moves from left to right over time + const position = (i / n) * 10 + elapsed * 0.5; // Scroll speed (slower) + + // Generate waveform using multiple sine waves for realism + const wave1 = Math.sin(position * 2) * 0.3; + const wave2 = Math.sin(position * 5 + elapsed) * 0.2; + const wave3 = Math.sin(position * 0.5 + elapsed * 0.3) * 0.4; + + // Add some randomness for natural variation + const noise = (Math.random() - 0.5) * 0.1; + + // Combine waves and add envelope for realistic amplitude variation + const envelope = Math.sin(elapsed * 0.7) * 0.5 + 0.5; // Slow amplitude modulation + let amplitude = (wave1 + wave2 + wave3 + noise) * envelope; + + // Clamp to 0-1 range + amplitude = Math.max(0, Math.min(1, amplitude * 0.5 + 0.3)); + + samples.push(amplitude); + } + + return samples; + }; + + // No setup needed - stub implementation + + // Canvas rendering with 60fps animation + useEffect(() => { + const canvas = canvasRef.current; + if (!canvas) return; + + const ctx = canvas.getContext("2d"); + if (!ctx) return; + + let animationId: number; + + const draw = () => { + const rect = canvas.getBoundingClientRect(); + const dpr = window.devicePixelRatio || 1; + + // Update canvas dimensions if container resized + if ( + canvas.width !== rect.width * dpr || + canvas.height !== rect.height * dpr + ) { + canvas.width = rect.width * dpr; + canvas.height = rect.height * dpr; + ctx.scale(dpr, dpr); + ctx.imageSmoothingEnabled = false; + } + + // Configuration + const barWidth = 2; + const minHeight = 2; + const maxHeight = 20; + const gap = 2; + const numSamples = Math.ceil(rect.width / (barWidth + gap)); + + // Get loudness data + const loudnessData = getLoudness(numSamples); + + // Clear canvas + ctx.clearRect(0, 0, rect.width, rect.height); + + // Get current foreground color + const computedStyle = getComputedStyle(canvas); + const currentForeground = computedStyle.color; + + // Draw bars + ctx.fillStyle = currentForeground; + const centerY = rect.height / 2; + + for (let i = 0; i < loudnessData.length; i++) { + const sample = loudnessData[i] ?? 0; + const barHeight = Math.round( + sample * (maxHeight - minHeight) + minHeight + ); + const x = Math.round(i * (barWidth + gap)); + const y = Math.round(centerY - barHeight / 2); + + ctx.fillRect(x, y, barWidth, barHeight); + } + + animationId = requestAnimationFrame(draw); + }; + + draw(); + + return () => { + if (animationId) { + cancelAnimationFrame(animationId); + } + }; + }, []); + + // Expose AudioRecorder API + useImperativeHandle( + ref, + () => ({ + get state() { + return "idle" as AudioRecorderState; + }, + start: async () => { + // Stub implementation - no actual recording + }, + stop: () => + new Promise((resolve) => { + // Stub implementation - return empty blob + const emptyBlob = new Blob([], { type: "audio/webm" }); + resolve(emptyBlob); + }), + dispose: () => { + // No cleanup needed + }, + }), + [] + ); + + return ( +
    + +
    + ); +}); + +CopilotChatAudioRecorder.displayName = "WebAudioRecorder"; diff --git a/src/v2.x/packages/react/src/components/chat/CopilotChatInput.tsx b/src/v2.x/packages/react/src/components/chat/CopilotChatInput.tsx new file mode 100644 index 0000000000..065316aa73 --- /dev/null +++ b/src/v2.x/packages/react/src/components/chat/CopilotChatInput.tsx @@ -0,0 +1,1097 @@ +import React, { + useState, + useRef, + KeyboardEvent, + ChangeEvent, + useEffect, + useLayoutEffect, + forwardRef, + useImperativeHandle, + useCallback, + useMemo, +} from "react"; +import { twMerge } from "tailwind-merge"; +import { Plus, Mic, ArrowUp, X, Check, Square } from "lucide-react"; + +import { + CopilotChatLabels, + useCopilotChatConfiguration, + CopilotChatDefaultLabels, +} from "@/providers/CopilotChatConfigurationProvider"; +import { Button } from "@/components/ui/button"; +import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; +import { + DropdownMenu, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSub, + DropdownMenuSubTrigger, + DropdownMenuSubContent, + DropdownMenuSeparator, +} from "@/components/ui/dropdown-menu"; + +import { CopilotChatAudioRecorder } from "./CopilotChatAudioRecorder"; +import { renderSlot, WithSlots } from "@/lib/slots"; + +export type CopilotChatInputMode = "input" | "transcribe" | "processing"; + +export type ToolsMenuItem = { + label: string; +} & ( + | { + action: () => void; + items?: never; + } + | { + action?: never; + items: (ToolsMenuItem | "-")[]; + } +); + +type CopilotChatInputSlots = { + textArea: typeof CopilotChatInput.TextArea; + sendButton: typeof CopilotChatInput.SendButton; + startTranscribeButton: typeof CopilotChatInput.StartTranscribeButton; + cancelTranscribeButton: typeof CopilotChatInput.CancelTranscribeButton; + finishTranscribeButton: typeof CopilotChatInput.FinishTranscribeButton; + addMenuButton: typeof CopilotChatInput.AddMenuButton; + audioRecorder: typeof CopilotChatAudioRecorder; +}; + +type CopilotChatInputRestProps = { + mode?: CopilotChatInputMode; + toolsMenu?: (ToolsMenuItem | "-")[]; + autoFocus?: boolean; + onSubmitMessage?: (value: string) => void; + onStop?: () => void; + isRunning?: boolean; + onStartTranscribe?: () => void; + onCancelTranscribe?: () => void; + onFinishTranscribe?: () => void; + onAddFile?: () => void; + value?: string; + onChange?: (value: string) => void; +} & Omit, "onChange">; + +type CopilotChatInputBaseProps = WithSlots; + +type CopilotChatInputChildrenArgs = CopilotChatInputBaseProps extends { children?: infer C } + ? C extends (props: infer P) => React.ReactNode + ? P + : never + : never; + +export type CopilotChatInputProps = Omit & { + children?: (props: CopilotChatInputChildrenArgs) => React.ReactNode; +}; + +const SLASH_MENU_MAX_VISIBLE_ITEMS = 5; +const SLASH_MENU_ITEM_HEIGHT_PX = 40; + +export function CopilotChatInput({ + mode = "input", + onSubmitMessage, + onStop, + isRunning = false, + onStartTranscribe, + onCancelTranscribe, + onFinishTranscribe, + onAddFile, + onChange, + value, + toolsMenu, + autoFocus = true, + textArea, + sendButton, + startTranscribeButton, + cancelTranscribeButton, + finishTranscribeButton, + addMenuButton, + audioRecorder, + children, + className, + ...props +}: CopilotChatInputProps) { + const isControlled = value !== undefined; + const [internalValue, setInternalValue] = useState(() => value ?? ""); + + useEffect(() => { + if (!isControlled && value !== undefined) { + setInternalValue(value); + } + }, [isControlled, value]); + + const resolvedValue = isControlled ? (value ?? "") : internalValue; + + const [layout, setLayout] = useState<"compact" | "expanded">("compact"); + const ignoreResizeRef = useRef(false); + const resizeEvaluationRafRef = useRef(null); + const isExpanded = mode === "input" && layout === "expanded"; + const [commandQuery, setCommandQuery] = useState(null); + const [slashHighlightIndex, setSlashHighlightIndex] = useState(0); + + const inputRef = useRef(null); + const gridRef = useRef(null); + const addButtonContainerRef = useRef(null); + const actionsContainerRef = useRef(null); + const audioRecorderRef = useRef>(null); + const slashMenuRef = useRef(null); + const config = useCopilotChatConfiguration(); + const labels = config?.labels ?? CopilotChatDefaultLabels; + + const previousModalStateRef = useRef(undefined); + const measurementCanvasRef = useRef(null); + const measurementsRef = useRef({ + singleLineHeight: 0, + maxHeight: 0, + paddingLeft: 0, + paddingRight: 0, + }); + + const commandItems = useMemo(() => { + const entries: ToolsMenuItem[] = []; + const seen = new Set(); + + const pushItem = (item: ToolsMenuItem | "-") => { + if (item === "-") { + return; + } + + if (item.items && item.items.length > 0) { + for (const nested of item.items) { + pushItem(nested); + } + return; + } + + if (!seen.has(item.label)) { + seen.add(item.label); + entries.push(item); + } + }; + + if (onAddFile) { + pushItem({ + label: labels.chatInputToolbarAddButtonLabel, + action: onAddFile, + }); + } + + if (toolsMenu && toolsMenu.length > 0) { + for (const item of toolsMenu) { + pushItem(item); + } + } + + return entries; + }, [labels.chatInputToolbarAddButtonLabel, onAddFile, toolsMenu]); + + const filteredCommands = useMemo(() => { + if (commandQuery === null) { + return [] as ToolsMenuItem[]; + } + + if (commandItems.length === 0) { + return [] as ToolsMenuItem[]; + } + + const query = commandQuery.trim().toLowerCase(); + if (query.length === 0) { + return commandItems; + } + + const startsWith: ToolsMenuItem[] = []; + const contains: ToolsMenuItem[] = []; + for (const item of commandItems) { + const label = item.label.toLowerCase(); + if (label.startsWith(query)) { + startsWith.push(item); + } else if (label.includes(query)) { + contains.push(item); + } + } + + return [...startsWith, ...contains]; + }, [commandItems, commandQuery]); + + useEffect(() => { + if (!autoFocus) { + previousModalStateRef.current = config?.isModalOpen; + return; + } + + if (config?.isModalOpen && !previousModalStateRef.current) { + inputRef.current?.focus(); + } + + previousModalStateRef.current = config?.isModalOpen; + }, [config?.isModalOpen, autoFocus]); + + useEffect(() => { + if (commandItems.length === 0 && commandQuery !== null) { + setCommandQuery(null); + } + }, [commandItems.length, commandQuery]); + + const previousCommandQueryRef = useRef(null); + + useEffect(() => { + if ( + commandQuery !== null && + commandQuery !== previousCommandQueryRef.current && + filteredCommands.length > 0 + ) { + setSlashHighlightIndex(0); + } + + previousCommandQueryRef.current = commandQuery; + }, [commandQuery, filteredCommands.length]); + + useEffect(() => { + if (commandQuery === null) { + setSlashHighlightIndex(0); + return; + } + + if (filteredCommands.length === 0) { + setSlashHighlightIndex(-1); + } else if (slashHighlightIndex < 0 || slashHighlightIndex >= filteredCommands.length) { + setSlashHighlightIndex(0); + } + }, [commandQuery, filteredCommands, slashHighlightIndex]); + + // Handle recording based on mode changes + useEffect(() => { + const recorder = audioRecorderRef.current; + if (!recorder) { + return; + } + + if (mode === "transcribe") { + // Start recording when entering transcribe mode + recorder.start().catch(console.error); + } else { + // Stop recording when leaving transcribe mode + if (recorder.state === "recording") { + recorder.stop().catch(console.error); + } + } + }, [mode]); + + useEffect(() => { + if (mode !== "input") { + setLayout("compact"); + setCommandQuery(null); + } + }, [mode]); + + const updateSlashState = useCallback( + (value: string) => { + if (commandItems.length === 0) { + setCommandQuery((prev) => (prev === null ? prev : null)); + return; + } + + if (value.startsWith("/")) { + const firstLine = value.split(/\r?\n/, 1)[0] ?? ""; + const query = firstLine.slice(1); + setCommandQuery((prev) => (prev === query ? prev : query)); + } else { + setCommandQuery((prev) => (prev === null ? prev : null)); + } + }, + [commandItems.length], + ); + + useEffect(() => { + updateSlashState(resolvedValue); + }, [resolvedValue, updateSlashState]); + + // Handlers + const handleChange = (e: ChangeEvent) => { + const nextValue = e.target.value; + if (!isControlled) { + setInternalValue(nextValue); + } + onChange?.(nextValue); + updateSlashState(nextValue); + }; + + const clearInputValue = useCallback(() => { + if (!isControlled) { + setInternalValue(""); + } + + if (onChange) { + onChange(""); + } + }, [isControlled, onChange]); + + const runCommand = useCallback( + (item: ToolsMenuItem) => { + clearInputValue(); + + item.action?.(); + + setCommandQuery(null); + setSlashHighlightIndex(0); + + requestAnimationFrame(() => { + inputRef.current?.focus(); + }); + }, + [clearInputValue], + ); + + const handleKeyDown = (e: KeyboardEvent) => { + if (commandQuery !== null && mode === "input") { + if (e.key === "ArrowDown") { + if (filteredCommands.length > 0) { + e.preventDefault(); + setSlashHighlightIndex((prev) => { + if (filteredCommands.length === 0) { + return prev; + } + const next = prev === -1 ? 0 : (prev + 1) % filteredCommands.length; + return next; + }); + } + return; + } + + if (e.key === "ArrowUp") { + if (filteredCommands.length > 0) { + e.preventDefault(); + setSlashHighlightIndex((prev) => { + if (filteredCommands.length === 0) { + return prev; + } + if (prev === -1) { + return filteredCommands.length - 1; + } + return prev <= 0 ? filteredCommands.length - 1 : prev - 1; + }); + } + return; + } + + if (e.key === "Enter") { + const selected = slashHighlightIndex >= 0 ? filteredCommands[slashHighlightIndex] : undefined; + if (selected) { + e.preventDefault(); + runCommand(selected); + return; + } + } + + if (e.key === "Escape") { + e.preventDefault(); + setCommandQuery(null); + return; + } + } + + if (e.key === "Enter" && !e.shiftKey) { + e.preventDefault(); + if (isProcessing) { + onStop?.(); + } else { + send(); + } + } + }; + + const send = () => { + if (!onSubmitMessage) { + return; + } + const trimmed = resolvedValue.trim(); + if (!trimmed) { + return; + } + + onSubmitMessage(trimmed); + + if (!isControlled) { + setInternalValue(""); + onChange?.(""); + } + + if (inputRef.current) { + inputRef.current.focus(); + } + }; + + const BoundTextArea = renderSlot(textArea, CopilotChatInput.TextArea, { + ref: inputRef, + value: resolvedValue, + onChange: handleChange, + onKeyDown: handleKeyDown, + autoFocus: autoFocus, + className: twMerge( + "w-full py-3", + isExpanded ? "px-5" : "pr-5", + ), + }); + + const isProcessing = mode !== "transcribe" && isRunning; + const canSend = resolvedValue.trim().length > 0 && !!onSubmitMessage; + const canStop = !!onStop; + + const handleSendButtonClick = () => { + if (isProcessing) { + onStop?.(); + return; + } + send(); + }; + + const BoundAudioRecorder = renderSlot(audioRecorder, CopilotChatAudioRecorder, { + ref: audioRecorderRef, + }); + + const BoundSendButton = renderSlot(sendButton, CopilotChatInput.SendButton, { + onClick: handleSendButtonClick, + disabled: isProcessing ? !canStop : !canSend, + children: isProcessing && canStop ? : undefined, + }); + + const BoundStartTranscribeButton = renderSlot(startTranscribeButton, CopilotChatInput.StartTranscribeButton, { + onClick: onStartTranscribe, + }); + + const BoundCancelTranscribeButton = renderSlot(cancelTranscribeButton, CopilotChatInput.CancelTranscribeButton, { + onClick: onCancelTranscribe, + }); + + const BoundFinishTranscribeButton = renderSlot(finishTranscribeButton, CopilotChatInput.FinishTranscribeButton, { + onClick: onFinishTranscribe, + }); + + const BoundAddMenuButton = renderSlot(addMenuButton, CopilotChatInput.AddMenuButton, { + disabled: mode === "transcribe", + onAddFile, + toolsMenu, + }); + + if (children) { + const childProps = { + textArea: BoundTextArea, + audioRecorder: BoundAudioRecorder, + sendButton: BoundSendButton, + startTranscribeButton: BoundStartTranscribeButton, + cancelTranscribeButton: BoundCancelTranscribeButton, + finishTranscribeButton: BoundFinishTranscribeButton, + addMenuButton: BoundAddMenuButton, + onSubmitMessage, + onStop, + isRunning, + onStartTranscribe, + onCancelTranscribe, + onFinishTranscribe, + onAddFile, + mode, + toolsMenu, + autoFocus, + } as CopilotChatInputChildrenArgs; + + return <>{children(childProps)}; + } + + const handleContainerClick = (e: React.MouseEvent) => { + // Don't focus if clicking on buttons or other interactive elements + const target = e.target as HTMLElement; + if (target.tagName !== "BUTTON" && !target.closest("button") && inputRef.current && mode === "input") { + inputRef.current.focus(); + } + }; + + const ensureMeasurements = useCallback(() => { + const textarea = inputRef.current; + if (!textarea) { + return; + } + + const previousValue = textarea.value; + const previousHeight = textarea.style.height; + + textarea.style.height = "auto"; + + const computedStyle = window.getComputedStyle(textarea); + const paddingLeft = parseFloat(computedStyle.paddingLeft) || 0; + const paddingRight = parseFloat(computedStyle.paddingRight) || 0; + const paddingTop = parseFloat(computedStyle.paddingTop) || 0; + const paddingBottom = parseFloat(computedStyle.paddingBottom) || 0; + + textarea.value = ""; + const singleLineHeight = textarea.scrollHeight; + textarea.value = previousValue; + + const contentHeight = singleLineHeight - paddingTop - paddingBottom; + const maxHeight = contentHeight * 5 + paddingTop + paddingBottom; + + measurementsRef.current = { + singleLineHeight, + maxHeight, + paddingLeft, + paddingRight, + }; + + textarea.style.height = previousHeight; + textarea.style.maxHeight = `${maxHeight}px`; + }, []); + + const adjustTextareaHeight = useCallback(() => { + const textarea = inputRef.current; + if (!textarea) { + return 0; + } + + if (measurementsRef.current.singleLineHeight === 0) { + ensureMeasurements(); + } + + const { maxHeight } = measurementsRef.current; + if (maxHeight) { + textarea.style.maxHeight = `${maxHeight}px`; + } + + textarea.style.height = "auto"; + const scrollHeight = textarea.scrollHeight; + if (maxHeight) { + textarea.style.height = `${Math.min(scrollHeight, maxHeight)}px`; + } else { + textarea.style.height = `${scrollHeight}px`; + } + + return scrollHeight; + }, [ensureMeasurements]); + + const updateLayout = useCallback((nextLayout: "compact" | "expanded") => { + setLayout((prev) => { + if (prev === nextLayout) { + return prev; + } + ignoreResizeRef.current = true; + return nextLayout; + }); + }, []); + + const evaluateLayout = useCallback(() => { + if (mode !== "input") { + updateLayout("compact"); + return; + } + + if (typeof window !== "undefined" && typeof window.matchMedia === "function") { + const isMobileViewport = window.matchMedia("(max-width: 767px)").matches; + if (isMobileViewport) { + ensureMeasurements(); + adjustTextareaHeight(); + updateLayout("expanded"); + return; + } + } + + const textarea = inputRef.current; + const grid = gridRef.current; + const addContainer = addButtonContainerRef.current; + const actionsContainer = actionsContainerRef.current; + + if (!textarea || !grid || !addContainer || !actionsContainer) { + return; + } + + if (measurementsRef.current.singleLineHeight === 0) { + ensureMeasurements(); + } + + const scrollHeight = adjustTextareaHeight(); + const baseline = measurementsRef.current.singleLineHeight; + const hasExplicitBreak = resolvedValue.includes("\n"); + const renderedMultiline = baseline > 0 ? scrollHeight > baseline + 1 : false; + let shouldExpand = hasExplicitBreak || renderedMultiline; + + if (!shouldExpand) { + const gridStyles = window.getComputedStyle(grid); + const paddingLeft = parseFloat(gridStyles.paddingLeft) || 0; + const paddingRight = parseFloat(gridStyles.paddingRight) || 0; + const columnGap = parseFloat(gridStyles.columnGap) || 0; + const gridAvailableWidth = grid.clientWidth - paddingLeft - paddingRight; + + if (gridAvailableWidth > 0) { + const addWidth = addContainer.getBoundingClientRect().width; + const actionsWidth = actionsContainer.getBoundingClientRect().width; + const compactWidth = Math.max(gridAvailableWidth - addWidth - actionsWidth - columnGap * 2, 0); + + const canvas = measurementCanvasRef.current ?? document.createElement("canvas"); + if (!measurementCanvasRef.current) { + measurementCanvasRef.current = canvas; + } + + const context = canvas.getContext("2d"); + if (context) { + const textareaStyles = window.getComputedStyle(textarea); + const font = + textareaStyles.font || + `${textareaStyles.fontStyle} ${textareaStyles.fontVariant} ${textareaStyles.fontWeight} ${textareaStyles.fontSize}/${textareaStyles.lineHeight} ${textareaStyles.fontFamily}`; + context.font = font; + + const compactInnerWidth = Math.max( + compactWidth - (measurementsRef.current.paddingLeft || 0) - (measurementsRef.current.paddingRight || 0), + 0, + ); + + if (compactInnerWidth > 0) { + const lines = resolvedValue.length > 0 ? resolvedValue.split("\n") : [""]; + let longestWidth = 0; + for (const line of lines) { + const metrics = context.measureText(line || " "); + if (metrics.width > longestWidth) { + longestWidth = metrics.width; + } + } + + if (longestWidth > compactInnerWidth) { + shouldExpand = true; + } + } + } + } + } + + const nextLayout = shouldExpand ? "expanded" : "compact"; + updateLayout(nextLayout); + }, [adjustTextareaHeight, ensureMeasurements, mode, resolvedValue, updateLayout]); + + useLayoutEffect(() => { + evaluateLayout(); + }, [evaluateLayout]); + + useEffect(() => { + if (typeof ResizeObserver === "undefined") { + return; + } + + const textarea = inputRef.current; + const grid = gridRef.current; + const addContainer = addButtonContainerRef.current; + const actionsContainer = actionsContainerRef.current; + + if (!textarea || !grid || !addContainer || !actionsContainer) { + return; + } + + const scheduleEvaluation = () => { + if (ignoreResizeRef.current) { + ignoreResizeRef.current = false; + return; + } + + if (typeof window === "undefined") { + evaluateLayout(); + return; + } + + if (resizeEvaluationRafRef.current !== null) { + cancelAnimationFrame(resizeEvaluationRafRef.current); + } + + resizeEvaluationRafRef.current = window.requestAnimationFrame(() => { + resizeEvaluationRafRef.current = null; + evaluateLayout(); + }); + }; + + const observer = new ResizeObserver(() => { + scheduleEvaluation(); + }); + + observer.observe(grid); + observer.observe(addContainer); + observer.observe(actionsContainer); + observer.observe(textarea); + + return () => { + observer.disconnect(); + if (typeof window !== "undefined" && resizeEvaluationRafRef.current !== null) { + cancelAnimationFrame(resizeEvaluationRafRef.current); + resizeEvaluationRafRef.current = null; + } + }; + }, [evaluateLayout]); + + const slashMenuVisible = commandQuery !== null && commandItems.length > 0; + + useEffect(() => { + if (!slashMenuVisible || slashHighlightIndex < 0) { + return; + } + + const active = slashMenuRef.current?.querySelector( + `[data-slash-index="${slashHighlightIndex}"]`, + ); + active?.scrollIntoView({ block: "nearest" }); + }, [slashMenuVisible, slashHighlightIndex]); + + const slashMenu = slashMenuVisible ? ( +
    + {filteredCommands.length === 0 ? ( +
    No commands found
    + ) : ( + filteredCommands.map((item, index) => { + const isActive = index === slashHighlightIndex; + return ( + + ); + }) + )} +
    + ) : null; + + return ( +
    +
    +
    + {BoundAddMenuButton} +
    +
    + {mode === "transcribe" ? ( + BoundAudioRecorder + ) : ( + <> + {BoundTextArea} + {slashMenu} + + )} +
    +
    + {mode === "transcribe" ? ( + <> + {onCancelTranscribe && BoundCancelTranscribeButton} + {onFinishTranscribe && BoundFinishTranscribeButton} + + ) : ( + <> + {onStartTranscribe && BoundStartTranscribeButton} + {BoundSendButton} + + )} +
    +
    +
    + ); +} + +// eslint-disable-next-line @typescript-eslint/no-namespace +export namespace CopilotChatInput { + export const SendButton: React.FC> = ({ className, children, ...props }) => ( +
    + +
    + ); + + export const ToolbarButton: React.FC< + React.ButtonHTMLAttributes & { + icon: React.ReactNode; + labelKey: keyof CopilotChatLabels; + defaultClassName?: string; + } + > = ({ icon, labelKey, defaultClassName, className, ...props }) => { + const config = useCopilotChatConfiguration(); + const labels = config?.labels ?? CopilotChatDefaultLabels; + return ( + + + + + +

    {labels[labelKey]}

    +
    +
    + ); + }; + + export const StartTranscribeButton: React.FC> = (props) => ( + } + labelKey="chatInputToolbarStartTranscribeButtonLabel" + defaultClassName="mr-2" + {...props} + /> + ); + + export const CancelTranscribeButton: React.FC> = (props) => ( + } + labelKey="chatInputToolbarCancelTranscribeButtonLabel" + defaultClassName="mr-2" + {...props} + /> + ); + + export const FinishTranscribeButton: React.FC> = (props) => ( + } + labelKey="chatInputToolbarFinishTranscribeButtonLabel" + defaultClassName="mr-[10px]" + {...props} + /> + ); + + export const AddMenuButton: React.FC< + React.ButtonHTMLAttributes & { + toolsMenu?: (ToolsMenuItem | "-")[]; + onAddFile?: () => void; + } + > = ({ className, toolsMenu, onAddFile, disabled, ...props }) => { + const config = useCopilotChatConfiguration(); + const labels = config?.labels ?? CopilotChatDefaultLabels; + + const menuItems = useMemo<(ToolsMenuItem | "-")[]>(() => { + const items: (ToolsMenuItem | "-")[] = []; + + if (onAddFile) { + items.push({ + label: labels.chatInputToolbarAddButtonLabel, + action: onAddFile, + }); + } + + if (toolsMenu && toolsMenu.length > 0) { + if (items.length > 0) { + items.push("-"); + } + + for (const item of toolsMenu) { + if (item === "-") { + if (items.length === 0 || items[items.length - 1] === "-") { + continue; + } + items.push(item); + } else { + items.push(item); + } + } + + while (items.length > 0 && items[items.length - 1] === "-") { + items.pop(); + } + } + + return items; + }, [onAddFile, toolsMenu, labels.chatInputToolbarAddButtonLabel]); + + const renderMenuItems = useCallback( + (items: (ToolsMenuItem | "-")[]): React.ReactNode => + items.map((item, index) => { + if (item === "-") { + return ; + } + + if (item.items && item.items.length > 0) { + return ( + + {item.label} + {renderMenuItems(item.items)} + + ); + } + + return ( + + {item.label} + + ); + }), + [], + ); + + const hasMenuItems = menuItems.length > 0; + const isDisabled = disabled || !hasMenuItems; + + return ( + + + + + + + + +

    + Add files and more + / +

    +
    +
    + {hasMenuItems && ( + + {renderMenuItems(menuItems)} + + )} +
    + ); + }; + + export type TextAreaProps = React.TextareaHTMLAttributes; + + export const TextArea = forwardRef(function TextArea( + { style, className, autoFocus, ...props }, + ref, + ) { + const internalTextareaRef = useRef(null); + const config = useCopilotChatConfiguration(); + const labels = config?.labels ?? CopilotChatDefaultLabels; + + useImperativeHandle(ref, () => internalTextareaRef.current as HTMLTextAreaElement); + + // Auto-scroll input into view on mobile when focused + useEffect(() => { + const textarea = internalTextareaRef.current; + if (!textarea) return; + + const handleFocus = () => { + // Small delay to let the keyboard start appearing + setTimeout(() => { + textarea.scrollIntoView({ behavior: "smooth", block: "nearest" }); + }, 300); + }; + + textarea.addEventListener("focus", handleFocus); + return () => textarea.removeEventListener("focus", handleFocus); + }, []); + + useEffect(() => { + if (autoFocus) { + internalTextareaRef.current?.focus(); + } + }, [autoFocus]); + + return ( +