diff --git a/.env.example b/.env.example index 0c3318bba..ebd206cb1 100644 --- a/.env.example +++ b/.env.example @@ -31,6 +31,9 @@ NEXT_PUBLIC_APP_URL=http://localhost:3000 # OpenAI API - Get from: https://platform.openai.com/api-keys OPENAI_API_KEY= OPENAI_ORG_ID= +# Optional: OpenAI model for the web action agent (Responses API); supports +# Codex/ChatGPT models such as codex-mini-latest. Defaults to gpt-4o-mini. +OPENAI_ACTION_MODEL= # Anthropic Claude API - Get from: https://console.anthropic.com/settings/keys ANTHROPIC_API_KEY= diff --git a/.github/workflows/AUDIT.md b/.github/workflows/AUDIT.md index 55c4cd66d..4baa8a692 100644 --- a/.github/workflows/AUDIT.md +++ b/.github/workflows/AUDIT.md @@ -66,4 +66,4 @@ valid. Referenced paths were checked against the working tree: | `agent-completion-enforcement.yml` | **ADD** | Protected-default-branch verifier that creates the independent **Agent completion enforcement** Check directly against the PR head SHA. It accepts only an exact-head machine-readable report from the configured dedicated GitHub App; missing/stale/mutable evidence, untrusted label provenance, and custom roles all fail closed. The existing `agent-completion/truth-gate` status stays advisory and must not be made required. | -The protected policy at `.github/agent-lock/trusted-publishers.json` starts with empty allowlists and therefore blocks until a repository administrator provisions the dedicated App and trusted actor identities through protected review. The repository ruleset must then require **Agent completion enforcement**, one independent approval, and resolved conversations. \ No newline at end of file +The protected policy at `.github/agent-lock/trusted-publishers.json` starts with empty allowlists. Until a repository administrator provisions the dedicated App and trusted actor identities through protected review, a head with no trusted publication receives a `neutral` advisory Check (`trust_policy_unprovisioned_no_publication`) instead of failing every pull request; a published report against empty allowlists, or a missing publication once any allowlist is populated, still fails closed. The repository ruleset must then require **Agent completion enforcement**, one independent approval, and resolved conversations. \ No newline at end of file diff --git a/.github/workflows/README.md b/.github/workflows/README.md index 61a3feca3..d91f8e569 100644 --- a/.github/workflows/README.md +++ b/.github/workflows/README.md @@ -129,4 +129,4 @@ A full audit of this directory was performed (see `pr-checks.yml` retains the advisory `agent-completion/truth-gate/pr-` status; it is never required. `agent-completion-enforcement.yml` runs protected default-branch code, does not execute PR code, and creates the separate **Agent completion enforcement** Check directly on the PR head SHA. It accepts only an exact-head, machine-readable report published by the configured dedicated GitHub App. Missing, stale, edited/deleted, ambiguous, or untrusted evidence fails closed. -Before enabling the rule, provision `.github/agent-lock/trusted-publishers.json` through protected review with the trusted App and actor allowlists. Empty lists intentionally block. Configure the repository ruleset to require **Agent completion enforcement**, one independent approval, and resolved conversations. Do not require `agent-completion/truth-gate`. \ No newline at end of file +Before enabling the rule, provision `.github/agent-lock/trusted-publishers.json` through protected review with the trusted App and actor allowlists. While a publication exists, empty lists intentionally block; until the App is provisioned (all three allowlists empty) a missing publication publishes a `neutral` advisory Check rather than failing every pull request. Configure the repository ruleset to require **Agent completion enforcement**, one independent approval, and resolved conversations only after provisioning. Do not require `agent-completion/truth-gate`. \ No newline at end of file diff --git a/.github/workflows/agent-completion-enforcement.yml b/.github/workflows/agent-completion-enforcement.yml index 1c4fd1b5d..68dc2c7af 100644 --- a/.github/workflows/agent-completion-enforcement.yml +++ b/.github/workflows/agent-completion-enforcement.yml @@ -41,11 +41,10 @@ jobs: # output/artifact. This workflow never executes PR-controlled code or # accepts an artifact produced by a pull_request workflow. gh api "repos/$REPO/commits/$head/check-runs" --jq '.check_runs[] | select(.name == "Agent Lock trusted publication") | .output.text' > trusted-report.json - if test -s trusted-report.json; then - python3 scripts/ci/agent_completion_enforcement.py trusted-report.json .github/agent-lock/trusted-publishers.json "$head" "$PR" > enforcement-verdict.json - else - printf '%s\n' '{"conclusion":"failure","reason":"missing_trusted_publication","details":{}}' > enforcement-verdict.json - fi + # The verifier fails closed on any provisioned policy; it publishes a + # neutral advisory verdict only when no publication exists and every + # trusted allowlist is verifiably empty (App not yet provisioned). + python3 scripts/ci/agent_completion_enforcement.py trusted-report.json .github/agent-lock/trusted-publishers.json "$head" "$PR" > enforcement-verdict.json - name: Publish the required head-bound Check run if: always() uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 @@ -73,7 +72,9 @@ jobs: } const conclusion = verdict.conclusion === 'success' ? 'success' - : 'failure'; + : verdict.conclusion === 'neutral' + ? 'neutral' + : 'failure'; const summary = JSON.stringify(verdict); await github.rest.checks.create({ owner: context.repo.owner, @@ -85,10 +86,12 @@ jobs: output: { title: conclusion === 'success' ? 'Trusted evidence verified' - : 'Trusted evidence blocked', + : conclusion === 'neutral' + ? 'Trusted publisher not yet provisioned' + : 'Trusted evidence blocked', summary: summary.slice(0, 60000) } }); - if (conclusion !== 'success') { + if (conclusion === 'failure') { core.setFailed(verdict.reason || 'trusted evidence blocked'); } diff --git a/apps/web/.env.example b/apps/web/.env.example index 31d0fd384..fde1b7f98 100644 --- a/apps/web/.env.example +++ b/apps/web/.env.example @@ -14,6 +14,9 @@ UVAI_AI_RATE_LIMIT_PER_MINUTE=12 # AI Gateway (Vercel AI SDK) OPENAI_API_KEY=sk-... +# Optional: OpenAI model for the action agent (Responses API); supports +# Codex/ChatGPT models such as codex-mini-latest. Defaults to gpt-4o-mini. +# OPENAI_ACTION_MODEL=codex-mini-latest ANTHROPIC_API_KEY=sk-ant-... # Supabase diff --git a/apps/web/src/lib/__tests__/action-agent-model.test.ts b/apps/web/src/lib/__tests__/action-agent-model.test.ts new file mode 100644 index 000000000..e62bcf644 --- /dev/null +++ b/apps/web/src/lib/__tests__/action-agent-model.test.ts @@ -0,0 +1,43 @@ +import { describe, it, expect, afterEach } from 'vitest'; +import { resolveOpenAIActionModel, AVAILABLE_TOOL_NAMES } from '@/lib/action-agent'; + +const ORIGINAL = process.env.OPENAI_ACTION_MODEL; + +afterEach(() => { + if (ORIGINAL === undefined) delete process.env.OPENAI_ACTION_MODEL; + else process.env.OPENAI_ACTION_MODEL = ORIGINAL; +}); + +describe('resolveOpenAIActionModel', () => { + it('defaults to gpt-4o-mini when OPENAI_ACTION_MODEL is unset', () => { + delete process.env.OPENAI_ACTION_MODEL; + expect(resolveOpenAIActionModel()).toBe('gpt-4o-mini'); + }); + + it('uses a configured Codex model on the Responses API', () => { + process.env.OPENAI_ACTION_MODEL = 'codex-mini-latest'; + expect(resolveOpenAIActionModel()).toBe('codex-mini-latest'); + }); + + it('trims whitespace and falls back to the default for blank values', () => { + process.env.OPENAI_ACTION_MODEL = ' gpt-5-codex '; + expect(resolveOpenAIActionModel()).toBe('gpt-5-codex'); + + process.env.OPENAI_ACTION_MODEL = ' '; + expect(resolveOpenAIActionModel()).toBe('gpt-4o-mini'); + }); + + it('resolves per call so runtime env changes take effect', () => { + process.env.OPENAI_ACTION_MODEL = 'codex-mini-latest'; + expect(resolveOpenAIActionModel()).toBe('codex-mini-latest'); + process.env.OPENAI_ACTION_MODEL = 'gpt-4o-mini'; + expect(resolveOpenAIActionModel()).toBe('gpt-4o-mini'); + }); +}); + +describe('action agent surface', () => { + it('still exposes the executable tool registry', () => { + expect(AVAILABLE_TOOL_NAMES).toContain('dispatch_agent'); + expect(AVAILABLE_TOOL_NAMES.length).toBeGreaterThan(0); + }); +}); diff --git a/apps/web/src/lib/action-agent.ts b/apps/web/src/lib/action-agent.ts index 9e2545557..97a2d4af0 100644 --- a/apps/web/src/lib/action-agent.ts +++ b/apps/web/src/lib/action-agent.ts @@ -10,7 +10,9 @@ import 'server-only'; * * Provider routing mirrors the rest of the app: OpenAI (Responses API, * multi-round function calling) is primary; Gemini is a single-round fallback - * used only when OpenAI is unavailable or quota-limited. + * used only when OpenAI is unavailable or quota-limited. The OpenAI model is + * configurable via `OPENAI_ACTION_MODEL`, so Codex/ChatGPT models served by + * the same Responses API (e.g. `codex-mini-latest`) can drive the agent. */ import OpenAI from 'openai'; @@ -33,11 +35,22 @@ function getOpenAI(): OpenAI { return _openai; } -const MODEL_OPENAI = 'gpt-4o-mini'; +const DEFAULT_MODEL_OPENAI = 'gpt-4o-mini'; const MODEL_GEMINI = GEMINI_FAST_MODEL; const MAX_TOOL_ROUNDS = 4; const MAX_TRANSCRIPT_CHARS = 8000; +/** + * Resolve the OpenAI model used for action extraction. Set `OPENAI_ACTION_MODEL` + * to route the agent to a Codex/ChatGPT model on the same Responses API + * (e.g. `codex-mini-latest` or `gpt-5-codex`); defaults to `gpt-4o-mini`. + * Resolved per call so Next.js runtime env changes take effect without a rebuild. + */ +export function resolveOpenAIActionModel(): string { + const model = process.env.OPENAI_ACTION_MODEL?.trim(); + return model || DEFAULT_MODEL_OPENAI; +} + const SYSTEM_PROMPT = `You are an action agent for a video-to-workflow platform. Read the transcript and decide which executable tools to call so the viewer can act on what was said. Call a tool for every concrete, practical item — do not @@ -85,10 +98,11 @@ function isPlainObject(v: unknown): v is Record { /** Run the OpenAI Responses-API tool-calling loop until the model stops calling tools. */ async function runWithOpenAI(opts: RunActionAgentOptions, ctx: ToolContext): Promise { const openai = getOpenAI(); + const model = resolveOpenAIActionModel(); const actions: AgentAction[] = []; let response = await openai.responses.create({ - model: MODEL_OPENAI, + model, instructions: SYSTEM_PROMPT, input: buildUserPrompt(opts.transcript, opts.videoTitle), tools: toOpenAITools(), @@ -150,7 +164,7 @@ async function runWithOpenAI(opts: RunActionAgentOptions, ctx: ToolContext): Pro } response = await openai.responses.create({ - model: MODEL_OPENAI, + model, previous_response_id: response.id, input: toolOutputs, tools: toOpenAITools(), diff --git a/docs/agent-completion-truth-gate.md b/docs/agent-completion-truth-gate.md index a5e9db8ae..1e5d837ec 100644 --- a/docs/agent-completion-truth-gate.md +++ b/docs/agent-completion-truth-gate.md @@ -4,7 +4,7 @@ The truth gate converts repository evidence into one deterministic verdict. It n ## Required enforcement rollout -`agent-completion/truth-gate` remains advisory and must not be added as a required status. The separate **Agent completion enforcement** workflow is the required, head-bound Check run. It verifies an exact-head machine-readable report published through a dedicated GitHub App and rejects missing, stale, edited, deleted, ambiguous, or self-published agent evidence. The protected policy is `.github/agent-lock/trusted-publishers.json`; empty trusted-publisher or trusted-actor allowlists are intentionally blocking. Custom roles are fail-closed. +`agent-completion/truth-gate` remains advisory and must not be added as a required status. The separate **Agent completion enforcement** workflow is the required, head-bound Check run. It verifies an exact-head machine-readable report published through a dedicated GitHub App and rejects missing, stale, edited, deleted, ambiguous, or self-published agent evidence. The protected policy is `.github/agent-lock/trusted-publishers.json`; while a published report exists, empty trusted-publisher or trusted-actor allowlists are intentionally blocking. When no trusted publication exists and every allowlist in a well-formed fail-closed policy is empty (the dedicated App is not yet provisioned), the workflow publishes a `neutral` advisory Check instead of failing every pull request; any provisioned or malformed policy with a missing publication still fails closed. Custom roles are fail-closed. The trusted publisher must bind report data to PR number, full head SHA, delivery/run identity, trusted label authorization, trusted human exemption (when applicable), append-only agent events, and per-path passed/failed/error counts. The required verifier never executes PR code. Repository rules must require **Agent completion enforcement**, one independent approval, and resolved conversations. They must not require the advisory custom status. diff --git a/package-lock.json b/package-lock.json index 6df12bb54..dae621f69 100644 --- a/package-lock.json +++ b/package-lock.json @@ -109,15 +109,6 @@ "fast-glob": "3.3.1" } }, - "apps/web/node_modules/@opentelemetry/api": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", - "license": "Apache-2.0", - "engines": { - "node": ">=8.0.0" - } - }, "apps/web/node_modules/@opentelemetry/exporter-trace-otlp-http": { "version": "0.220.0", "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.220.0.tgz", @@ -165,749 +156,65 @@ "@opentelemetry/sdk-logs": "0.220.0", "@opentelemetry/sdk-metrics": "2.9.0", "@opentelemetry/sdk-trace": "2.9.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.3.0" - } - }, - "apps/web/node_modules/@opentelemetry/sdk-logs": { - "version": "0.220.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.220.0.tgz", - "integrity": "sha512-WywcTkQtv2iNmt+6y5Kcd4rzvx9bLVsBa2Nwcmg01IUaBTkTow3W4d9KE5vNBpEDtb9tp21WcRBY/lANRrApYA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api-logs": "0.220.0", - "@opentelemetry/core": "2.9.0", - "@opentelemetry/resources": "2.9.0", - "@opentelemetry/semantic-conventions": "^1.29.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.4.0 <1.10.0" - } - }, - "apps/web/node_modules/@opentelemetry/sdk-metrics": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.9.0.tgz", - "integrity": "sha512-Xx8RGS4H5XEBl01WuCreMIpiah9cCXMbSkeuIePPdD2cUpq/vUzYmj8E/MK1OsbOc93FuAD4jfn2WOacKwLn7Q==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "2.9.0", - "@opentelemetry/resources": "2.9.0" - }, - "engines": { - "node": "^18.19.0 || >=20.6.0" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.9.0 <1.10.0" - } - }, - "apps/web/node_modules/@opentelemetry/semantic-conventions": { - "version": "1.43.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.43.0.tgz", - "integrity": "sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, - "apps/web/node_modules/@sentry/browser": { - "version": "10.65.0", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-10.65.0.tgz", - "integrity": "sha512-XUDDsx0qxzeIlcOu1fDEqTcDl0eiOqghsgV+ReuuNP4jYjZ9kUQxE3rXWM5mlT1pBi4VaQ4FHqvQZZrRXy+oDw==", - "license": "MIT", - "dependencies": { - "@sentry/browser-utils": "10.65.0", - "@sentry/conventions": "^0.15.1", - "@sentry/core": "10.65.0", - "@sentry/feedback": "10.65.0", - "@sentry/replay": "10.65.0", - "@sentry/replay-canvas": "10.65.0" - }, - "engines": { - "node": ">=18" - } - }, - "apps/web/node_modules/@sentry/browser-utils": { - "version": "10.65.0", - "resolved": "https://registry.npmjs.org/@sentry/browser-utils/-/browser-utils-10.65.0.tgz", - "integrity": "sha512-4J0mkfNJAGUOkpg1ZggizyftFTn9N20b+Jl87UnWsDUkNG0Ic1l/FIzMPTVxXrAnhBGu0ULO0TFWMoQ5s3QtZw==", - "license": "MIT", - "dependencies": { - "@sentry/conventions": "^0.15.1", - "@sentry/core": "10.65.0" - }, - "engines": { - "node": ">=18" - } - }, - "apps/web/node_modules/@sentry/conventions": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@sentry/conventions/-/conventions-0.15.1.tgz", - "integrity": "sha512-ZLP8bRdMON3prWE2tJyImuYscCxdcJeIPIhrOs/rgyFm3C1nCh1B6gdvPj3AZ5zW08oSFFCsq7T+tYEW3h8MNA==", - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "apps/web/node_modules/@sentry/core": { - "version": "10.65.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.65.0.tgz", - "integrity": "sha512-3aqtmM5NgNGo45BNaaBzi0LPQZAw//NEL4HKS5fXm12pJMa4KEkze8DEKnkTEIrGnWaOJKamecHKlnNg/Mqf/Q==", - "license": "MIT", - "dependencies": { - "@sentry/conventions": "^0.15.1" - }, - "engines": { - "node": ">=18" - } - }, - "apps/web/node_modules/@sentry/feedback": { - "version": "10.65.0", - "resolved": "https://registry.npmjs.org/@sentry/feedback/-/feedback-10.65.0.tgz", - "integrity": "sha512-ck8h7wgd3F3bYNk0v1OgohmyLBeXcKxqlfBJRtQq4k6KZUq+pXimOG7ckNguVMYjCo3PEfuG+ckKc21yqotKug==", - "license": "MIT", - "dependencies": { - "@sentry/core": "10.65.0" - }, - "engines": { - "node": ">=18" - } - }, - "apps/web/node_modules/@sentry/nextjs": { - "version": "10.65.0", - "resolved": "https://registry.npmjs.org/@sentry/nextjs/-/nextjs-10.65.0.tgz", - "integrity": "sha512-9gDKQAAXcWh210fMI/ZNCa7940HYt7dGjnJVP0Tk9ozUR57W4C9vXvHJDTYPJrFxYxTHw7lwxWGervk8a6Tf4g==", - "license": "MIT", - "dependencies": { - "@opentelemetry/api": "^1.9.1", - "@rollup/plugin-commonjs": "28.0.1", - "@sentry/browser-utils": "10.65.0", - "@sentry/bundler-plugin-core": "^5.3.0", - "@sentry/conventions": "^0.15.1", - "@sentry/core": "10.65.0", - "@sentry/node": "10.65.0", - "@sentry/opentelemetry": "10.65.0", - "@sentry/react": "10.65.0", - "@sentry/vercel-edge": "10.65.0", - "@sentry/webpack-plugin": "^5.3.0", - "rollup": "^4.60.3", - "stacktrace-parser": "^0.1.11" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "next": "^13.2.0 || ^14.0 || ^15.0.0-rc.0 || ^16.0.0-0" - } - }, - "apps/web/node_modules/@sentry/nextjs/node_modules/@opentelemetry/api": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz", - "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==", - "license": "Apache-2.0", - "engines": { - "node": ">=8.0.0" - } - }, - "apps/web/node_modules/@sentry/node": { - "version": "10.65.0", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-10.65.0.tgz", - "integrity": "sha512-t35dcdyksysVch/m/XdLgGJqGKJhr9eMD30Ctn3TeQ8yMB0wNXySfjPR5Yg93fpjmfaHtzc6iYIXRAvgNVfrvA==", - "license": "MIT", - "dependencies": { - "@opentelemetry/api": "^1.9.1", - "@opentelemetry/instrumentation": "^0.220.0", - "@opentelemetry/sdk-trace-base": "^2.9.0", - "@sentry/conventions": "^0.15.1", - "@sentry/core": "10.65.0", - "@sentry/node-core": "10.65.0", - "@sentry/opentelemetry": "10.65.0", - "@sentry/server-utils": "10.65.0", - "import-in-the-middle": "^3.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "apps/web/node_modules/@sentry/node-core": { - "version": "10.65.0", - "resolved": "https://registry.npmjs.org/@sentry/node-core/-/node-core-10.65.0.tgz", - "integrity": "sha512-U01X9mPT+jZnsLPmPWfBU67Ka+t/Sdd9RGAuvGoKdrI6N47a/9PDkM9oCW+kj0fmZwogZHTgSnzJU5oi3pImgA==", - "license": "MIT", - "dependencies": { - "@sentry/conventions": "^0.15.1", - "@sentry/core": "10.65.0", - "@sentry/opentelemetry": "10.65.0", - "import-in-the-middle": "^3.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.9.0", - "@opentelemetry/core": "^1.30.1 || ^2.1.0", - "@opentelemetry/exporter-trace-otlp-http": ">=0.57.0 <1", - "@opentelemetry/instrumentation": ">=0.57.1 <1", - "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - }, - "@opentelemetry/core": { - "optional": true - }, - "@opentelemetry/exporter-trace-otlp-http": { - "optional": true - }, - "@opentelemetry/instrumentation": { - "optional": true - }, - "@opentelemetry/sdk-trace-base": { - "optional": true - } - } - }, - "apps/web/node_modules/@sentry/node/node_modules/@opentelemetry/api": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz", - "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==", - "license": "Apache-2.0", - "engines": { - "node": ">=8.0.0" - } - }, - "apps/web/node_modules/@sentry/opentelemetry": { - "version": "10.65.0", - "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-10.65.0.tgz", - "integrity": "sha512-8C6FPvm3XBvUrkM52dX3Gz0p2H0Ij8t4sahUA+GTiCz0WM0fnyPeQPGC/b6I4jamV9UXyCZRnE1UEEGCoD+c7A==", - "license": "MIT", - "dependencies": { - "@sentry/conventions": "^0.15.1", - "@sentry/core": "10.65.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.9.0", - "@opentelemetry/core": "^1.30.1 || ^2.1.0", - "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0" - } - }, - "apps/web/node_modules/@sentry/react": { - "version": "10.65.0", - "resolved": "https://registry.npmjs.org/@sentry/react/-/react-10.65.0.tgz", - "integrity": "sha512-fvHxpuvid0wt9/1N3itcKDyKOjqmYHw3MBSt5Pki3Iz4CL2CmgQp9ZFv/CA7UhMnEvn2Gd+Qc2UKxujZWd8FLg==", - "license": "MIT", - "dependencies": { - "@sentry/browser": "10.65.0", - "@sentry/conventions": "^0.15.1", - "@sentry/core": "10.65.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "react": "^16.14.0 || 17.x || 18.x || 19.x" - } - }, - "apps/web/node_modules/@sentry/replay": { - "version": "10.65.0", - "resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-10.65.0.tgz", - "integrity": "sha512-aW988CcQBNArbOMzOFOziipHz6uQyXSa4i5CPWsu+nhVPTJHafosi5Lv9n6NM/icDX5e23VdnX6mZd8SyJuo8A==", - "license": "MIT", - "dependencies": { - "@sentry/browser-utils": "10.65.0", - "@sentry/core": "10.65.0" - }, - "engines": { - "node": ">=18" - } - }, - "apps/web/node_modules/@sentry/replay-canvas": { - "version": "10.65.0", - "resolved": "https://registry.npmjs.org/@sentry/replay-canvas/-/replay-canvas-10.65.0.tgz", - "integrity": "sha512-A7X3RVk1Gk+knK8Ip/2EjejckNCLgCfRZo6eGlsy6qyz904KBpYmys1a0o7QkzFRjhIndjHAfcVxwt6jSLJlrQ==", - "license": "MIT", - "dependencies": { - "@sentry/core": "10.65.0", - "@sentry/replay": "10.65.0" - }, - "engines": { - "node": ">=18" - } - }, - "apps/web/node_modules/@sentry/server-utils": { - "version": "10.65.0", - "resolved": "https://registry.npmjs.org/@sentry/server-utils/-/server-utils-10.65.0.tgz", - "integrity": "sha512-80toEFD6s+0Le7jrYB6pHWLF703WSg0WyavAWqrBGWG8JkREHgedAxzFYgoY5GlMI756qk6Ea7UzhJTHd2zAXA==", - "license": "MIT", - "dependencies": { - "@apm-js-collab/code-transformer": "^0.15.0", - "@apm-js-collab/code-transformer-bundler-plugins": "^0.5.0", - "@apm-js-collab/tracing-hooks": "^0.10.1", - "@sentry/conventions": "^0.15.1", - "@sentry/core": "10.65.0", - "magic-string": "~0.30.0" - }, - "engines": { - "node": ">=18" - } - }, - "apps/web/node_modules/@sentry/vercel-edge": { - "version": "10.65.0", - "resolved": "https://registry.npmjs.org/@sentry/vercel-edge/-/vercel-edge-10.65.0.tgz", - "integrity": "sha512-Z1sk2yBHrcsk/QMIzgMRTHitUN1zogzn5eQEc7umWmWwpP6zpDLMDxeeH2F1Cy2vzQFKa53PaWz7HXk4n617eg==", - "license": "MIT", - "dependencies": { - "@opentelemetry/api": "^1.9.1", - "@sentry/core": "10.65.0" - }, - "engines": { - "node": ">=18" - } - }, - "apps/web/node_modules/@sentry/vercel-edge/node_modules/@opentelemetry/api": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.1.tgz", - "integrity": "sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==", - "license": "Apache-2.0", - "engines": { - "node": ">=8.0.0" - } - }, - "apps/web/node_modules/@stripe/stripe-js": { - "version": "9.9.0", - "resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-9.9.0.tgz", - "integrity": "sha512-Vwqe6Q5cU4i82tPyAv2BpaW/fQSNdOSO4/J8EeDLPp5/oIZiMmdB+Hgh863zFH+rtoxpuWGvD1L7QPh8k1Rdvw==", - "license": "MIT", - "engines": { - "node": ">=12.16" - } - }, - "apps/web/node_modules/@tailwindcss/node": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.2.tgz", - "integrity": "sha512-yWP/sqEcBLaD8JuA6zNwxoYKr75qxTioYwlRwekj5Jr/I5GXnoJfjetH/psLUIv74cYTH2lBUEzBkinthoYcBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/remapping": "^2.3.5", - "enhanced-resolve": "5.21.6", - "jiti": "^2.7.0", - "lightningcss": "1.32.0", - "magic-string": "^0.30.21", - "source-map-js": "^1.2.1", - "tailwindcss": "4.3.2" - } - }, - "apps/web/node_modules/@tailwindcss/oxide": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.2.tgz", - "integrity": "sha512-z8ZgnzX8gdNoWLBLqBPoh/sjnxkwvf9ZuWjnO0l0yIzbLa5/9S+eC5QxGZKRobVHIC3/1BoMWjHblqWjcgFgag==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 20" - }, - "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.3.2", - "@tailwindcss/oxide-darwin-arm64": "4.3.2", - "@tailwindcss/oxide-darwin-x64": "4.3.2", - "@tailwindcss/oxide-freebsd-x64": "4.3.2", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.2", - "@tailwindcss/oxide-linux-arm64-gnu": "4.3.2", - "@tailwindcss/oxide-linux-arm64-musl": "4.3.2", - "@tailwindcss/oxide-linux-x64-gnu": "4.3.2", - "@tailwindcss/oxide-linux-x64-musl": "4.3.2", - "@tailwindcss/oxide-wasm32-wasi": "4.3.2", - "@tailwindcss/oxide-win32-arm64-msvc": "4.3.2", - "@tailwindcss/oxide-win32-x64-msvc": "4.3.2" - } - }, - "apps/web/node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.2.tgz", - "integrity": "sha512-WHxqIuHpvZ5VtdX6GTl1Ik/Vp2YuN42Et+0CdeaVd/frQ9jAvGmvR8vLT+jk3e8/Q3x8kECB9+R17pgpp2BulA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 20" - } - }, - "apps/web/node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.2.tgz", - "integrity": "sha512-GZypeUY/IDJW3877KeM+O67vbXr3MBnbtEL4aYhNErv/JWZhye2vGSWWG9tB6iiqR2MqRNkY8IOUy4NdSZV26w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 20" - } - }, - "apps/web/node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.2.tgz", - "integrity": "sha512-UIIzmefR6KO1sDU7MzRqAxC8iBpft/VhkGjTjnhoS6k7Z3rQ9wEgA1ODSiyH/tcSYssulNm4Ci3hOeK1jH7ccQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 20" - } - }, - "apps/web/node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.2.tgz", - "integrity": "sha512-GN+uAmcI6DNspnCDwtOAZrTz6oukJnp337qZvxqCGLd3BHBzJpO0ZbTLRvJNdztOeAmTzewewGIMPb0tk2R4WA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 20" - } - }, - "apps/web/node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.2.tgz", - "integrity": "sha512-4ABn7qSbdHRwTiDiuWNegCyb5+2FJ4vKIKc3DmKrvAFw7MU1Lm11dIkTPwUaFdTzc7IsOpDbqBrlh0x6y36U/w==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "apps/web/node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.2.tgz", - "integrity": "sha512-wDgEIGwoM8w8pufh9LVt1PahDgNdKXrLC2qfAnV3vAmococ9RWbxeAw4pxPttd/TsJfwjyLf90Dg1y9y8I6Emw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "apps/web/node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.2.tgz", - "integrity": "sha512-J5Nuk0uZQIiMTJj3LEx4sAA9tMFUoXQZFv1J6An+QGYe53HKRJuFDi0rpq/tuouCZeAbOBY3kQ6g8qeD4TUjtA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "apps/web/node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.2.tgz", - "integrity": "sha512-kqCZpSKOBEJO4mz7OqWoofBZeXTAwaVGPj0ErAj7CojmhKpWVWVOnrt9dE8odoIraZq4oj3ausM37kXi+Tow8w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "apps/web/node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.2.tgz", - "integrity": "sha512-cixpqbh2toJDmkuCRI68nXA8ZxNmdK9Y+9v5h3MC3ZQKy/0BO8AWzlkWyRM7JAFSGBlfig4YVTPsK6MVgqz1uw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "apps/web/node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.2.tgz", - "integrity": "sha512-4ec2Z/LOmRsAgU23CS4xeJfcJlmRg94A/XrbGRCF1gyU/zdDfRLYDVsS+ynSZCmGNxQ1jQriQOKMQeQxBA3Isw==", - "bundleDependencies": [ - "@napi-rs/wasm-runtime", - "@emnapi/core", - "@emnapi/runtime", - "@tybys/wasm-util", - "@emnapi/wasi-threads", - "tslib" - ], - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.11.1", - "@emnapi/runtime": "^1.11.1", - "@emnapi/wasi-threads": "^1.2.2", - "@napi-rs/wasm-runtime": "^1.1.4", - "@tybys/wasm-util": "^0.10.2", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "apps/web/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { - "version": "1.11.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.2", - "tslib": "^2.4.0" - } - }, - "apps/web/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "1.11.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "apps/web/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { - "version": "1.2.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "apps/web/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.4", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" - } - }, - "apps/web/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { - "version": "0.10.2", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "apps/web/node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { - "version": "2.8.1", - "dev": true, - "inBundle": true, - "license": "0BSD", - "optional": true - }, - "apps/web/node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.2.tgz", - "integrity": "sha512-Zyr/M0+XcYZu3bZrUytc7TXvrk0ftWfl8gN2MwekNDzhqhKRUucMPSeOzM0o0wH5AWOU49BsKRrfKxI2atCPMQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 20" - } - }, - "apps/web/node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.2.tgz", - "integrity": "sha512-QI9BO7KlNZsp2GuO0jwAAj5jCDABOKXRkCk2XuKTSaNEFSdfzqswYVTtCHBNKHLsqyjFyFkqlDiwkNbTYSssMQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], + }, "engines": { - "node": ">= 20" - } - }, - "apps/web/node_modules/@tailwindcss/postcss": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.3.2.tgz", - "integrity": "sha512-rjVWYCa7Ngbi5AarT6k8TkxUG3Wl1QKzHdIZVsjZSzf36Jmo2IKZt/NHRAwly8oDkbBOH0YTu+CHuf9jPxMc+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "@tailwindcss/node": "4.3.2", - "@tailwindcss/oxide": "4.3.2", - "postcss": "^8.5.15", - "tailwindcss": "4.3.2" + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" } }, - "apps/web/node_modules/@types/node": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-26.0.0.tgz", - "integrity": "sha512-vf2YFi1iY9lHGwNJMs01biZFbKJkrZR1T6/MlzjhJLPdntOHLhTrDSnSVcdtvjihi4VQNlrFRIxLsDBlQpAipA==", - "dev": true, - "license": "MIT", + "apps/web/node_modules/@opentelemetry/sdk-logs": { + "version": "0.220.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.220.0.tgz", + "integrity": "sha512-WywcTkQtv2iNmt+6y5Kcd4rzvx9bLVsBa2Nwcmg01IUaBTkTow3W4d9KE5vNBpEDtb9tp21WcRBY/lANRrApYA==", + "license": "Apache-2.0", "dependencies": { - "undici-types": "~8.3.0" + "@opentelemetry/api-logs": "0.220.0", + "@opentelemetry/core": "2.9.0", + "@opentelemetry/resources": "2.9.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, - "apps/web/node_modules/autoprefixer": { - "version": "10.5.2", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.2.tgz", - "integrity": "sha512-rD5t5DwOjJdmSORcTq64j8MawTC+tbQ+HHqjR4NDumamy/ambn1UJrlKL+KdwujWxMkFjPM3pPHOEA9tl4767Q==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", + "apps/web/node_modules/@opentelemetry/sdk-metrics": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.9.0.tgz", + "integrity": "sha512-Xx8RGS4H5XEBl01WuCreMIpiah9cCXMbSkeuIePPdD2cUpq/vUzYmj8E/MK1OsbOc93FuAD4jfn2WOacKwLn7Q==", + "license": "Apache-2.0", "dependencies": { - "browserslist": "^4.28.4", - "caniuse-lite": "^1.0.30001799", - "fraction.js": "^5.3.4", - "picocolors": "^1.1.1", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" + "@opentelemetry/core": "2.9.0", + "@opentelemetry/resources": "2.9.0" }, "engines": { - "node": "^10 || ^12 || >=14" + "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "postcss": "^8.1.0" + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "apps/web/node_modules/@opentelemetry/semantic-conventions": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.43.0.tgz", + "integrity": "sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" } }, - "apps/web/node_modules/browserslist": { - "version": "4.28.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.4.tgz", - "integrity": "sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==", + "apps/web/node_modules/@types/node": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.0.0.tgz", + "integrity": "sha512-vf2YFi1iY9lHGwNJMs01biZFbKJkrZR1T6/MlzjhJLPdntOHLhTrDSnSVcdtvjihi4VQNlrFRIxLsDBlQpAipA==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.10.38", - "caniuse-lite": "^1.0.30001799", - "electron-to-chromium": "^1.5.376", - "node-releases": "^2.0.48", - "update-browserslist-db": "^1.2.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + "undici-types": "~8.3.0" } }, "apps/web/node_modules/eslint-config-next": { @@ -975,13 +282,6 @@ "node": "^10 || ^12 || >=14" } }, - "apps/web/node_modules/tailwindcss": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.2.tgz", - "integrity": "sha512-WtctNNSH8A9jlMIqxzuYumOHU5uGZyRv0Q5svQl+oEPy5w84YpBxdb7MdqyiSPQge5jTJ6zFQLq0PFygdccSBA==", - "dev": true, - "license": "MIT" - }, "apps/web/node_modules/zod": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", @@ -1093,9 +393,9 @@ } }, "node_modules/@apm-js-collab/code-transformer": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@apm-js-collab/code-transformer/-/code-transformer-0.15.0.tgz", - "integrity": "sha512-XmXYVs8CzJ1Aj79noVbn2weUO/XWtRyURpGqx7aU7DOXlUQhR0WKOQNF0okh7PCeY37vxf7kU3v57OAkEPm3ww==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@apm-js-collab/code-transformer/-/code-transformer-0.18.0.tgz", + "integrity": "sha512-aN3Oq8r1J3gPJtCwErP664gM0+HhM1I1lujPr9TMTCcEl/joQQbpGpeMdts9B1+W2wHMsvioDMv5F4PvMWE6gw==", "license": "Apache-2.0", "dependencies": { "@types/estree": "^1.0.8", @@ -1110,12 +410,12 @@ } }, "node_modules/@apm-js-collab/code-transformer-bundler-plugins": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@apm-js-collab/code-transformer-bundler-plugins/-/code-transformer-bundler-plugins-0.5.0.tgz", - "integrity": "sha512-YxLBY5nGlurL7QeJLq6e5g0ouBpAp0pwgyA/5rHXEXwhiPLn9ZHbT+Y2LlP90GT872cSocfjWRYu/fnpuBudNQ==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@apm-js-collab/code-transformer-bundler-plugins/-/code-transformer-bundler-plugins-0.7.2.tgz", + "integrity": "sha512-GvpKWDmzBFzbtVElU+tEDMDYyMY8SnMH4NNwwlUQioNHZvE6sSuHEqNkfU3iYhWL+/XO5Ej+MzhVJoqM10NYCQ==", "license": "MIT", "dependencies": { - "@apm-js-collab/code-transformer": "^0.15.0", + "@apm-js-collab/code-transformer": "^0.18.0", "es-module-lexer": "^2.1.0", "magic-string": "^0.30.21", "module-details-from-path": "^1.0.4" @@ -1125,12 +425,12 @@ } }, "node_modules/@apm-js-collab/tracing-hooks": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@apm-js-collab/tracing-hooks/-/tracing-hooks-0.10.1.tgz", - "integrity": "sha512-w2OWXR7FWrKqSziuE9+QclaZrStxO/8+OwbXM635s/zs0Eez1Qo3ivSPdB2WsaPY/iznKTytONPx/PitD7IXcA==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@apm-js-collab/tracing-hooks/-/tracing-hooks-0.13.0.tgz", + "integrity": "sha512-mTvWz9rnQwx1U3h0XPTHaX7bgfkpipLLTQyjlC2cdhQpQEuoLT0AGzoydeoq2NxfEVv6fWOOETcSbb2nptleyw==", "license": "Apache-2.0", "dependencies": { - "@apm-js-collab/code-transformer": "^0.15.0", + "@apm-js-collab/code-transformer": "^0.18.0", "debug": "^4.4.1", "module-details-from-path": "^1.0.4" } @@ -3919,6 +3219,36 @@ "node": ">= 18" } }, + "node_modules/@sentry/browser": { + "version": "10.67.0", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-10.67.0.tgz", + "integrity": "sha512-/ZhsAvte4rYhg0A0RtSFFgAgXhyMOfQIeOAfMfptN+X6IVSYOfkA9jtrP+Ej4+6vlaUFWRir1HweF56y63dEEA==", + "license": "MIT", + "dependencies": { + "@sentry/browser-utils": "10.67.0", + "@sentry/conventions": "^0.16.0", + "@sentry/core": "10.67.0", + "@sentry/feedback": "10.67.0", + "@sentry/replay": "10.67.0", + "@sentry/replay-canvas": "10.67.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry/browser-utils": { + "version": "10.67.0", + "resolved": "https://registry.npmjs.org/@sentry/browser-utils/-/browser-utils-10.67.0.tgz", + "integrity": "sha512-HUzaf0xAnPAB+OHBkD7N1Py+CTbD5InHulQ/pdhX4JctWtxuwD8odMD1LzdPnW8J6gVHlDVvcVBR8mXMZYSLSw==", + "license": "MIT", + "dependencies": { + "@sentry/conventions": "^0.16.0", + "@sentry/core": "10.67.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@sentry/bundler-plugin-core": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/@sentry/bundler-plugin-core/-/bundler-plugin-core-5.3.0.tgz", @@ -4095,23 +3425,232 @@ "win32" ], "engines": { - "node": ">=10" + "node": ">=10" + } + }, + "node_modules/@sentry/cli-win32-x64": { + "version": "2.58.6", + "resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-2.58.6.tgz", + "integrity": "sha512-R35WJ17oF4D2eqI1DR2sQQqr0fjRTt5xoP16WrTu91XM2lndRMFsnjh+/GttbxapLCBNlrjzia99MJ0PZHZpgA==", + "cpu": [ + "x64" + ], + "license": "FSL-1.1-MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@sentry/conventions": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@sentry/conventions/-/conventions-0.16.0.tgz", + "integrity": "sha512-fO9PLmHdVURcSPUpWCItWAtgKiMwGdJHbovoSEyLplX5sxs2ugvI4CBPTrkkgqhObnZOD0CnWBKDzSVQYBKEyQ==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/@sentry/core": { + "version": "10.67.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.67.0.tgz", + "integrity": "sha512-b6U3pJ8AUvN9aouq0vl+VZI8KT8RslBsfGMFuNwRr313zOmdmFJBZqTiUw9VGgJ2jGKxLO9alm9rlxBfX4hf+w==", + "license": "MIT", + "dependencies": { + "@sentry/conventions": "^0.16.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry/feedback": { + "version": "10.67.0", + "resolved": "https://registry.npmjs.org/@sentry/feedback/-/feedback-10.67.0.tgz", + "integrity": "sha512-I4ML2/SF3enwikb6ZSoRiqolQrx0zSzTSnUgwCmugICF/jpHW0th1pCray9R+t1Zzibw/Dpj4t/DNXaSDRa2MA==", + "license": "MIT", + "dependencies": { + "@sentry/core": "10.67.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry/nextjs": { + "version": "10.67.0", + "resolved": "https://registry.npmjs.org/@sentry/nextjs/-/nextjs-10.67.0.tgz", + "integrity": "sha512-errNYlnhQBTDGzgAH0kkneD3/sK+VqW1qBixV8S/Gg4ygj6+QjigOU9idJA980H7mjyxrM1iT74qtcFt24nFow==", + "license": "MIT", + "dependencies": { + "@opentelemetry/api": "^1.9.1", + "@rollup/plugin-commonjs": "28.0.1", + "@sentry/browser-utils": "10.67.0", + "@sentry/bundler-plugin-core": "^5.3.0", + "@sentry/conventions": "^0.16.0", + "@sentry/core": "10.67.0", + "@sentry/node": "10.67.0", + "@sentry/opentelemetry": "10.67.0", + "@sentry/react": "10.67.0", + "@sentry/server-utils": "10.67.0", + "@sentry/vercel-edge": "10.67.0", + "@sentry/webpack-plugin": "^5.3.0", + "rollup": "^4.60.3", + "stacktrace-parser": "^0.1.11" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "next": "^13.2.0 || ^14.0 || ^15.0.0-rc.0 || ^16.0.0-0" + } + }, + "node_modules/@sentry/node": { + "version": "10.67.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-10.67.0.tgz", + "integrity": "sha512-SFKpZGqOCEFSmP93NdDP6ikZp4NS7A/JR8+2ofK3jF6Y9Vyox7pX0pxdOnPLpFcPtMybMahfWqSAWJvsFs4RmA==", + "license": "MIT", + "dependencies": { + "@opentelemetry/api": "^1.9.1", + "@opentelemetry/instrumentation": "^0.220.0", + "@opentelemetry/sdk-trace-base": "^2.9.0", + "@sentry/conventions": "^0.16.0", + "@sentry/core": "10.67.0", + "@sentry/node-core": "10.67.0", + "@sentry/opentelemetry": "10.67.0", + "@sentry/server-utils": "10.67.0", + "import-in-the-middle": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry/node-core": { + "version": "10.67.0", + "resolved": "https://registry.npmjs.org/@sentry/node-core/-/node-core-10.67.0.tgz", + "integrity": "sha512-dBHHRwZyan1pOnFJ+sNBvR8TkXbZAfZU/jpxmALS3JZ2/8AGR7cQKL+b7SleKuJ7iUDZyklN3Nqi0i5JkcA+HA==", + "license": "MIT", + "dependencies": { + "@sentry/conventions": "^0.16.0", + "@sentry/core": "10.67.0", + "@sentry/opentelemetry": "10.67.0", + "import-in-the-middle": "^3.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/core": "^1.30.1 || ^2.1.0", + "@opentelemetry/exporter-trace-otlp-http": ">=0.57.0 <1", + "@opentelemetry/instrumentation": ">=0.57.1 <1", + "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@opentelemetry/core": { + "optional": true + }, + "@opentelemetry/exporter-trace-otlp-http": { + "optional": true + }, + "@opentelemetry/instrumentation": { + "optional": true + }, + "@opentelemetry/sdk-trace-base": { + "optional": true + } + } + }, + "node_modules/@sentry/opentelemetry": { + "version": "10.67.0", + "resolved": "https://registry.npmjs.org/@sentry/opentelemetry/-/opentelemetry-10.67.0.tgz", + "integrity": "sha512-oLTOrAK1rOqmYRktOJZwz37B1seXPx1W2FTMVtzTVNjMFA/LZwGzePeZzhUOgzZgfLHixMd/ceWtGqoxAndcjQ==", + "license": "MIT", + "dependencies": { + "@sentry/conventions": "^0.16.0", + "@sentry/core": "10.67.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/core": "^1.30.1 || ^2.1.0", + "@opentelemetry/sdk-trace-base": "^1.30.1 || ^2.1.0" + } + }, + "node_modules/@sentry/react": { + "version": "10.67.0", + "resolved": "https://registry.npmjs.org/@sentry/react/-/react-10.67.0.tgz", + "integrity": "sha512-fS0DplcP9eMxBIRurPC/uxa4NrFK+l9ZsnvQo7wZvNutc7DpTAH0hgFt6laVNCe57s1pFo+OZuKsYBA6JDvH4Q==", + "license": "MIT", + "dependencies": { + "@sentry/browser": "10.67.0", + "@sentry/conventions": "^0.16.0", + "@sentry/core": "10.67.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^16.14.0 || 17.x || 18.x || 19.x" + } + }, + "node_modules/@sentry/replay": { + "version": "10.67.0", + "resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-10.67.0.tgz", + "integrity": "sha512-nkEUgPCR82EcyJkCf3XCE9H0R5KisCqyCAaSGxe7NpAoQbvASHx4MUNgXVAn+D0M494gvPZh6lFH7JgzqTcSqQ==", + "license": "MIT", + "dependencies": { + "@sentry/browser-utils": "10.67.0", + "@sentry/core": "10.67.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry/replay-canvas": { + "version": "10.67.0", + "resolved": "https://registry.npmjs.org/@sentry/replay-canvas/-/replay-canvas-10.67.0.tgz", + "integrity": "sha512-neNA4T6MFtZzMdKYetiR+LZd9BNSd0q2szMn0wk+A15PqHE/IN7a34V6JZc9rCtmzB0wldh0eWGOBb49MSNKjA==", + "license": "MIT", + "dependencies": { + "@sentry/core": "10.67.0", + "@sentry/replay": "10.67.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@sentry/server-utils": { + "version": "10.67.0", + "resolved": "https://registry.npmjs.org/@sentry/server-utils/-/server-utils-10.67.0.tgz", + "integrity": "sha512-GQ9t+RSTx5s3b/aZrLFuL4nrwPLMah5NiZk5cjxJmgmOSgm3nMdO8gdqCedDch5B13F3YsxtaQYjSJnzLz8M5A==", + "license": "MIT", + "dependencies": { + "@apm-js-collab/code-transformer-bundler-plugins": "^0.7.1", + "@apm-js-collab/tracing-hooks": "^0.13.0", + "@sentry/conventions": "^0.16.0", + "@sentry/core": "10.67.0" + }, + "engines": { + "node": ">=18" } }, - "node_modules/@sentry/cli-win32-x64": { - "version": "2.58.6", - "resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-2.58.6.tgz", - "integrity": "sha512-R35WJ17oF4D2eqI1DR2sQQqr0fjRTt5xoP16WrTu91XM2lndRMFsnjh+/GttbxapLCBNlrjzia99MJ0PZHZpgA==", - "cpu": [ - "x64" - ], - "license": "FSL-1.1-MIT", - "optional": true, - "os": [ - "win32" - ], + "node_modules/@sentry/vercel-edge": { + "version": "10.67.0", + "resolved": "https://registry.npmjs.org/@sentry/vercel-edge/-/vercel-edge-10.67.0.tgz", + "integrity": "sha512-tez7Kwz24PE4BCjzc2kcRc0gF4DWxYlSn61we9nRKSBh/wwUsQEznJfQ7tYxWbc67kxayQiyS5Vl9zzv5urTGw==", + "license": "MIT", + "dependencies": { + "@opentelemetry/api": "^1.9.1", + "@sentry/core": "10.67.0" + }, "engines": { - "node": ">=10" + "node": ">=18" } }, "node_modules/@sentry/webpack-plugin": { @@ -4135,6 +3674,15 @@ "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "license": "MIT" }, + "node_modules/@stripe/stripe-js": { + "version": "9.12.0", + "resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-9.12.0.tgz", + "integrity": "sha512-wCUYZNYo7E/6B3Rv2i/g/Rmj2mTiOX6HEwWYUWUuNUKwEhzTo/SXuQ1IoNo3mknWIHUQyqdakv1Nl2SiYPrCrw==", + "license": "MIT", + "engines": { + "node": ">=12.16" + } + }, "node_modules/@supabase/auth-js": { "version": "2.110.7", "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.110.7.tgz", @@ -4228,6 +3776,289 @@ "tslib": "^2.8.0" } }, + "node_modules/@tailwindcss/node": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz", + "integrity": "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.24.1", + "jiti": "^2.7.0", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.3" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.3.tgz", + "integrity": "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-x64": "4.3.3", + "@tailwindcss/oxide-freebsd-x64": "4.3.3", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-x64-musl": "4.3.3", + "@tailwindcss/oxide-wasm32-wasi": "4.3.3", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz", + "integrity": "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz", + "integrity": "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz", + "integrity": "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz", + "integrity": "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz", + "integrity": "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz", + "integrity": "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz", + "integrity": "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz", + "integrity": "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz", + "integrity": "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz", + "integrity": "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.11.1", + "@emnapi/runtime": "^1.11.1", + "@emnapi/wasi-threads": "^1.2.2", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz", + "integrity": "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz", + "integrity": "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.3.3.tgz", + "integrity": "sha512-JTSZZGQi1AyKirbLN3azmjVzef92tcX7h+iSqPdaeStyFpGpDlKvvpxeOE8njhbUanbRwr3z8DyzhICWnMtQeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.3.3", + "@tailwindcss/oxide": "4.3.3", + "postcss": "^8.5.16", + "tailwindcss": "4.3.3" + } + }, "node_modules/@turbo/darwin-64": { "version": "2.10.5", "resolved": "https://registry.npmjs.org/@turbo/darwin-64/-/darwin-64-2.10.5.tgz", @@ -5509,6 +5340,43 @@ "node": ">= 0.4" } }, + "node_modules/autoprefixer": { + "version": "10.5.4", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.4.tgz", + "integrity": "sha512-MaU0U/za7N3r6brxD4YB/l4NSrFzLPlANv6wEuQVaIPlD3L4W9rFcQPbL/EilY9BHhHvhfcz3gInDLrEtWT4EA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.6", + "caniuse-lite": "^1.0.30001806", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -6278,9 +6146,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.21.6", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.6.tgz", - "integrity": "sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==", + "version": "5.24.3", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.3.tgz", + "integrity": "sha512-PwKooW9JUzh5chmYfHM3IQl5OkK2u2Nm011MgeZrss3JmFraUx/fqrf78kk8GUMYoibx/14MdwTl/1WKkG7TpQ==", "dev": true, "license": "MIT", "dependencies": { @@ -11539,6 +11407,13 @@ "url": "https://github.com/sponsors/dcastil" } }, + "node_modules/tailwindcss": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz", + "integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==", + "dev": true, + "license": "MIT" + }, "node_modules/tapable": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", diff --git a/scripts/ci/agent_completion_enforcement.py b/scripts/ci/agent_completion_enforcement.py index f746c6f20..3b21ee20c 100644 --- a/scripts/ci/agent_completion_enforcement.py +++ b/scripts/ci/agent_completion_enforcement.py @@ -21,6 +21,37 @@ def verdict(reason: str, **details: Any) -> dict[str, Any]: return {"conclusion": "failure", "reason": reason, "details": details} +def _policy_allowlists(policy: Any) -> tuple[Any, Any, Any] | None: + """Return the three allowlists when the policy shape is valid, else None.""" + if not isinstance(policy, dict) or policy.get("custom_role_policy") != "fail_closed": + return None + apps = policy.get("trusted_check_app_slugs") + labels = policy.get("trusted_label_actors") + exemptions = policy.get("trusted_human_exemption_actors") + if not all(isinstance(value, list) and all(isinstance(item, str) and item for item in value) + for value in (apps, labels, exemptions)): + return None + return apps, labels, exemptions + + +def missing_publication(policy: Any) -> dict[str, Any]: + """Verdict for a head with no trusted publication. + + Only a well-formed, fail-closed policy whose three allowlists are all empty + proves the trusted App is intentionally unprovisioned; that state publishes + a neutral, advisory verdict instead of failing every pull request. Any + provisioned or malformed policy still fails closed. + """ + allowlists = _policy_allowlists(policy) + if allowlists is not None and not any(allowlists): + return { + "conclusion": "neutral", + "reason": "trust_policy_unprovisioned_no_publication", + "details": {}, + } + return verdict("missing_trusted_publication") + + def verify(payload: Any, policy: Any, head_sha: str, pull_number: int) -> dict[str, Any]: if not isinstance(payload, dict) or not isinstance(policy, dict): return verdict("invalid_payload") @@ -81,9 +112,14 @@ def main() -> int: if len(sys.argv) != 5: raise SystemExit("usage: verifier REPORT POLICY HEAD_SHA PULL_NUMBER") report, policy, head, pull = sys.argv[1:] - result = verify(json.loads(Path(report).read_text()), json.loads(Path(policy).read_text()), head, int(pull)) + policy_data = json.loads(Path(policy).read_text()) + report_path = Path(report) + if not report_path.exists() or not report_path.read_text().strip(): + result = missing_publication(policy_data) + else: + result = verify(json.loads(report_path.read_text()), policy_data, head, int(pull)) print(json.dumps(result, sort_keys=True)) - return 0 if result["conclusion"] == "success" else 1 + return 0 if result["conclusion"] in {"success", "neutral"} else 1 if __name__ == "__main__": diff --git a/tests/unit/test_agent_completion_enforcement.py b/tests/unit/test_agent_completion_enforcement.py index 722a5cdd8..ab362db66 100644 --- a/tests/unit/test_agent_completion_enforcement.py +++ b/tests/unit/test_agent_completion_enforcement.py @@ -1,6 +1,6 @@ import unittest -from scripts.ci.agent_completion_enforcement import verify +from scripts.ci.agent_completion_enforcement import missing_publication, verify HEAD = "a" * 40 @@ -30,3 +30,23 @@ def test_error_cannot_be_erased(self): def test_untrusted_label_blocks(self): body = report(); body["label_authorization"]["applied_by"] = "agent" self.assertEqual(verify(body, POLICY, HEAD, 9)["reason"], "untrusted_label_authorization") + + def test_missing_publication_with_unprovisioned_policy_is_neutral(self): + policy = dict(POLICY, trusted_check_app_slugs=[], trusted_label_actors=[], trusted_human_exemption_actors=[]) + result = missing_publication(policy) + self.assertEqual(result["conclusion"], "neutral") + self.assertEqual(result["reason"], "trust_policy_unprovisioned_no_publication") + + def test_missing_publication_with_provisioned_policy_blocks(self): + self.assertEqual(missing_publication(POLICY)["reason"], "missing_trusted_publication") + + def test_missing_publication_with_partially_provisioned_policy_blocks(self): + policy = dict(POLICY, trusted_check_app_slugs=[], trusted_label_actors=[]) + self.assertEqual(missing_publication(policy)["reason"], "missing_trusted_publication") + + def test_missing_publication_with_malformed_policy_blocks(self): + for policy in (None, [], {}, dict(POLICY, custom_role_policy="open", trusted_check_app_slugs=[], trusted_label_actors=[], trusted_human_exemption_actors=[]), dict(POLICY, trusted_check_app_slugs=None, trusted_label_actors=[], trusted_human_exemption_actors=[])): + with self.subTest(policy=policy): + result = missing_publication(policy) + self.assertEqual(result["conclusion"], "failure") + self.assertEqual(result["reason"], "missing_trusted_publication")